394x PCI cards, 3985 PCI card, and several versions of the Adaptec
built-in SCSI controllers on various PC motherboards. Information on
the configuration options for this controller can be found by checking
- the help file for each of the available configuration options.
-
-Enable tagged command queueing
-CONFIG_AIC7XXX_TAGGED_QUEUEING
- Tagged command queueing is used to allow acceptable devices to have more
- than one SCSI command active on the SCSI bus at the same time. Regardless
- of this option setting, only devices that report they are capable of
- tagged queueing will use this support. This option is highly recommended
- if you use any SCSI hard drives on your aic7xxx SCSI controller for
- performance reasons. Default: Y
+ the README.aic7xxx file, usually in /usr/src/linux/drivers/scsi.
Override driver defaults for commands per LUN
CONFIG_OVERRIDE_CMDS
active at one time. This option only effects tagged queueing capable
devices. The driver uses a "failsafe" value of 8 by default. This is
much lower than many devices can handle, but left in place for safety sake.
+ NOTE: This does not actually enabled tagged queueing on any particular
+ device. The driver has changed in this respect. Please see the file
+ README.aic7xxx in /usr/src/linux/drivers/scsi for more information on how
+ to get particular devices to use tagged command queueing.
Default: N
Maximum number of commands per LUN
per lun reduced, but is a waste of memory if all of your devices end
up reducing this number down to a more reasonable figure. Default: 24
-Enable SCB paging
-CONFIG_AIC7XXX_PAGE_ENABLE
- This option allows the driver to issue more commands to the controller
- than it has physical space to store. Since some aic7xxx chipsets can only
- store 3 commands, and the majority can only store 16, not enabling this
- capability can effectively negate any performance increase you might get
- from enabling Tagged Queueing. Default: Y
-
Collect statistics to report in /proc
CONFIG_AIC7XXX_PROC_STATS
This option tells the driver to keep track of how many commands have been
ready for the next command, but most hard drives and CD-ROM devices are
ready in only a few seconds. This option has a maximum upper limit of
20 seconds to avoid bad interactions between the aic7xxx driver and the
- rest of the linux kernel. The default value is a "failsafe" value that
- should work with just about any device. Default: 15
+ rest of the linux kernel. The default value has been reduced. If this
+ doesn't work with your hardware, try increasing this value. Default: 5
BusLogic SCSI support
CONFIG_SCSI_BUSLOGIC
8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
P: Paul Gortmaker
-M gpg109@rsphy1.anu.edu.au
+M: gpg109@rsphy1.anu.edu.au
L: linux-net@vger.rutgers.edu
S: Maintained
static inline void
mul64 (const unsigned long a, const unsigned long b, unsigned long c[2])
{
- asm ("mulq %2,%3,%0\n\t"
- "umulh %2,%3,%1"
- : "r="(c[0]), "r="(c[1]) : "r"(a), "r"(b));
+ c[0] = a * b;
+ asm ("umulh %1,%2,%0" : "=r"(c[1]) : "r"(a), "r"(b));
}
{
unsigned long res, sticky;
- if (!a->f[0] && !a->f[1]) {
+ if (!a->e && !a->f[0] && !a->f[1]) {
*b = (unsigned long) a->s << 63; /* return +/-0 */
return 0;
}
{
unsigned long res, sticky;
- if (!a->f[0] && !a->f[1]) {
+ if (!a->e && !a->f[0] && !a->f[1]) {
*b = (unsigned long) a->s << 63; /* return +/-0 */
return 0;
}
a->e = -0x3ff;
}
}
- if (a->e > 0x3ff) {
+ if (a->e >= 0x3ff) {
res = FPCR_OVF | FPCR_INE;
if (f & IEEE_TRAP_ENABLE_OVF) {
a->e -= 0x600; /* scale down result by 2^alpha */
return 0;
}
op_c.s = op_a.s ^ op_b.s;
- op_c.e = op_a.e + op_b.e;
+ op_c.e = op_a.e + op_b.e - 55;
mul64(op_a.f[0], op_b.f[0], op_c.f);
- normalize(&op_c);
- op_c.e -= 55; /* drop the 55 original bits. */
-
return round_s_ieee(f, &op_c, c);
}
return 0;
}
op_c.s = op_a.s ^ op_b.s;
- op_c.e = op_a.e + op_b.e;
+ op_c.e = op_a.e + op_b.e - 55;
mul64(op_a.f[0], op_b.f[0], op_c.f);
- normalize(&op_c);
- op_c.e -= 55; /* drop the 55 original bits. */
-
return round_t_ieee(f, &op_c, c);
}
}
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
if (p->ldt)
- set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512);
+ set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, LDT_ENTRIES);
else
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);
p->tss.bitmap = offsetof(struct thread_struct,io_bitmap);
int sig = irqnumber >> 8;
int irq = irqnumber & 255;
handle_irq_zombies();
- if (!suser()) return -EPERM;
+ if (!suser() || securelevel > 0) return -EPERM;
if (!((1 << sig) & ALLOWED_SIGS)) return -EPERM;
if ( (irq<3) || (irq>15) ) return -EPERM;
if (vm86_irqs[irq].tsk) return -EPERM;
* d4 - machine type
*/
+#include <linux/config.h>
#include <linux/autoconf.h>
#include <linux/linkage.h>
#include <asm/bootinfo.h>
* acknowledge media change on removable drives
* add work-around for BMI drives
* remove "LBA" from boot messages
+ * Version 5.53.1 add UDMA "CRC retry" support
*
* Some additional driver compile-time options are in ide.h
*
unsigned long chs_sects = id->cyls * id->heads * id->sectors;
unsigned long _10_percent = chs_sects / 10;
+ /* very large drives (8GB+) may lie about the number of cylinders */
+ if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) {
+ id->cyls = lba_sects / (16 * 63); /* correct cyls */
+ return 1; /* lba_capacity is our only option */
+ }
/* perform a rough sanity check on lba_sects: within 10% is "okay" */
if ((lba_sects - chs_sects) < _10_percent)
return 1; /* lba_capacity is good */
/* Determine capacity, and use LBA if the drive properly supports it */
if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
if (id->lba_capacity >= capacity) {
+ drive->cyl = id->lba_capacity / (drive->head * drive->sect);
capacity = id->lba_capacity;
drive->select.b.lba = 1;
}
} else {
if (drive->media == ide_disk && (stat & ERR_STAT)) {
/* err has different meaning on cdrom and tape */
- if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
+ if (err == ABRT_ERR) {
+ if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
+ return; /* some newer drives don't support WIN_SPECIFY */
+ } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
+ ; /* UDMA crc error -- just retry the operation */
+ else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
rq->errors = ERROR_MAX;
else if (err & TRK0_ERR) /* help it find track zero */
rq->errors |= ERROR_RECAL;
- else if (err & MC_ERR)
- drive->special.b.mc = 1;
}
if ((stat & DRQ_STAT) && rq->cmd != WRITE)
try_to_flush_leftover_data(drive);
{
byte args[4], *argbuf = args;
int argsize = 4;
- if (!suser()) return -EACCES;
+ if (!suser() || securelevel > 0) return -EACCES;
if (NULL == (void *) arg) {
err = ide_do_drive_cmd(drive, &rq, ide_wait);
} else if (!(err = verify_area(VERIFY_READ,(void *)arg, 4))) {
drive->head = id->heads;
drive->sect = id->sectors;
}
+
+ /* calculate drive capacity, and select LBA if possible */
+ (void) current_capacity (drive);
+
/* Correct the number of cyls if the bios value is too small */
if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
if (drive->cyl > drive->bios_cyl)
drive->bios_cyl = drive->cyl;
}
- (void) current_capacity (drive); /* initialize LBA selection */
-
if (!strncmp(id->model, "BMI ", 4) &&
strstr(id->model, " ENHANCED IDE ") &&
drive->select.b.lba)
drive->special.b.set_multmode = 1;
}
if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
- if (!(HWIF(drive)->dmaproc(ide_dma_check, drive)))
- printk(", DMA");
+ if (!(HWIF(drive)->dmaproc(ide_dma_check, drive))) {
+ if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7))
+ printk(", UDMA");
+ else
+ printk(", DMA");
+ }
}
printk("\n");
}
static int config_drive_for_dma (ide_drive_t *drive)
{
const char **list;
-
struct hd_driveid *id = drive->id;
+
if (id && (id->capability & 1)) {
- /* Enable DMA on any drive that supports mword2 DMA */
- if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) {
- drive->using_dma = 1;
- return 0; /* DMA enabled */
- }
+ /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+ if (id->field_valid & 4) /* UltraDMA */
+ if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+ drive->using_dma = 1;
+ return 0; /* dma enabled */
+ }
+ /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+ if (id->field_valid & 2) /* regular DMA */
+ if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) {
+ drive->using_dma = 1;
+ return 0; /* dma enabled */
+ }
/* Consult the list of known "good" drives */
list = good_dma_drives;
while (*list) {
switch ((u_long) address) {
case 0x00000:
case 0xC8000: break; /*initial: 0x320 */
- case 0xCA000: if (xd[3]<=0) xd_iobase = 0x324;
+ case 0xCA000: xd_iobase = 0x324;
break;
case 0xD0000: /*5150CX*/
case 0xD8000: break; /*5150CX & 5150XL*/
__set_origin(__real_origin);
}
-void scrup(int currcons, unsigned int t, unsigned int b)
+static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr)
{
int hardscroll = hardscroll_enabled;
- if (b > video_num_lines || t >= b)
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > video_num_lines || t >= b || nr < 1)
return;
- if (t || b != video_num_lines)
+ if (t || b != video_num_lines || nr > 1)
hardscroll = 0;
if (hardscroll) {
origin += video_size_row;
set_origin(currcons);
} else {
unsigned short * d = (unsigned short *) (origin+video_size_row*t);
- unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1));
- unsigned int count = (b-t-1) * video_num_columns;
+ unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr));
- while (count) {
- count--;
- scr_writew(scr_readw(s++), d++);
- }
- count = video_num_columns;
- while (count) {
- count--;
- scr_writew(video_erase_char, d++);
- }
+ memcpyw(d, s, (b-t-nr) * video_size_row);
+ memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr);
}
}
-void
-scrdown(int currcons, unsigned int t, unsigned int b)
+static void
+scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr)
{
- unsigned short *d, *s;
+ unsigned short *s;
unsigned int count;
+ unsigned int step;
- if (b > video_num_lines || t >= b)
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > video_num_lines || t >= b || nr < 1)
return;
- d = (unsigned short *) (origin+video_size_row*b);
- s = (unsigned short *) (origin+video_size_row*(b-1));
- count = (b-t-1)*video_num_columns;
- while (count) {
- count--;
- scr_writew(scr_readw(--s), --d);
+ s = (unsigned short *) (origin+video_size_row*(b-nr-1));
+ step = video_num_columns * nr;
+ count = b - t - nr;
+ while (count--) {
+ memcpyw(s + step, s, video_size_row);
+ s -= video_num_columns;
}
- count = video_num_columns;
- while (count) {
- count--;
- scr_writew(video_erase_char, --d);
+ while (nr--) {
+ s += video_num_columns;
+ memsetw(s, video_erase_char, video_size_row);
}
has_scrolled = 1;
}
* if below scrolling region
*/
if (y+1 == bottom)
- scrup(currcons,top,bottom);
+ scrup(currcons,top,bottom, 1);
else if (y < video_num_lines-1) {
y++;
pos += video_size_row;
* if above scrolling region
*/
if (y == top)
- scrdown(currcons,top,bottom);
+ scrdown(currcons,top,bottom, 1);
else if (y > 0) {
y--;
pos -= video_size_row;
need_wrap = 0;
}
-static void insert_line(int currcons)
+static void insert_line(int currcons, unsigned int nr)
{
- scrdown(currcons,y,bottom);
+ scrdown(currcons, y, bottom, nr);
need_wrap = 0;
}
need_wrap = 0;
}
-static void delete_line(int currcons)
+static void delete_line(int currcons, unsigned int nr)
{
- scrup(currcons,y,bottom);
+ scrup(currcons, y, bottom, nr);
need_wrap = 0;
}
nr = video_num_lines;
else if (!nr)
nr = 1;
- while (nr--)
- insert_line(currcons);
+ insert_line(currcons, nr);
}
static void csi_P(int currcons, unsigned int nr)
nr = video_num_lines;
else if (!nr)
nr=1;
- while (nr--)
- delete_line(currcons);
+ delete_line(currcons, nr);
}
static void save_cur(int currcons)
* You must set these - there is no sane way to probe for this board.
*/
-int io=0x240;
-int irq=14;
+static int io=0x240;
+static int irq=14;
#define WD_TIMO (100*60) /* 1 minute */
/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
/*
- Written 1993-1998 by Donald Becker.
+ Written 1993-1997 by Donald Becker.
- Copyright 1994-1998 by Donald Becker.
+ Copyright 1994-1997 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
FIXES:
Alan Cox: Removed the 'Unexpected interrupt' bug.
Michael Meskes: Upgraded to Donald Becker's version 1.07.
- Alan Cox: Increased the eeprom delay. Regardless of
+ Alan Cox: Increased the eeprom delay. Regardless of
what the docs say some people definitely
get problems with lower (but in card spec)
delays
v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
other cleanups. -djb
- v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
- v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
- v1.15 1/31/98 Faster recovery for Tx errors. -djb
- v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
*/
-static char *version = "3c509.c:1.16C 2/5/98 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
/* A few values that may be tweaked. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (400*HZ/1000)
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
+#define INTR_WORK 10
#include <linux/module.h>
int el3_debug = 2;
#endif
-#define test_and_set_bit(val, addr) set_bit(val, addr)
-
/* To minimize the size of the driver source I only define operating
- constants if they are used several times and make the code clearer.
- You'll need the manual to understand hardware operation. */
+ constants if they are used several times. You'll need the manual
+ anyway if you want to understand driver details. */
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
+#define EEPROM_READ 0x80
#define EL3_IO_EXTENT 16
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
/* Register window 1 offsets, the window used in normal operation. */
-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 {
- Wn0_IRQ = 8, /* Window 0: Set IRQ line in bits 12-15. */
- Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
- Wn0EepromData = 12, /* Window 0: EEPROM results register. */
- IntrStatus=0x0E, /* Valid in all windows. */
-};
-enum Win0_EEPROM_bits {
- EEPROM_Read = 0x80, EEPROM_Busy = 0x8000,
-};
+#define TX_FIFO 0x00
+#define RX_FIFO 0x00
+#define RX_STATUS 0x08
+#define TX_STATUS 0x0B
+#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
+#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+/*
+ * Must be a power of two (we use a binary and in the
+ * circular queue)
+ */
+#define SKB_QUEUE_SIZE 64
+
struct el3_private {
struct enet_statistics stats;
struct device *next_dev;
/* skb send-queue */
int head, size;
+ struct sk_buff *queue[SKB_QUEUE_SIZE];
};
-
-static int id_port = 0x110; /* Start with 0x110 to avoid new sound cards.*/
+static int id_port = 0x100;
static struct device *el3_root_dev = NULL;
static ushort id_read_eeprom(int index);
-static ushort read_eeprom(int ioaddr, int index);
+static ushort read_eeprom(short ioaddr, int index);
static int el3_open(struct device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void update_stats(int addr, struct device *dev);
static struct enet_statistics *el3_get_stats(struct device *dev);
-static int el3_rx(struct device *dev, int max_work);
+static int el3_rx(struct device *dev);
static int el3_close(struct device *dev);
-static void set_rx_mode(struct device *dev);
+static void set_multicast_list(struct device *dev);
\f
int el3_probe(struct device *dev)
{
short lrs_state = 0xff, i;
- int ioaddr, irq, if_port;
- u16 phys_addr[3];
+ ushort ioaddr, irq, if_port;
+ short phys_addr[3];
static int current_tag = 0;
/* First check all slots of the EISA bus. The next slot address to
ioaddr = eisa_addr;
eisa_addr += 0x1000;
- if (check_region(ioaddr, EL3_IO_EXTENT))
- continue;
/* Check the standard EISA ID register for an encoded '3Com'. */
if (inw(ioaddr + 0xC80) != 0x6d50)
continue;
- /* Check for a product that we support, 3c579 any rev. */
- if (inb(ioaddr + 0xC82) != 0x57)
- continue;
/* Change the register set to the configuration window 0. */
outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
- irq = inw(ioaddr + Wn0_IRQ) >> 12;
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
for (i = 0; i < 8; i++)
if ((mca_adaptor_id(i) | 1) == 0x627c) {
ioaddr = mca_pos_base_addr(i);
- irq = inw(ioaddr + Wn0_IRQ) >> 12;
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
outb(0x02, 0x279); /* Select PnP config control register. */
outb(0x02, 0xA79); /* Return to WaitForKey state. */
/* Select an open I/O location at 0x1*0 to do contention select. */
- for ( ; id_port < 0x200; id_port += 0x10) {
+ for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
if (check_region(id_port, 1))
continue;
outb(0x00, id_port);
}
if (id_port >= 0x200) { /* GCC optimizes this test out. */
/* Rare -- do we really need a warning? */
- printk(KERN_WARNING " WARNING: No I/O port available for 3c509 "
- "activation sequence.\n");
+ printk(" WARNING: No I/O port available for 3c509 activation.\n");
return -ENODEV;
}
/* Next check for all ISA bus boards by sending the ID sequence to the
}
{
- unsigned int iobase = id_read_eeprom(8);
+ unsigned short iobase = id_read_eeprom(8);
if_port = iobase >> 14;
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
}
- irq = id_read_eeprom(9) >> 12;
-
- if (dev) { /* Set passed-in IRQ or I/O Addr. */
- if (dev->irq > 1 && dev->irq < 16)
- irq = dev->irq;
-
- if (dev->base_addr) {
- if (dev->mem_end == 0x3c509 /* Magic key */
- && dev->base_addr >= 0x200 && dev->base_addr <= 0x3e0)
- ioaddr = dev->base_addr & 0x3f0;
- else if (dev->base_addr != ioaddr)
- return -ENODEV;
- }
+ if (dev && dev->irq > 1 && dev->irq < 16)
+ irq = dev->irq;
+ else
+ irq = id_read_eeprom(9) >> 12;
+
+ if (dev && dev->base_addr != 0
+ && dev->base_addr != (unsigned short)ioaddr) {
+ return -ENODEV;
}
/* Set the adaptor tag so that the next card can be found. */
return -ENODEV;
/* Free the interrupt so that some other card can use it. */
- outw(0x0f00, ioaddr + Wn0_IRQ);
+ outw(0x0f00, ioaddr + WN0_IRQ);
found:
if (dev == NULL) {
dev = init_etherdev(dev, sizeof(struct el3_private));
{
const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
- printk(KERN_INFO"%s: 3c509 at %#3.3lx tag %d, %s port, address ",
+ printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
}
- /* Show the station address. */
- for (i = 0; i < 5; i++)
- printk("%2.2x:", dev->dev_addr[i]);
- printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], dev->irq);
+ /* Read in the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i]);
+ printk(", IRQ %d.\n", dev->irq);
/* Make up a EL3-specific-data structure. */
if (dev->priv == NULL)
el3_root_dev = dev;
if (el3_debug > 0)
- printk(KERN_INFO"%s", version);
+ printk(version);
/* The EL3-specific entries in the device structure. */
dev->open = &el3_open;
dev->hard_start_xmit = &el3_start_xmit;
dev->stop = &el3_close;
dev->get_stats = &el3_get_stats;
- dev->set_multicast_list = &set_rx_mode;
+ dev->set_multicast_list = &set_multicast_list;
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
/* Read a word from the EEPROM using the regular EEPROM access register.
Assume that we are in register window zero.
*/
-static ushort read_eeprom(int ioaddr, int index)
+static ushort read_eeprom(short ioaddr, int index)
{
- int boguscheck;
- outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
+ outw(EEPROM_READ + index, ioaddr + 10);
/* Pause for at least 162 us. for the read to take place. */
- for (boguscheck = 20; boguscheck >= 0; boguscheck--) {
- udelay(40);
- if ((inw(ioaddr + Wn0EepromCmd) & EEPROM_Busy) == 0)
- break;
- }
- return inw(ioaddr + Wn0EepromData);
+ udelay (500);
+ return inw(ioaddr + 12);
}
/* Read a word from the EEPROM when in the ISA ID probe state. */
/* Issue read command, and pause for at least 162 us. for it to complete.
Assume extra-fast 16Mhz bus. */
- outb(EEPROM_Read + index, id_port);
+ outb(EEPROM_READ + index, id_port);
/* Pause for at least 162 us. for the read to take place. */
udelay (500);
word = (word << 1) + (inb(id_port) & 0x01);
if (el3_debug > 3)
- printk(KERN_DEBUG" 3c509 EEPROM word %d %#4.4x.\n", index, word);
+ printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
return word;
}
outw(RxReset, ioaddr + EL3_CMD);
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
- if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev))
+ if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
return -EAGAIN;
+ }
EL3WINDOW(0);
if (el3_debug > 3)
- printk(KERN_DEBUG"%s: Opening 3c509, IRQ %d status@%x %4.4x.\n",
- dev->name, dev->irq, ioaddr + EL3_STATUS,
- inw(ioaddr + EL3_STATUS));
+ printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
+ dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
- /* Activate board: this is usually unnecessary. */
+ /* Activate board: this is probably unnecessary. */
outw(0x0001, ioaddr + 4);
/* Set the IRQ line. */
- outw((dev->irq << 12) | 0x0f00, ioaddr + Wn0_IRQ);
+ outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
/* Set the station address in window 2 each time opened. */
EL3WINDOW(2);
EL3WINDOW(1);
/* Accept b-case and phys addr only. */
- set_rx_mode(dev);
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
dev->interrupt = 0;
/* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
- outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull,
ioaddr + EL3_CMD);
if (el3_debug > 3)
- printk(KERN_DEBUG "%s: Opened 3c509 IRQ %d status %4.4x.\n",
+ printk("%s: Opened 3c509 IRQ %d status %4.4x.\n",
dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
MOD_INC_USE_COUNT;
return 0; /* Always succeed */
}
-static void el3_tx_timeout(struct device *dev)
-{
- struct el3_private *lp = (struct el3_private *)dev->priv;
- int ioaddr = dev->base_addr;
- ushort status = inw(ioaddr + EL3_STATUS);
-
- printk(KERN_ERR"%s: transmit timed out, Tx_status %2.2x status %4.4x "
- "Tx FIFO room %d.\n",
- dev->name, inb(ioaddr + TxStatus), status,
- inw(ioaddr + TxFree));
-
- /* Error-checking code for the common IRQ line block. */
- if (status & 0x0001 /* IRQ line active, missed one. */
- && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
- printk(KERN_CRIT"%s: Interrupt line blocked? 3c509 status %4.4x"
- " Tx %2.2x Rx %4.4x.\n", dev->name, status,
- inb(ioaddr + TxStatus), inw(ioaddr + RxStatus));
- }
-
- lp->stats.tx_errors++;
- dev->trans_start = jiffies;
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->tbusy = 0;
-}
-
static int
el3_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
int ioaddr = dev->base_addr;
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- if (jiffies - dev->trans_start >= TX_TIMEOUT)
- el3_tx_timeout(dev);
- return 1;
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < TX_TIMEOUT)
+ return 1;
+ printk("%s: transmit timed out, Tx_status %2.2x status %4.4x "
+ "Tx FIFO room %d.\n",
+ dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
+ inw(ioaddr + TX_FREE));
+ lp->stats.tx_errors++;
+ dev->trans_start = jiffies;
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
}
if (el3_debug > 4) {
- printk(KERN_DEBUG"%s: el3_start_xmit(length = %ld) called, status "
- "%4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+ printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
+ dev->name, skb->len, inw(ioaddr + EL3_STATUS));
}
-
- /* Put out the doubleword header... */
- outl(skb->len, ioaddr + TX_FIFO);
-
- /* ... and the packet rounded to a doubleword. */
+#if 0
+#ifndef final_version
+ { /* Error-checking code, delete someday. */
+ ushort status = inw(ioaddr + EL3_STATUS);
+ if (status & 0x0001 /* IRQ line active, missed one. */
+ && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
+ printk("%s: Missed interrupt, status then %04x now %04x"
+ " Tx %2.2x Rx %4.4x.\n", dev->name, status,
+ inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
+ inw(ioaddr + RX_STATUS));
+ /* Fake interrupt trigger by masking, acknowledge interrupts. */
+ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ }
+ }
+#endif
+#endif
+ /* Avoid timer-based retransmission conflicts. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
#ifdef __powerpc__
- outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
#else
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
#endif
- dev->trans_start = jiffies;
- 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);
+
+ dev->trans_start = jiffies;
+ if (inw(ioaddr + TX_FREE) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
dev_kfree_skb (skb, FREE_WRITE);
short tx_status;
int i = 4;
- while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
+ while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
- outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
}
}
return 0;
{
struct device *dev = (struct device *)dev_id;
int ioaddr, status;
- int work_budget = max_interrupt_work;
+ int i = INTR_WORK;
- if (dev->interrupt) {
- printk(KERN_ERR"%s: Re-entering the interrupt handler.\n", dev->name);
+ if (dev == NULL) {
+ printk ("el3_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;
status = inw(ioaddr + EL3_STATUS);
if (el3_debug > 4)
- printk(KERN_DEBUG"%s: interrupt, status %4.4x.\n", dev->name, status);
+ printk("%s: interrupt, status %4.4x.\n", dev->name, status);
while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | StatsFull)) {
if (status & RxComplete)
- work_budget = el3_rx(dev, work_budget);
+ el3_rx(dev);
if (status & TxAvailable) {
if (el3_debug > 5)
- printk(KERN_DEBUG" TX room bit was handled.\n");
+ 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);
}
- if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
/* Handle all uncommon interrupts. */
if (status & StatsFull) /* Empty statistics. */
update_stats(ioaddr, dev);
if (status & RxEarly) { /* Rx early is unused. */
- work_budget = el3_rx(dev, work_budget);
+ el3_rx(dev);
outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
}
- if (status & TxComplete) { /* Really Tx error. */
- struct el3_private *lp = (struct el3_private *)dev->priv;
- short tx_status;
- int i = 4;
-
- while (--i>0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
- if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
- if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
- if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
- outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
- }
- }
if (status & AdapterFailure) {
/* Adapter failure requires Rx reset and reinit. */
outw(RxReset, ioaddr + EL3_CMD);
/* Set the Rx filter to the current state. */
- set_rx_mode(dev);
+ 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 (--work_budget < 0) {
- printk(KERN_WARNING"%s: Too much work in interrupt, status "
- "%4.4x.\n", dev->name, status);
+ if (--i < 0) {
+ printk("%s: Infinite loop in interrupt, status %4.4x.\n",
+ dev->name, status);
/* Clear all interrupts. */
outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
break;
}
if (el3_debug > 4) {
- printk(KERN_DEBUG"%s: exiting interrupt, status %4.4x.\n", dev->name,
+ printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
inw(ioaddr + EL3_STATUS));
}
struct el3_private *lp = (struct el3_private *)dev->priv;
if (el3_debug > 5)
- printk(KERN_DEBUG" Updating the statistics.\n");
+ printk(" Updating the statistics.\n");
/* Turn off statistics updates while reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Switch to the stats window, and read everything. */
return;
}
-static int el3_rx(struct device *dev, int work_budget)
+static int
+el3_rx(struct device *dev)
{
struct el3_private *lp = (struct el3_private *)dev->priv;
int ioaddr = dev->base_addr;
short rx_status;
if (el3_debug > 5)
- printk(KERN_DEBUG" 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 && --work_budget >= 0) {
+ printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
if (rx_status & 0x4000) { /* Error, update stats. */
short error = rx_status & 0x3800;
-
- outw(RxDiscard, ioaddr + EL3_CMD);
lp->stats.rx_errors++;
switch (error) {
case 0x0000: lp->stats.rx_over_errors++; break;
skb = dev_alloc_skb(pkt_len+5);
if (el3_debug > 4)
- printk(KERN_DEBUG"Receiving packet size %d status %4.4x.\n",
+ printk("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
skb->dev = dev;
(pkt_len + 3) >> 2);
#endif
- outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
lp->stats.rx_packets++;
continue;
- }
- outw(RxDiscard, ioaddr + EL3_CMD);
- lp->stats.rx_dropped++;
- if (el3_debug)
- printk(KERN_NOTICE"%s: Couldn't allocate a sk_buff of size "
- "%d.\n", dev->name, pkt_len);
+ } else if (el3_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
}
- inw(ioaddr + EL3_STATUS); /* Delay. */
+ lp->stats.rx_dropped++;
+ outw(RxDiscard, ioaddr + EL3_CMD);
while (inw(ioaddr + EL3_STATUS) & 0x1000)
- printk(KERN_NOTICE" Waiting for 3c509 to discard packet, status %x.\n",
+ printk(" Waiting for 3c509 to discard packet, status %x.\n",
inw(ioaddr + EL3_STATUS) );
}
- return work_budget;
+ return 0;
}
/*
* Set or clear the multicast filter for this adaptor.
*/
static void
-set_rx_mode(struct device *dev)
+set_multicast_list(struct device *dev)
{
- int ioaddr = dev->base_addr;
-
- if (dev->flags & IFF_PROMISC) {
+ short ioaddr = dev->base_addr;
+ if (el3_debug > 1) {
+ static int old = 0;
+ if (old != dev->mc_count) {
+ old = dev->mc_count;
+ printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count);
+ }
+ }
+ if (dev->flags&IFF_PROMISC) {
outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
ioaddr + EL3_CMD);
- } else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) {
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast,
- ioaddr + EL3_CMD);
- } else
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ }
+ else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
+ }
+ else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
}
static int
int ioaddr = dev->base_addr;
if (el3_debug > 2)
- printk(KERN_DEBUG"%s: Shutting down ethercard.\n", dev->name);
+ printk("%s: Shutting down ethercard.\n", dev->name);
dev->tbusy = 1;
dev->start = 0;
/* Switching back to window 0 disables the IRQ. */
EL3WINDOW(0);
/* But we explicitly zero the IRQ line select anyway. */
- outw(0x0f00, ioaddr + Wn0_IRQ);
+ outw(0x0f00, ioaddr + WN0_IRQ);
update_stats(ioaddr, dev);
MOD_DEC_USE_COUNT;
}
#ifdef MODULE
-/* Parameters that may be passed into the module. */
+/* Parameter that may be passed into the module. */
static int debug = -1;
static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};
/*
* Local variables:
* compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c"
- * c-indent-level: 4
- * c-basic-offset: 4
+ * version-control: t
+ * kept-new-versions: 5
* tab-width: 4
* End:
*/
/* 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux. */
/*
- Written 1997 by Donald Becker.
+ Written 1997-1998 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
*/
-static char *version = "3c515.c:v0.06 12/5/97 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c515.c:v0.99 4/7/98 becker@cesdis.gsfc.nasa.gov\n";
#define CORKSCREW 1
/* "Knobs" that adjust features and parameters. */
*/
#define CORKSCREW_TOTAL_SIZE 0x20
+#ifdef HAVE_DEVLIST
+struct netdev_entry tc515_drv =
+{"3c515", tc515_probe, CORKSCREW_TOTAL_SIZE, NULL};
+#endif
+
#ifdef DRIVER_DEBUG
int vortex_debug = DRIVER_DEBUG;
#else
}
#else
-int isa515_probe(struct device *dev)
+int tc515_probe(struct device *dev)
{
int cards_found = 0;
*/
static char *version =
-"3c59x.c:v0.49J 2/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.99 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
unsigned char pci_bus, pci_device_fn;
for (;pci_index < 0xff; pci_index++) {
- unsigned char pci_irq_line, pci_latency;
+ u8 pci_irq_line, pci_latency;
u16 pci_command, new_command, vendor, device;
- unsigned int pci_ioaddr;
+ u32 pci_ioaddr;
if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
pci_index, &pci_bus, &pci_device_fn)
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
dev_kfree_skb (skb, FREE_WRITE);
if (inw(ioaddr + TxFree) > 1536) {
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else
/* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
dev_kfree_skb (skb, FREE_WRITE);
if (inw(ioaddr + TxFree) > 1536) {
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else
/* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
vp->tx_full = 1;
else { /* Clear previous interrupt enable. */
prev_entry->status &= ~TxIntrUploaded;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
}
dev->trans_start = jiffies;
return 0;
printk(KERN_DEBUG " 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;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
lp->tx_full= 0;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
}
#ifdef VORTEX_BUS_MASTER
if (status & DMADone) {
outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */
mark_bh(NET_BH);
}
dev->name, status);
dev->interrupt = 0;
- lp->in_interrupt = 0;
+ clear_bit(0, (void*)&lp->in_interrupt);
return;
}
/*
* Local variables:
* compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * compile-command-redhat: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
- * compile-command-alt0: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
* compile-command-alt1: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c59x_cb.o"
* c-indent-level: 4
* c-basic-offset: 4
TLan Device Driver change log.
-0.42 - Coverted tranceiver from misused per-tranceiver functions
- to a timer-oriented path that covers four possible classes
- of tranceivers:
- 1. Unmanaged
- 2. Manual configuration
- 3. Autonegotiation w/ manual configuration
- 4. Autonegotiation w/ auto configuration
- - Added ability to force speed and duplex settings.
- - Made speed, duplex, sa_int, etc, to be set per adapter with
- ether= command.
- - Added support for Olicom OC-2326
-
-0.41 - Added non-bounce buffer paths. Added TLan_FreeLists to
- dispose of unused sk_buff's at device close time.
- - Discovered inlined functions aren't being inlined, or at
- least take up more space than macros would.
-
-0.40 - Refined polarity checking to handle case when polarity
- changes to normal from abnormal.
- - Cleaned up TLan_Probe routine.
- - Added an option for the SA_INTERRUPT flag to be set.
- - Created FAQ.
- - Removed all C++ style comments.
- - Added error message if devices busmastering is inactive.
- Also will now skip device.
- - Put cli and sti back into TLan_HandleInterrupt. It makes
- me feel better.
- - Moved the code that checks for boot parameter options to
- tlan_probe.
-
-0.39 - Minor cosmetic cleanups (especially variable declarations).
- - Changes low level TLAN functions to use dev structures instead
- individual data elements.
- - Changed low level TLAN functions not to play with sti and cli
- if in an interrupt routine.
- - Removed cli and sti from TLan_HandleInterrupt.
-
0.38 - Added code to isolate the external PHY if the internal PHY is
being used for AUI/BNC connectivity. Also set the aui and
debug variables from mem_start and mem_end if the driver is
#
tristate 'Dummy net driver support' CONFIG_DUMMY
tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
-tristate 'Frame relay DLCI support' CONFIG_DLCI
-if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
- int ' Max open DLCI' CONFIG_DLCI_COUNT 24
- int ' Max DLCI per device' CONFIG_DLCI_MAX 8
- dep_tristate ' SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Frame relay DLCI support (EXPERIMENTAL)' CONFIG_DLCI
+ if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
+ int ' Max open DLCI' CONFIG_DLCI_COUNT 24
+ int ' Max DLCI per device' CONFIG_DLCI_MAX 8
+ dep_tristate ' SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI
+ fi
fi
tristate 'PLIP (parallel port) support' CONFIG_PLIP
tristate 'PPP (point-to-point) support' CONFIG_PPP
#
bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET
if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
- bool '3COM cards' CONFIG_NET_VENDOR_3COM
+ bool '3COM ISA, EISA and PCI cards' CONFIG_NET_VENDOR_3COM
if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
tristate '3c501 support' CONFIG_EL1
tristate '3c503 support' CONFIG_EL2
tristate '3c507 support' CONFIG_EL16
fi
tristate '3c509/3c579 support' CONFIG_EL3
- tristate '3c515 support' CONFIG_CORKSCREW
+ tristate '3c515 ISA Fast EtherLink' CONFIG_3C515
tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX
fi
- tristate 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
- bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
+ bool 'Western Digital/SMC ISA and EISA cards' CONFIG_NET_VENDOR_SMC
if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
tristate 'WD80*3 support' CONFIG_WD80x3
tristate 'SMC Ultra support' CONFIG_ULTRA
tristate 'SMC Ultra32 support' CONFIG_ULTRA32
tristate 'SMC 9194 support' CONFIG_SMC9194
fi
+ bool 'PCI Ethernet adapters' CONFIG_NET_PCI
+ if [ "$CONFIG_NET_PCI" = "y" ]; then
+ tristate 'AMD PCI PCnet32 (PCI bus NE2100 cards) support' CONFIG_PCNET32
+ tristate 'Intel EtherExpressPro PCI 10+/100B/100+ support' CONFIG_EEXPRESS_PRO100B
+ tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5
+ tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
+ tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
+ tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
+ tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN
+ tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139
+ tristate 'SMC EPIC/100 (EtherPower II) support' CONFIG_EPIC
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
+ fi
+ fi
bool 'Other ISA cards' CONFIG_NET_ISA
if [ "$CONFIG_NET_ISA" = "y" ]; then
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'AT1700 support (EXPERIMENTAL)' CONFIG_AT1700
- fi
+ tristate 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
+ tristate 'AT1700 (Fujitsu 86965) support' CONFIG_AT1700
tristate 'Cabletron E21xx support' CONFIG_E2100
tristate 'DEPCA, DE10x, DE200, DE201, DE202, DE422 support' CONFIG_DEPCA
tristate 'EtherWORKS 3 (DE203, DE204, DE205) support' CONFIG_EWRK3
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
fi
- tristate 'NE2000/NE1000 support' CONFIG_NE2000
+ tristate 'NE2000/NE1000 ISA support' CONFIG_NE2000
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'NI5210 support' CONFIG_NI52
tristate 'NI6510 support' CONFIG_NI65
fi
bool 'SK_G16 support' CONFIG_SK_G16
fi
- bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
+ bool 'EISA, VLB and other board controllers' CONFIG_NET_EISA
if [ "$CONFIG_NET_EISA" = "y" ]; then
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
fi
- tristate 'AMD PCInet32 (VLB and PCI) support' CONFIG_LANCE32
tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
- tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5
- tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
- tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
- tristate 'Intel EtherExpress/Pro 100B support' CONFIG_EEXPRESS_PRO100B
- tristate 'NE2000 PCI support' CONFIG_PCI_NE2000
- tristate 'RTL 8139 support' CONFIG_RTL8139
- tristate 'SMC EtherPower II support' CONFIG_SMC_EPIC
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
fi
fi
bool 'Pocket and portable adaptors' CONFIG_NET_POCKET
if [ "$CONFIG_NET_POCKET" = "y" ]; then
- bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP
+ tristate 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP
tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600
tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620
fi
fi
-bool 'Ethernet (Gigabit)' CONFIG_GIGAETHER
-if [ "$CONFIG_GIGAETHER" = "y" ]; then
- bool 'Packet Engines G-NIC PCI Gigabit Ethernet Adapter' CONFIG_YELLOWFIN
-fi
-
bool 'Token Ring driver support' CONFIG_TR
if [ "$CONFIG_TR" = "y" ]; then
tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR
endif
endif
-ifeq ($(CONFIG_PCI_NE2000),y)
-L_OBJS += ne2k-pci.o
-CONFIG_8390_BUILTIN = y
-else
- ifeq ($(CONFIG_PCI_NE2000),m)
- CONFIG_8390_MODULE = y
- M_OBJS += ne2k-pci.o
- endif
-endif
-
ifeq ($(CONFIG_HPLAN),y)
L_OBJS += hp.o
CONFIG_8390_BUILTIN = y
L_OBJS += smc-ultra32.o
CONFIG_8390_BUILTIN = y
else
- ifeq ($(CONFIG_ULTRA32),m)
+ ifeq ($(CONFIG_ULTRA),m)
CONFIG_8390_MODULE = y
M_OBJS += smc-ultra32.o
endif
ifeq ($(CONFIG_LANCE),y)
L_OBJS += lance.o
- ifeq ($(CONFIG_LANCE32),y)
- L_OBJS += pcnet32.o
+else
+ ifeq ($(CONFIG_LANCE),m)
+ M_OBJS += lance.o
endif
endif
endif
endif
-ifeq ($(CONFIG_CORKSCREW),y)
+ifeq ($(CONFIG_3C515),y)
L_OBJS += 3c515.o
else
- ifeq ($(CONFIG_CORKSCREW),m)
+ ifeq ($(CONFIG_3C515),m)
M_OBJS += 3c515.o
endif
endif
endif
endif
+ifeq ($(CONFIG_EPIC),y)
+L_OBJS += epic100.o
+else
+ ifeq ($(CONFIG_EPIC),m)
+ M_OBJS += epic100.o
+ endif
+endif
-ifeq ($(CONFIG_WAVELAN),y)
-L_OBJS += wavelan.o
+ifeq ($(CONFIG_NE2K_PCI),y)
+L_OBJS += ne2k-pci.o
+CONFIG_8390_BUILTIN = y
else
- ifeq ($(CONFIG_WAVELAN),m)
- M_OBJS += wavelan.o
+ ifeq ($(CONFIG_NE2K_PCI),m)
+ CONFIG_8390_MODULE = y
+ M_OBJS += ne2k-pci.o
endif
endif
-ifeq ($(CONFIG_TLAN),y)
-L_OBJS += tlan.o
+ifeq ($(CONFIG_PCNET32),y)
+L_OBJS += pcnet32.o
else
- ifeq ($(CONFIG_TLAN),m)
- M_OBJS += tlan.o
+ ifeq ($(CONFIG_PCNET32),m)
+ M_OBJS += pcnet32.o
+ endif
+endif
+
+ifeq ($(CONFIG_RTL8139),y)
+L_OBJS += rtl8139.o
+else
+ ifeq ($(CONFIG_RTL8139),m)
+ M_OBJS += rtl8139.o
+ endif
+endif
+
+ifeq ($(CONFIG_YELLOWFIN),y)
+L_OBJS += yellowfin.o
+else
+ ifeq ($(CONFIG_YELLOWFIN),m)
+ M_OBJS += yellowfin.o
+ endif
+endif
+
+ifeq ($(CONFIG_WAVELAN),y)
+L_OBJS += wavelan.o
+else
+ ifeq ($(CONFIG_WAVELAN),m)
+ M_OBJS += wavelan.o
endif
endif
ifeq ($(CONFIG_ATP),y)
L_OBJS += atp.o
+else
+ ifeq ($(CONFIG_ATP),m)
+ M_OBJS += atp.o
+ endif
endif
ifeq ($(CONFIG_DE4X5),y)
endif
endif
+ifeq ($(CONFIG_TLAN),y)
+L_OBJS += tlan.o
+else
+ ifeq ($(CONFIG_TLAN),m)
+ M_OBJS += tlan.o
+ endif
+endif
+
ifeq ($(CONFIG_ARCNET),y)
L_OBJS += arcnet.o
else
endif
endif
-ifeq ($(CONFIG_YELLOWFIN),y)
-L_OBJS += yellowfin.o
-else
- ifeq ($(CONFIG_YELLOWFIN),m)
- M_OBJS += yellowfin.o
- endif
-endif
-
-ifeq ($(CONFIG_SMC_EPIC),y)
-L_OBJS += epic100.o
-else
- ifeq ($(CONFIG_SMC_EPIC),m)
- M_OBJS += epic100.o
- endif
-endif
-
-ifeq ($(CONFIG_RTL8139),y)
-L_OBJS += rtl8139.o
-else
- ifeq ($(CONFIG_RTL8139),m)
- M_OBJS += rtl8139.o
- endif
-endif
-
include $(TOPDIR)/Rules.make
clean:
extern int wd_probe(struct device *dev);
extern int el2_probe(struct device *dev);
extern int ne_probe(struct device *dev);
+extern int ne2k_pci_probe(struct device *dev);
extern int hp_probe(struct device *dev);
extern int hp_plus_probe(struct device *dev);
extern int znet_probe(struct device *);
extern int express_probe(struct device *);
extern int eepro_probe(struct device *);
-extern int eepro100_probe(struct device *);
extern int el3_probe(struct device *);
extern int at1500_probe(struct device *);
extern int at1700_probe(struct device *);
extern int ariadne_probe(struct device *);
extern int hydra_probe(struct device *);
extern int yellowfin_probe(struct device *);
-extern int epic100_probe(struct device *);
+extern int eepro100_probe(struct device *);
+extern int epic_probe(struct device *);
extern int rtl8139_probe(struct device *);
extern int tlan_probe(struct device *);
extern int isa515_probe(struct device *);
return 1; /* ENXIO */
if (1
+ /* All PCI probes are safe, and thus should be first. */
+#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
+ && de4x5_probe(dev)
+#endif
+#ifdef CONFIG_DGRS
+ && dgrs_probe(dev)
+#endif
+#ifdef CONFIG_EEXPRESS_PRO100B /* Intel EtherExpress Pro100B */
+ && eepro100_probe(dev)
+#endif
#ifdef CONFIG_SMC_EPIC
- && epic100_probe(dev)
+ && epic_probe(dev)
#endif
-#ifdef CONFIG_YELLOWFIN
- && yellowfin_probe(dev)
+#if defined(CONFIG_HP100)
+ && hp100_probe(dev)
+#endif
+#if defined(CONFIG_NE2K_PCI)
+ && ne2k_pci_probe(dev)
+#endif
+#ifdef CONFIG_PCNET32
+ && pcnet32_probe(dev)
#endif
#ifdef CONFIG_RTL8139
&& rtl8139_probe(dev)
#endif
-#ifdef CONFIG_DGRS
- && dgrs_probe(dev)
-#endif
#if defined(CONFIG_VORTEX)
&& tc59x_probe(dev)
#endif
-#if defined(CONFIG_SEEQ8005)
- && seeq8005_probe(dev)
-#endif
#if defined(CONFIG_DEC_ELCP)
&& tulip_probe(dev)
#endif
-#if defined(CONFIG_HP100)
- && hp100_probe(dev)
-#endif
-#if defined(CONFIG_ULTRA)
- && ultra_probe(dev)
+#ifdef CONFIG_YELLOWFIN
+ && yellowfin_probe(dev)
+#endif
+ /* Next mostly-safe EISA-only drivers. */
+#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */
+ && ac3200_probe(dev)
#endif
#if defined(CONFIG_ULTRA32)
&& ultra32_probe(dev)
+#endif
+ /* Third, sensitive ISA boards. */
+#ifdef CONFIG_AT1700
+ && at1700_probe(dev)
+#endif
+#if defined(CONFIG_ULTRA)
+ && ultra_probe(dev)
#endif
#if defined(CONFIG_SMC9194)
&& smc_init(dev)
#if defined(CONFIG_HPLAN_PLUS)
&& hp_plus_probe(dev)
#endif
-#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */
- && ac3200_probe(dev)
+#if defined(CONFIG_SEEQ8005)
+ && seeq8005_probe(dev)
#endif
#ifdef CONFIG_E2100 /* Cabletron E21xx series. */
&& e2100_probe(dev)
#endif
-#if defined(CONFIG_NE2000) || defined(NE2000)
+#if defined(CONFIG_NE2000)
&& ne_probe(dev)
#endif
#ifdef CONFIG_AT1500
&& at1500_probe(dev)
#endif
-#ifdef CONFIG_AT1700
- && at1700_probe(dev)
-#endif
#ifdef CONFIG_FMV18X /* Fujitsu FMV-181/182 */
&& fmv18x_probe(dev)
#endif
#ifdef CONFIG_EL3 /* 3c509 */
&& el3_probe(dev)
#endif
+#ifdef CONFIG_3C515 /* 3c515 */
+ && tc515_probe(dev)
+#endif
#ifdef CONFIG_ZNET /* Zenith Z-Note and some IBM Thinkpads. */
&& znet_probe(dev)
#endif
#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */
&& eepro_probe(dev)
#endif
-#ifdef CONFIG_EEXPRESS_PRO100B /* Intel EtherExpress Pro100B */
- && eepro100_probe(dev)
-#endif
#ifdef CONFIG_DEPCA /* DEC DEPCA */
&& depca_probe(dev)
#endif
#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */
&& ewrk3_probe(dev)
#endif
-#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
- && de4x5_probe(dev)
-#endif
#ifdef CONFIG_APRICOT /* Apricot I82596 */
&& apricot_probe(dev)
#endif
#ifdef CONFIG_NI65
&& ni65_probe(dev)
#endif
+#ifdef CONFIG_LANCE /* ISA LANCE boards */
+ && lance_probe(dev)
+#endif
#ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */
&& atarilance_probe(dev)
#endif
#ifdef CONFIG_TLAN
&& tlan_probe(dev)
#endif
-#ifdef CONFIG_LANCE32
+#ifdef CONFIG_PCNET32
&& pcnet32_probe(dev)
#endif
-#ifdef CONFIG_CORKSCREW
- && isa515_probe(dev)
-#endif
#ifdef CONFIG_LANCE
&& lance_probe(dev)
#endif
*/
static const char *version =
- "at1700.c:v1.13 1/31/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+ "at1700.c:v1.15 4/7/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#include <linux/module.h>
/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */
#define MC_FILTERBREAK 64
-/* This unusual address order is used to verify the CONFIG register. */
+/* These unusual address orders are used to verify the CONFIG register. */
static int at1700_probe_list[] =
{0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0};
+static int fmv18x_probe_list[] =
+{0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0};
/* use 0 for production, 1 for verification, >2 for debug */
struct net_local {
struct enet_statistics stats;
unsigned char mc_filter[8];
+ uint jumpered:1; /* Set iff the board has jumper config. */
uint tx_started:1; /* Packets are on the Tx queue. */
+ uint invalid_irq:1;
uchar tx_queue; /* Number of packet on the Tx queue. */
ushort tx_queue_len; /* Current length of the Tx queue. */
};
#define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */
#define TX_START 10
#define MODE13 13
+/* Configuration registers only on the '865A/B chips. */
#define EEPROM_Ctrl 16
#define EEPROM_Data 17
-#define IOCONFIG 19
+#define IOCONFIG 18 /* Either read the jumper, or move the I/O. */
+#define IOCONFIG1 19
+#define SAPROM 20 /* The station address PROM, if no EEPROM. */
#define RESET 31 /* Write to reset some parts of the chip. */
#define AT1700_IO_EXTENT 32
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */
-#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */
-#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */
-#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
-
-/* Delay between EEPROM clock transitions. */
-#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5 << 6)
-#define EE_READ_CMD (6 << 6)
-#define EE_ERASE_CMD (7 << 6)
-
-
/* Index to functions, as function prototypes. */
extern int at1700_probe(struct device *dev);
int at1700_probe1(struct device *dev, int ioaddr)
{
- char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
- unsigned int i, irq;
+ char fmv_irqmap[4] = {3, 7, 10, 15};
+ char at1700_irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
+ unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0;
/* Resetting the chip doesn't reset the ISA interface, so don't bother.
That means we have to be careful with the register values we probe for.
ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
#endif
- if (at1700_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr
- || read_eeprom(ioaddr, 4) != 0x0000
- || (read_eeprom(ioaddr, 5) & 0xff00) != 0xF400)
+ /* We must check for the EEPROM-config boards first, else accessing
+ IOCONFIG0 will move the board! */
+ if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr
+ && read_eeprom(ioaddr, 4) == 0x0000
+ && (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400)
+ is_at1700 = 1;
+ else if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] == ioaddr
+ && inb(ioaddr + SAPROM ) == 0x00
+ && inb(ioaddr + SAPROM + 1) == 0x00
+ && inb(ioaddr + SAPROM + 2) == 0x0e)
+ is_fmv18x = 1;
+ else
return -ENODEV;
/* Reset the internal state machines. */
outb(0, ioaddr + RESET);
- irq = irqmap[(read_eeprom(ioaddr, 12)&0x04)
- | (read_eeprom(ioaddr, 0)>>14)];
-
- /* Snarf the interrupt vector now. */
- if (request_irq(irq, &net_interrupt, 0, "at1700", NULL)) {
- printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
- "IRQ %d.\n", ioaddr, irq);
- return EAGAIN;
- }
-
/* Allocate a new 'dev' if needed. */
if (dev == NULL)
dev = init_etherdev(0, sizeof(struct net_local));
+ if (is_at1700)
+ irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04)
+ | (read_eeprom(ioaddr, 0)>>14)];
+ else
+ irq = fmv_irqmap[(inb(ioaddr + IOCONFIG)>>6) & 0x03];
+
/* Grab the region so that we can find another board if the IRQ request
fails. */
- request_region(ioaddr, AT1700_IO_EXTENT, "at1700");
+ request_region(ioaddr, AT1700_IO_EXTENT, dev->name);
printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
ioaddr, irq);
dev->base_addr = ioaddr;
dev->irq = irq;
- irq2dev_map[irq] = dev;
for(i = 0; i < 3; i++) {
unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
}
/* Set the station address in bank zero. */
- outb(0xe0, ioaddr + 7);
+ outb(0xe0, ioaddr + CONFIG_1);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + 8 + i);
/* Switch to bank 1 and set the multicast table to accept none. */
- outb(0xe4, ioaddr + 7);
+ outb(0xe4, ioaddr + CONFIG_1);
for (i = 0; i < 8; i++)
outb(0x00, ioaddr + 8 + i);
outb(0xda, ioaddr + CONFIG_0);
/* Switch to bank 2 and lock our I/O address. */
- outb(0xe8, ioaddr + 7);
+ outb(0xe8, ioaddr + CONFIG_1);
outb(dev->if_port, MODE13);
/* Power-down the chip. Aren't we green! */
dev->set_multicast_list = &set_rx_mode;
/* Fill in the fields of 'dev' with ethernet-generic values. */
-
ether_setup(dev);
+
+ {
+ struct net_local *lp = (struct net_local *)dev->priv;
+ lp->jumpered = is_fmv18x;
+ /* Snarf the interrupt vector now. */
+ if (request_irq(irq, &net_interrupt, 0, dev->name, dev)) {
+ printk (" AT1700 at %#3x is unusable due to a conflict on"
+ "IRQ %d.\n", ioaddr, irq);
+ lp->invalid_irq = 1;
+ return 0;
+ }
+ }
+
return 0;
}
+\f
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */
+#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */
+#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */
+#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() do {} while (0);
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
static int read_eeprom(int ioaddr, int location)
{
int i;
int ee_addr = ioaddr + EEPROM_Ctrl;
int ee_daddr = ioaddr + EEPROM_Data;
int read_cmd = location | EE_READ_CMD;
- short ctrl_val = EE_CS;
-
- outb(ctrl_val, ee_addr);
-
+
/* Shift the read command bits out. */
for (i = 9; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outb(EE_CS, ee_addr);
outb(dataval, ee_daddr);
- outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
eeprom_delay();
- outb(EE_CS, ee_addr); /* Finish EEPROM a clock tick. */
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
eeprom_delay();
}
- outb(EE_CS, ee_addr);
-
+ outb(EE_DATA_WRITE, ee_daddr);
for (i = 16; i > 0; i--) {
+ outb(EE_CS, ee_addr);
+ eeprom_delay();
outb(EE_CS | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
- outb(EE_CS, ee_addr);
- eeprom_delay();
}
/* Terminate the EEPROM access. */
- ctrl_val &= ~EE_CS;
- outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- outb(ctrl_val, ee_addr);
+ outb(EE_CS, ee_addr);
eeprom_delay();
+ outb(EE_SHIFT_CLK, ee_addr);
+ outb(0, ee_addr);
return retval;
}
bus access, and two 4K Tx queues. */
outb(0xda, ioaddr + CONFIG_0);
- /* Same config 0, except enable the Rx and Tx. */
- outb(0x5a, ioaddr + CONFIG_0);
- /* Switch to register bank 2 for the run-time registers. */
- outb(0xe8, ioaddr + CONFIG_1);
+ /* Switch to register bank 2, enable the Rx and Tx. */
+ outw(0xe85a, ioaddr + CONFIG_0);
lp->tx_started = 0;
lp->tx_queue = 0;
/* Turn off the possible Tx interrupts. */
outb(0x00, ioaddr + TX_INTR);
-
+
outw(length, ioaddr + DATAPORT);
outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
static void
net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
}
if (net_debug > 5)
- printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
+ printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
dev->name, inb(ioaddr + RX_MODE), i);
}
return;
/* The inverse routine to net_open(). */
static int net_close(struct device *dev)
{
+ struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
dev->tbusy = 1;
/* Set configuration register 0 to disable Tx and Rx. */
outb(0xda, ioaddr + CONFIG_0);
- /* Update the statistics -- ToDo. */
+ /* No statistic counters on the chip to update. */
+
+#if 0
+ /* Disable the IRQ on boards where it is feasible. */
+ if (lp->jumpered) {
+ outb(0x00, ioaddr + IOCONFIG1);
+ free_irq(dev->irq, dev);
+ }
+#endif
/* Power-down the chip. Green, green, green! */
outb(0x00, ioaddr + CONFIG_1);
memset(mc_filter, 0, sizeof(mc_filter));
for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
i++, mclist = mclist->next)
- set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26,
mc_filter);
}
save_flags(flags);
cli();
if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
- int saved_bank = inb(ioaddr + CONFIG_1);
+ int saved_bank = inw(ioaddr + CONFIG_0);
/* Switch to bank 1 and set the multicast table. */
- outb(0xe4, ioaddr + CONFIG_1);
+ outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0);
for (i = 0; i < 8; i++)
outb(mc_filter[i], ioaddr + 8 + i);
memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
- outb(saved_bank, ioaddr + CONFIG_1);
+ outw(saved_bank, ioaddr + CONFIG_0);
}
restore_flags(flags);
return;
/* If we don't do this, we can't re-insmod it later. */
free_irq(dev_at1700.irq, NULL);
- irq2dev_map[dev_at1700.irq] = NULL;
release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
}
#endif /* MODULE */
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
/*
NOTICE: this version tested with kernels 1.3.72 and later only!
- Written 1996-1997 by Donald Becker.
+ Written 1996-1998 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
*/
static const char *version =
-"eepro100.c:v0.36 10/20/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+"eepro100.c:v0.99B 4/7/98 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
/* A few user-configurable values that apply to all boards.
First set are undocumented and spelled per Intel recommendations. */
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
+static int multicast_filter_limit = 64;
+
#include <linux/config.h>
#ifdef MODULE
#ifdef MODVERSIONS
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-
-/* A nominally proper method to handle version dependencies is to use
- LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
-#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
-#ifdef MODULE
-#if (LINUX_VERSION_CODE < VERSION(1,3,0))
-#define KERNEL_1_2
-#else /* 1.3.0 */
-#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
-#define NEW_MULTICAST
-#define LINUX_1_4
-#else
-#warning "This driver is tested for 1.3.44 and later development kernels only."
-#endif /* 1.3.44 */
-#endif
-#else
-
-#if (LINUX_VERSION_CODE >= 0x10344)
-#define NEW_MULTICAST
#include <linux/delay.h>
-#endif
-#ifdef HAVE_HEADER_CACHE
-#define LINUX_1_4
-#define NEW_MULTICAST
-#else
-#ifdef ETH_P_DDCMP /* Warning: Bogus! This means IS_LINUX_1_3. */
-#define KERNEL_1_3
-#else
-#define KERNEL_1_2
-#endif
+/* Unused in the 2.0.* version, but retained for documentation. */
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(congenb, "i");
+MODULE_PARM(txfifo, "i");
+MODULE_PARM(rxfifo, "i");
+MODULE_PARM(txdmacount, "i");
+MODULE_PARM(rxdmacount, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(multicast_filter_limit, "i");
#endif
-#endif
-/* This should be in a header file. */
-#if (LINUX_VERSION_CODE < VERSION(1,3,44))
-struct device *init_etherdev(struct device *dev, int sizeof_priv,
- unsigned long *mem_startp);
-#endif
-#if LINUX_VERSION_CODE < 0x10300
-#define RUN_AT(x) (x) /* What to put in timer->expires. */
-#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
-#define virt_to_bus(addr) ((unsigned long)addr)
-#define bus_to_virt(addr) ((void*)addr)
-#else /* 1.3.0 and later */
#define RUN_AT(x) (jiffies + (x))
-#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
-#endif
#if (LINUX_VERSION_CODE < 0x20123)
#define test_and_set_bit(val, addr) set_bit(val, addr)
#endif
-/* The total I/O port extent of the board. Nominally 0x18, but rounded up
- for PCI allocation. */
+/* The total I/O port extent of the board.
+ The registers beyond 0x18 only exist on the i82558. */
#define SPEEDO3_TOTAL_SIZE 0x20
-#ifdef HAVE_DEVLIST
-struct netdev_entry eepro100_drv =
-{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL};
-#endif
-
-#ifdef SPEEDO3_DEBUG
-int speedo_debug = SPEEDO3_DEBUG;
-#else
-int speedo_debug = 3;
-#endif
+int speedo_debug = 1;
/*
Theory of Operation
#define PKT_BUF_SZ 1536
/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT ((400*HZ)/1000)
+#define TX_TIMEOUT ((800*HZ)/1000)
/* How to wait for the command unit to accept a command.
Typically this takes 0 ticks. */
/* Operational parameter that usually are not changed. */
-#ifndef PCI_VENDOR_ID_INTEL /* Now defined in linux/pci.h */
-#define PCI_VENDOR_ID_INTEL 0x8086 /* Hmmmm, how did they pick that? */
-#endif
-#ifndef PCI_DEVICE_ID_INTEL_82557
-#define PCI_DEVICE_ID_INTEL_82557 0x1229
-#endif
-
/* The rest of these values should never change. */
/* Offsets to the various registers.
/* Rx descriptor ring & addresses of receive-in-place skbuffs. */
struct RxFD *rx_ringp[RX_RING_SIZE];
struct sk_buff* rx_skbuff[RX_RING_SIZE];
-#if (LINUX_VERSION_CODE < 0x10300) /* Kernel v1.2.*. */
- struct RxFD saved_skhead[RX_RING_SIZE]; /* Saved skbuff header chunk. */
-#endif
struct RxFD *last_rxf; /* Last command sent. */
struct enet_statistics stats;
struct speedo_stats lstats;
u8 config_cmd_data[22]; /* .. and setup parameters. */
int mc_setup_frm_len; /* The length of an allocated.. */
struct descriptor *mc_setup_frm; /* ..multicast setup frame. */
+ int in_interrupt; /* Word-aligned dev->interrupt */
char rx_mode; /* Current PROMISC/ALLMULTI setting. */
unsigned int tx_full:1; /* The Tx queue is full. */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
static void speedo_found1(struct device *dev, int ioaddr, int irq,
- int options, int card_idx);
+ int card_idx);
static int read_eeprom(int ioaddr, int location);
static int mdio_read(int ioaddr, int phy_id, int location);
static void speedo_init_rx_ring(struct device *dev);
static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
static int speedo_rx(struct device *dev);
-#ifdef SA_SHIRQ
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-#else
-static void speedo_interrupt(int irq, struct pt_regs *regs);
-#endif
static int speedo_close(struct device *dev);
static struct enet_statistics *speedo_get_stats(struct device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd);
-#endif
static void set_rx_mode(struct device *dev);
\f
/* 'options' is used to pass a transceiver override or full-duplex flag
e.g. "options=16" for FD, "options=32" for 100mbps-only. */
static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#ifdef MODULE
static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+#ifdef MODULE
static int debug = -1; /* The debug level */
#endif
static int pci_index = 0;
for (; pci_index < 8; pci_index++) {
unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
-#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
int pci_ioaddr;
-#else
- long pci_ioaddr;
-#endif
- unsigned short pci_command;
+
+ unsigned short pci_command, new_command;
if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82557,
/* Get and check the bus-master and latency values. */
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;
+ new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+ if (pci_command != new_command) {
+ printk(KERN_INFO " The PCI BIOS has not enabled this"
+ " device! Updating PCI command %4.4x->%4.4x.\n",
+ pci_command, new_command);
pcibios_write_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, pci_command);
+ PCI_COMMAND, new_command);
}
pcibios_read_config_byte(pci_bus, pci_device_fn,
PCI_LATENCY_TIMER, &pci_latency);
- if (pci_latency < 10) {
+ if (pci_latency < 32) {
printk(" PCI latency timer (CFLT) is unreasonably low at %d."
- " Setting to 255 clocks.\n", pci_latency);
+ " Setting to 32 clocks.\n", pci_latency);
pcibios_write_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, 255);
+ PCI_LATENCY_TIMER, 32);
} else if (speedo_debug > 1)
printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency);
-#ifdef MODULE
- speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found],
- cards_found);
-#else
- speedo_found1(dev, pci_ioaddr, pci_irq_line,
- dev ? dev->mem_start : 0, -1);
-#endif
+ speedo_found1(dev, pci_ioaddr, pci_irq_line, cards_found);
dev = NULL;
cards_found++;
}
return cards_found;
}
-static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
+static void speedo_found1(struct device *dev, int ioaddr, int irq,
int card_idx)
{
static int did_version = 0; /* Already printed version info. */
struct speedo_private *sp;
char *product;
- int i;
+ int i, option;
u16 eeprom[0x40];
if (speedo_debug > 0 && did_version++ == 0)
printk(version);
-#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
dev = init_etherdev(dev, sizeof(struct speedo_private));
-#else
- dev = init_etherdev(dev, sizeof(struct speedo_private), 0);
-#endif
+
+ if (dev->mem_start > 0)
+ option = dev->mem_start;
+ else if (card_idx >= 0 && options[card_idx] >= 0)
+ option = options[card_idx];
+ else
+ option = 0;
/* Read the station address EEPROM before doing the reset.
Perhaps this should even be done before accepting the device,
if (eeprom[7] & 0x0700)
printk(KERN_INFO " Secondary interface chip %s.\n",
phys[(eeprom[7]>>8)&7]);
-#if defined(notdef)
- /* ToDo: Read and set PHY registers through MDIO port. */
- for (i = 0; i < 2; i++)
- printk(KERN_INFO" MDIO register %d is %4.4x.\n",
- i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
- for (i = 5; i < 7; i++)
- printk(KERN_INFO" MDIO register %d is %4.4x.\n",
- i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
- printk(KERN_INFO" MDIO register %d is %4.4x.\n",
- 25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
-#endif
if (((eeprom[6]>>8) & 0x3f) == DP83840
|| ((eeprom[6]>>8) & 0x3f) == DP83840A) {
int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
mdi_reg23);
mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
}
- if ((options >= 0) && (options & 0x60)) {
+ if ((option >= 0) && (option & 0x70)) {
printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n",
- (options & 0x20 ? 100 : 10),
- (options & 0x10 ? "full" : "half"));
+ (option & 0x20 ? 100 : 10),
+ (option & 0x10 ? "full" : "half"));
mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
- ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */
- ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ ((option & 0x20) ? 0x2000 : 0) | /* 100mbps? */
+ ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
}
/* Perform a system self-test. */
self_test_results[1] = -1;
outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
do {
-#ifdef _LINUX_DELAY_H
udelay(10);
-#else
- SLOW_DOWN_IO;
-#endif
} while (self_test_results[1] == -1 && --boguscnt >= 0);
if (boguscnt < 0) { /* Test optimized out. */
sp->next_module = root_speedo_dev;
root_speedo_dev = dev;
+ sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
if (card_idx >= 0) {
if (full_duplex[card_idx] >= 0)
sp->full_duplex = full_duplex[card_idx];
- } else
- sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
- sp->default_port = options >= 0 ? (options & 0x0f) : 0;
+ }
+ sp->default_port = option >= 0 ? (option & 0x0f) : 0;
sp->phy[0] = eeprom[6];
sp->phy[1] = eeprom[7];
dev->hard_start_xmit = &speedo_start_xmit;
dev->stop = &speedo_close;
dev->get_stats = &speedo_get_stats;
-#ifdef NEW_MULTICAST
dev->set_multicast_list = &set_rx_mode;
-#endif
-#ifdef HAVE_PRIVATE_IOCTL
dev->do_ioctl = &speedo_ioctl;
-#endif
return;
}
#define EE_ENB (0x4800 | EE_CS)
/* Delay between EEPROM clock transitions.
- This is a "nasty" timing loop, but PC compatible machines are defined
- to delay an ISA compatible period for the SLOW_DOWN_IO macro. */
-#ifdef _LINUX_DELAY_H
+ This will actually work with no delay on 33Mhz PCI. */
#define eeprom_delay(nanosec) udelay(1);
-#else
-#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-#endif
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << 6)
eeprom_delay(100);
outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
eeprom_delay(150);
- outw(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */
- eeprom_delay(250);
}
outw(EE_ENB, ee_addr);
static int mdio_read(int ioaddr, int phy_id, int location)
{
- int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+ int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */
outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
do {
-#ifdef _LINUX_DELAY_H
- udelay(16);
-#else
- SLOW_DOWN_IO;
-#endif
val = inl(ioaddr + SCBCtrlMDI);
if (--boguscnt < 0) {
printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
static int mdio_write(int ioaddr, int phy_id, int location, int value)
{
- int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+ int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */
outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
ioaddr + SCBCtrlMDI);
do {
-#ifdef _LINUX_DELAY_H
- udelay(16);
-#else
- SLOW_DOWN_IO;
-#endif
val = inl(ioaddr + SCBCtrlMDI);
if (--boguscnt < 0) {
printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
#ifdef notdef
/* We could reset the chip, but should not need to. */
outl(0, ioaddr + SCBPort);
- for (i = 40; i >= 0; i--)
- SLOW_DOWN_IO; /* At least 250ns */
+ udelay(10);
#endif
-#ifdef SA_SHIRQ
if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
"Intel EtherExpress Pro 10/100 Ethernet", dev)) {
return -EAGAIN;
}
-#else
-#ifdef USE_SHARED_IRQ
- if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
- "Intel EtherExpress Pro 10/100 Ethernet"))
- return -EAGAIN;
-#else
- if (dev->irq < 2 || dev->irq > 15 || irq2dev_map[dev->irq] != NULL)
- return -EAGAIN;
- irq2dev_map[dev->irq] = dev;
- if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
- irq2dev_map[dev->irq] = NULL;
- return -EAGAIN;
- }
-#endif
-#endif
-
if (speedo_debug > 1)
printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
dev->if_port = sp->default_port;
+ sp->in_interrupt = 0;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
-#ifndef KERNEL_1_2
- skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-#else
skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
-#endif
sp->rx_skbuff[i] = skb;
if (skb == NULL)
break; /* Bad news! */
skb->dev = dev; /* Mark as being used by this device. */
-#if LINUX_VERSION_CODE >= 0x10300
rxf = (struct RxFD *)skb->tail;
skb_reserve(skb, sizeof(struct RxFD));
-#else
- /* Save the data in the header region -- it's restored later. */
- rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD));
- memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD));
-#endif
sp->rx_ringp[i] = rxf;
if (last_rxf)
last_rxf->link = virt_to_bus(rxf);
last_rxf = rxf;
rxf->status = 0x00000001; /* '1' is flag value only. */
rxf->link = 0; /* None yet. */
-#if LINUX_VERSION_CODE < 0x10300
/* This field unused by i82557, we use it as a consistency check. */
- rxf->rx_buf_addr = virt_to_bus(skb->data);
-#else
rxf->rx_buf_addr = virt_to_bus(skb->tail);
-#endif
+
rxf->count = 0;
rxf->size = PKT_BUF_SZ;
}
{
struct speedo_private *sp = (struct speedo_private *)dev->priv;
int ioaddr = dev->base_addr;
- int i;
printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
"command %4.4x.\n",
dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
-#ifndef final_version
- printk(KERN_WARNING "%s: Tx timeout fill index %d scavenge index %d.\n",
- dev->name, sp->cur_tx, sp->dirty_tx);
- printk(KERN_WARNING " Tx queue ");
- for (i = 0; i < TX_RING_SIZE; i++)
- printk(" %8.8x", (int)sp->tx_ring[i].status);
- printk(".\n" KERN_WARNING " Rx ring ");
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(" %8.8x", (int)sp->rx_ringp[i]->status);
- printk(".\n");
-#else
- dev->if_port ^= 1;
- printk(KERN_WARNING " (Media type switching not yet implemented.)\n");
- /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
-#endif
if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
dev->name);
} else {
outw(DRVR_INT, ioaddr + SCBCmd);
}
- /* Reset the MII transceiver. */
- if ((sp->phy[0] & 0x8000) == 0)
- mdio_write(ioaddr, sp->phy[0] & 0x1f, 0, 0x8000);
+ /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
+ if ((sp->phy[0] & 0x8000) == 0) {
+ int phy_addr = sp->phy[0] & 0x1f;
+ mdio_write(ioaddr, phy_addr, 0, 0x0400);
+ mdio_write(ioaddr, phy_addr, 1, 0x0000);
+ mdio_write(ioaddr, phy_addr, 4, 0x0000);
+ mdio_write(ioaddr, phy_addr, 0, 0x8000);
+ }
sp->stats.tx_errors++;
dev->trans_start = jiffies;
return;
int ioaddr = dev->base_addr;
int entry;
- if (skb == NULL || skb->len <= 0) {
- printk(KERN_ERR "%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 (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
sp->tx_full = 1;
else
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
dev->trans_start = jiffies;
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
-#ifdef SA_SHIRQ
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-#else
-static void speedo_interrupt(int irq, struct pt_regs *regs)
-#endif
{
-#ifdef SA_SHIRQ
struct device *dev = (struct device *)dev_instance;
-#else
-#ifdef USE_SHARED_IRQ
- struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
-#else
- struct device *dev = (struct device *)(irq2dev_map[irq]);
-#endif
-#endif
struct speedo_private *sp;
int ioaddr, boguscnt = max_interrupt_work;
unsigned short status;
ioaddr = dev->base_addr;
sp = (struct speedo_private *)dev->priv;
#ifndef final_version
- if (dev->interrupt) {
- printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+ /* A lock to prevent simultaneous entry on SMP machines. */
+ if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
+ printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+ dev->name);
return;
}
dev->interrupt = 1;
speedo_rx(dev);
if (status & 0x1000) {
-#ifdef notdef
- int i;
- printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
- " state -- %4.4x! Index %d (%d).\n", dev->name, status,
- sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
- printk(KERN_WARNING " Rx ring:\n ");
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(" %d %8.8x %8.8x %8.8x %d %d.\n",
- i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
- sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count,
- sp->rx_ringp[i]->size);
-#endif
-
if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
outw(RX_RESUMENR, ioaddr + SCBCmd);
else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
&& dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
/* The ring is no longer full, clear tbusy. */
sp->tx_full = 0;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
dev->name, inw(ioaddr + SCBStatus));
-#ifndef final_version
- /* Special code for testing *only*. */
- {
- static int stopit = 100;
- if (dev->start == 0 && --stopit < 0) {
- printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
- dev->name);
-#ifdef SA_SHIRQ
- free_irq(irq, dev);
-#else
- free_irq(irq);
-#endif
- }
- }
-#endif
-
dev->interrupt = 0;
+ clear_bit(0, (void*)&sp->in_interrupt);
return;
}
/* Pass up the skb already on the Rx ring. */
skb = sp->rx_skbuff[entry];
-#ifdef KERNEL_1_2
- temp = skb->data;
- if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
- printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
- " in speedo_rx: %p vs. %p / %p.\n", dev->name,
- bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
- temp, skb->data);
- /* Get a fresh skbuff to replace the filled one. */
- newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
-#else
temp = skb_put(skb, pkt_len);
if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
/* Get a fresh skbuff to replace the filled one. */
newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-#endif
+
if (newskb) {
struct RxFD *rxf;
rx_in_place = 1;
sp->rx_skbuff[entry] = newskb;
newskb->dev = dev;
-#ifdef KERNEL_1_2
- /* Restore the data in the old header region. */
- memcpy(skb->data - sizeof(struct RxFD),
- &sp->saved_skhead[entry], sizeof(struct RxFD));
- /* Save the data in this header region. */
- rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD));
- sp->rx_ringp[entry] = rxf;
- memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD));
- rxf->rx_buf_addr = virt_to_bus(newskb->data);
-#else
rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
skb_reserve(newskb, sizeof(struct RxFD));
/* Unused by i82557, consistency check only. */
rxf->rx_buf_addr = virt_to_bus(newskb->tail);
-#endif
rxf->status = 0x00000001;
} else /* No memory, drop the packet. */
skb = 0;
} else
-#ifdef KERNEL_1_2
- skb = alloc_skb(pkt_len, GFP_ATOMIC);
-#else
skb = dev_alloc_skb(pkt_len + 2);
-#endif
if (skb == NULL) {
int i;
printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name);
break;
}
skb->dev = dev;
-#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
if (! rx_in_place) {
skb_reserve(skb, 2); /* 16 byte align the data fields */
#if defined(__i386) && notyet
#endif
}
skb->protocol = eth_type_trans(skb, dev);
-#else
-#ifdef KERNEL_1_3
-#warning This code has only been tested with later 1.3.* kernels.
- skb->len = pkt_len;
- memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
- pkt_len);
- /* Needed for 1.3.*. */
- skb->protocol = eth_type_trans(skb, dev);
-#else /* KERNEL_1_2 */
- skb->len = pkt_len;
- if (! rx_in_place) {
- memcpy(skb->data,
- bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
- }
-#endif
-#endif
netif_rx(skb);
sp->stats.rx_packets++;
}
outw(INT_MASK, ioaddr + SCBCmd);
outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
-#ifdef SA_SHIRQ
free_irq(dev->irq, dev);
-#else
- free_irq(dev->irq);
- irq2dev_map[dev->irq] = 0;
-#endif
/* Free all the skbuffs in the Rx and Tx queues. */
for (i = 0; i < RX_RING_SIZE; i++) {
return &sp->stats;
}
-#ifdef HAVE_PRIVATE_IOCTL
static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
struct speedo_private *sp = (struct speedo_private *)dev->priv;
return -EOPNOTSUPP;
}
}
-#endif /* HAVE_PRIVATE_IOCTL */
/* Set or clear the multicast filter for this adaptor.
This is very ugly with Intel chips -- we usually have to execute an
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
new_rx_mode = 3;
- } else if (dev->flags & IFF_ALLMULTI) {
+ } else if ((dev->flags & IFF_ALLMULTI) ||
+ dev->mc_count > multicast_filter_limit) {
new_rx_mode = 1;
} else
new_rx_mode = 0;
struct dev_mc_list *mclist;
u16 *eaddrs;
struct descriptor *mc_setup_frm = sp->mc_setup_frm;
- u16 *setup_params = (u16 *)mc_setup_frm->params;
+ u16 *setup_params;
int i;
if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
}
\f
#ifdef MODULE
-#if (LINUX_VERSION_CODE < VERSION(1,3,38)) /* 1.3.38 and later */
-char kernel_version[] = UTS_RELEASE;
-#endif
-
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(congenb, "i");
-MODULE_PARM(txfifo, "i");
-MODULE_PARM(rxfifo, "i");
-MODULE_PARM(txdmacount, "i");
-MODULE_PARM(rxdmacount, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(max_interrupt_work, "i");
-#endif
int
init_module(void)
\f
/*
* Local variables:
- * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
*/
static const char *version =
-"epic100.c:v0.99B 2/6/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
+"epic100.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
/* A few user-configurable values. */
if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
flag = 0x10; /* No interrupt */
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
flag = 0x14; /* Tx-done intr. */
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
flag = 0x10; /* No Tx-done intr. */
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else {
/* Leave room for two additional entries. */
flag = 0x14; /* Tx-done intr. */
&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
/* The ring is no longer full, clear tbusy. */
lp->tx_full = 0;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
return -EPERM;
}
if (register_netdev(dev) != 0) {
- printk(KERN_WARNING "ne.c: No PCnet/LANCE card found (i/o = 0x%x).\n", io[this_dev]);
+ printk(KERN_WARNING "lance.c: No PCnet/LANCE card found (i/o = 0x%x).\n", io[this_dev]);
if (found != 0) return 0; /* Got at least one. */
return -ENXIO;
}
* This driver is for AMD PCnet-PCI based ethercards
*/
-static const char *version = "pcnet32.c:v0.99A 4/2/98 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v0.99B 4/4/98 DJBecker/TSBogend.\n";
/* A few user-configurable values. */
unsigned long lock;
};
+static struct pcnet_chip_type {
+ int id_number;
+ const char *name;
+ int flags;
+} chip_table[] = {
+ {0x2420, "PCnet/PCI 79C970", 0},
+ {0x2430, "PCnet32", 0},
+ {0x2621, "PCnet/PCI II 79C970A", 0},
+ {0x2623, "PCnet/PCI II 79C971A", 0},
+ {0x0, "PCnet32 (unknown)", 0},
+};
+\f
+/* Index of functions. */
int pcnet32_probe(struct device *dev);
static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line);
static int pcnet32_open(struct device *dev);
{
struct pcnet32_private *lp;
int i;
- char *chipname;
+ const char *chipname;
/* check if there is really a pcnet chip on that ioaddr */
if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57))
if ((chip_version & 0xfff) != 0x003)
return ENODEV;
chip_version = (chip_version >> 12) & 0xffff;
- switch (chip_version) {
- case 0x2420:
- chipname = "PCnet/PCI 79C970";
- break;
- case 0x2430:
- chipname = "PCnet32";
- break;
- case 0x2621:
- chipname = "PCnet/PCI II 79C970A";
- break;
- default:
- printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
- return ENODEV;
- }
+ for (i = 0; chip_table[i].id_number; i++)
+ if (chip_table[i].id_number == chip_version)
+ break;
+ chipname = chip_table[i].name;
}
dev = init_etherdev(dev, 0);
dev->name, (void *)dev)) {
return -EAGAIN;
}
+ MOD_INC_USE_COUNT;
/* Reset the PCNET32 */
inw(ioaddr+PCNET32_RESET);
outw(0x0004, ioaddr+PCNET32_DATA);
free_irq(dev->irq, dev);
+ MOD_DEC_USE_COUNT;
return 0;
}
Support and updates available at
http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html
+
+ Twister-tuning code contributed by Kinston <shangh@realtek.com.tw>.
*/
static const char *version =
-"rtl8139.c:v0.99A 2/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n";
+"rtl8139.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n";
/* A few user-configurable values. */
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
#include <linux/delay.h>
-#ifdef SA_SHIRQ
-#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
-#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
-#else
-#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
-#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
-#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
-#endif
-
#if (LINUX_VERSION_CODE < 0x20123)
#define test_and_set_bit(val, addr) set_bit(val, addr)
#endif
FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B,
MultiIntr=0x5C, TxSummary=0x60,
BMCR=0x62, BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, NWayExpansion=0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS=0x70, /* FIFO Test Mode Select */
+ CSCR=0x74, /* Chip Status and Configuration Register. */
+ PARA78=0x78, PARA7c=0x7c, /* Magic transceiver parameter register. */
};
enum ChipCmdBits {
RxBadAlign=0x0002, RxStatusOK=0x0001,
};
+enum CSCRBits {
+ CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
+ CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
+ CSCR_LinkDownCmd=0x0f3c0,
+};
+
+/* Twister tuning parameters from RealTek. Completely undocumented. */
+unsigned long param[4][4]={
+ {0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43},
+ {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
+ {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
+ {0x0bb39de43,0x0bb39ce43,0x0bb39ce83,0x0bb39ce83}
+};
+
struct rtl8129_private {
char devname[8]; /* Used only for kernel debugging. */
const char *product_name;
unsigned char *tx_bufs; /* Tx bounce buffer region. */
unsigned char mc_filter[8]; /* Current multicast filter. */
char phys[4]; /* MII device addresses. */
- int in_interrupt; /* Alpha need word-wide lock. */
+ int in_interrupt; /* Alpha needs word-wide lock. */
unsigned int tx_full:1; /* The Tx queue is full. */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int default_port:4; /* Last dev->if_port value. */
/* Soft reset the chip. */
outb(CmdReset, ioaddr + ChipCmd);
- if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ,
- "RealTek RTL8129/39 Fast Ethernet", dev)) {
+ if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, dev)) {
return -EAGAIN;
}
int ioaddr = dev->base_addr;
int i;
+ /* Disable interrupts by clearing the interrupt mask. */
+ outw(0x0000, ioaddr + IntrMask);
+
if (rtl8129_debug > 0)
printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x.\n",
dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus));
dev->name, inw(ioaddr + BMSR));
}
-#ifdef notdef
/* Soft reset the chip. */
outb(CmdReset, ioaddr + ChipCmd);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + MAC0 + i);
-#endif
+
+ { /* Save the unsent Tx packets. */
+ struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
+ int j = 0;
+ for (; tp->cur_tx - tp->dirty_tx > 0 ; tp->dirty_tx++)
+ saved_skb[j++] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC];
+ tp->dirty_tx = tp->cur_tx = 0;
+
+ for (i = 0; i < j; i++) {
+ skb = tp->tx_skbuff[i] = saved_skb[i];
+ if ((long)skb->data & 3) { /* Must use alignment buffer. */
+ memcpy(tp->tx_buf[i], skb->data, skb->len);
+ outl(virt_to_bus(tp->tx_buf[i]), ioaddr + TxAddr0 + i*4);
+ } else
+ outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + i*4);
+ /* Note: the chip doesn't have auto-pad! */
+ outl(((TX_FIFO_THRESH<<11) & 0x003f0000) |
+ (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
+ ioaddr + TxStat0 + i*4);
+ }
+ tp->cur_tx = i;
+ while (i < NUM_TX_DESC)
+ tp->tx_skbuff[i] = 0;
+ if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
+ dev->tbusy = 0;
+ } else {
+ tp->tx_full = 1;
+ }
+ }
/* Must enable Tx/Rx before setting transfer thresholds! */
set_rx_mode(dev);
dev->trans_start = jiffies;
tp->stats.tx_errors++;
+ /* Enable all known interrupts by setting the interrupt mask. */
+ outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
+ | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
return;
}
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
-static void rtl8129_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
+static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
struct device *dev = (struct device *)dev_instance;
struct rtl8129_private *tp;
dev->name, inl(ioaddr + IntrStatus));
dev->interrupt = 0;
- tp->in_interrupt = 0;
+ clear_bit(0, (void*)&tp->in_interrupt);
return;
}
-/* Todo: The data sheet doesn't describe the Rx ring at all, so I'm winging
- it here until I have a chip to play with. 8/30/97 */
+/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
+ field alignments and semantics. */
static int
rtl8129_rx(struct device *dev)
{
tp->stats.rx_frame_errors++;
if (rx_status & (RxRunt|RxTooLong)) tp->stats.rx_length_errors++;
if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++;
+ /* Reset the receiver, based on RealTek recommendation. (Bug?) */
+ tp->cur_rx = 0;
+ outb(CmdTxEnb, ioaddr + ChipCmd);
+ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
+ outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) |
+ (RX_DMA_BURST<<8), ioaddr + RxConfig);
} else {
/* Malloc up new buffer, compatible with net-2e. */
/* Omit the four octet CRC from the length. */
return crc;
}
-
static void set_rx_mode(struct device *dev)
{
int ioaddr = dev->base_addr;
\f
/*
* Local variables:
- * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int ultra32_open(struct device *dev)
{
int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */
+ int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ;
- if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
+ if (request_irq(dev->irq, ei_interrupt, irq_flags, ei_status.name, dev))
return -EAGAIN;
outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */
* tlan.c
* by James Banks, james.banks@caldera.com
*
- * (C) 1997-1998 Caldera, Inc.
+ * (C) 1997 Caldera, Inc.
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
- ** This file is best viewed/edited with columns>=132.
+ ** This file is best viewed/edited with tabstop=4 and colums>=132.
*
** Useful (if not required) reading:
*
* Texas Instruments, ThunderLAN Programmer's Guide,
* TI Literature Number SPWU013A
* available in PDF format from www.ti.com
- * Level One, LXT901 and LXT970 Data Sheets
- * available in PDF format from www.level1.com
* National Semiconductor, DP83840A Data Sheet
* available in PDF format from www.national.com
* Microchip Technology, 24C01A/02A/04A Data Sheet
static int debug = 0;
static int aui = 0;
-static int sa_int = 0;
-static int bbuf = 0;
-static int duplex = 0;
-static int speed = 0;
static u8 *TLanPadBuffer;
static char TLanSignature[] = "TLAN";
static int TLanVersionMajor = 0;
-static int TLanVersionMinor = 42;
+static int TLanVersionMinor = 38;
-static TLanAdapterEntry TLanAdapterList[] = {
+static TLanPciId TLanDeviceList[] = {
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETELLIGENT_10,
- "Compaq Netelligent 10",
- TLAN_ADAPTER_ACTIVITY_LED
+ "Compaq Netelligent 10"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETELLIGENT_10_100,
- "Compaq Netelligent 10/100",
- TLAN_ADAPTER_ACTIVITY_LED
+ "Compaq Netelligent 10/100"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED,
- "Compaq Integrated NetFlex-3/P",
- TLAN_ADAPTER_NONE
+ "Compaq Integrated NetFlex-3/P"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETFLEX_3P,
- "Compaq NetFlex-3/P",
- TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY
+ "Compaq NetFlex-3/P"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETFLEX_3P_BNC,
- "Compaq NetFlex-3/P",
- TLAN_ADAPTER_NONE
+ "Compaq NetFlex-3/P"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT,
- "Compaq ProLiant Netelligent 10/100",
- TLAN_ADAPTER_NONE
+ "Compaq ProLiant Netelligent 10/100"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL,
- "Compaq Dual Port Netelligent 10/100",
- TLAN_ADAPTER_NONE
+ "Compaq Dual Port Netelligent 10/100"
},
{ PCI_VENDOR_ID_COMPAQ,
PCI_DEVICE_ID_DESKPRO_4000_5233MMX,
- "Compaq Integrated Netelligent 10/100",
- TLAN_ADAPTER_NONE
- },
- { PCI_VENDOR_ID_OLICOM,
- PCI_DEVICE_ID_OC_2326,
- "Olicom OC-2326",
- TLAN_ADAPTER_USE_INTERN_10
+ "Compaq Deskpro 4000 5233MMX"
},
{ 0,
0,
- NULL,
- 0
+ NULL
} /* End of List */
};
static void TLan_Timer( unsigned long );
static void TLan_ResetLists( struct device * );
-static void TLan_FreeLists( struct device * );
static void TLan_PrintDio( u16 );
static void TLan_PrintList( TLanList *, char *, int );
static void TLan_ReadAndClearStats( struct device *, int );
-static void TLan_ResetAdapter( struct device * );
-static void TLan_FinishReset( struct device * );
+static int TLan_Reset( struct device * );
static void TLan_SetMac( struct device *, int areg, char *mac );
-static void TLan_PhyPrint( struct device * );
-static void TLan_PhyDetect( struct device * );
-static void TLan_PhyPowerDown( struct device * );
-static void TLan_PhyPowerUp( struct device * );
-static void TLan_PhyReset( struct device * );
-static void TLan_PhyStartLink( struct device * );
-static void TLan_PhyFinishAutoNeg( struct device * );
-/*
static int TLan_PhyNop( struct device * );
+static void TLan_PhyPrint( struct device * );
+static void TLan_PhySelect( struct device * );
static int TLan_PhyInternalCheck( struct device * );
static int TLan_PhyInternalService( struct device * );
static int TLan_PhyDp83840aCheck( struct device * );
-*/
-static int TLan_MiiReadReg( struct device *, u16, u16, u16 * );
+static int TLan_MiiReadReg(u16, u16, u16, u16 *);
static void TLan_MiiSendData( u16, u32, unsigned );
-static void TLan_MiiSync( u16 );
-static void TLan_MiiWriteReg( struct device *, u16, u16, u16 );
+static void TLan_MiiSync(u16);
+static void TLan_MiiWriteReg(u16, u16, u16, u16);
static void TLan_EeSendStart( u16 );
static int TLan_EeSendByte( u16, u8, int );
static void TLan_EeReceiveByte( u16, u8 *, int );
-static int TLan_EeReadByte( struct device *, u8, u8 * );
+static int TLan_EeReadByte( u16, u8, u8 * );
static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
TLan_HandleRxEOC
};
-static inline void
-TLan_SetTimer( struct device *dev, u32 ticks, u32 type )
-{
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-
- cli();
- if ( priv->timer.function != NULL ) {
- return;
- }
- priv->timer.function = &TLan_Timer;
- sti();
-
- priv->timer.data = (unsigned long) dev;
- priv->timer.expires = jiffies + ticks;
- priv->timerSetAt = jiffies;
- priv->timerType = type;
- add_timer( &priv->timer );
-
-} /* TLan_SetTimer */
/*****************************************************************************
*
* This function begins the setup of the driver creating a
* pad buffer, finding all TLAN devices (matching
- * TLanAdapterList entries), and creating and initializing a
+ * TLanDeviceList entries), and creating and initializing a
* device structure for each adapter.
*
**************************************************************/
struct device *dev;
size_t dev_size;
u8 dfn;
- u32 index;
+ u32 dl_ix;
int failed;
int found;
u32 io_base;
u8 irq;
u8 rev;
- printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n",
+ printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
TLanVersionMajor,
TLanVersionMinor
);
dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
- while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) {
+ while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) {
dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
if ( dev == NULL ) {
printk( "TLAN: Could not allocate memory for device.\n" );
dev->irq = irq;
dev->init = TLan_Init;
- priv->adapter = &TLanAdapterList[index];
- priv->adapterRev = rev;
- priv->aui = aui;
- priv->duplex = duplex;
- priv->speed = speed;
- priv->sa_int = sa_int;
- priv->debug = debug;
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
ether_setup( dev );
failed = register_netdev( dev );
if ( failed ) {
- printk( "TLAN: Could not register device.\n" );
+ printk( "TLAN: Could not register network device. Freeing struct.\n" );
kfree( dev );
} else {
priv->nextDevice = TLanDevices;
TLanDevices = dev;
TLanDevicesInstalled++;
- printk("TLAN: %s irq=%2d io=%04x, %s, Rev. %d\n",
- dev->name,
- (int) dev->irq,
- (int) dev->base_addr,
- priv->adapter->deviceLabel,
- priv->adapterRev );
+ printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName );
}
}
- /* printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); */
+ // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled );
return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
struct device *dev;
TLanPrivateInfo *priv;
- while ( TLanDevicesInstalled ) {
+ while (TLanDevicesInstalled) {
dev = TLanDevices;
priv = (TLanPrivateInfo *) dev->priv;
- if ( priv->dmaStorage ) {
+ if ( priv->dmaStorage )
kfree( priv->dmaStorage );
- }
release_region( dev->base_addr, 0x10 );
unregister_netdev( dev );
TLanDevices = priv->nextDevice;
extern int tlan_probe( struct device *dev )
{
- TLanPrivateInfo *priv;
static int pad_allocated = 0;
int found;
+ TLanPrivateInfo *priv;
u8 bus, dfn, irq, rev;
- u32 io_base, index;
-
- found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index );
+ u32 io_base, dl_ix;
- if ( ! found ) {
- return -ENODEV;
- }
-
- dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
-
- if ( dev->priv == NULL ) {
- printk( "TLAN: Could not allocate memory for device.\n" );
- return -ENOMEM;
- }
-
- memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
-
- if ( ! pad_allocated ) {
- TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,
-// ( GFP_KERNEL | GFP_DMA )
- ( GFP_KERNEL )
- );
- if ( TLanPadBuffer == NULL ) {
- printk( "TLAN: Could not allocate memory for padding.\n" );
- kfree( dev->priv );
- return -ENOMEM;
- } else {
- pad_allocated = 1;
- memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+ found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
+ if ( found ) {
+ dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+ if ( dev->priv == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
}
- }
-
- priv = (TLanPrivateInfo *) dev->priv;
-
- dev->name = priv->devName;
- strcpy( priv->devName, " " );
-
- dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+ memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+ priv = (TLanPrivateInfo *) dev->priv;
- dev->base_addr = io_base;
- dev->irq = irq;
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+ dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
- priv->adapter = &TLanAdapterList[index];
- priv->adapterRev = rev;
- priv->aui = dev->mem_start & 0x01;
- priv->duplex = ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : dev->mem_start & 0x0C >> 2;
- priv->speed = ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : dev->mem_start & 0x30 >> 4;
- priv->sa_int = dev->mem_start & 0x02;
- priv->debug = dev->mem_end;
+ dev->base_addr = io_base;
+ dev->irq = irq;
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
- printk("TLAN %d.%d: %s irq=%2d io=%04x, %s, Rev. %d\n",
- TLanVersionMajor,
- TLanVersionMinor,
- dev->name,
- (int) irq,
- io_base,
- priv->adapter->deviceLabel,
- priv->adapterRev );
-
- TLan_Init( dev );
+ if ( ! pad_allocated ) {
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ } else {
+ pad_allocated = 1;
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+ }
+ }
+ printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n",TLanVersionMajor,
+ TLanVersionMinor,
+ dev->name,
+ (int) irq,
+ io_base,
+ TLanDeviceList[dl_ix].deviceName );
+ TLan_Init( dev );
+ }
- return 0;
+ return ( ( found ) ? 0 : -ENODEV );
} /* tlan_probe */
* of the adapter.
*
* This function searches for an adapter with PCI vendor
- * and device IDs matching those in the TLanAdapterList.
+ * and device IDs matching those in the TLanDeviceList.
* The function 'remembers' the last device it found,
* and so finds a new device (if anymore are to be found)
* each time the function is called. It then looks up
return 0;
}
- for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) {
+ for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
not_found = pcibios_find_device(
- TLanAdapterList[dl_index].vendorId,
- TLanAdapterList[dl_index].deviceId,
+ TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId,
pci_index,
pci_bus,
pci_dfn
TLAN_DBG(
TLAN_DEBUG_GNRL,
"TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
- TLanAdapterList[dl_index].vendorId,
- TLanAdapterList[dl_index].deviceId
+ TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId
);
pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev);
if ( *pci_io_base == 0 )
printk("TLAN: IO mapping not available, ignoring device.\n");
- if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) {
- pcibios_write_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER );
- printk( "TLAN: Attempting to activate busmastering.\n" );
- printk( "TLAN: You may need to set busmastering to on in the CMOS\n" );
- printk( "TLAN: before this card will work.\n" );
- *pci_io_base = 0;
+ if (pci_command & PCI_COMMAND_MASTER) {
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n");
}
pci_index++;
int TLan_Init( struct device *dev )
{
- int dma_size;
- int err;
- int i;
+ int dma_size;
+ int err;
+ int i;
TLanPrivateInfo *priv;
priv = (TLanPrivateInfo *) dev->priv;
}
request_region( dev->base_addr, 0x10, TLanSignature );
- if ( bbuf ) {
- dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
* ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
- } else {
- dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
- * ( sizeof(TLanList) );
- }
-
priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
if ( priv->dmaStorage == NULL ) {
printk( "TLAN: Could not allocate lists and buffers for %s.\n",
priv->rxList = (TLanList *)
( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
-
- if ( bbuf ) {
- priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
- priv->txBuffer = priv->rxBuffer
- + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
- }
+ priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+ priv->txBuffer = priv->rxBuffer
+ + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
err = 0;
for ( i = 0; i < 6 ; i++ )
- err |= TLan_EeReadByte( dev,
+ err |= TLan_EeReadByte( dev->base_addr,
(u8) 0x83 + i,
(u8 *) &dev->dev_addr[i] );
- if ( err ) {
+ if ( err )
printk( "TLAN: %s: Error reading MAC from eeprom: %d\n",
dev->name,
err );
- }
-
dev->addr_len = 6;
dev->open = &TLan_Open;
dev->get_stats = &TLan_GetStats;
dev->set_multicast_list = &TLan_SetMulticastList;
+#ifndef MODULE
+
+ aui = dev->mem_start & 0x01;
+ debug = dev->mem_end;
+
+#endif /* MODULE */
return 0;
int TLan_Open( struct device *dev )
{
+ int err;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- int err;
priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
- if ( priv->sa_int ) {
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Using SA_INTERRUPT\n" );
- err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev );
- } else {
- err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
- }
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
if ( err ) {
printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
return -EAGAIN;
}
-
+
MOD_INC_USE_COUNT;
dev->tbusy = 0;
*/
TLan_ResetLists( dev );
TLan_ReadAndClearStats( dev, TLAN_IGNORE );
- TLan_ResetAdapter( dev );
+ TLan_Reset( dev );
+ TLan_Reset( dev );
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+ if ( debug >= 1 )
+ outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+
+ init_timer( &priv->timer );
+ priv->timer.data = (unsigned long) dev;
+ priv->timer.function = &TLan_Timer;
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Opened. TLAN Chip Rev: %x\n", dev->name, priv->tlanRev );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev );
return 0;
int TLan_StartTx( struct sk_buff *skb, struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- TLanList *tail_list;
- u8 *tail_buffer;
- int pad;
+ TLanList *tail_list;
+ u8 *tail_buffer;
+ int pad;
if ( ! priv->phyOnline ) {
TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name );
}
tail_list = priv->txList + priv->txTail;
-
if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
dev->tbusy = 1;
priv->txBusyCount++;
return 1;
}
-
tail_list->forward = 0;
-
- if ( bbuf ) {
- tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
- memcpy( tail_buffer, skb->data, skb->len );
- } else {
- tail_list->buffer[0].address = virt_to_bus( skb->data );
- tail_list->buffer[9].address = (u32) skb;
- }
-
+ tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+ memcpy( tail_buffer, skb->data, skb->len );
pad = TLAN_MIN_FRAME_SIZE - skb->len;
-
if ( pad > 0 ) {
tail_list->frameSize = (u16) skb->len + pad;
tail_list->buffer[0].count = (u32) skb->len;
tail_list->buffer[1].count = 0;
tail_list->buffer[1].address = 0;
}
-
+ // are we transferring?
cli();
tail_list->cStat = TLAN_CSTAT_READY;
if ( ! priv->txInProgress ) {
outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
} else {
TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail );
- if ( priv->txTail == 0 ) {
+ if ( priv->txTail == 0 )
( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
- } else {
+ else
( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
- }
}
sti();
+ priv->txTail++;
+ if ( priv->txTail >= TLAN_NUM_TX_LISTS )
+ priv->txTail = 0;
- CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
-
- if ( bbuf ) {
- dev_kfree_skb( skb, FREE_WRITE );
- }
+ dev_kfree_skb( skb, FREE_WRITE );
dev->trans_start = jiffies;
return 0;
void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- u32 ack;
+ u32 ack;
struct device *dev;
- u32 host_cmd;
- u16 host_int;
- int type;
+ u32 host_cmd;
+ u16 host_int;
+ int type;
dev = (struct device *) dev_id;
- cli();
- if ( dev->interrupt ) {
+ if ( dev->interrupt )
printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
- }
dev->interrupt++;
+ cli();
+
host_int = inw( dev->base_addr + TLAN_HOST_INT );
- outw( host_int, dev->base_addr + TLAN_HOST_INT );
+ outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
ack = TLanIntVector[type]( dev, host_int );
+ sti();
+
if ( ack ) {
host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
}
dev->interrupt--;
- sti();
} /* TLan_HandleInterrupts */
TLan_ReadAndClearStats( dev, TLAN_RECORD );
outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
- if ( priv->timer.function != NULL )
+ if ( priv->timerSetAt != 0 )
del_timer( &priv->timer );
free_irq( dev->irq, dev );
- TLan_FreeLists( dev );
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name );
MOD_DEC_USE_COUNT;
void TLan_SetMulticastList( struct device *dev )
{
struct dev_mc_list *dmi = dev->mc_list;
- u32 hash1 = 0;
- u32 hash2 = 0;
- int i;
- u32 offset;
- u8 tmp;
+ u32 hash1 = 0;
+ u32 hash2 = 0;
+ int i;
+ u32 offset;
+ u8 tmp;
if ( dev->flags & IFF_PROMISC ) {
tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
{
host_int = 0;
- /* printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); */
+ // printk( "TLAN: Invalid interrupt on %s.\n", dev->name );
return 0;
} /* TLan_HandleInvalid */
u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- int eoc = 0;
- TLanList *head_list;
- u32 ack = 1;
+ int eoc = 0;
+ TLanList *head_list;
+ u32 ack = 1;
TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
host_int = 0;
head_list = priv->txList + priv->txHead;
-
- if ( ! bbuf ) {
- dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE );
- head_list->buffer[9].address = 0;
- }
-
if ( head_list->cStat & TLAN_CSTAT_EOC )
eoc = 1;
if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
}
+ // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
#if LINUX_KERNEL_VERSION > 0x20100
priv->stats->tx_bytes += head_list->frameSize;
head_list->cStat = TLAN_CSTAT_UNUSED;
dev->tbusy = 0;
- CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS );
+ priv->txHead++;
+ if ( priv->txHead >= TLAN_NUM_TX_LISTS )
+ priv->txHead = 0;
if ( eoc ) {
TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
head_list = priv->txList + priv->txHead;
priv->txInProgress = 0;
}
}
-
- if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
- if ( priv->timer.function == NULL ) {
- TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
- } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("TxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
priv->timerSetAt = jiffies;
+ // printk("TxEOF continuing timer...\n");
}
}
host_int = 0;
head_list = priv->rxList + priv->rxHead;
tail_list = priv->rxList + priv->rxTail;
-
- if ( head_list->cStat & TLAN_CSTAT_EOC ) {
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
eoc = 1;
- }
-
if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
printk( "TLAN: Received interrupt for uncompleted RX frame.\n" );
- } else if ( bbuf ) {
+ } else {
skb = dev_alloc_skb( head_list->frameSize + 7 );
if ( skb == NULL ) {
printk( "TLAN: Couldn't allocate memory for received data.\n" );
skb->dev = dev;
skb_reserve( skb, 2 );
t = (void *) skb_put( skb, head_list->frameSize );
+ // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
#if LINUX_KERNEL_VERSION > 0x20100
priv->stats->rx_bytes += head_list->frameSize;
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
}
- } else {
- skb = (struct sk_buff *) head_list->buffer[9].address;
- head_list->buffer[9].address = 0;
- skb_trim( skb, head_list->frameSize );
-
-#if LINUX_KERNEL_VERSION > 0x20100
- priv->stats->rx_bytes += head_list->frameSize;
-#endif
-
- skb->protocol = eth_type_trans( skb, dev );
- netif_rx( skb );
-
- skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
- if ( skb == NULL ) {
- printk( "TLAN: Couldn't allocate memory for received data.\n" );
- /* If this ever happened it would be a problem */
- } else {
- skb->dev = dev;
- skb_reserve( skb, 2 );
- t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
- head_list->buffer[0].address = virt_to_bus( t );
- head_list->buffer[9].address = (u32) skb;
- }
}
-
head_list->forward = 0;
head_list->frameSize = TLAN_MAX_FRAME_SIZE;
head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
tail_list->forward = virt_to_bus( head_list );
-
- CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS );
- CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS );
-
+ priv->rxHead++;
+ if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
+ priv->rxHead = 0;
+ priv->rxTail++;
+ if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
+ priv->rxTail = 0;
if ( eoc ) {
TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
head_list = priv->rxList + priv->rxHead;
ack |= TLAN_HC_GO | TLAN_HC_RT;
priv->rxEocCount++;
}
-
- if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
- if ( priv->timer.function == NULL ) {
- TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
- } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("RxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ // printk("RxEOF tarting continuing timer...\n");
priv->timerSetAt = jiffies;
}
}
-
dev->last_rx = jiffies;
return ack;
u32 TLan_HandleDummy( struct device *dev, u16 host_int )
{
host_int = 0;
- printk( "TLAN: Test interrupt on %s.\n", dev->name );
+ printk( "TLAN: Dummy interrupt on %s.\n", dev->name );
return 1;
} /* TLan_HandleDummy */
u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
{
+ u32 ack;
+ u32 error;
+ u8 net_sts;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u32 ack;
- u32 error;
- u8 net_sts;
- u32 phy;
- u16 tlphy_ctl;
- u16 tlphy_sts;
ack = 1;
if ( host_int & TLAN_HI_IV_MASK ) {
error = inl( dev->base_addr + TLAN_CH_PARM );
- printk( "TLAN: %s: Adaptor Error = 0x%x\n", dev->name, error );
+ printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error );
TLan_ReadAndClearStats( dev, TLAN_RECORD );
outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
- TLan_FreeLists( dev );
TLan_ResetLists( dev );
- TLan_ResetAdapter( dev );
+ TLan_Reset( dev );
dev->tbusy = 0;
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ if ( priv->timerType == 0 ) {
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ //printk( " RX GO---->\n" );
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+ }
ack = 0;
} else {
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Status Check\n", dev->name );
- phy = priv->phy[priv->phyNum];
-
net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
- if ( net_sts ) {
+ if ( net_sts )
TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Net_Sts = %x\n", dev->name, (unsigned) net_sts );
- }
- if ( ( net_sts & TLAN_NET_STS_MIRQ ) && ( priv->phyNum == 0 ) ) {
- TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts );
- TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
- if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
- tlphy_ctl |= TLAN_TC_SWAPOL;
- TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
- } else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
- tlphy_ctl &= ~TLAN_TC_SWAPOL;
- TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
- }
-
+ if ( net_sts & TLAN_NET_STS_MIRQ ) {
+ (*priv->phyService)( dev );
if (debug) {
TLan_PhyPrint( dev );
}
}
- udelay(10000000);
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
}
return ack;
u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- TLanList *head_list;
- u32 ack = 1;
+ TLanList *head_list;
+ u32 ack = 1;
host_int = 0;
if ( priv->tlanRev < 0x30 ) {
void TLan_Timer( unsigned long data )
{
struct device *dev = (struct device *) data;
+ u16 gen_sts;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u32 elapsed;
- priv->timer.function = NULL;
+ // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType );
switch ( priv->timerType ) {
- case TLAN_TIMER_PHY_PDOWN:
- TLan_PhyPowerDown( dev );
- break;
- case TLAN_TIMER_PHY_PUP:
- TLan_PhyPowerUp( dev );
- break;
- case TLAN_TIMER_PHY_RESET:
- TLan_PhyReset( dev );
- break;
- case TLAN_TIMER_PHY_START_LINK:
- TLan_PhyStartLink( dev );
- break;
- case TLAN_TIMER_PHY_FINISH_AN:
- TLan_PhyFinishAutoNeg( dev );
- break;
- case TLAN_TIMER_FINISH_RESET:
- TLan_FinishReset( dev );
+ case TLAN_TIMER_LINK:
+ TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
+ if ( gen_sts & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
+ add_timer( &priv->timer );
+ }
break;
- case TLAN_TIMER_ACTIVITY:
- cli();
- if ( priv->timer.function == NULL ) {
- elapsed = jiffies - priv->timerSetAt;
- if ( elapsed >= TLAN_TIMER_ACT_DELAY ) {
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
- } else {
- priv->timer.function = &TLan_Timer;
- priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
- sti();
- add_timer( &priv->timer );
- }
+ case TLAN_TIMER_ACT:
+ if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+ add_timer( &priv->timer );
}
- sti();
break;
default:
break;
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
int i;
TLanList *list;
- struct sk_buff *skb;
- void *t = NULL;
priv->txHead = 0;
priv->txTail = 0;
for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
list = priv->txList + i;
list->cStat = TLAN_CSTAT_UNUSED;
- if ( bbuf ) {
- list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
- } else {
- list->buffer[0].address = 0;
- }
+ list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
list->buffer[2].count = 0;
list->buffer[2].address = 0;
}
list->cStat = TLAN_CSTAT_READY;
list->frameSize = TLAN_MAX_FRAME_SIZE;
list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
- if ( bbuf ) {
- list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
- } else {
- skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
- if ( skb == NULL ) {
- printk( "TLAN: Couldn't allocate memory for received data.\n" );
- /* If this ever happened it would be a problem */
- } else {
- skb->dev = dev;
- skb_reserve( skb, 2 );
- t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
- }
- list->buffer[0].address = virt_to_bus( t );
- list->buffer[9].address = (u32) skb;
- }
+ list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
list->buffer[1].count = 0;
list->buffer[1].address = 0;
if ( i < TLAN_NUM_RX_LISTS - 1 )
} /* TLan_ResetLists */
-void TLan_FreeLists( struct device *dev )
-{
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- int i;
- TLanList *list;
- struct sk_buff *skb;
-
- if ( ! bbuf ) {
- for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
- list = priv->txList + i;
- skb = (struct sk_buff *) list->buffer[9].address;
- if ( skb ) {
- dev_kfree_skb( skb, FREE_WRITE );
- list->buffer[9].address = 0;
- }
- }
-
- for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
- list = priv->rxList + i;
- skb = (struct sk_buff *) list->buffer[9].address;
- if ( skb ) {
- dev_kfree_skb( skb, FREE_READ );
- list->buffer[9].address = 0;
- }
- }
- }
-
-} /* TLan_FreeLists */
-
-
/***************************************************************
printk( "TLAN: Forward = 0x%08x\n", list->forward );
printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
- /* for ( i = 0; i < 10; i++ ) { */
+ // for ( i = 0; i < 10; i++ ) {
for ( i = 0; i < 2; i++ ) {
printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
}
void TLan_ReadAndClearStats( struct device *dev, int record )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u32 tx_good, tx_under;
- u32 rx_good, rx_over;
- u32 def_tx, crc, code;
- u32 multi_col, single_col;
- u32 excess_col, late_col, loss;
+ u32 tx_good, tx_under;
+ u32 rx_good, rx_over;
+ u32 def_tx, crc, code;
+ u32 multi_col, single_col;
+ u32 excess_col, late_col, loss;
outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
tx_good = inb( dev->base_addr + TLAN_DIO_DATA );
*
**************************************************************/
-void
-TLan_ResetAdapter( struct device *dev )
+int TLan_Reset( struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
int i;
u32 data;
u8 data8;
- priv->tlanFullDuplex = FALSE;
-/* 1. Assert reset bit. */
+// 1. Assert reset bit.
data = inl(dev->base_addr + TLAN_HOST_CMD);
data |= TLAN_HC_AD_RST;
outl(data, dev->base_addr + TLAN_HOST_CMD);
-
- udelay(1000);
-/* 2. Turn off interrupts. ( Probably isn't necessary ) */
+// 2. Turn off interrupts. ( Probably isn't necessary )
data = inl(dev->base_addr + TLAN_HOST_CMD);
data |= TLAN_HC_INT_OFF;
outl(data, dev->base_addr + TLAN_HOST_CMD);
-/* 3. Clear AREGs and HASHs. */
+// 3. Clear AREGs and HASHs.
for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) {
TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
}
-/* 4. Setup NetConfig register. */
+// 4. Setup NetConfig register.
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
-/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
+// 5. Load Ld_Tmr and Ld_Thr in HOST_CMD.
outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
-/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */
+// 6. Unreset the MII by setting NMRST (in NetSio) to 1.
outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
TLan_SetBit( TLAN_NET_SIO_NMRST, addr );
-/* 7. Setup the remaining registers. */
+// 7. Setup the remaining registers.
if ( priv->tlanRev >= 0x30 ) {
data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 );
}
- TLan_PhyDetect( dev );
+ TLan_PhySelect( dev );
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
- if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) {
+ if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) {
data |= TLAN_NET_CFG_BIT;
- if ( priv->aui == 1 ) {
+ if ( aui == 1 ) {
TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a );
- } else if ( priv->duplex == TLAN_DUPLEX_FULL ) {
- TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 );
- priv->tlanFullDuplex = TRUE;
} else {
TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 );
}
}
- if ( priv->phyNum == 0 ) {
+ if ( priv->phyFlags & TLAN_PHY_INTERNAL ) {
data |= TLAN_NET_CFG_PHY_EN;
}
TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
-
- if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
- TLan_FinishReset( dev );
- } else {
- TLan_PhyPowerDown( dev );
- }
-
-} /* TLan_ResetAdapter */
-
-
-
-
-void
-TLan_FinishReset( struct device *dev )
-{
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u8 data;
- u32 phy;
- u8 sio;
- u16 status;
- u16 tlphy_ctl;
-
- phy = priv->phy[priv->phyNum];
-
- data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
- if ( priv->tlanFullDuplex ) {
- data |= TLAN_NET_CMD_DUPLEX;
- }
- TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data );
- data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;
- if ( priv->phyNum == 0 ) {
- data |= TLAN_NET_MASK_MASK7;
- }
- TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data );
+ (*priv->phyCheck)( dev );
+ data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 );
+ data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;
+ if ( priv->phyFlags & TLAN_PHY_INTS ) {
+ data8 |= TLAN_NET_MASK_MASK7;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE );
- if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) {
- status = MII_GS_LINK;
- printk( "TLAN: %s: Link forced.\n", dev->name );
- } else {
- TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
- udelay( 1000 );
- TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
- if ( status & MII_GS_LINK ) {
- printk( "TLAN: %s: Link active.\n", dev->name );
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
- }
- }
-
- if ( priv->phyNum == 0 ) {
- TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
- tlphy_ctl |= TLAN_TC_INTEN;
- TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl );
- sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
- sio |= TLAN_NET_SIO_MINTEN;
- TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
- }
-
- if ( status & MII_GS_LINK ) {
- TLan_SetMac( dev, 0, dev->dev_addr );
+ if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) {
priv->phyOnline = 1;
- outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
- if ( debug >= 1 ) {
- outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
- }
- outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
- outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
- } else {
- printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name );
- TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET );
- return;
}
-} /* TLan_FinishReset */
+ return 0;
+
+} /* TLan_Reset */
ThunderLAN Driver PHY Layer Routines
+ The TLAN chip can drive any number of PHYs (physical devices). Rather
+ than having lots of 'if' or '#ifdef' statements, I have created a
+ second driver layer for the PHYs. Each PHY can be identified from its
+ id in registers 2 and 3, and can be given a Check and Service routine
+ that will be called when the adapter is reset and when the adapter
+ receives a Network Status interrupt, respectively.
+
******************************************************************************
*****************************************************************************/
+static TLanPhyIdEntry TLanPhyIdTable[] = {
+ { 0x4000,
+ 0x5014,
+ &TLan_PhyInternalCheck,
+ &TLan_PhyInternalService,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
+ { 0x4000,
+ 0x5015,
+ &TLan_PhyInternalCheck,
+ &TLan_PhyInternalService,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
+ { 0x4000,
+ 0x5016,
+ &TLan_PhyInternalCheck,
+ &TLan_PhyInternalService,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
+ { 0x2000,
+ 0x5C00,
+ &TLan_PhyDp83840aCheck,
+ &TLan_PhyNop,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
+ { 0x2000,
+ 0x5C01,
+ &TLan_PhyDp83840aCheck,
+ &TLan_PhyNop,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
+ { 0x7810,
+ 0x0000,
+ &TLan_PhyDp83840aCheck,
+ &TLan_PhyNop,
+ TLAN_PHY_AUTONEG },
+ { 0x0000,
+ 0x0000,
+ NULL,
+ NULL,
+ 0
+ }
+ };
+
/*********************************************************************
* TLan_PhyPrint
* Returns:
* Nothing
* Parms:
- * dev A pointer to the device structure of the
- * TLAN device having the PHYs to be detailed.
+ * dev A pointer to the device structure of the adapter
+ * which the desired PHY is located.
*
- * This function prints the registers a PHY (aka tranceiver).
+ * This function prints the registers a PHY.
*
********************************************************************/
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
u16 i, data0, data1, data2, data3, phy;
+ u32 io;
- phy = priv->phy[priv->phyNum];
+ phy = priv->phyAddr;
+ io = dev->base_addr;
- if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
- printk( "TLAN: Device %s, Unmanaged PHY.\n", dev->name );
- } else if ( phy <= TLAN_PHY_MAX_ADDR ) {
+ if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) {
printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy );
printk( "TLAN: Off. +0 +1 +2 +3 \n" );
for ( i = 0; i < 0x20; i+= 4 ) {
printk( "TLAN: 0x%02x", i );
- TLan_MiiReadReg( dev, phy, i, &data0 );
+ TLan_MiiReadReg( io, phy, i, &data0 );
printk( " 0x%04hx", data0 );
- TLan_MiiReadReg( dev, phy, i + 1, &data1 );
+ TLan_MiiReadReg( io, phy, i + 1, &data1 );
printk( " 0x%04hx", data1 );
- TLan_MiiReadReg( dev, phy, i + 2, &data2 );
+ TLan_MiiReadReg( io, phy, i + 2, &data2 );
printk( " 0x%04hx", data2 );
- TLan_MiiReadReg( dev, phy, i + 3, &data3 );
+ TLan_MiiReadReg( io, phy, i + 3, &data3 );
printk( " 0x%04hx\n", data3 );
}
} else {
- printk( "TLAN: Device %s, Invalid PHY.\n", dev->name );
+ printk( "TLAN: Device %s, PHY 0x%02x (Unmanaged/Unknown).\n",
+ dev->name,
+ phy
+ );
}
} /* TLan_PhyPrint */
/*********************************************************************
- * TLan_PhyDetect
+ * TLan_PhySelect
*
* Returns:
* Nothing
* dev A pointer to the device structure of the adapter
* for which the PHY needs determined.
*
- * So far I've found that adapters which have external PHYs
- * may also use the internal PHY for part of the functionality.
- * (eg, AUI/Thinnet). This function finds out if this TLAN
- * chip has an internal PHY, and then finds the first external
- * PHY (starting from address 0) if it exists).
+ * This function decides which PHY amoung those attached to the
+ * TLAN chip is to be used. The TLAN chip can be attached to
+ * multiple PHYs, and the driver needs to decide which one to
+ * talk to. Currently this routine picks the PHY with the lowest
+ * address as the internal PHY address is 0x1F, the highest
+ * possible. This strategy assumes that there can be only one
+ * other PHY, and, if it exists, it is the one to be used. If
+ * token ring PHYs are ever supported, this routine will become
+ * a little more interesting...
*
********************************************************************/
-void TLan_PhyDetect( struct device *dev )
+void TLan_PhySelect( struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 control;
- u16 hi;
- u16 lo;
- u32 phy;
+ int phy;
+ int entry;
+ u16 id_hi[TLAN_PHY_MAX_ADDR + 1];
+ u16 id_lo[TLAN_PHY_MAX_ADDR + 1];
+ u16 hi;
+ u16 lo;
+ u16 vendor;
+ u16 device;
+
+ priv->phyCheck = &TLan_PhyNop; // Make sure these aren't ever NULL
+ priv->phyService = &TLan_PhyNop;
+
+ vendor = TLanDeviceList[priv->pciEntry].vendorId;
+ device = TLanDeviceList[priv->pciEntry].deviceId;
- if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
- priv->phyNum = 0xFFFF;
- return;
- }
+ // This is a bit uglier than I'd like, but the 0xF130 device must
+ // NOT be assigned a valid PHY as it uses an unmanaged, bit-rate
+ // PHY. It is simplest just to use another goto, rather than
+ // nesting the two for loops in the if statement.
- TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi );
+ if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) &&
+ ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) {
+ entry = 0;
+ phy = 0;
+ goto FINISH;
+ }
- if ( hi != 0xFFFF ) {
- priv->phy[0] = TLAN_PHY_MAX_ADDR;
- } else {
- priv->phy[0] = TLAN_PHY_NONE;
+ for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+ hi = lo = 0;
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi );
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo );
+ id_hi[phy] = hi;
+ id_lo[phy] = lo;
+ TLAN_DBG( TLAN_DEBUG_GNRL,
+ "TLAN: Phy %2x, hi = %hx, lo = %hx\n",
+ phy,
+ hi,
+ lo
+ );
}
- priv->phy[1] = TLAN_PHY_NONE;
for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
- TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control );
- TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi );
- TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo );
- if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) {
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo );
- if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
- priv->phy[1] = phy;
+ if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
+ if ( id_hi[phy] != 0xFFFF ) {
+ TLan_MiiSync(dev->base_addr);
+ TLan_MiiWriteReg(dev->base_addr,
+ phy,
+ MII_GEN_CTL,
+ MII_GC_PDOWN |
+ MII_GC_LOOPBK |
+ MII_GC_ISOLATE );
+
+ }
+ continue;
+ }
+ for ( entry = 0; TLanPhyIdTable[entry].check; entry++) {
+ if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) &&
+ ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) {
+ TLAN_DBG( TLAN_DEBUG_GNRL,
+ "TLAN: Selected Phy %hx\n",
+ phy
+ );
+ goto FINISH;
}
}
}
- if ( priv->phy[1] != TLAN_PHY_NONE ) {
- priv->phyNum = 1;
- } else if ( priv->phy[0] != TLAN_PHY_NONE ) {
- priv->phyNum = 0;
- } else {
- printk( "TLAN: Cannot initialize device, no PHY was found!\n" );
- }
-
-} /* TLan_PhyDetect */
-
-
-
+ entry = 0;
+ phy = 0;
-void TLan_PhyPowerDown( struct device *dev )
-{
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 value;
-
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering down PHY(s).\n", dev->name );
- value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
- TLan_MiiSync( dev->base_addr );
- TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
- if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) {
- TLan_MiiSync( dev->base_addr );
- TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value );
+FINISH:
+
+ if ( ( entry == 0 ) && ( phy == 0 ) ) {
+ priv->phyAddr = phy;
+ priv->phyEntry = entry;
+ priv->phyCheck = TLan_PhyNop;
+ priv->phyService = TLan_PhyNop;
+ priv->phyFlags = TLAN_PHY_BIT_RATE |
+ TLAN_PHY_UNMANAGED |
+ TLAN_PHY_ACTIVITY;
+ } else {
+ priv->phyAddr = phy;
+ priv->phyEntry = entry;
+ priv->phyCheck = TLanPhyIdTable[entry].check;
+ priv->phyService = TLanPhyIdTable[entry].service;
+ priv->phyFlags = TLanPhyIdTable[entry].flags;
}
- /* Wait for 5 jiffies (50 ms) and powerup
- * This is abitrary. It is intended to make sure the
- * tranceiver settles.
- */
- TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP );
+} /* TLan_PhySelect */
-} /* TLan_PhyPowerDown */
+ /***************************************************************
+ * TLan_PhyNop
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure.
+ *
+ * This function does nothing and is meant as a stand-in
+ * for when a Check or Service function would be
+ * meaningless.
+ *
+ **************************************************************/
-void TLan_PhyPowerUp( struct device *dev )
+int TLan_PhyNop( struct device *dev )
{
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 value;
-
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering up PHY.\n", dev->name );
- TLan_MiiSync( dev->base_addr );
- value = MII_GC_LOOPBK;
- TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+ dev = NULL;
+ return 0;
- /* Wait for 50 jiffies (500 ms) and reset the
- * tranceiver. The TLAN docs say both 50 ms and
- * 500 ms, so do the longer, just in case
- */
- TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET );
+} /* TLan_PhyNop */
-} /* TLan_PhyPowerUp */
+ /***************************************************************
+ * TLan_PhyInternalCheck
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure of the
+ * adapter holding the PHY to be checked.
+ *
+ * This function resets the internal PHY on a TLAN chip.
+ * See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
+ * Programmer's Guide"
+ *
+ **************************************************************/
-void TLan_PhyReset( struct device *dev )
+int TLan_PhyInternalCheck( struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 phy;
- u16 value;
-
- phy = priv->phy[priv->phyNum];
-
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Reseting PHY.\n", dev->name );
- TLan_MiiSync( dev->base_addr );
- value = MII_GC_LOOPBK | MII_GC_RESET;
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value );
- TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
- while ( value & MII_GC_RESET ) {
- TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+ u16 gen_ctl;
+ u32 io;
+ u16 phy;
+ u16 value;
+ u8 sio;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ udelay(50000);
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+
+ udelay(500000);
+
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ if ( aui )
+ value |= TLAN_TC_AUISEL;
+ else
+ value &= ~TLAN_TC_AUISEL;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( ( value & MII_GS_LINK ) || aui ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
}
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
- /* Wait for 50 jiffies (500 ms) and initialize.
- * I don't remember why I wait this long.
- */
- TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK );
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-} /* TLan_PhyReset */
+ sio = TLan_DioRead8( io, TLAN_NET_SIO );
+ sio |= TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( io, TLAN_NET_SIO, sio );
+
+ return 0;
+} /* TLanPhyInternalCheck */
-void TLan_PhyStartLink( struct device *dev )
-{
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 ability;
- u16 control;
- u16 data;
- u16 phy;
- u16 status;
- u16 tctl;
-
- phy = priv->phy[priv->phyNum];
-
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Trying to activate link.\n", dev->name );
- TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
- if ( ( status & MII_GS_AUTONEG ) &&
- ( priv->duplex == TLAN_DUPLEX_DEFAULT ) &&
- ( priv->speed == TLAN_SPEED_DEFAULT ) &&
- ( ! priv->aui ) ) {
- ability = status >> 11;
-
- if ( priv->speed == TLAN_SPEED_10 ) {
- ability &= 0x0003;
- } else if ( priv->speed == TLAN_SPEED_100 ) {
- ability &= 0x001C;
- }
- if ( priv->duplex == TLAN_DUPLEX_FULL ) {
- ability &= 0x000A;
- } else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
- ability &= 0x0005;
- }
+ /***************************************************************
+ * TLan_PhyInternalService
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure of the
+ * adapter holding the PHY to be serviced.
+ *
+ * This function services an interrupt generated by the
+ * internal PHY. It can turn on/off the link LED. See
+ * Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
+ * Programmer's Guide".
+ *
+ **************************************************************/
- TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
-
- /* Wait for 400 jiffies (4 sec) for autonegotiation
- * to complete. The max spec time is less than this
- * but the card need additional time to start AN.
- * .5 sec should be plenty extra.
- */
- printk( "TLAN: %s: Starting autonegotiation.\n", dev->name );
- TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN );
- return;
+int TLan_PhyInternalService( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 tlphy_sts;
+ u16 gen_sts;
+ u16 an_exp;
+ u32 io;
+ u16 phy;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts );
+ TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp );
+ if ( ( gen_sts & MII_GS_LINK ) || aui ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
}
- if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
- priv->phyNum = 0;
- data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
- TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
- TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN );
- return;
- } else if ( priv->phyNum == 0 ) {
- TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl );
- if ( priv->aui ) {
- tctl |= TLAN_TC_AUISEL;
- } else {
- tctl &= ~TLAN_TC_AUISEL;
- control = 0;
- if ( priv->duplex == TLAN_DUPLEX_FULL ) {
- control |= MII_GC_DUPLEX;
- priv->tlanFullDuplex = TRUE;
- }
- if ( priv->speed == TLAN_SPEED_100 ) {
- control |= MII_GC_SPEEDSEL;
- }
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control );
- }
- TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl );
- }
+ if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) {
+ u16 value;
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value);
+ value |= TLAN_TC_SWAPOL;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value);
+ }
- /* Wait for 100 jiffies (1 sec) to give the tranceiver time
- * to establish link.
- */
- TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET );
+ return 0;
-} /* TLan_PhyStartLink */
+} /* TLan_PhyInternalService */
-void TLan_PhyFinishAutoNeg( struct device *dev )
+ /***************************************************************
+ * TLan_PhyDp83840aCheck
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure of the
+ * adapter holding the PHY to be reset.
+ *
+ * This function resets a National Semiconductor DP83840A
+ * 10/100 Mb/s PHY device. See National Semiconductor's
+ * data sheet for more info. This PHY is used on Compaq
+ * Netelligent 10/100 cards.
+ *
+ **************************************************************/
+
+static int TLan_PhyDp83840aCheck( struct device *dev )
{
TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 an_adv;
- u16 an_lpa;
- u16 data;
- u16 mode;
- u16 phy;
- u16 status;
-
- phy = priv->phy[priv->phyNum];
-
- TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
- if ( ! ( status & MII_GS_AUTOCMPLT ) ) {
- /* Wait for 800 jiffies (8 sec) to give the process
- * more time. Perhaps we should fail after a while.
- */
- printk( "TLAN: Giving autonegotiation more time.\n" );
- TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN );
- return;
- }
-
- printk( "TLAN: %s: Autonegotiation complete.\n", dev->name );
- TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv );
- TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa );
- mode = an_adv & an_lpa & 0x03E0;
- if ( mode & 0x0100 ) {
- priv->tlanFullDuplex = TRUE;
- } else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) {
- priv->tlanFullDuplex = TRUE;
- }
+ u16 gen_ctl;
+ int i;
+ u32 io;
+ u16 phy;
+ u16 value;
+ u8 sio;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+ TLan_MiiReadReg( io, phy, MII_AN_ADV, &value );
+ value &= ~0x0140;
+ TLan_MiiWriteReg( io, phy, MII_AN_ADV, value );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
+
+ for ( i = 0; i < 50000; i++ )
+ SLOW_DOWN_IO;
- if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) {
- priv->phyNum = 0;
- data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
- TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
- TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN );
- return;
+/*
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( value & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
}
- if ( priv->phyNum == 0 ) {
- if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) {
- TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX );
- }
- }
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+*/
+ sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+ sio &= ~TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
+// priv->phyOnline = 1;
+
+ return 0;
- /* Wait for 10 jiffies (100 ms). No reason in partiticular.
- */
- TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET );
-
-} /* TLan_PhyFinishAutoNeg */
+} /* TLan_PhyDp83840aCheck */
* 1 otherwise.
*
* Parms:
- * dev The device structure containing
- * The io address and interrupt count
- * for this device.
- * phy The address of the PHY to be queried.
+ * base_port The base IO port of the adapter in
+ * question.
+ * dev The address of the PHY to be queried.
* reg The register whose contents are to be
* retreived.
* val A pointer to a variable to store the
*
**************************************************************/
-int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val )
+int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
{
u8 nack;
- u16 sio, tmp;
- u32 i;
+ u16 sio, tmp;
+ u32 i;
int err;
- int minten;
+ int minten;
err = FALSE;
- outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
- sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- if ( dev->interrupt == 0 )
- cli();
- dev->interrupt++;
+ cli();
- TLan_MiiSync(dev->base_addr);
+ TLan_MiiSync(base_port);
minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
if ( minten )
TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
- TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Start ( 01b ) */
- TLan_MiiSendData( dev->base_addr, 0x2, 2 ); /* Read ( 10b ) */
- TLan_MiiSendData( dev->base_addr, phy, 5 ); /* Device # */
- TLan_MiiSendData( dev->base_addr, reg, 5 ); /* Register # */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
*val = tmp;
- dev->interrupt--;
- if ( dev->interrupt == 0 )
- sti();
+ sti();
return err;
* Returns:
* Nothing
* Parms:
- * dev The device structure for the device
- * to write to.
- * phy The address of the PHY to be written to.
+ * base_port The base IO port of the adapter in
+ * question.
+ * dev The address of the PHY to be written to.
* reg The register whose contents are to be
* written.
* val The value to be written to the register.
*
**************************************************************/
-void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val )
+void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
{
- u16 sio;
+ u16 sio;
int minten;
- outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
- sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- if ( dev->interrupt == 0 )
- cli();
- dev->interrupt++;
+ cli();
- TLan_MiiSync( dev->base_addr );
+ TLan_MiiSync( base_port );
minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
if ( minten )
TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
- TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Start ( 01b ) */
- TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Write ( 01b ) */
- TLan_MiiSendData( dev->base_addr, phy, 5 ); /* Device # */
- TLan_MiiSendData( dev->base_addr, reg, 5 ); /* Register # */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
- TLan_MiiSendData( dev->base_addr, 0x2, 2 ); /* Send ACK */
- TLan_MiiSendData( dev->base_addr, val, 16 ); /* Send Data */
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */
+ TLan_MiiSendData( base_port, val, 16 ); /* Send Data */
TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
if ( minten )
TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
- dev->interrupt--;
- if ( dev->interrupt == 0 )
- sti();
+ sti();
} /* TLan_MiiWriteReg */
-
-
-
/*****************************************************************************
******************************************************************************
outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
- /* Assume clock is low, tx is enabled; */
+ // Assume clock is low, tx is enabled;
for ( place = 0x80; place != 0; place >>= 1 ) {
if ( place & data )
TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
if ( ( ! err ) && stop ) {
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
}
sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
*data = 0;
- /* Assume clock is low, tx is enabled; */
+ // Assume clock is low, tx is enabled;
TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
for ( place = 0x80; place; place >>= 1 ) {
TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
if ( ! stop ) {
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* Ack = 0 */
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0
TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
} else {
- TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); /* No ack = 1 (?) */
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?)
TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
}
*
**************************************************************/
-int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data )
+int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
{
int err;
- if ( dev->interrupt == 0 )
- cli();
- dev->interrupt++;
+ cli();
- TLan_EeSendStart( dev->base_addr );
- err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK );
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
if (err)
return 1;
- err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK );
+ err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
if (err)
return 2;
- TLan_EeSendStart( dev->base_addr );
- err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK );
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
if (err)
return 3;
- TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP );
+ TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
- dev->interrupt--;
- if ( dev->interrupt == 0 )
- sti();
+ sti();
return 0;
* tlan.h
* by James Banks, james.banks@caldera.com
*
- * (C) 1997-1998 Caldera, Inc.
+ * (C) 1997 Caldera, Inc.
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
****************************************************************/
-#define FALSE 0
-#define TRUE 1
+#define FALSE 0
+#define TRUE 1
#define TLAN_MIN_FRAME_SIZE 64
#define TLAN_MAX_FRAME_SIZE 1600
-#define TLAN_NUM_RX_LISTS 4
-#define TLAN_NUM_TX_LISTS 8
+#define TLAN_NUM_RX_LISTS 4
+#define TLAN_NUM_TX_LISTS 8
-#define TLAN_IGNORE 0
-#define TLAN_RECORD 1
+#define TLAN_IGNORE 0
+#define TLAN_RECORD 1
#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args );
-#define TLAN_DEBUG_GNRL 0x0001
-#define TLAN_DEBUG_TX 0x0002
-#define TLAN_DEBUG_RX 0x0004
-#define TLAN_DEBUG_LIST 0x0008
+#define TLAN_DEBUG_GNRL 0x0001
+#define TLAN_DEBUG_TX 0x0002
+#define TLAN_DEBUG_RX 0x0004
+#define TLAN_DEBUG_LIST 0x0008
*
****************************************************************/
-#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
-#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
-#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
-#define PCI_DEVICE_ID_NETFLEX_3P 0xF130
-#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
-#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
-#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
-#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
-#define PCI_DEVICE_ID_OC_2326 0x0014
-
-typedef struct tlan_adapter_entry {
+ /* NOTE: These have been moved to pci.h, will use them
+ eventually */
+#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
+#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
+#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
+#define PCI_DEVICE_ID_NETFLEX_3P 0xF130
+#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
+#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
+
+
+typedef struct tlan_pci_id {
u16 vendorId;
u16 deviceId;
- char *deviceLabel;
- u32 flags;
-} TLanAdapterEntry;
-
-#define TLAN_ADAPTER_NONE 0x00000000
-#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001
-#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002
-#define TLAN_ADAPTER_USE_INTERN_10 0x00000004
-#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008
-
-#define TLAN_SPEED_DEFAULT 0
-#define TLAN_SPEED_10 10
-#define TLAN_SPEED_100 100
-
-#define TLAN_DUPLEX_DEFAULT 0
-#define TLAN_DUPLEX_HALF 1
-#define TLAN_DUPLEX_FULL 2
+ char *deviceName;
+} TLanPciId;
****************************************************************/
#define TLAN_PHY_MAX_ADDR 0x1F
-#define TLAN_PHY_NONE 0x20
+
+#define TLAN_PHY_ACTIVITY 0x00000001
+#define TLAN_PHY_AUTONEG 0x00000002
+#define TLAN_PHY_INTS 0x00000004
+#define TLAN_PHY_BIT_RATE 0x00000008
+#define TLAN_PHY_UNMANAGED 0x00000010
+#define TLAN_PHY_INTERNAL 0x00000020
+
+
+typedef int (TLanPhyFunc)( struct device * );
+
+
+typedef struct tlan_phy_id_entry_tag {
+ u16 idHi;
+ u16 idLo;
+ TLanPhyFunc *check;
+ TLanPhyFunc *service;
+ u32 flags;
+} TLanPhyIdEntry;
u32 txInProgress;
u32 txTail;
u32 txBusyCount;
+ u32 phyAddr;
+ u32 phyEntry;
u32 phyOnline;
+ u32 phyFlags;
+ TLanPhyFunc *phyCheck;
+ TLanPhyFunc *phyService;
u32 timerSetAt;
u32 timerType;
struct timer_list timer;
struct net_device_stats stats;
- TLanAdapterEntry *adapter;
- u32 adapterRev;
- u32 aui;
- u32 debug;
- u32 duplex;
- u32 phy[2];
- u32 phyNum;
- u32 sa_int;
- u32 speed;
+ u32 pciEntry;
+ u8 pciRevision;
+ u8 pciBus;
+ u8 pciDeviceFn;
u8 tlanRev;
- u8 tlanFullDuplex;
char devName[8];
} TLanPrivateInfo;
****************************************************************/
#define TLAN_TIMER_LINK 1
-#define TLAN_TIMER_ACTIVITY 2
-#define TLAN_TIMER_PHY_PDOWN 3
-#define TLAN_TIMER_PHY_PUP 4
-#define TLAN_TIMER_PHY_RESET 5
-#define TLAN_TIMER_PHY_START_LINK 6
-#define TLAN_TIMER_PHY_FINISH_AN 7
-#define TLAN_TIMER_FINISH_RESET 8
+#define TLAN_TIMER_ACT 2
-#define TLAN_TIMER_ACT_DELAY 10
+#define TLAN_TIMER_LINK_DELAY 230
+#define TLAN_TIMER_ACT_DELAY 10
*
****************************************************************/
-#define TLAN_HOST_CMD 0x00
+#define TLAN_HOST_CMD 0x00
#define TLAN_HC_GO 0x80000000
-#define TLAN_HC_STOP 0x40000000
+#define TLAN_HC_STOP 0x40000000
#define TLAN_HC_ACK 0x20000000
-#define TLAN_HC_CS_MASK 0x1FE00000
+#define TLAN_HC_CS_MASK 0x1FE00000
#define TLAN_HC_EOC 0x00100000
#define TLAN_HC_RT 0x00080000
#define TLAN_HC_NES 0x00040000
-#define TLAN_HC_AD_RST 0x00008000
-#define TLAN_HC_LD_TMR 0x00004000
-#define TLAN_HC_LD_THR 0x00002000
-#define TLAN_HC_REQ_INT 0x00001000
-#define TLAN_HC_INT_OFF 0x00000800
-#define TLAN_HC_INT_ON 0x00000400
-#define TLAN_HC_AC_MASK 0x000000FF
-#define TLAN_CH_PARM 0x04
-#define TLAN_DIO_ADR 0x08
-#define TLAN_DA_ADR_INC 0x8000
-#define TLAN_DA_RAM_ADR 0x4000
-#define TLAN_HOST_INT 0x0A
-#define TLAN_HI_IV_MASK 0x1FE0
-#define TLAN_HI_IT_MASK 0x001C
-#define TLAN_DIO_DATA 0x0C
+#define TLAN_HC_AD_RST 0x00008000
+#define TLAN_HC_LD_TMR 0x00004000
+#define TLAN_HC_LD_THR 0x00002000
+#define TLAN_HC_REQ_INT 0x00001000
+#define TLAN_HC_INT_OFF 0x00000800
+#define TLAN_HC_INT_ON 0x00000400
+#define TLAN_HC_AC_MASK 0x000000FF
+#define TLAN_CH_PARM 0x04
+#define TLAN_DIO_ADR 0x08
+#define TLAN_DA_ADR_INC 0x8000
+#define TLAN_DA_RAM_ADR 0x4000
+#define TLAN_HOST_INT 0x0A
+#define TLAN_HI_IV_MASK 0x1FE0
+#define TLAN_HI_IT_MASK 0x001C
+#define TLAN_DIO_DATA 0x0C
/* ThunderLAN Internal Register DIO Offsets */
#define TLAN_NET_STS_MIRQ 0x80
#define TLAN_NET_STS_HBEAT 0x40
#define TLAN_NET_STS_TXSTOP 0x20
-#define TLAN_NET_STS_RXSTOP 0x10
+#define TLAN_NET_STS_RXSTOP 0x10
#define TLAN_NET_STS_RSRVD 0x0F
#define TLAN_NET_MASK 0x03
#define TLAN_NET_MASK_MASK7 0x80
#define TLAN_NET_CFG_MTEST 0x0100
#define TLAN_NET_CFG_PHY_EN 0x0080
#define TLAN_NET_CFG_MSMASK 0x007F
-#define TLAN_MAN_TEST 0x06
-#define TLAN_DEF_VENDOR_ID 0x08
-#define TLAN_DEF_DEVICE_ID 0x0A
-#define TLAN_DEF_REVISION 0x0C
-#define TLAN_DEF_SUBCLASS 0x0D
-#define TLAN_DEF_MIN_LAT 0x0E
-#define TLAN_DEF_MAX_LAT 0x0F
+#define TLAN_MAN_TEST 0x06
+#define TLAN_DEF_VENDOR_ID 0x08
+#define TLAN_DEF_DEVICE_ID 0x0A
+#define TLAN_DEF_REVISION 0x0C
+#define TLAN_DEF_SUBCLASS 0x0D
+#define TLAN_DEF_MIN_LAT 0x0E
+#define TLAN_DEF_MAX_LAT 0x0F
#define TLAN_AREG_0 0x10
#define TLAN_AREG_1 0x16
#define TLAN_AREG_2 0x1C
#define TLAN_AREG_3 0x22
#define TLAN_HASH_1 0x28
#define TLAN_HASH_2 0x2C
-#define TLAN_GOOD_TX_FRMS 0x30
-#define TLAN_TX_UNDERUNS 0x33
-#define TLAN_GOOD_RX_FRMS 0x34
-#define TLAN_RX_OVERRUNS 0x37
-#define TLAN_DEFERRED_TX 0x38
-#define TLAN_CRC_ERRORS 0x3A
-#define TLAN_CODE_ERRORS 0x3B
-#define TLAN_MULTICOL_FRMS 0x3C
-#define TLAN_SINGLECOL_FRMS 0x3E
-#define TLAN_EXCESSCOL_FRMS 0x40
-#define TLAN_LATE_COLS 0x41
-#define TLAN_CARRIER_LOSS 0x42
-#define TLAN_ACOMMIT 0x43
-#define TLAN_LED_REG 0x44
-#define TLAN_LED_ACT 0x10
-#define TLAN_LED_LINK 0x01
-#define TLAN_BSIZE_REG 0x45
+#define TLAN_GOOD_TX_FRMS 0x30
+#define TLAN_TX_UNDERUNS 0x33
+#define TLAN_GOOD_RX_FRMS 0x34
+#define TLAN_RX_OVERRUNS 0x37
+#define TLAN_DEFERRED_TX 0x38
+#define TLAN_CRC_ERRORS 0x3A
+#define TLAN_CODE_ERRORS 0x3B
+#define TLAN_MULTICOL_FRMS 0x3C
+#define TLAN_SINGLECOL_FRMS 0x3E
+#define TLAN_EXCESSCOL_FRMS 0x40
+#define TLAN_LATE_COLS 0x41
+#define TLAN_CARRIER_LOSS 0x42
+#define TLAN_ACOMMIT 0x43
+#define TLAN_LED_REG 0x44
+#define TLAN_LED_ACT 0x10
+#define TLAN_LED_LINK 0x01
+#define TLAN_BSIZE_REG 0x45
#define TLAN_MAX_RX 0x46
#define TLAN_INT_DIS 0x48
#define TLAN_ID_TX_EOC 0x04
#define TLAN_INT_NONE 0x0000
#define TLAN_INT_TX_EOF 0x0001
-#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_STAT_OVERFLOW 0x0002
#define TLAN_INT_RX_EOF 0x0003
#define TLAN_INT_DUMMY 0x0004
#define TLAN_INT_TX_EOC 0x0005
-#define TLAN_INT_STATUS_CHECK 0x0006
+#define TLAN_INT_STATUS_CHECK 0x0006
#define TLAN_INT_RX_EOC 0x0007
/* Generic MII/PHY Registers */
-#define MII_GEN_CTL 0x00
+#define MII_GEN_CTL 0x00
#define MII_GC_RESET 0x8000
#define MII_GC_LOOPBK 0x4000
#define MII_GC_SPEEDSEL 0x2000
#define MII_GC_DUPLEX 0x0100
#define MII_GC_COLTEST 0x0080
#define MII_GC_RESERVED 0x007F
-#define MII_GEN_STS 0x01
+#define MII_GEN_STS 0x01
#define MII_GS_100BT4 0x8000
#define MII_GS_100BTXFD 0x4000
#define MII_GS_100BTXHD 0x2000
#define MII_GS_10BTHD 0x0800
#define MII_GS_RESERVED 0x07C0
#define MII_GS_AUTOCMPLT 0x0020
-#define MII_GS_RFLT 0x0010
+#define MII_GS_RFLT 0x0010
#define MII_GS_AUTONEG 0x0008
-#define MII_GS_LINK 0x0004
+#define MII_GS_LINK 0x0004
#define MII_GS_JABBER 0x0002
#define MII_GS_EXTCAP 0x0001
#define MII_GEN_ID_HI 0x02
#define MII_GEN_ID_LO 0x03
-#define MII_GIL_OUI 0xFC00
+#define MII_GIL_OUI 0xFC00
#define MII_GIL_MODEL 0x03F0
#define MII_GIL_REVISION 0x000F
-#define MII_AN_ADV 0x04
-#define MII_AN_LPA 0x05
-#define MII_AN_EXP 0x06
+#define MII_AN_ADV 0x04
+#define MII_AN_LPA 0x05
+#define MII_AN_EXP 0x06
/* ThunderLAN Specific MII/PHY Registers */
#define TLAN_TS_RESERVED 0x0FFF
-#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
/* Routines to access internal registers. */
-#if 0
+
inline void TLan_ClearBit(u8 bit, u16 port)
{
outb_p(inb_p(port) & ~bit, port);
{
outb_p(inb_p(port) | bit, port);
}
-#endif
-
-#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port)
-#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit))
-#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port)
inline u32 xor( u32 a, u32 b )
http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
*/
-static const char *version = "tulip.c:v0.87K 1/22/98 becker@cesdis.gsfc.nasa.gov\n";
+#define SMP_CHECK
+static const char version[] = "tulip.c:v0.88 4/7/98 becker@cesdis.gsfc.nasa.gov\n";
/* A few user-configurable values. */
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
+static int max_interrupt_work = 25;
#define MAX_UNITS 8
/* Used to pass the full-duplex flag, etc. */
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
#ifdef __alpha__
-static const rx_copybreak = 1518;
+static int rx_copybreak = 1518;
#else
-static const rx_copybreak = 100;
+static int rx_copybreak = 100;
#endif
/* The following example shows how to always use the 10base2 port. */
#endif
#if (LINUX_VERSION_CODE < 0x20123)
+#define hard_smp_processor_id() smp_processor_id()
#define test_and_set_bit(val, addr) set_bit(val, addr)
#endif
This device driver is designed for the DECchip "Tulip", Digital's
single-chip ethernet controllers for PCI. Supported members of the family
-are the 21040, 21041, 21140, 21140A and 21142. These chips are used on
+are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on
many PCI boards including the SMC EtherPower series.
#ifndef PCI_VENDOR_ID_MXIC
#define PCI_VENDOR_ID_MXIC 0x10d9
-#define PCI_DEVICE_ID_PMAC 0x0200
+#define PCI_DEVICE_ID_MX98713 0x0512
+#define PCI_DEVICE_ID_MX98715 0x0531
+#define PCI_DEVICE_ID_MX98725 0x0531
#endif
/* The rest of these values should never change. */
static void tulip_timer(unsigned long data);
+static void t21142_timer(unsigned long data);
+static void mxic_timer(unsigned long data);
+static void pnic_timer(unsigned long data);
/* A table describing the chip types. */
-enum tbl_flag { HAS_MII=1, };
+enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2};
static struct tulip_chip_table {
- int device_id;
- char *chip_name;
- int flags;
- void (*media_timer)(unsigned long data);
+ int device_id;
+ char *chip_name;
+ int io_size;
+ int valid_intrs; /* CSR7 interrupt enable settings */
+ int flags;
+ void (*media_timer)(unsigned long data);
} tulip_tbl[] = {
- { PCI_DEVICE_ID_DEC_TULIP, "Digital DC21040 Tulip", 0, tulip_timer },
- { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DC21041 Tulip", 0, tulip_timer },
- { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", HAS_MII,
- tulip_timer },
- { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", HAS_MII,
- tulip_timer },
- { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 0, tulip_timer },
- { PCI_DEVICE_ID_PMAC, "Macronix 98713 PMAC", HAS_MII, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP, "Digital DC21040 Tulip", 128, 0x0001ebef,
+ 0, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DC21041 Tulip", 128, 0x0001ebef,
+ HAS_MEDIA_TABLE, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 128, 0x0001ebef,
+ HAS_MII | HAS_MEDIA_TABLE, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 256, 0x0801fbff,
+ HAS_MII | HAS_MEDIA_TABLE, t21142_timer },
+ { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 256, 0x0001ebef,
+ 0, pnic_timer },
+ { PCI_DEVICE_ID_MX98713, "Macronix 98713 PMAC", 128, 0x0001ebef,
+ HAS_MII | HAS_MEDIA_TABLE, tulip_timer /* Tulip-like! */ },
+ { PCI_DEVICE_ID_MX98715, "Macronix 98715 PMAC", 256, 0x0001ebef,
+ HAS_MEDIA_TABLE, mxic_timer },
+ { PCI_DEVICE_ID_MX98725, "Macronix 98725 PMAC", 256, 0x0001ebef,
+ HAS_MEDIA_TABLE, mxic_timer },
{0, 0, 0, 0},
};
/* This matches the table above. */
-enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, LC82C168, MX98713};
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
+ LC82C168, MX98713, MX98715, MX98725};
static const char * const medianame[] = {
"10baseT", "10base2", "AUI", "100baseTx",
TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
NormalIntr=0x10000, AbnormalIntr=0x8000,
RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
- TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02,
- TxIntr=0x01,
+ TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
};
/* The Tulip Rx and Tx buffer descriptors. */
#endif
struct timer_list timer; /* Media selection timer. */
int interrupt; /* In-interrupt flag. */
+#ifdef SMP_CHECK
+ int smp_proc_id; /* Which processor in IRQ handler. */
+#endif
unsigned int cur_rx, cur_tx; /* The next free ring entry */
unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
unsigned int tx_full:1; /* The Tx queue is full. */
signed char phys[4], mii_cnt; /* MII device addresses. */
struct mediatable *mtable;
int cur_index; /* Current media index. */
- unsigned char pci_bus, pci_device_fn;
+ unsigned char pci_bus, pci_dev_fn;
int pad0, pad1; /* Used for 8-byte alignment */
};
-static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+static struct device *tulip_probe1(int pci_bus, int pci_devfn,
+ struct device *dev, int ioaddr,
int chip_id, int options);
static void parse_eeprom(struct device *dev);
static int read_eeprom(int ioaddr, int location);
static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
static int tulip_close(struct device *dev);
static struct enet_statistics *tulip_get_stats(struct device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+#endif
#ifdef NEW_MULTICAST
static void set_rx_mode(struct device *dev);
#else
\f
-#ifdef MODULE
/* A list of all installed Tulip devices, for removing the driver module. */
static struct device *root_tulip_dev = NULL;
-#endif
/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
but now receives directly into full-sized skbuffs that are allocated
unsigned char pci_bus, pci_device_fn;
for (;pci_index < 0xff; pci_index++) {
- unsigned char pci_irq_line;
u16 vendor, device, pci_command, new_command;
u32 pci_ioaddr;
int chip_idx = 0;
PCI_VENDOR_ID, &vendor);
pcibios_read_config_word(pci_bus, pci_device_fn,
PCI_DEVICE_ID, &device);
- 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. */
if (vendor == PCI_VENDOR_ID_LITEON)
device = PCI_DEVICE_ID_PNIC_X;
else if (vendor == PCI_VENDOR_ID_MXIC)
- device = PCI_DEVICE_ID_PMAC;
+ device = PCI_DEVICE_ID_MX98713;
for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
if (device == tulip_tbl[chip_idx].device_id)
break;
if (tulip_tbl[chip_idx].chip_name == 0) {
- printk(KERN_INFO "Unknown Digital PCI ethernet chip type"
- " %4.4x"" detected: not configured.\n", device);
+ printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type"
+ " %4.4x %4.4x"" detected: not configured.\n",
+ vendor, device);
continue;
}
if (tulip_debug > 2)
- printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
- pci_ioaddr, pci_irq_line);
+ printk(KERN_DEBUG "Found %s at I/O %#x.\n",
+ tulip_tbl[chip_idx].chip_name, pci_ioaddr);
- if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
+ if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
continue;
pcibios_read_config_word(pci_bus, pci_device_fn,
PCI_COMMAND, new_command);
}
- dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
- cards_found);
+ dev = tulip_probe1(pci_bus, pci_device_fn, dev,
+ pci_ioaddr, chip_idx, cards_found);
/* Get and check the bus-master and latency values. */
if (dev) {
return cards_found ? 0 : -ENODEV;
}
-static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
+ struct device *dev, int ioaddr,
int chip_id, int board_idx)
{
static int did_version = 0; /* Already printed version info. */
/* See note below on the multiport cards. */
static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
static int last_irq = 0;
+ u8 pci_irq_line;
int i;
unsigned short sum;
dev = init_etherdev(dev, 0);
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
printk(KERN_INFO "%s: %s at %#3x,",
dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
/* Stop the chip's Tx and Rx processes. */
outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
/* Clear the missed-packet counter. */
- (volatile)inl(ioaddr + CSR8);
+ (volatile int)inl(ioaddr + CSR8);
if (chip_id == DC21041) {
if (inl(ioaddr + CSR9) & 0x8000) {
dev->dev_addr[i] = last_phys_addr[i];
dev->dev_addr[i] = last_phys_addr[i] + 1;
#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */
- irq = last_irq;
+ pci_irq_line = last_irq;
#endif
}
for (i = 0; i < 6; i++)
printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
- printk(", IRQ %d.\n", irq);
- last_irq = irq;
+ printk(", IRQ %d.\n", pci_irq_line);
+ last_irq = pci_irq_line;
/* We do a request_region() only to register /proc/ioports info. */
+ /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */
request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
dev->base_addr = ioaddr;
- dev->irq = irq;
+ dev->irq = pci_irq_line;
/* Make certain the data structures are quadword aligned. */
tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
memset(tp, 0, sizeof(*tp));
dev->priv = tp;
-#ifdef MODULE
tp->next_module = root_tulip_dev;
root_tulip_dev = dev;
-#endif
+ tp->pci_bus = pci_bus;
+ tp->pci_dev_fn = pci_device_fn;
tp->chip_id = chip_id;
#ifdef TULIP_FULL_DUPLEX
/* The lower four bits are the media type. */
if (board_idx >= 0 && board_idx < MAX_UNITS) {
tp->default_port = options[board_idx] & 15;
- if ((options[board_idx]&16) || full_duplex[board_idx]>0)
+ if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
tp->full_duplex = 1;
if (mtu[board_idx] > 0)
dev->mtu = mtu[board_idx];
tp->full_duplex_lock = 1;
/* This is logically part of probe1(), but too complex to write inline. */
- if (chip_id != DC21040 && chip_id != LC82C168)
+ if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE)
parse_eeprom(dev);
if (media_cap[tp->default_port] & MediaIsMII) {
printk(KERN_INFO "%s: MII transceiver found at MDIO address "
"%d, config %4.4x status %4.4x.\n",
dev->name, phy, mii_reg0, mii_status);
- if (media_cap[tp->default_port] & MediaIsMII) {
+ if (1 || (media_cap[tp->default_port] & MediaIsMII)) {
printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d,"
" previously advertising %4.4x.\n",
dev->name, reg4, phy, mdio_read(ioaddr, phy, 4));
dev->hard_start_xmit = &tulip_start_xmit;
dev->stop = &tulip_close;
dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &private_ioctl;
+#endif
#ifdef HAVE_MULTICAST
dev->set_multicast_list = &set_rx_mode;
#endif
outl(0x00000000, ioaddr + CSR13);
outl(0x00000004, ioaddr + CSR13);
break;
+ case DC21140: default:
+ if (tp->mtable)
+ outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
+ break;
+ case DC21142:
+ outl(0x82420200, ioaddr + CSR6);
+ outl(0x0001, ioaddr + CSR13);
+ outl(0x0003FFFF, ioaddr + CSR14);
+ outl(0x0008, ioaddr + CSR15);
+ outl(0x0001, ioaddr + CSR13);
+ outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+ break;
case LC82C168:
outl(0x00420000, ioaddr + CSR6);
outl(0x30, ioaddr + CSR12);
outl(0x0001F078, ioaddr + 0xB8);
outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
break;
- case DC21140: case DC21142: case MX98713: default:
- if (tp->mtable)
- outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
+ case MX98713: case MX98715: case MX98725:
+ outl(0x00000000, ioaddr + CSR6);
+ outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+ outl(0x00000001, ioaddr + CSR13);
break;
}
{0, 0, 0, 0, {}}};
static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 non-MII PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
+ "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
#define EEPROM_SIZE 128
#if defined(__i386__)
/* The last media info list parsed, for multiport boards. */
static struct mediatable *last_mediatable = NULL;
static unsigned char *last_ee_data = NULL;
- static controller_index = 0;
+ static int controller_index = 0;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int ioaddr = dev->base_addr;
unsigned char *ee_data = tp->eeprom;
leaf->type = 0;
leaf->media = p[0] & 0x3f;
leaf->leafdata = p;
+ if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
+ mtable->has_mii = 1;
p += 4;
} else {
leaf->type = p[1];
/* When a module we don't have 'x86' to check. */
outl(0x01A00000 | 0x4800, ioaddr + CSR0);
#else
-#if (LINUX_VERSION_CODE > 0x20172)
+#if (LINUX_VERSION_CODE > 0x2014c)
#define x86 boot_cpu_data.x86
#endif
outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
#endif
#ifdef SA_SHIRQ
- if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ,
- tulip_tbl[tp->chip_id].chip_name, dev)) {
+ if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) {
return -EAGAIN;
}
#else
outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
if (dev->if_port == 0)
- dev->if_port = tp->default_port;
+ dev->if_port = tp->default_port;
if (tp->chip_id == DC21041 && dev->if_port > 4)
/* Invalid: Select initial TP, autosense, autonegotiate. */
dev->if_port = 4;
;
media_picked:
- tp->cur_index = i;
tp->csr6 = 0;
- select_media(dev, 1);
+ tp->cur_index = i;
+ if (dev->if_port == 0 && tp->chip_id == DC21142) {
+ tp->csr6 = 0x82420200;
+ outl(0x0003FFFF, ioaddr + CSR14);
+ outl(0x0008, ioaddr + CSR15);
+ outl(0x0001, ioaddr + CSR13);
+ outl(0x1301, ioaddr + CSR12);
+ } else
+ select_media(dev, 1);
/* Start the chip's Tx to process setup frame. */
outl(tp->csr6, ioaddr + CSR6);
tp->interrupt = 0;
dev->start = 1;
-
/* Enable interrupts by setting the interrupt mask. */
- outl(0x0001ebef, ioaddr + CSR7);
+ outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
outl(tp->csr6 | 0x2002, ioaddr + CSR6);
outl(0, ioaddr + CSR2); /* Rx poll demand */
/* Set the timer to switch to check for link beat and perhaps switch
to an alternate media type. */
init_timer(&tp->timer);
- tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
+ tp->timer.expires = RUN_AT(5*HZ);
tp->timer.data = (unsigned long)dev;
- tp->timer.function = &tulip_timer; /* timer handler */
+ tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
add_timer(&tp->timer);
return 0;
outl(setup[1], ioaddr + CSR14);
outl(setup[2], ioaddr + CSR15);
outl(setup[0], ioaddr + CSR13);
- for (i = 0; i < 3; i++)
+ for (i = 0; i < 3; i++) /* Re-fill setup[] */
setup[i] = get_u16(&p[i*2 + 7]);
- } else {
+ } else if (dev->if_port <= 4) {
outl(0, ioaddr + CSR13);
outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+ } else {
+ outl(0, ioaddr + CSR14);
+ outl(8, ioaddr + CSR15);
+ outl(0, ioaddr + CSR13);
}
outl(setup[0]<<16, ioaddr + CSR15); /* Direction */
outl(setup[1]<<16, ioaddr + CSR15); /* Data */
if (mleaf->type == 4)
- new_csr6 = 0x02020000 | ((setup[2] & 0x71) << 18);
+ new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
else
new_csr6 = 0x82420000;
break;
break;
}
break;
- case LC82C168: {
- int phy_reg = inl(ioaddr + 0xB8);
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
- dev->name, phy_reg, inl(ioaddr + CSR5));
- if (phy_reg & 0x04000000) { /* Remote link fault */
- /*outl(0x0201F078, ioaddr + 0xB8);*/
- next_tick = (24*HZ)/10;
- } else
- next_tick = 10*HZ;
- if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
- "CSR5 %8.8x, PHY %3.3x.\n",
- dev->name, medianame[dev->if_port], csr12,
- inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
- if (dev->if_port == 0) {
- dev->if_port = 3;
- } else
- dev->if_port = 0;
- next_tick = (24*HZ)/10;
- select_media(dev, 0);
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
- dev->trans_start = jiffies;
- }
- break;
- }
case DC21140: case DC21142: case MX98713: default: {
struct medialeaf *mleaf;
unsigned char *p;
p = mleaf->leafdata;
switch (mleaf->type) {
case 0: case 4: {
- /* Type 0 non-MII or #4 SYM transceiver. Check the link beat bit. */
- s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
- " %d, expecting %d.\n",
- dev->name, csr12, (bitnum >> 1) & 7,
- (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
- (bitnum >= 0));
- /* Check that the specified bit has the proper value. */
- if ((bitnum < 0) !=
- ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
- medianame[mleaf->media]);
- break;
- }
- if (tp->medialock)
- break;
+ /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */
+ int offset = mleaf->type == 4 ? 5 : 2;
+ s8 bitnum = p[offset];
+ if (p[offset+1] & 0x80) {
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG"%s: Transceiver monitor tick "
+ "CSR12=%#2.2x, no media sense.\n",
+ dev->name, csr12);
+ if (mleaf->type == 4) {
+ if (mleaf->media == 3 && (csr12 & 0x02))
+ goto select_next_media;
+ }
+ break;
+ }
+ if (tulip_debug > 2)
+ printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
+ " bit %d is %d, expecting %d.\n",
+ dev->name, csr12, (bitnum >> 1) & 7,
+ (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+ (bitnum >= 0));
+ /* Check that the specified bit has the proper value. */
+ if ((bitnum < 0) !=
+ ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
+ medianame[mleaf->media]);
+ if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */
+ goto actually_mii;
+ break;
+ }
+ if (tp->medialock)
+ break;
select_next_media:
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
- if (media_cap[dev->if_port] & MediaIsFD)
- goto select_next_media; /* Skip FD entries. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No link beat on media %s,"
- " trying transceiver type %s.\n",
- dev->name, medianame[mleaf->media & 15],
- medianame[tp->mtable->mleaf[tp->cur_index].media]);
- select_media(dev, 0);
- /* Restart the transmit process. */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
- next_tick = (24*HZ)/10;
- break;
- }
- case 1: case 3: /* 21140, 21142 MII */
- {
- int mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
- int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
- printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
- "%4.4x, CSR12 %2.2x, %cD.\n",
- dev->name, mii_reg1, mii_reg5, csr12,
- tp->full_duplex ? 'F' : 'H');
- if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) {
- int new_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
- if ((new_reg1 & 0x0004) == 0)
- printk(KERN_INFO "%s: No link beat on the MII interface,"
- " status then %4.4x now %4.4x.\n",
- dev->name, mii_reg1, new_reg1);
+ if (--tp->cur_index < 0) {
+ /* We start again, but should instead look for default. */
+ tp->cur_index = tp->mtable->leafcount - 1;
+ }
+ dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+ if (media_cap[dev->if_port] & MediaIsFD)
+ goto select_next_media; /* Skip FD entries. */
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG "%s: No link beat on media %s,"
+ " trying transceiver type %s.\n",
+ dev->name, medianame[mleaf->media & 15],
+ medianame[tp->mtable->mleaf[tp->cur_index].media]);
+ select_media(dev, 0);
+ /* Restart the transmit process. */
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ next_tick = (24*HZ)/10;
+ break;
+ }
+ case 1: case 3: { /* 21140, 21142 MII */
+ int mii_reg1, mii_reg5;
+ actually_mii:
+ mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
+ mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+ if (tulip_debug > 1)
+ printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
+ "%4.4x, CSR12 %2.2x, %cD.\n",
+ dev->name, mii_reg1, mii_reg5, csr12,
+ tp->full_duplex ? 'F' : 'H');
+ if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) {
+ int new_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
+ if ((new_reg1 & 0x0004) == 0)
+ printk(KERN_INFO "%s: No link beat on the MII interface,"
+ " status then %4.4x now %4.4x.\n",
+ dev->name, mii_reg1, new_reg1);
#ifdef notyet
- goto select_next_media;
-#endif
- }
- if (mii_reg5 == 0xffff || mii_reg5 == 0x0000)
- ; /* No MII device or no link partner report */
- else if (tp->full_duplex_lock)
- ;
- else if ((mii_reg5 & 0x0100) != 0
- || (mii_reg5 & 0x00C0) == 0x0040) {
- /* 100baseTx-FD or 10T-FD, but not 100-HD */
- if (tp->full_duplex == 0) {
- tp->full_duplex = 1;
- tp->csr6 |= 0x0200;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
- }
- if (tulip_debug > 0) /* Gurppp, should be >1 */
- printk(KERN_INFO "%s: Setting %s-duplex based on MII"
- " Xcvr #%d parter capability of %4.4x.\n",
- dev->name, tp->full_duplex ? "full" : "half",
- tp->phys[0], mii_reg5);
- }
- }
- break;
+ goto select_next_media;
+#endif
+ }
+ if (mii_reg5 == 0xffff || mii_reg5 == 0x0000)
+ ; /* No MII device or no link partner report */
+ else if (tp->full_duplex_lock)
+ ;
+ else {
+ int negotiated = mii_reg5 & tp->advertising[0];
+ int duplex = ((negotiated & 0x0100) != 0
+ || (negotiated & 0x00C0) == 0x0040);
+ /* 100baseTx-FD or 10T-FD, but not 100-HD */
+ if (tp->full_duplex != duplex) {
+ tp->full_duplex = duplex;
+ if (tp->full_duplex)
+ tp->csr6 |= 0x0200;
+ else
+ tp->csr6 &= ~0x0200;
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ if (tulip_debug > 0) /* Gurppp, should be >1 */
+ printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+ " Xcvr #%d parter capability of %4.4x.\n",
+ dev->name, tp->full_duplex ? "full" : "half",
+ tp->phys[0], mii_reg5);
+ }
+ }
+ next_tick = 60*HZ;
+ break;
+ }
case 2: /* 21142 serial block has no link beat. */
default:
break;
}
}
+/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
+ of available transceivers. */
+static void t21142_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int csr12 = inl(ioaddr + CSR12);
+ int next_tick = 60*HZ;
+ int new_csr6 = 0;
+
+ if (tulip_debug > 1)
+ printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n",
+ dev->name, csr12, medianame[dev->if_port]);
+ if (dev->if_port == 3) {
+ if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */
+ new_csr6 = 0x82420200;
+ outl(new_csr6, ioaddr + CSR6);
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0003FFFF, ioaddr + CSR14);
+ outl(0x0008, ioaddr + CSR15);
+ outl(0x0001, ioaddr + CSR13);
+ outl(0x1301, ioaddr + CSR12); /* Start NWay. */
+ }
+ } else if ((csr12 & 0x7000) != 0x5000) {
+ /* Negotiation failed. Search media types. */
+ if (tulip_debug > 1)
+ printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n",
+ dev->name, csr12);
+ if (!(csr12 & 4)) { /* 10mbps link beat good. */
+ new_csr6 = 0x82420000;
+ dev->if_port = 0;
+ outl(0, ioaddr + CSR13);
+ outl(0x0003FFFF, ioaddr + CSR14);
+ outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+ outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+ } else if (csr12 & 0x100) {
+ new_csr6 = 0x82420200;
+ dev->if_port = 2;
+ outl(0, ioaddr + CSR13);
+ outl(0x0003FFFF, ioaddr + CSR14);
+ outl(0x0008, ioaddr + CSR15);
+ outl(0x0001, ioaddr + CSR13);
+ } else {
+ /* Select 100mbps port to check for link beat. */
+ new_csr6 = 0x83860000;
+ dev->if_port = 3;
+ outl(0, ioaddr + CSR13);
+ outl(0x0003FF7F, ioaddr + CSR14);
+ outl(8, ioaddr + CSR15);
+ outl(1, ioaddr + CSR13);
+ }
+ if (tulip_debug > 1)
+ printk(KERN_INFO"%s: Testing new 21142 media %s.\n",
+ dev->name, medianame[dev->if_port]);
+ if (new_csr6 != (tp->csr6 & ~0x00D5)) {
+ tp->csr6 &= 0x00D5;
+ tp->csr6 |= new_csr6;
+ outl(0x0301, ioaddr + CSR12);
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ }
+ }
+ tp->timer.expires = RUN_AT(next_tick);
+ add_timer(&tp->timer);
+}
+
+static void t21142_lnk_change( struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int csr12 = inl(ioaddr + CSR12);
+
+ if (tulip_debug > 1)
+ printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n",
+ dev->name, csr12, inl(ioaddr + CSR5));
+
+ if ((csr12 & 0x7000) == 0x5000) {
+ if (csr12 & 0x01800000) {
+ /* Switch to 100mbps mode. */
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ if (csr12 & 0x01000000) {
+ dev->if_port = 5;
+ tp->csr6 = 0x83860200;
+ } else {
+ dev->if_port = 3;
+ tp->csr6 = 0x83860000;
+ }
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ } /* Else 10baseT-FD is handled automatically. */
+ } else if (dev->if_port == 3) {
+ if (!(csr12 & 2))
+ printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n",
+ dev->name);
+ else
+ dev->if_port = 0;
+ } else if (dev->if_port == 0) {
+ if (!(csr12 & 4))
+ printk(KERN_INFO"%s: 21142 10baseT link beat good.\n",
+ dev->name);
+ } else if (!(csr12 & 4)) { /* 10mbps link beat good. */
+ printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n",
+ dev->name);
+ dev->if_port = 0;
+ } else { /* 100mbps link beat good. */
+ printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n",
+ dev->name);
+ dev->if_port = 3;
+ tp->csr6 = 0x83860000;
+ outl(0x0003FF7F, ioaddr + CSR14);
+ outl(0x0301, ioaddr + CSR12);
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ }
+}
+
+
+static void mxic_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int next_tick = 60*HZ;
+
+ if (tulip_debug > 3) {
+ printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
+ inl(ioaddr + CSR12));
+ }
+ if (next_tick) {
+ tp->timer.expires = RUN_AT(next_tick);
+ add_timer(&tp->timer);
+ }
+}
+
+static void pnic_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int csr12 = inl(ioaddr + CSR12);
+ int phy_reg = inl(ioaddr + 0xB8);
+ int next_tick = 60*HZ;
+ int duplex;
+
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
+ dev->name, phy_reg, inl(ioaddr + CSR5));
+ if (tp->full_duplex_lock)
+ return; /* Do not bother to set timer. */
+ duplex = phy_reg & 0x30000000 ? 1 : 0;
+ if (tp->full_duplex != duplex) {
+ tp->full_duplex = duplex;
+ if (tp->full_duplex)
+ tp->csr6 |= 0x0200;
+ else
+ tp->csr6 &= ~0x0200;
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ if (tulip_debug > 0) /* Gurppp, should be >1 */
+ printk(KERN_INFO "%s: Setting %s-duplex based on"
+ " PNIC PHY report of %8.8x.\n",
+ dev->name, tp->full_duplex ? "full" : "half", phy_reg);
+ }
+ if (phy_reg & 0x04000000) { /* Remote link fault */
+ /*outl(0x0201F078, ioaddr + 0xB8);*/
+ next_tick = 3*HZ;
+ }
+ if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
+ "CSR5 %8.8x, PHY %3.3x.\n",
+ dev->name, medianame[dev->if_port], csr12,
+ inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
+ if (dev->if_port == 0) {
+ dev->if_port = 3;
+ } else
+ dev->if_port = 0;
+ next_tick = 3*HZ;
+ select_media(dev, 0);
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ dev->trans_start = jiffies;
+ }
+ tp->timer.expires = RUN_AT(next_tick);
+ add_timer(&tp->timer);
+}
+
static void tulip_tx_timeout(struct device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
ioaddr = dev->base_addr;
tp = (struct tulip_private *)dev->priv;
if (test_and_set_bit(0, (void*)&tp->interrupt)) {
+#ifdef SMP_CHECK
+ printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d,"
+ " proc %d already handling.\n", dev->name,
+ tp->smp_proc_id, hard_smp_processor_id());
+#else
printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
return;
+#endif
}
dev->interrupt = 1;
+#ifdef SMP_CHECK
+ tp->smp_proc_id = hard_smp_processor_id();
+#endif
+
do {
csr5 = inl(ioaddr + CSR5);
/* Acknowledge all of the current interrupt sources ASAP. */
work_budget -= tulip_rx(dev);
if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
- int dirty_tx;
+ unsigned int dirty_tx;
- for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) {
+ for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
+ dirty_tx++) {
int entry = dirty_tx % TX_RING_SIZE;
int status = tp->tx_ring[entry].status;
#ifdef ETHER_STATS
if (status & 0x0001) tp->stats.tx_deferred++;
#endif
-#if LINUX_VERSION_CODE > 0x20139
- tp->tx_bytes += tp->tx_ring[entry].length & 0x7ff;
+#if LINUX_VERSION_CODE > 0x20127
+ tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
#endif
tp->stats.collisions += (status >> 3) & 15;
tp->stats.tx_packets++;
}
/* Free the original skb. */
+#if (LINUX_VERSION_CODE > 0x20155)
+ dev_kfree_skb(tp->tx_skbuff[entry]);
+#else
dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
+#endif
tp->tx_skbuff[entry] = 0;
}
#endif
if (tp->tx_full && dev->tbusy
- && dirty_tx > tp->cur_tx - TX_RING_SIZE + 2) {
+ && tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) {
/* The ring is no longer full, clear tbusy. */
tp->tx_full = 0;
dev->tbusy = 0;
dev->name, csr5);
/* Hmmmmm, it's not clear what to do here. */
}
+ if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)
+ && tp->chip_id == DC21142) {
+ if (tulip_debug > 1)
+ printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n",
+ dev->name, csr5);
+ t21142_lnk_change(dev);
+ }
/* Clear all error sources, included undocumented ones! */
- outl(0x000f7ba, ioaddr + CSR5);
+ outl(0x0800f7ba, ioaddr + CSR5);
}
if (--work_budget < 0) {
- printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n",
- dev->name, csr5);
+ if (tulip_debug > 1)
+ printk(KERN_WARNING "%s: Too much work at interrupt, "
+ "csr5=0x%8.8x.\n", dev->name, csr5);
/* Acknowledge all interrupt sources. */
- outl(0x0001ffff, ioaddr + CSR5);
+ outl(0x8001ffff, ioaddr + CSR5);
#ifdef notdef
/* Clear all but standard interrupt sources. */
outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
}
#endif
- tp->interrupt = 0;
dev->interrupt = 0;
+ clear_bit(0, (void*)&tp->interrupt);
return;
}
netif_rx(skb);
dev->last_rx = jiffies;
tp->stats.rx_packets++;
-#if LINUX_VERSION_CODE > 0x20139
- tp->rx_bytes += pkt_len;
+#if LINUX_VERSION_CODE > 0x20127
+ tp->stats.rx_bytes += pkt_len;
#endif
}
entry = (++tp->cur_rx) % RX_RING_SIZE;
}
/* Refill the Rx ring buffers. */
- for (; tp->dirty_rx < tp->cur_rx; tp->dirty_rx++) {
+ for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
entry = tp->dirty_rx % RX_RING_SIZE;
if (tp->rx_skbuff[entry] == NULL) {
struct sk_buff *skb;
#if LINUX_VERSION_CODE < 0x20100
skb->free = 1;
#endif
+#if (LINUX_VERSION_CODE > 0x20155)
+ dev_kfree_skb(skb);
+#else
dev_kfree_skb(skb, FREE_WRITE);
+#endif
}
}
for (i = 0; i < TX_RING_SIZE; i++) {
if (tp->tx_skbuff[i])
+#if (LINUX_VERSION_CODE > 0x20155)
+ dev_kfree_skb(tp->tx_skbuff[i]);
+#else
dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+#endif
tp->tx_skbuff[i] = 0;
}
return &tp->stats;
}
+#ifdef HAVE_PRIVATE_IOCTL
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ u16 *data = (u16 *)&rq->ifr_data;
+ int phy = tp->phys[0] & 0x1f;
+ long flags;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ if (tp->mtable && tp->mtable->has_mii)
+ data[0] = phy;
+ else if (tp->chip_id == DC21142)
+ data[0] = 32;
+ else
+ return -ENODEV;
+ return 0;
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ if (data[0] == 32) { /* 21142 pseudo-MII */
+ int csr12 = inl(ioaddr + CSR12);
+ int csr14 = inl(ioaddr + CSR14);
+ switch (data[1]) {
+ case 0: {
+ data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000);
+ break; }
+ case 1:
+ data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
+ + (csr12&0x06 ? 0x04 : 0);
+ break;
+ case 4: {
+ int csr14 = inl(ioaddr + CSR14);
+ data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1;
+ break;
+ }
+ case 5: data[3] = inl(ioaddr + CSR12) >> 16; break;
+ default: data[3] = 0; break;
+ }
+ } else {
+ save_flags(flags);
+ cli();
+ data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+ restore_flags(flags);
+ }
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!suser())
+ return -EPERM;
+ if (data[0] == 32) { /* 21142 pseudo-MII */
+ } else {
+ save_flags(flags);
+ cli();
+ mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ restore_flags(flags);
+ }
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+#endif /* HAVE_PRIVATE_IOCTL */
+
/* Set or clear the multicast filter for this adaptor.
Note that we only use exclusion around actually queueing the
new frame, not around filling tp->setup_frame. This is non-deterministic
outl(csr6 | 0x0000, ioaddr + CSR6);
}
}
+\f
+#ifdef CARDBUS
+
+#include <pcmcia/driver_ops.h>
+
+static dev_node_t *tulip_attach(dev_locator_t *loc)
+{
+ u16 dev_id;
+ u32 io;
+ u8 bus, devfn;
+ struct device *dev;
+
+ if (loc->bus != LOC_PCI) return NULL;
+ bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
+ printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn);
+ pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
+ pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
+ io &= ~3;
+ dev = tulip_probe1(bus, devfn, NULL, io, DC21142, -1);
+ if (dev) {
+ dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
+ strcpy(node->dev_name, dev->name);
+ node->major = node->minor = 0;
+ node->next = NULL;
+ MOD_INC_USE_COUNT;
+ return node;
+ }
+ return NULL;
+}
+
+static void tulip_detach(dev_node_t *node)
+{
+ struct device **devp, **next;
+ printk(KERN_INFO "tulip_detach(%s)\n", node->dev_name);
+ for (devp = &root_tulip_dev; *devp; devp = next) {
+ next = &((struct tulip_private *)(*devp)->priv)->next_module;
+ if (strcmp((*devp)->name, node->dev_name) == 0) break;
+ }
+ if (*devp) {
+ unregister_netdev(*devp);
+ kfree(*devp);
+ *devp = *next;
+ kfree(node);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+struct driver_operations tulip_ops = {
+ "tulip_cb", tulip_attach, NULL, NULL, tulip_detach
+};
+
+#endif /* Cardbus support */
+
\f
#ifdef MODULE
#if LINUX_VERSION_CODE > 0x20118
if (debug >= 0)
tulip_debug = debug;
- root_tulip_dev = NULL;
+#ifdef CARDBUS
+ register_driver(&tulip_ops);
+ return 0;
+#else
return tulip_probe(NULL);
+#endif
}
void
{
struct device *next_dev;
+#ifdef CARDBUS
+ unregister_driver(&tulip_ops);
+#endif
+
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
while (root_tulip_dev) {
next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
\f
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
- * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html
*/
-static const char *version = "yellowfin.c:v0.99 1/21/98 becker@cesdis.gsfc.nasa.gov\n";
+static const char *version = "yellowfin.c:v0.99A 4/7/98 becker@cesdis.gsfc.nasa.gov\n";
/* A few user-configurable values. */
static int bogus_rx = 0;
static int dma_ctrl = 0x004A0263; /* Constrained by errata */
static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */
-#elif YF_NEW
+#elif YF_NEW /* A future perfect board :->. */
static int dma_ctrl = 0x00CAC277; /* Override when loading module! */
static int fifo_cfg = 0x0028;
#else
unsigned char pci_bus, pci_device_fn;
for (;pci_index < 0xff; pci_index++) {
- unsigned char pci_irq_line, pci_latency;
- unsigned short pci_command, vendor, device;
- unsigned int pci_ioaddr, chip_idx = 0;
+ u8 pci_irq_line, pci_latency;
+ u16 pci_command, vendor, device;
+ u32 pci_ioaddr, chip_idx = 0;
#ifdef REVERSE_PROBE_ORDER
if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
outl(0x10001000, dev->base_addr + TxCtrl);
if (yp->cur_tx - yp->dirty_tx < TX_RING_SIZE - 1)
- dev->tbusy = 0; /* Typical path */
+ clear_bit(0, (void*)&dev->tbusy); /* Typical path */
else
yp->tx_full = 1;
dev->trans_start = jiffies;
yellowfin_rx(dev);
#ifdef NO_TXSTATS
- for (; dirty_tx < lp->cur_tx; dirty_tx++) {
+ for (; lp->cur_tx - dirty_tx > 0; dirty_tx++) {
int entry = dirty_tx % TX_RING_SIZE;
if (lp->tx_ring[entry].status == 0)
break;
lp->stats.tx_packets++;
}
if (lp->tx_full && dev->tbusy
- && dirty_tx > lp->cur_tx - TX_RING_SIZE + 4) {
+ && lp->cur_tx - dirty_tx < TX_RING_SIZE - 4) {
/* The ring is no longer full, clear tbusy. */
lp->tx_full = 0;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
lp->dirty_tx = dirty_tx;
if (intr_status & IntrTxDone
|| lp->tx_status[dirty_tx % TX_RING_SIZE].tx_errs) {
- for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
+ for (dirty_tx = lp->dirty_tx; lp->cur_tx - dirty_tx > 0;
+ dirty_tx++) {
/* Todo: optimize this. */
int entry = dirty_tx % TX_RING_SIZE;
u16 tx_errs = lp->tx_status[entry].tx_errs;
#endif
if (lp->tx_full && dev->tbusy
- && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ && lp->cur_tx - dirty_tx < TX_RING_SIZE - 2) {
/* The ring is no longer full, clear tbusy. */
lp->tx_full = 0;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
}
dev->interrupt = 0;
- lp->in_interrupt = 0;
+ clear_bit(0, (void*)&lp->in_interrupt);
return;
}
#endif
} else {
u8 bogus_cnt = buf_addr[frm_size - 8];
- short pkt_len = frm_size - 8 - bogus_cnt;
+ int pkt_len = frm_size - 8 - bogus_cnt;
struct sk_buff *skb;
int rx_in_place = 0;
}
-static void
#ifdef NEW_MULTICAST
-set_rx_mode(struct device *dev)
+static void set_rx_mode(struct device *dev)
#else
static void set_rx_mode(struct device *dev, int num_addrs, void *addrs);
#endif
\f
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
*
* Copyright 1993, 1994, 1995 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
+ *
+ * Apr 12, 1998 : Fixed handling of alien header types. [mj]
*/
+
#include <linux/config.h>
#include <linux/ptrace.h>
#include <linux/types.h>
DEVICE( INTEL, INTEL_P6, "Orion P6"),
DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"),
DEVICE( KTI, KTI_ET32P2, "ET32P2"),
+ DEVICE( ADAPTEC, ADAPTEC_7810, "AIC-7810 RAID Controller"),
DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"),
DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"),
DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"),
DEVICE( ADAPTEC, ADAPTEC_7872, "AIC-7872"),
DEVICE( ADAPTEC, ADAPTEC_7873, "AIC-7873"),
DEVICE( ADAPTEC, ADAPTEC_7874, "AIC-7874"),
+ DEVICE( ADAPTEC, ADAPTEC_7895, "AIC-7895U"),
DEVICE( ADAPTEC, ADAPTEC_7880, "AIC-7880U"),
DEVICE( ADAPTEC, ADAPTEC_7881, "AIC-7881U"),
DEVICE( ADAPTEC, ADAPTEC_7882, "AIC-7882U"),
static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
{
unsigned int devfn, l, max;
- unsigned char cmd, tmp, hdr_type = 0;
+ unsigned char cmd, tmp, hdr_type, is_multi = 0;
struct pci_dev_info *info;
struct pci_dev *dev;
struct pci_bus *child;
max = bus->secondary;
for (devfn = 0; devfn < 0xff; ++devfn) {
- if (PCI_FUNC(devfn) == 0) {
- pcibios_read_config_byte(bus->number, devfn,
- PCI_HEADER_TYPE, &hdr_type);
- } else if (!(hdr_type & 0x80)) {
- /* not a multi-function device */
+ if (PCI_FUNC(devfn) && !is_multi) {
+ /* Not a multi-function device */
continue;
}
+ pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type);
+ if (!PCI_FUNC(devfn))
+ is_multi = hdr_type & 0x80;
- pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID,
- &l);
+ pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l);
/* some broken boards return 0 if a slot is empty: */
if (l == 0xffffffff || l == 0x00000000) {
- hdr_type = 0;
+ is_multi = 0;
continue;
}
dev = pci_malloc(sizeof(*dev), mem_startp);
dev->bus = bus;
- /*
- * Put it into the simple chain of devices on this
- * bus. It is used to find devices once everything is
- * set up.
- */
- dev->next = pci_devices;
- pci_devices = dev;
-
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
*/
info = pci_lookup_dev(dev->vendor, dev->device);
if (!info) {
- printk("Warning : Unknown PCI device (%x:%x). Please read include/linux/pci.h \n",
+#if 0
+ printk("Warning : Unknown PCI device (%x:%x). Please read include/linux/pci.h\n",
dev->vendor, dev->device);
+#endif
} else {
/* Some BIOS' are lazy. Let's do their job: */
if (info->bridge_type != 0xff) {
PCI_CLASS_REVISION, &l);
l = l >> 8; /* upper 3 bytes */
dev->class = l;
+
+ /*
+ * Check if the header type is known and consistent with
+ * device type. Bridges should have hdr_type 1, all other
+ * devices 0.
+ */
+ if ((dev->class >> 8 == PCI_CLASS_BRIDGE_PCI) != (hdr_type & 0x7f)) {
+ printk(KERN_WARNING "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
+ bus->number, dev->devfn, dev->vendor, dev->device, dev->class, hdr_type);
+ continue;
+ }
+
+ /*
+ * Put it into the simple chain of all PCI devices.
+ * It is used to find devices once everything is set up.
+ */
+ dev->next = pci_devices;
+ pci_devices = dev;
+
/*
* Now insert it into the list of devices held
* by the parent bus.
dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
- bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
bool ' Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
- int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+ int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24
fi
- bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
- int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+ int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
fi
dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
AIC7xxx Driver for Linux
- April 15, 1996
Introduction
-------------------------
+----------------------------
The AIC7xxx SCSI driver adds support for Adaptec (http://www.adaptec.com)
SCSI controllers and chipsets. Major portions of the driver and driver
development are shared between both Linux and FreeBSD. Support for the
2.1.0 or later.
Supported cards/chipsets
- ------------------------
+ ----------------------------
Adaptec Cards
- -----------------------
- AHA-274x
- AHA-2842
+ ----------------------------
+ AHA-274x
+ AHA-274xT
+ AHA-2842
+ AHA-2910B
AHA-2940
AHA-2940W
AHA-2940U
- AHA-2940UW
+ AHA-2940UW
+ AHA-2940AU
AHA-2944D
AHA-2944WD
+ AHA-2944UD
+ AHA-2944UWD
AHA-3940
+ AHA-3940U
AHA-3940W
+ AHA-3940UW
AHA-3985
+ AHA-3985U
AHA-3985W
+ AHA-3985UW
Motherboard Chipsets
- -----------------------
+ ----------------------------
AIC-777x
AIC-785x
+ AIC-786x
AIC-787x
AIC-788x
+ AIC-7895
Bus Types
- -----------------------
+ ----------------------------
W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support
SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s.
U - Ultra SCSI, transfer rates up to 40MB/s.
AHA-398x - PCI RAID controllers with three separate SCSI controllers
on-board.
- NOTE: The AHA-2920 is NOT a AIC-7xxx based controller, and is not
+ NOTE: The AHA-2920 is NOT an AIC-7xxx based controller, and is not
handled by this driver.
People
- ------------------------
- Justin T Gibbs gibbs@freefall.FreeBSD.org (BSD Driver Author)
- Dan Eischen deischen@iworks.InterWorks.org (Linux Driver Co-maintainer)
- Dean Gehnert deang@teleport.com (Linux FTP/patch maintainer)
- Jess Johnson jester@frenzy.com (AIC7xxx FAQ author)
-
+ ------------------------------
+ Justin T Gibbs gibbs@plutotech.com
+ (BSD Driver Author)
+ Dan Eischen deischen@iworks.InterWorks.org
+ (Original Linux Driver Co-maintainer)
+ Dean Gehnert deang@teleport.com
+ (Original Linux FTP/patch maintainer)
+ Jess Johnson jester@frenzy.com
+ (AIC7xxx FAQ author)
+ Doug Ledford dledford@dialnet.net
+ (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
+
Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
author of the driver. John has since retired from the project. Thanks
again for all his work!
-
+
Mailing list
- ------------------------
+ ------------------------------
There is a mailing list available for users who want to track development
and converse with other users and developers. This list is for both
FreeBSD and Linux support of the AIC7xxx chipsets.
Send regular messages and replies to: AIC7xxx@FreeBSD.ORG
- Command line options
- ------------------------
+ Boot Command line options
+ ------------------------------
"aic7xxx=no_reset" - Eliminate the SCSI reset delay during startup.
Some SCSI devices need some extra time to reset.
+ "aic7xxx=reverse_scan" - Have the driver register the SCSI cards in the
+ reverse of the normal order. This may help those people who have more
+ than one PCI Adaptec controller force the correct controller to be
+ scsi0 under linux so that their boot hard drive is also sda under
+ linux
+ "aic7xxx=extended" - Force the driver to detect extended drive translation
+ on your controller. This helps those people who have cards without
+ a SEEPROM make sure that linux and all other operating systems think
+ the same way about your hard drives.
+ "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel
+ to use the correct IRQ type for your card. This only applies to EISA
+ based controllers. On these controllers, 0 is for Edge triggered
+ interrupts, and 1 is for Level triggered interrupts. If you aren't
+ sure or don't know which IRQ trigger type your EISA card uses, then
+ let the kernel autodetect the trigger type.
+ "aic7xxx=verbose" - This option can be used in one of two ways. If you
+ simply specify aic7xxx=verbose, then the kernel will automatically pick
+ the default set of verbose messages for you to see. Alternatively, you
+ can specify the command as "aic7xxx=verbose:0xXXXX" where the X entries
+ are replaced with hexadecimal digits. This option is a bit field type
+ option. For a full listing of the available options, search for the
+ #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want verbose
+ messages, then it is recommended that you simply use the aic7xxx=verbose
+ variant of this command.
+ "aic7xxx=7895_irq_hack:x" - This option enables some work around code to
+ fix a bug in the Tyan Thunder II motherboard BIOS. The BIOS
+ incorrectly sets the IRQs on the two channels of the 7895 to two
+ different values even though the motherboard hardware doesn't support
+ this mode of operation. The valid values for x are: 0 to force
+ both channels to use the IRQ assigned to Channel A, 1 to force both
+ channels to use the IRQ assigned to Channel B, and -1 will disable
+ this horrible abomination of a hack. The default is disabled (-1).
+ "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
+ tagged queueing on specific devices. As of driver version 5.0.6, we
+ now globally enable tagged queueing by default, but we also disable
+ tagged queueing on all individual devices by default. In order to
+ enable tagged queueing for certian devices at boot time, a user may
+ use this boot param. The driver will then parse this message out
+ and enable the specific device entries that are present based upon
+ the value given. The param line is parsed in the following manner:
+
+ { - first instance indicates the start of this parameter values
+ second instance is the start of entries for a particular
+ device entry
+ } - end the entries for a particular host adapter, or end the entire
+ set of parameter entries
+ , - move to next entry. Inside of a set of device entries, this
+ moves us to the next device on the list. Outside of device
+ entries, this moves us to the next host adapter
+ . - Same effect as , but is safe to use with insmod.
+ x - the number to enter into the array at this position.
+ 0 = Enable tagged queueing on this device and use the default
+ queue depth
+ 1-254 = Enable tagged queueing on this device and use this
+ number as the queue depth
+ 255 = Disable tagged queueing on this device.
+ Note: anything above 32 for an actual queue depth is wasteful
+ and not recommended.
+
+ A few examples of how this can be used:
+
+ tag_info:{{8,12,,0,,255,4}}
+ This line will only effect the first aic7xxx card registered. It
+ will set scsi id 0 to a queue depth of 8, id 1 to 12, leave id 2
+ at the default, set id 3 to tagged queueing enabled and use the
+ default queue depth, id 4 default, id 5 disabled, and id 6 to 4.
+ Any not specified entries stay at the default value, repeated
+ commas with no value specified will simply increment to the next id
+ without changing anything for the missing values.
+
+ tag_info:{{8,8},,{8,8}}
+ First adapter, scsi id 0 to 8, id 1 to 8, remainder stay at their
+ default. Second adapter stays entirely at default. Third
+ adapter, id 0 to 8, id 1 to 8, remainder at default (identical to
+ first adapter).
+
+ tag_info:{,,,{,,,64}}
+ First, second, and third adapters at default values. Fourth
+ adapter, id 3 to 64. Notice that leading commas simply increment
+ what the first number effects, and there are no need for trailing
+ commas. When you close out an adapter, or the entire entry,
+ anything not explicitly set stays at the default value.
+
+ A final note on this option. The scanner I used for this isn't
+ perfect or highly robust. If you mess the line up, the worst that
+ should happen is that the line will get ignored. If you don't
+ close out the entire entry with the final bracket, then any other
+ aic7xxx options after this will get ignored. So, in general, be
+ sure of what you are entering, and after you have it right, just
+ add it to the lilo.conf file so there won't be any mistakes. As
+ a means of checking this parser, the entire tag_info array for
+ each card is now printed out in the /proc/scsi/aic7xxx/x file. You
+ can use that to verify that your options were parsed correctly.
+
+ Boot command line options may be combined to form the proper set of options
+ a user might need. For example, the following is valid:
+
+ aic7xxx=verbose,extended,irq_trigger:1
+
+ The only requirement is that individual options be separated by a comma on
+ the command line.
+
+ Module Loading command options
+ ------------------------------
+ When loading the aic7xxx driver as a module, the exact same options are
+ available to the user. However, the syntax to specify the options changes
+ slightly. For insmod, you need to wrap the aic7xxx= argument in quotes
+ and replace all ',' with '.'. So, for example, a valid insmod line
+ would be:
+
+ insmod aic7xxx aic7xxx='verbose.irq_trigger:1.extended'
+
+ This line should result in the *exact* same behaviour as if you typed
+ it in at the lilo prompt and the driver was compiled into the kernel
+ instead of being a module. The reason for the single quote is so that
+ the shell won't try to interpret anything in the line, such as {.
+ Insmod assumes any options starting with a letter instead of a number
+ is a character string (which is what we want) and by switching all of
+ the commas to periods, insmod won't interpret this as more than one
+ string and write junk into our binary image. I consider it a bug in
+ the insmod program that even if you wrap your string in quotes (quotes
+ that pass the shell mind you and that insmod sees) it still treates
+ a comma inside of those quotes as starting a new variable, resulting
+ in memory scribbles if you don't switch the commas to periods.
+
+
+ Kernel Compile options
+ ------------------------------
+ The various kernel compile time options for this driver are now fairly
+ well documented in the file Documentation/Configure.help. In order to
+ see this documentation, you need to use one of the advanced configuration
+ programs (menuconfig and xconfig). If you are using the "make menuconfig"
+ method of configuring your kernel, then you would simply highlight the
+ option in question and hit the F1 key. If you are using the "make xconfig"
+ method of configuring your kernel, then simply click on the help button next
+ to the option you have questions about. The help information from the
+ Configure.help file will then get automatically displayed.
/proc support
- ------------------------
+ ------------------------------
The /proc support for the AIC7xxx can be found in the /proc/scsi/aic7xxx/
directory. That directory contains a file for each SCSI controller in
the system. Each file presents the current configuration and transfer
Matthew Jacob for statistics support.
FTP sites
- ------------------------
- ftp://ftp.teleport.com/users/deang/Linux/aic7xxx/
- - Main Linux AIC7xxx driver release/pre-release site
- - Experimental/development patches and bootdisks
+ ------------------------------
ftp://ftp.dialnet.net/pub/linux/aic7xxx/
+ - Primary site for Doug Ledford developed driver releases
- US Linux mirror of Teleport site
+ ftp://ftp.pcnet.com/users/eischen/Linux/
+ - Dan Eischen's driver distribution area
ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
- European Linux mirror of Teleport site
$Revision: 3.0 $
+Modified by Doug Ledford 1998
* Parts of this driver were also based on the FreeBSD driver by
* Justin T. Gibbs. His copyright follows:
*
- * --------------------------------------------------------------------------
+ * --------------------------------------------------------------------------
* Copyright (c) 1994-1997 Justin Gibbs.
* All rights reserved.
*
* $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
*-M*************************************************************************/
+/*+M**************************************************************************
+ *
+ * Further driver modifications made by Doug Ledford <dledford@dialnet.net>
+ *
+ * Copyright (c) 1997-1998 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs. Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ * 1: Import of the latest FreeBSD sequencer code for this driver
+ * 2: Modification of kernel code to accomodate different sequencer semantics
+ * 3: Extensive changes throughout kernel portion of driver to improve
+ * abort/reset processing and error hanndling
+ * 4: Other work contributed by various people on the Internet
+ * 5: Changes to printk information and verbosity selection code
+ * 6: General reliability related changes, especially in IRQ management
+ * 7: Modifications to the default probe/attach order for supported cards
+ * 8: SMP friendliness has been improved
+ *
+ * Overall, this driver represents a significant departure from the official
+ * aic7xxx driver released by Dan Eischen in two ways. First, in the code
+ * itself. A diff between the two version of the driver is now a several
+ * thousand line diff. Second, in approach to solving the same problem. The
+ * problem is importing the FreeBSD aic7xxx driver code to linux can be a
+ * difficult and time consuming process, that also can be error prone. Dan
+ * Eischen's official driver uses the approach that the linux and FreeBSD
+ * drivers should be as identical as possible. To that end, his next version
+ * of this driver will be using a mid-layer code library that he is developing
+ * to moderate communications between the linux mid-level SCSI code and the
+ * low level FreeBSD driver. He intends to be able to essentially drop the
+ * FreeBSD driver into the linux kernel with only a few minor tweaks to some
+ * include files and the like and get things working, making for fast easy
+ * imports of the FreeBSD code into linux.
+ *
+ * I disagree with Dan's approach. Not that I don't think his way of doing
+ * things would be nice, easy to maintain, and create a more uniform driver
+ * between FreeBSD and Linux. I have no objection to those issues. My
+ * disagreement is on the needed functionality. There simply are certain
+ * things that are done differently in FreeBSD than linux that will cause
+ * problems for this driver regardless of any middle ware Dan implements.
+ * The biggest example of this at the moment is interrupt semantics. Linux
+ * doesn't provide the same protection techniques as FreeBSD does, nor can
+ * they be easily implemented in any middle ware code since they would truly
+ * belong in the kernel proper and would effect all drivers. For the time
+ * being, I see issues such as these as major stumbling blocks to the
+ * reliability of code based upon such middle ware. Therefore, I choose to
+ * use a different approach to importing the FreeBSD code that doesn't
+ * involve any middle ware type code. My approach is to import the sequencer
+ * code from FreeBSD wholesale. Then, to only make changes in the kernel
+ * portion of the driver as they are needed for the new sequencer semantics.
+ * In this way, the portion of the driver that speaks to the rest of the
+ * linux kernel is fairly static and can be changed/modified to solve
+ * any problems one might encounter without concern for the FreeBSD driver.
+ *
+ * Note: If time and experience should prove me wrong that the middle ware
+ * code Dan writes is reliable in its operation, then I'll retract my above
+ * statements. But, for those that don't know, I'm from Missouri (in the US)
+ * and our state motto is "The Show-Me State". Well, before I will put
+ * faith into it, you'll have to show me that it works :)
+ *
+ *_M*************************************************************************/
+
#ifdef MODULE
#include <linux/module.h>
#endif
#include <stdarg.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <linux/version.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/blk.h>
+#include <linux/tqueue.h>
+#include <linux/tasks.h>
#include "sd.h"
#include "scsi.h"
#include "hosts.h"
#include "aic7xxx/scsi_message.h"
#include "aic7xxx_reg.h"
#include "aic7xxx_seq.h"
+
#include <linux/stat.h>
-#include <linux/malloc.h> /* for kmalloc() */
+#include <linux/malloc.h> /* for kmalloc() */
-#include <linux/config.h> /* for CONFIG_PCI */
+#include <linux/config.h> /* for CONFIG_PCI */
/*
* To generate the correct addresses for the controller to issue
0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
-#define AIC7XXX_C_VERSION "$Revision: 4.1.1 $"
+#define AIC7XXX_C_VERSION "5.0.13"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define ALL_TARGETS -1
-#define ALL_CHANNELS '\0'
+#define ALL_CHANNELS -1
#define ALL_LUNS -1
#define MAX_TARGETS 16
#define MAX_LUNS 8
# define FALSE 0
#endif
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
/*
- * Defines for PCI bus support, testing twin bus support, DMAing of
- * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset
- * delay time.
- *
- * o PCI bus support - this has been implemented and working since
- * the December 1, 1994 release of this driver. If you don't have
- * a PCI bus, then you can configure your kernel without PCI
- * support because all PCI dependent code is bracketed with
- * "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
- *
- * o Twin bus support - this has been tested and does work. It is
- * not an option anymore.
- *
- * o Tagged queueing - this driver is capable of tagged queueing
- * but I am unsure as to how well the higher level driver implements
- * tagged queueing. Therefore, the maximum commands per lun is
- * set to 2. If you want to implement tagged queueing, ensure
- * this define is not commented out.
- *
- * o Commands per lun - If tagged queueing is enabled, then you
- * may want to try increasing AIC7XXX_CMDS_PER_LUN to more
- * than 2. By default, we limit the SCBs per LUN to 2 with
- * or without tagged queueing enabled. If tagged queueing is
- * disabled, the sequencer will keep the 2nd SCB in the input
- * queue until the first one completes - so it is OK to to have
- * more than 1 SCB queued. If tagged queueing is enabled, then
- * the sequencer will attempt to send the 2nd SCB to the device
- * while the first SCB is executing and the device is disconnected.
- * For adapters limited to 4 SCBs, you may want to actually
- * decrease the commands per LUN to 1, if you often have more
- * than 2 devices active at the same time. This will allocate
- * 1 SCB for each device and ensure that there will always be
- * a free SCB for up to 4 devices active at the same time.
- * When SCB paging is enabled, set the commands per LUN to 8
- * or higher (see SCB paging support below). Note that if
- * AIC7XXX_CMDS_PER_LUN is not defined and tagged queueing is
- * enabled, the driver will attempt to set the commands per
- * LUN using its own heuristic based on the number of available
- * SCBs.
- *
- * o 3985 support - The 3985 adapter is much like the 3940, but has
- * three 7870 controllers as opposed to two for the 3940. It will
- * be probed and recognized as three different adapters, but all
- * three controllers can share the same external bank of 255 SCBs.
- * If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
- * to use and share the common bank of SCBs between the three
- * controllers of the 3985. This is experimental and hasn't been
- * been tested. By default, we do not use external SCB RAM, and
- * force the controllers to use their own internal bank of 16 SCBs.
- * Please let us know if using the external SCB array works.
- *
- * o SCB paging support - SCB paging is enabled by defining
- * AIC7XXX_PAGE_ENABLE. Support for this was taken from the
- * FreeBSD driver (by Justin Gibbs) and allows for up to 255
- * active SCBs. This will increase performance when tagged
- * queueing is enabled. Note that you should increase the
- * AIC7XXX_CMDS_PER_LUN to 8 as most tagged queueing devices
- * allow at least this many.
- *
- * Note that sharing of IRQs is not an option any longer. Linux supports
- * it so we support it.
- *
- * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
+ * We need the bios32.h file if we are kernel version 2.1.92 or less. The
+ * full set of pci_* changes wasn't in place until 2.1.93
*/
-/* Uncomment this for tagged queueing. */
-#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
-#define AIC7XXX_TAGGED_QUEUEING
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+# if defined(__sparc_v9__) || defined(__powerpc__)
+# error "PPC and Sparc platforms are only support under 2.1.x and above"
+# endif
+# include <linux/bios32.h>
+#endif
+
+#if !defined(__alpha__)
+# define MMAPIO
+#endif
+
+#if defined(__powerpc__)
+# ifdef mb
+# undef mb
+# endif
+# define mb() \
+ __asm__ __volatile__("eieio" ::: "memory")
+#elif defined(__i386__)
+# ifdef mb
+# undef mb
+# endif
+# define mb() \
+ __asm__ __volatile__("lock ; addl $0,0(%%esp)": : :"memory")
+#elif defined(__alpha__)
+# ifdef mb
+# undef mb
+# endif
+# define mb() \
+ __asm__ __volatile__("mb": : :"memory")
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+# include <asm/spinlock.h>
+# include <linux/smp.h>
+# define cpuid smp_processor_id()
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+# define DRIVER_LOCK_INIT \
+ spin_lock_init(&p->spin_lock);
+# define DRIVER_LOCK \
+ if(!p->cpu_lock_count[cpuid]) { \
+ spin_lock_irqsave(&p->spin_lock, cpu_flags); \
+ p->cpu_lock_count[cpuid]++; \
+ } else { \
+ p->cpu_lock_count[cpuid]++; \
+ }
+# define DRIVER_UNLOCK \
+ if(--p->cpu_lock_count[cpuid] == 0) \
+ spin_unlock_irqrestore(&p->spin_lock, cpu_flags);
+# else
+# define DRIVER_LOCK_INIT
+# define DRIVER_LOCK
+# define DRIVER_UNLOCK
+# endif
+#else
+# define cpuid 0
+# define DRIVER_LOCK_INIT
+# define DRIVER_LOCK \
+ save_flags(cpu_flags); \
+ cli();
+# define DRIVER_UNLOCK \
+ restore_flags(cpu_flags);
+# define le32_to_cpu(x) (x)
+# define cpu_to_le32(x) (x)
#endif
/*
#ifdef CONFIG_AIC7XXX_RESET_DELAY
#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
#else
-#define AIC7XXX_RESET_DELAY 15
+#define AIC7XXX_RESET_DELAY 5
#endif
/*
#endif
/*
- * Enable SCB paging.
- */
-#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
-#define AIC7XXX_PAGE_ENABLE
-#endif
-
-/*
- * Uncomment the following to enable use of the external bank
- * of 255 SCBs. For 3985 adapters, this will also enable sharing
- * of the SCB array across all three controllers.
- */
-#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
-#define AIC7XXX_USE_EXT_SCBRAM
-#endif
-
-/*
- * For debugging the abort/reset code.
- */
-#define AIC7XXX_DEBUG_ABORT
-
-/*
- * For general debug messages
- */
-#define AIC7XXX_DEBUG
-
-/*
+ * NOTE: Uncommenting the define below no longer has any effect, the
+ * tagged queue value array is always active now. I've added
+ * a setup option to set this particular array and I'm hoping
+ * insmod will be smart enough to set it properly as well. It's
+ * by use of this array that a person can disable tagged queueing.
+ * The DEFAULT_TAG_COMMANDS define has been changed to disable
+ * tagged queueing by default, so if your devices can handle tagged
+ * queueing you will need to add a line to their lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
* Set this for defining the number of tagged commands on a device
* by device, and controller by controller basis. The first set
* of tagged commands will be used for the first detected aic7xxx
*
* When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
* own algorithm to determine the commands/LUN. If SCB paging is
- * enabled, the commands/LUN is 8. When SCB paging is not enabled,
- * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
- * and 4 commands/LUN for adapters with 3 or 4 SCBs.
- *
+ * enabled, which is always now, the default is 8 commands per lun
+ * that indicates it supports tagged queueing. All non-tagged devices
+ * use an internal queue depth of 3, with no more than one of those
+ * three commands active at one time.
*/
/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
-#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
typedef struct
{
- unsigned char tag_commands[16]; /* Allow for wide/twin channel adapters. */
+ unsigned char tag_commands[16]; /* Allow for wide/twin adapters. */
} adapter_tag_info_t;
/*
- * Make a define that will tell the driver to use it's own algorithm
- * for determining commands/LUN (see Determining commands per LUN
- * above).
+ * Make a define that will tell the driver not to use tagged queueing
+ * by default.
*/
-#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
+ 255, 255, 255, 255, 255, 255, 255, 255}
/*
* Modify this as you see fit for your system. By setting tag_commands
* to 0, the driver will use it's own algorithm for determining the
- * number of commands to use (see above). When -1, the driver will
+ * number of commands to use (see above). When 255, the driver will
* not enable tagged queueing for that particular device. When positive
- * (> 0) the values in the array are used for the queue_depth. Note
- * that the maximum value for an entry is 127.
+ * (> 0) and (< 255) the values in the array are used for the queue_depth.
+ * Note that the maximum value for an entry is 254, but you're insane if
+ * you try to use that many commands on one device.
*
- * In this example, the first line will enable tagged queueing for all
- * the devices on the first probed aic7xxx adapter and tells the driver
- * to use it's own algorithm for determining commands/LUN.
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
*
* The second line enables tagged queueing with 4 commands/LUN for IDs
* (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
* for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
* IDs 2, 5-7, and 9-15.
*/
+
+/*
adapter_tag_info_t aic7xxx_tag_info[] =
{
{DEFAULT_TAG_COMMANDS},
- {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+ {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
{DEFAULT_TAG_COMMANDS},
- {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+ {{255, 16, 4, 255, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
};
-#endif
-
-/*
- * Don't define this unless you have problems with the driver
- * interrupt handler. The old method would register the drivers
- * interrupt handler as a "fast" type interrupt handler that would
- * lock out other interrupts. Since this driver can spend a lot
- * of time in the interrupt handler, this is _not_ a good idea.
- * It also conflicts with some of the more common ethernet drivers
- * that don't use fast interrupts. Currently, Linux does not allow
- * IRQ sharing unless both drivers can agree on the type of interrupt
- * handler.
- */
-/* #define AIC7XXX_OLD_ISR_TYPE */
-
-
-/*
- * Controller type and options
- */
-typedef enum {
- AIC_NONE,
- AIC_7770, /* EISA aic7770 on motherboard */
- AIC_7771, /* EISA aic7771 on 274x */
- AIC_284x, /* VLB aic7770 on 284x, BIOS disabled */
- AIC_7850, /* PCI aic7850 */
- AIC_7855, /* PCI aic7855 */
- AIC_7860, /* PCI aic7860 (7850 Ultra) */
- AIC_7861, /* PCI aic7861 on 2940AU */
- AIC_7870, /* PCI aic7870 on motherboard */
- AIC_7871, /* PCI aic7871 on 294x */
- AIC_7872, /* PCI aic7872 on 3940 */
- AIC_7873, /* PCI aic7873 on 3985 */
- AIC_7874, /* PCI aic7874 on 294x Differential */
- AIC_7880, /* PCI aic7880 on motherboard */
- AIC_7881, /* PCI aic7881 on 294x Ultra */
- AIC_7882, /* PCI aic7882 on 3940 Ultra */
- AIC_7883, /* PCI aic7883 on 3985 Ultra */
- AIC_7884 /* PCI aic7884 on 294x Ultra Differential */
-} aha_chip_type;
-
-typedef enum {
- AIC_777x, /* AIC-7770 based */
- AIC_785x, /* AIC-7850 based (3 SCBs)*/
- AIC_786x, /* AIC-7860 based (7850 ultra) */
- AIC_787x, /* AIC-7870 based */
- AIC_788x /* AIC-7880 based (ultra) */
-} aha_chip_class_type;
-
-typedef enum {
- AIC_SINGLE, /* Single Channel */
- AIC_TWIN, /* Twin Channel */
- AIC_WIDE /* Wide Channel */
-} aha_bus_type;
-
-typedef enum {
- AIC_UNKNOWN,
- AIC_ENABLED,
- AIC_DISABLED
-} aha_status_type;
-
-typedef enum {
- LIST_HEAD,
- LIST_SECOND
-} insert_type;
-
-typedef enum {
- ABORT_RESET_INACTIVE,
- ABORT_RESET_PENDING,
- ABORT_RESET_SUCCESS
-} aha_abort_reset_type;
+*/
/*
* Define an array of board names that can be indexed by aha_type.
* Don't forget to change this when changing the types!
*/
static const char *board_names[] = {
- "AIC-7xxx Unknown", /* AIC_NONE */
- "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
- "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
- "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
- "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
- "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
- "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
- "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
- "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
- "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
- "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
- "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
- "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
- "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
- "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
- "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
- "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
- "Adaptec AHA-2944 Ultra SCSI host adapter" /* AIC_7884 */
+ "AIC-7xxx Unknown", /* AIC_NONE */
+ "Adaptec AIC-7810 Hardware RAID Controller", /* AIC_7810 */
+ "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
+ "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
+ "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
+ "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
+ "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
+ "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
+ "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
+ "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
+ "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
+ "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
+ "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
+ "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
+ "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
+ "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
+ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
+ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
+ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */
+ "Adaptec AIC-7895 Ultra SCSI host adapter" /* AIC_7895 */
};
/*
#define DID_RETRY_COMMAND DID_ERROR
#define HSCSIID 0x07
-#define HWSCSIID 0x0F
#define SCSI_RESET 0x040
/*
* EISA/VL-bus stuff
*/
-#define MINSLOT 1
-#define MAXSLOT 15
-#define SLOTBASE(x) ((x) << 12)
+#define MINSLOT 1
+#define MAXSLOT 15
+#define SLOTBASE(x) ((x) << 12)
#define BASE_TO_SLOT(x) ((x) >> 12)
/*
* Standard EISA Host ID regs (Offset from slot base)
*/
-#define HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
-#define HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
-#define HID2 0x82 /* product */
-#define HID3 0x83 /* firmware revision */
+#define HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
+#define HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
+#define HID2 0x82 /* product */
+#define HID3 0x83 /* firmware revision */
/*
* AIC-7770 I/O range to reserve for a card
*/
-#define MINREG 0xC00
-#define MAXREG 0xCBF
+#define MINREG 0xC00
+#define MAXREG 0xCBF
-#define INTDEF 0x5C /* Interrupt Definition Register */
+#define INTDEF 0x5C /* Interrupt Definition Register */
/*
* AIC-78X0 PCI registers
*/
-#define CLASS_PROGIF_REVID 0x08
-#define DEVREVID 0x000000FFul
-#define PROGINFC 0x0000FF00ul
-#define SUBCLASS 0x00FF0000ul
-#define BASECLASS 0xFF000000ul
-
-#define CSIZE_LATTIME 0x0C
-#define CACHESIZE 0x0000003Ful /* only 5 bits */
-#define LATTIME 0x0000FF00ul
-
-#define DEVCONFIG 0x40
-#define MPORTMODE 0x00000400ul /* aic7870 only */
-#define RAMPSM 0x00000200ul /* aic7870 only */
-#define VOLSENSE 0x00000100ul
-#define SCBRAMSEL 0x00000080ul
-#define MRDCEN 0x00000040ul
-#define EXTSCBTIME 0x00000020ul /* aic7870 only */
-#define EXTSCBPEN 0x00000010ul /* aic7870 only */
-#define BERREN 0x00000008ul
-#define DACEN 0x00000004ul
-#define STPWLEVEL 0x00000002ul
-#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+#define CLASS_PROGIF_REVID 0x08
+#define DEVREVID 0x000000FFul
+#define PROGINFC 0x0000FF00ul
+#define SUBCLASS 0x00FF0000ul
+#define BASECLASS 0xFF000000ul
+
+#define CSIZE_LATTIME 0x0C
+#define CACHESIZE 0x0000003Ful /* only 5 bits */
+#define LATTIME 0x0000FF00ul
+
+#define DEVCONFIG 0x40
+#define SCBSIZE32 0x00010400ul /* aic789X only */
+#define MPORTMODE 0x00000400ul /* aic7870 only */
+#define RAMPSM 0x00000200ul /* aic7870 only */
+#define VOLSENSE 0x00000100ul
+#define SCBRAMSEL 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
/*
/*
* SCSI ID Configuration Flags
*/
-#define CFXFER 0x0007 /* synchronous transfer rate */
-#define CFSYNCH 0x0008 /* enable synchronous transfer */
-#define CFDISC 0x0010 /* enable disconnection */
-#define CFWIDEB 0x0020 /* wide bus device (wide card) */
-/* UNUSED 0x00C0 */
-#define CFSTART 0x0100 /* send start unit SCSI command */
-#define CFINCBIOS 0x0200 /* include in BIOS scan */
-#define CFRNFOUND 0x0400 /* report even if not found */
-/* UNUSED 0xF800 */
- unsigned short device_flags[16]; /* words 0-15 */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device (wide card) */
+#define CFSYNCHISULTRA 0x0040 /* CFSYNC is an ultra offset */
+/* UNUSED 0x0080 */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+#define CFMULTILUN 0x0800 /* probe mult luns in BIOS scan */
+/* UNUSED 0xF000 */
+ unsigned short device_flags[16]; /* words 0-15 */
/*
* BIOS Control Bits
*/
-#define CFSUPREM 0x0001 /* support all removable drives */
-#define CFSUPREMB 0x0002 /* support removable drives for boot only */
-#define CFBIOSEN 0x0004 /* BIOS enabled */
-/* UNUSED 0x0008 */
-#define CFSM2DRV 0x0010 /* support more than two drives */
-#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
-/* UNUSED 0x0040 */
-#define CFEXTEND 0x0080 /* extended translation enabled */
-/* UNUSED 0xFF00 */
- unsigned short bios_control; /* word 16 */
+#define CFSUPREM 0x0001 /* support all removable drives */
+#define CFSUPREMB 0x0002 /* support removable drives for boot only */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+/* UNUSED 0x0008 */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+/* UNUSED 0x0040 */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+/* UNUSED 0xFF00 */
+ unsigned short bios_control; /* word 16 */
/*
* Host Adapter Control Bits
*/
-#define CFAUTOTERM 0x0001 /* Perform Auto termination */
-#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
-#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
-#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
-#define CFSTERM 0x0004 /* SCSI low byte termination */
-#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
-#define CFSPARITY 0x0010 /* SCSI parity */
-#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
-#define CFRESETB 0x0040 /* reset SCSI bus at boot */
-/* UNUSED 0xFF80 */
- unsigned short adapter_control; /* word 17 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
+#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
+#define CFBPRIMARY 0x0100 /* Channel B primary on 7895 chipsets */
+/* UNUSED 0xFE80 */
+ unsigned short adapter_control; /* word 17 */
/*
* Bus Release, Host Adapter ID
*/
-#define CFSCSIID 0x000F /* host adapter SCSI ID */
-/* UNUSED 0x00F0 */
-#define CFBRTIME 0xFF00 /* bus release time */
- unsigned short brtime_id; /* word 18 */
+#define CFSCSIID 0x000F /* host adapter SCSI ID */
+/* UNUSED 0x00F0 */
+#define CFBRTIME 0xFF00 /* bus release time */
+ unsigned short brtime_id; /* word 18 */
/*
* Maximum targets
*/
-#define CFMAXTARG 0x00FF /* maximum targets */
-/* UNUSED 0xFF00 */
- unsigned short max_targets; /* word 19 */
+#define CFMAXTARG 0x00FF /* maximum targets */
+/* UNUSED 0xFF00 */
+ unsigned short max_targets; /* word 19 */
- unsigned short res_1[11]; /* words 20-30 */
- unsigned short checksum; /* word 31 */
+ unsigned short res_1[11]; /* words 20-30 */
+ unsigned short checksum; /* word 31 */
};
-#define SELBUS_MASK 0x0a
-#define SELNARROW 0x00
-#define SELBUSB 0x08
-#define SINGLE_BUS 0x00
+#define SELBUS_MASK 0x0a
+#define SELNARROW 0x00
+#define SELBUSB 0x08
+#define SINGLE_BUS 0x00
#define SCB_TARGET(scb) \
(((scb)->hscb->target_channel_lun & TID) >> 4)
* condition in this location. This then will modify a DID_OK status
* into an appropriate error for the higher-level SCSI code.
*/
-#define aic7xxx_error(cmd) ((cmd)->SCp.Status)
+#define aic7xxx_error(cmd) ((cmd)->SCp.Status)
/*
* Keep track of the targets returned status.
*/
-#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command)
+#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command)
/*
* The position of the SCSI commands scb within the scb array.
*/
-#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in)
-
-/*
- * "Static" structures. Note that these are NOT initialized
- * to zero inside the kernel - we have to initialize them all
- * explicitly.
- *
- * We support multiple adapter cards per interrupt, but keep a
- * linked list of Scsi_Host structures for each IRQ. On an interrupt,
- * use the IRQ as an index into aic7xxx_boards[] to locate the card
- * information.
- */
-static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1];
+#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in)
/*
- * When we detect and register the card, it is possible to
- * have the card raise a spurious interrupt. Because we need
- * to support multiple cards, we cannot tell which card caused
- * the spurious interrupt. And, we might not even have added
- * the card info to the linked list at the time the spurious
- * interrupt gets raised. This variable is suppose to keep track
- * of when we are registering a card and how many spurious
- * interrupts we have encountered.
- *
- * 0 - do not allow spurious interrupts.
- * 1 - allow 1 spurious interrupt
- * 2 - have 1 spurious interrupt, do not allow any more.
- *
- * I've made it an integer instead of a boolean in case we
- * want to allow more than one spurious interrupt for debugging
- * purposes. Otherwise, it could just go from true to false to
- * true (or something like that).
- *
- * When the driver detects the cards, we'll set the count to 1
- * for each card detection and registration. After the registration
- * of a card completes, we'll set the count back to 0. So far, it
- * seems to be enough to allow a spurious interrupt only during
- * card registration; if a spurious interrupt is going to occur,
- * this is where it happens.
- *
- * We should be able to find a way to avoid getting the spurious
- * interrupt. But until we do, we have to keep this ugly code.
+ * So we can keep track of our host structs
*/
-static int aic7xxx_spurious_count;
+static struct aic7xxx_host *first_aic7xxx = NULL;
/*
* As of Linux 2.1, the mid-level SCSI code uses virtual addresses
/*
* Maximum number of SG segments these cards can support.
*/
-#define AIC7XXX_MAX_SG 122
+#define AIC7XXX_MAX_SG 128
/*
* The maximum number of SCBs we could have for ANY type
* of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
* SEQUENCER CODE IF THIS IS MODIFIED!
*/
-#define AIC7XXX_MAXSCB 255
+#define AIC7XXX_MAXSCB 255
struct aic7xxx_hwscb {
/*16*/ unsigned int data_count;
/*20*/ unsigned int SCSI_cmd_pointer;
/*24*/ unsigned char SCSI_cmd_length;
-/*25*/ u_char tag; /* Index into our kernel SCB array.
- * Also used as the tag for tagged I/O
- */
-#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
- * via PIO to initialize a transaction.
- */
-/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
- * or disconnected down in the sequencer.
- */
+/*25*/ unsigned char tag; /* Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
+ * via PIO to initialize a transaction.
+ */
+/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
+ * or disconnected down in the sequencer.
+ */
/*27*/ unsigned char prev;
-/*28*/ unsigned int pad; /*
- * Unused by the kernel, but we require
- * the padding so that the array of
- * hardware SCBs is alligned on 32 byte
- * boundaries so the sequencer can index
- */
+/*28*/ unsigned int pad; /*
+ * Unused by the kernel, but we require
+ * the padding so that the array of
+ * hardware SCBs is alligned on 32 byte
+ * boundaries so the sequencer can index
+ */
};
typedef enum {
- SCB_FREE = 0x0000,
- SCB_ACTIVE = 0x0001,
- SCB_ABORTED = 0x0002,
- SCB_DEVICE_RESET = 0x0004,
- SCB_SENSE = 0x0008,
- SCB_TIMEDOUT = 0x0010,
- SCB_QUEUED_FOR_DONE = 0x0020,
- SCB_RECOVERY_SCB = 0x0040,
- SCB_WAITINGQ = 0x0080,
- SCB_ASSIGNEDQ = 0x0100,
- SCB_SENTORDEREDTAG = 0x0200,
- SCB_MSGOUT_SDTR = 0x0400,
- SCB_MSGOUT_WDTR = 0x0800,
- SCB_ABORT = 0x1000,
- SCB_QUEUED_ABORT = 0x2000,
- SCB_RESET = 0x4000,
- SCB_WAS_BUSY = 0x8000
+ SCB_FREE = 0x0000,
+ SCB_WDTR_16BIT = 0x0001,
+ SCB_WAITINGQ = 0x0002,
+ SCB_ACTIVE = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_ABORT = 0x0010,
+ SCB_DEVICE_RESET = 0x0020,
+ SCB_RESET = 0x0040,
+ SCB_RECOVERY_SCB = 0x0080,
+ SCB_WAS_BUSY = 0x0100,
+ SCB_MSGOUT_SDTR = 0x0400,
+ SCB_MSGOUT_WDTR = 0x0800,
+ SCB_MSGOUT_WDTR_8BIT = 0x0800,
+ SCB_MSGOUT_WDTR_16BIT = 0x0801,
+ SCB_MSGOUT_BITS = SCB_MSGOUT_SDTR | SCB_MSGOUT_WDTR_16BIT,
+ SCB_QUEUED_ABORT = 0x1000,
+ SCB_QUEUED_FOR_DONE = 0x2000
} scb_flag_type;
+typedef enum {
+ AHC_FNONE = 0x00000000,
+ AHC_PAGESCBS = 0x00000001,
+ AHC_CHANNEL_B_PRIMARY = 0x00000002,
+ AHC_USEDEFAULTS = 0x00000004,
+ AHC_INDIRECT_PAGING = 0x00000008,
+ AHC_CHNLB = 0x00000020,
+ AHC_CHNLC = 0x00000040,
+ AHC_EXTEND_TRANS_A = 0x00000100,
+ AHC_EXTEND_TRANS_B = 0x00000200,
+ AHC_TERM_ENB_A = 0x00000400,
+ AHC_TERM_ENB_B = 0x00000800,
+ AHC_HANDLING_REQINITS = 0x00001000,
+ AHC_TARGETMODE = 0x00002000,
+ AHC_NEWEEPROM_FMT = 0x00004000,
+ /*
+ * Here ends the FreeBSD defined flags and here begins the linux defined
+ * flags. NOTE: I did not preserve the old flag name during this change
+ * specifically to force me to evaluate what flags were being used properly
+ * and what flags weren't. This way, I could clean up the flag usage on
+ * a use by use basis. Doug Ledford
+ */
+ AHC_A_SCANNED = 0x00100000,
+ AHC_B_SCANNED = 0x00200000,
+ AHC_MULTI_CHANNEL = 0x00400000,
+ AHC_BIOS_ENABLED = 0x00800000,
+ AHC_ABORT_PENDING = 0x02000000,
+ AHC_RESET_PENDING = 0x04000000,
+ AHC_IN_ISR = 0x10000000,
+ AHC_IN_ABORT = 0x20000000,
+ AHC_IN_RESET = 0x40000000
+} ahc_flag_type;
+
+typedef enum {
+ AHC_NONE = 0x00000000,
+ AHC_ULTRA = 0x00000001,
+ AHC_WIDE = 0x00000002,
+ AHC_TWIN = 0x00000008,
+ AHC_AIC7770 = 0x00000010,
+ AHC_AIC7850 = 0x00000020,
+ AHC_AIC7860 = 0x00000021,
+ AHC_AIC7870 = 0x00000040,
+ AHC_AIC7880 = 0x00000041,
+ AHC_AIC7895 = 0x00000081,
+ AHC_AIC78x0 = 0x000000E0,
+ AHC_274 = 0x00000110,
+ AHC_284 = 0x00000210,
+ AHC_294AU = 0x00000421,
+ AHC_294 = 0x00000440,
+ AHC_294U = 0x00000441,
+ AHC_394 = 0x00000840,
+ AHC_394U = 0x00000841,
+ AHC_394AU = 0x00000881,
+ AHC_398 = 0x00001040,
+ AHC_398U = 0x00001041,
+ AHC_39x = 0x00001880
+} ahc_type;
+
struct aic7xxx_scb {
struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */
- Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
+ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
struct aic7xxx_scb *q_next; /* next scb in queue */
- scb_flag_type flags; /* current state of scb */
- struct hw_scatterlist *sg_list; /* SG list in adapter format */
+ scb_flag_type flags; /* current state of scb */
+ struct hw_scatterlist *sg_list; /* SG list in adapter format */
+ unsigned char tag_action;
unsigned char sg_count;
- unsigned char sense_cmd[6]; /*
+ unsigned char sense_cmd[6]; /*
* Allocate 6 characters for
* sense command.
*/
+ unsigned int sg_length; /* We init this during buildscb so we
+ * don't have to calculate anything
+ * during underflow/overflow/stat code
+ */
};
/*
{ ILLHADDR, "Illegal Host Access" },
{ ILLSADDR, "Illegal Sequencer Address referenced" },
{ ILLOPCODE, "Illegal Opcode in sequencer program" },
- { PARERR, "Sequencer Ram Parity Error" }
+ { SQPARERR, "Sequencer Ram Parity Error" }
};
static unsigned char
unsigned char maxhscbs; /* hardware scbs */
unsigned char maxscbs; /* max scbs including pageable scbs */
struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB];
- unsigned int reserve[100];
} scb_data_type;
+typedef struct {
+ unsigned char period;
+ unsigned char offset;
+} syncinfo_type;
+
/*
- * Define a structure used for each host adapter, only one per IRQ.
+ * Define a structure used for each host adapter. Note, in order to avoid
+ * problems with architectures I can't test on (because I don't have one,
+ * such as the Alpha based systems) which happen to give faults for
+ * non-aligned memory accesses, care was taken to align this structure
+ * in a way that gauranteed all accesses larger than 8 bits were aligned
+ * on the appropriate boundary. It's also organized to try and be more
+ * cache line efficient. Be careful when changing this lest you might hurt
+ * overall performance and bring down the wrath of the masses.
*/
struct aic7xxx_host {
+ /*
+ * This is the first 64 bytes in the host struct
+ */
+
struct Scsi_Host *host; /* pointer to scsi host */
+ struct aic7xxx_host *next; /* allow for multiple IRQs */
int host_no; /* SCSI host number */
- int instance; /* aic7xxx instance number */
- int scsi_id; /* host adapter SCSI ID */
- int scsi_id_b; /* channel B for twin adapters */
- int irq; /* IRQ for this adapter */
- int base; /* card base address */
- unsigned int mbase; /* I/O memory address */
+ unsigned long base; /* card base address */
volatile unsigned char *maddr; /* memory mapped address */
-#define A_SCANNED 0x0001
-#define B_SCANNED 0x0002
-#define EXTENDED_TRANSLATION 0x0004
-#define FLAGS_CHANNEL_B_PRIMARY 0x0008
-#define MULTI_CHANNEL 0x0010
-#define ULTRA_ENABLED 0x0020
-#define PAGE_ENABLED 0x0040
-#define USE_DEFAULTS 0x0080
-#define BIOS_ENABLED 0x0100
-#define IN_ISR 0x0200
-#define ABORT_PENDING 0x0400
-#define SHARED_SCBDATA 0x0800
-#define HAVE_SEEPROM 0x1000
-#define RESET_PENDING 0x2000
-#define IN_ABORT 0x4000
- unsigned int flags;
- unsigned long last_reset;
- unsigned long reset_start;
- unsigned int isr_count; /* Interrupt count */
- unsigned short needsdtr_copy; /* default config */
- unsigned short needsdtr;
- unsigned short sdtr_pending;
- unsigned short needwdtr_copy; /* default config */
- unsigned short needwdtr;
- unsigned short wdtr_pending;
- unsigned short orderedtag;
- unsigned short discenable; /* Targets allowed to disconnect */
- aha_chip_type chip_type; /* card type */
- aha_chip_class_type chip_class;
- aha_bus_type bus_type; /* normal/twin/wide bus */
- unsigned char chan_num; /* for 39xx, channel number */
+ unsigned long mbase; /* I/O memory address */
+ volatile ahc_flag_type flags;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+ spinlock_t spin_lock;
+#endif
+ volatile unsigned char cpu_lock_count[NR_CPUS];
+ ahc_type type; /* card type */
+ unsigned long last_reset;
+ unsigned long isr_count; /* Interrupt count */
+ unsigned long spurious_int;
+ unsigned short discenable; /* Targets allowed to disconnect */
+ unsigned short tagenable; /* Targets using tagged I/O */
+ unsigned short orderedtag; /* Ordered Q tags allowed */
+ volatile unsigned char activescbs; /* active scbs */
+ unsigned char max_activescbs;
unsigned char unpause; /* unpause value for HCNTRL */
unsigned char pause; /* pause value for HCNTRL */
- unsigned char qcntmask;
- unsigned char qfullcount;
- unsigned char cmdoutcnt;
- unsigned char curqincnt;
- struct Scsi_Host *next; /* allow for multiple IRQs */
- unsigned char activescbs; /* active scbs */
- scb_queue_type waiting_scbs; /*
+ volatile unsigned char qoutfifonext;
+ volatile unsigned char qinfifonext;
+
+ /*
+ * MAX_TARGETS is currently == 16, so that makes these entries the next
+ * 64 bytes
+ */
+
+#define DEVICE_PRESENT 0x01
+#define BUS_DEVICE_RESET_PENDING 0x02
+#define DEVICE_TIMEOUT 0x04
+#define DEVICE_PRINT_SDTR 0x08
+#define DEVICE_PRINT_WDTR 0x10
+#define DEVICE_SUCCESS 0x20
+#define DEVICE_TAGGED_SUCCESS 0x40
+ volatile unsigned char dev_flags[MAX_TARGETS];
+ volatile unsigned char dev_active_cmds[MAX_TARGETS];
+ unsigned char dev_temp_queue_depth[MAX_TARGETS];
+ unsigned char dev_commands_sent[MAX_TARGETS];
+
+ /*
+ * The next 64....
+ */
+
+ long dev_last_reset[MAX_TARGETS];
+
+ /*
+ * The next 64....
+ */
+
+ unsigned char dev_mid_level_queue_depth[MAX_TARGETS];
+ unsigned char dev_last_queue_full[MAX_TARGETS];
+ unsigned char dev_last_queue_full_count[MAX_TARGETS];
+ unsigned char dev_max_queue_depth[MAX_TARGETS];
+
+ /*
+ * The next 128....
+ */
+
+ volatile scb_queue_type delayed_scbs[MAX_TARGETS];
+
+ /*
+ *
+ */
+
+ struct timer_list dev_timer[MAX_TARGETS];
+
+ /*
+ * The next 64....
+ */
+
+ unsigned char msg_buf[9]; /* The message for the target */
+ unsigned char msg_type;
+#define MSG_TYPE_NONE 0x00
+#define MSG_TYPE_INITIATOR_MSGOUT 0x01
+#define MSG_TYPE_INITIATOR_MSGIN 0x02
+ unsigned char msg_len; /* Length of message */
+ unsigned char msg_index; /* Index into msg_buf array */
+ syncinfo_type syncinfo[MAX_TARGETS];
+ volatile scb_queue_type waiting_scbs; /*
* SCBs waiting for space in
* the QINFIFO.
*/
Scsi_Cmnd *head;
Scsi_Cmnd *tail;
} completeq;
- struct aic7xxx_device_status {
- unsigned char active_cmds;
- unsigned char max_queue_depth;
- unsigned char temp_queue_depth;
- unsigned char last_queue_full;
- unsigned char last_queue_full_count;
- struct timer_list timer;
- long last_reset;
-#define DEVICE_SUCCESS 0x01
-#define BUS_DEVICE_RESET_PENDING 0x02
-#define DEVICE_TIMEOUT 0x04
- int flags;
- int commands_sent;
- scb_queue_type delayed_scbs;
- Scsi_Cmnd *scsi_cmnd0;
- Scsi_Cmnd *scsi_cmnd1;
- } device_status[16];
+
+
+ /*
+ * We put the less frequently used host structure items after the more
+ * frequently used items to try and ease the burden on the cache subsystem.
+ * These entries are not *commonly* accessed, whereas the preceding entries
+ * are accessed very often. The only exceptions are the qinfifo, qoutfifo,
+ * and untagged_scbs array. But, they are often accessed only once and each
+ * access into these arrays is likely to blow a cache line, so they are put
+ * down here so we can minimize the number of cache lines required to hold
+ * the preceeding entries.
+ */
+
+ volatile unsigned char untagged_scbs[256];
+ volatile unsigned char qoutfifo[256];
+ volatile unsigned char qinfifo[256];
+ unsigned short needsdtr;
+ unsigned short sdtr_pending;
+ unsigned short needwdtr;
+ unsigned short wdtr_pending;
+ int instance; /* aic7xxx instance number */
+ int scsi_id; /* host adapter SCSI ID */
+ int scsi_id_b; /* channel B for twin adapters */
+ unsigned int bios_address;
+ int board_name_index;
+ unsigned long reset_start;
+ unsigned short needsdtr_copy; /* default config */
+ unsigned short needwdtr_copy; /* default config */
+ unsigned short ultraenb; /* Ultra mode target list */
+ unsigned short bios_control; /* bios control - SEEPROM */
+ unsigned short adapter_control; /* adapter control - SEEPROM */
+ unsigned char pci_bus;
+ unsigned char pci_device_fn;
+ unsigned char irq; /* IRQ for this adapter */
+
#ifdef AIC7XXX_PROC_STATS
/*
* Statistics Kept:
* < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
*
* Total amounts read/written above 512 bytes (amts under ignored)
+ *
+ * NOTE: Enabling this feature is likely to cause a noticeable performance
+ * decrease as the accesses into the stats structures blows apart multiple
+ * cache lines and is CPU time consuming.
*/
struct aic7xxx_xferstats {
long xfers; /* total xfer count */
static struct {
short period;
/* Rates in Ultra mode have bit 8 of sxfr set */
-#define ULTRA_SXFR 0x100
+#define ULTRA_SXFR 0x100
short rate;
const char *english;
} aic7xxx_syncrates[] = {
static int num_aic7xxx_syncrates =
sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
-#ifdef CONFIG_PCI
-static int number_of_3940s = 0;
-static int number_of_3985s = 0;
-#endif /* CONFIG_PCI */
-
-#ifdef AIC7XXX_DEBUG
-
-#if 0
-static void
-debug_scb(struct aic7xxx_scb *scb)
-{
- struct aic7xxx_hwscb *hscb = scb->hscb;
-
- printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
- scb,
- hscb->control,
- hscb->target_channel_lun,
- hscb->SCSI_cmd_length,
- hscb->SCSI_cmd_pointer );
- printk(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
- hscb->data_count,
- hscb->data_pointer,
- hscb->SG_segment_count,
- hscb->SG_list_pointer);
- printk(" sg_addr:%lx sg_len:%ld\n",
- hscb->sg_list[0].address,
- hscb->sg_list[0].length);
-}
-#endif
-
-#else
-# define debug_scb(x)
-#endif AIC7XXX_DEBUG
-
#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \
(((scb->hscb)->target_channel_lun >> 4) & 0xf), \
((scb->hscb)->target_channel_lun & 0x07)
((cmd->target) & 0x0f), \
((cmd->lun) & 0x07)
-static inline int
-CHAN_TO_INT(char chan)
-{
- switch(chan)
- {
- case ALL_CHANNELS:
- return(-1);
- case 'B':
- case 'b':
- return(1);
- case 'A':
- case 'a':
- return(0);
- default:
- printk(KERN_WARNING "aic7xxx: Bad usage of char channel.\n");
- return(0);
- }
-}
-
-static inline char
-INT_TO_CHAN(int chan)
-{
- switch(chan)
- {
- case -1:
- return(ALL_CHANNELS);
- case 1:
- return('B');
- case 0:
- return('A');
- default:
- printk(KERN_WARNING "aic7xxx: Bad usage of int channel.\n");
- return('A');
- }
-}
+#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
+/*
+ * A nice little define to make doing our printks a little easier
+ */
-#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
+#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
+#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
/*
* XXX - these options apply unilaterally to _all_ 274x/284x/294x
* cards in the system. This should be fixed.
*/
+static int aic7xxx_7895_irq_hack = -1; /* This enables a hack to fix
+ * IRQ settings on buggy 7895
+ * MB controller setups
+ * -1 == Disable this hack
+ * 0 == Use the Channel A IRQ
+ * 1 == Use the Channel B IRQ
+ */
static unsigned int aic7xxx_extended = 0; /* extended translation on? */
static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */
static int aic7xxx_irq_trigger = -1; /*
* 0 use edge triggered
* 1 use level triggered
*/
-static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */
-static int aic7xxx_verbose = 0; /* verbose messages */
+static int aic7xxx_reverse_scan = 0; /*
+ * Set this to anything but 0
+ * to make the probe code
+ * reverse the order of PCI
+ * devices
+ */
+static int aic7xxx_override_term = 0; /*
+ * Set this to non-0 to make the
+ * driver override any BIOS
+ * configured termination
+ * settings based upon the
+ * results of the cable detect
+ * logic. This only applies
+ * to cards that have cable
+ * detection logic and a SEEPROM
+ */
+static int aic7xxx_panic_on_abort = 0; /*
+ * Set this to non-0 in order
+ * to force the driver to panic
+ * the kernel and print out
+ * debugging info on an abort
+ * or reset call into the
+ * driver.
+ */
+
+/*
+ * So that insmod can find the variable and make it point to something
+ */
+#ifdef MODULE
+static char * aic7xxx = NULL;
+
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+#endif
+
+/*
+ * See the comments earlier in the file for what this item is all about
+ * If you have more than 4 controllers, you will need to increase the
+ * the number of items in the array below. Additionally, if you don't
+ * want to have lilo pass a humongous config line to the aic7xxx driver,
+ * then you can get in and manually adjust these instead of leaving them
+ * at the default. Pay attention to the comments earlier in this file
+ * concerning this array if you are going to hand modify these values.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS}
+};
+
+#define VERBOSE_NORMAL 0x0000
+#define VERBOSE_NEGOTIATION 0x0001
+#define VERBOSE_SEQINT 0x0002
+#define VERBOSE_SCSIINT 0x0004
+#define VERBOSE_PROBE 0x0008
+#define VERBOSE_PROBE2 0x0010
+#define VERBOSE_QUEUE 0x0020
+#define VERBOSE_MINOR_ERROR 0x0040
+#define VERBOSE_QUEUE_FULL 0x0080
+#define VERBOSE_ABORT 0x0f00
+#define VERBOSE_ABORT_MID 0x0100
+#define VERBOSE_ABORT_FIND 0x0200
+#define VERBOSE_ABORT_PROCESS 0x0400
+#define VERBOSE_ABORT_RETURN 0x0800
+#define VERBOSE_RESET 0xf000
+#define VERBOSE_RESET_MID 0x1000
+#define VERBOSE_RESET_FIND 0x2000
+#define VERBOSE_RESET_PROCESS 0x4000
+#define VERBOSE_RESET_RETURN 0x8000
+static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
+ VERBOSE_PROBE; /* verbose messages */
/****************************************************************************
*
***************************************************************************/
+
static inline unsigned char
aic_inb(struct aic7xxx_host *p, long port)
{
- if (p->maddr != NULL)
- return (p->maddr[port]);
+ unsigned char x;
+ if(p->maddr)
+ x = p->maddr[port];
else
- return (inb(p->base + port));
+ x = inb(p->base + port);
+ mb();
+ return(x);
}
static inline void
aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
{
- if (p->maddr != NULL)
+ if(p->maddr)
p->maddr[port] = val;
else
outb(val, p->base + port);
+ mb();
}
static inline void
aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
{
- if (p->maddr != NULL)
+ if(p->maddr)
{
-#ifdef __alpha__
int i;
for (i=0; i < size; i++)
{
p->maddr[port] = valp[i];
}
-#else
- __asm __volatile("
- cld;
- 1: lodsb;
- movb %%al,(%0);
- loop 1b" :
- :
- "r" (p->maddr + port),
- "S" (valp), "c" (size) :
- "%esi", "%ecx", "%eax");
-#endif
}
else
- {
outsb(p->base + port, valp, size);
- }
+ mb();
}
/*+F*************************************************************************
{
int i, n;
char *p;
+ char *end;
static struct {
const char *name;
{ "extended", &aic7xxx_extended },
{ "no_reset", &aic7xxx_no_reset },
{ "irq_trigger", &aic7xxx_irq_trigger },
- { "ultra", &aic7xxx_enable_ultra },
{ "verbose", &aic7xxx_verbose },
- { NULL, NULL }
+ { "reverse_scan",&aic7xxx_reverse_scan },
+ { "7895_irq_hack", &aic7xxx_7895_irq_hack },
+ { "override_term", &aic7xxx_override_term },
+ { "panic_on_abort", &aic7xxx_panic_on_abort },
+ { "tag_info", NULL }
};
- for (p = strtok(s, ","); p; p = strtok(NULL, ","))
+ end = strchr(s, '\0');
+
+ for (p = strtok(s, ",."); p; p = strtok(NULL, ",."))
{
- for (i = 0; options[i].name; i++)
+ for (i = 0; i < NUMBER(options); i++)
{
n = strlen(options[i].name);
if (!strncmp(options[i].name, p, n))
{
- if (p[n] == ':')
+ if (!strncmp(p, "tag_info", n))
+ {
+ if (p[n] == ':')
+ {
+ char *base;
+ char *tok, *tok_end, *tok_end2;
+ char tok_list[] = { '.', ',', '{', '}', '\0' };
+ int i, instance = -1, device = -1;
+ unsigned char done = FALSE;
+
+ base = p;
+ tok = base + n + 1; /* Forward us just past the ':' */
+ tok_end = strchr(tok, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while(!done)
+ {
+ switch(*tok)
+ {
+ case '{':
+ if (instance == -1)
+ instance = 0;
+ else if (device == -1)
+ device = 0;
+ tok++;
+ break;
+ case '}':
+ if (device != -1)
+ device = -1;
+ else if (instance != -1)
+ instance = -1;
+ tok++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (device >= 0)
+ device++;
+ else if (instance >= 0)
+ instance++;
+ if ( (device >= MAX_TARGETS) ||
+ (instance >= NUMBER(aic7xxx_tag_info)) )
+ done = TRUE;
+ tok++;
+ if (!done)
+ {
+ base = tok;
+ }
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ done = TRUE;
+ tok_end = strchr(tok, '\0');
+ for(i=0; tok_list[i]; i++)
+ {
+ tok_end2 = strchr(tok, tok_list[i]);
+ if ( (tok_end2) && (tok_end2 < tok_end) )
+ {
+ tok_end = tok_end2;
+ done = FALSE;
+ }
+ }
+ if ( (instance >= 0) && (device >= 0) &&
+ (instance < NUMBER(aic7xxx_tag_info)) &&
+ (device < MAX_TARGETS) )
+ aic7xxx_tag_info[instance].tag_commands[device] =
+ simple_strtoul(tok, NULL, 0) & 0xff;
+ tok = tok_end;
+ break;
+ }
+ }
+ while((p != base) && (p != NULL))
+ p = strtok(NULL, ",.");
+ }
+ }
+ else if (p[n] == ':')
{
*(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
}
+ else if (!strncmp(p, "verbose", n))
+ {
+ *(options[i].flag) = 0xff69;
+ }
else
{
- *(options[i].flag) += 1;
+ *(options[i].flag) = ~(*(options[i].flag));
}
}
}
static inline void
pause_sequencer(struct aic7xxx_host *p)
{
- outb(p->pause, p->base + HCNTRL);
- while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+ aic_outb(p, p->pause, HCNTRL);
+ while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
{
;
}
unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
{
if (unpause_always ||
- ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
+ ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
+ !(p->flags & AHC_HANDLING_REQINITS) ) )
{
- outb(p->unpause, p->base + HCNTRL);
+ aic_outb(p, p->unpause, HCNTRL);
}
}
restart_sequencer(struct aic7xxx_host *p)
{
/* Set the sequencer address to 0. */
- outb(0, p->base + SEQADDR0);
- outb(0, p->base + SEQADDR1);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
/*
* Reset and unpause the sequencer. The reset is suppose to
- * start the sequencer running, but we do an unpause to make
- * sure.
+ * start the sequencer running, so we immediately do a pause_sequencer
+ * since some of our code expects the sequencer paused after a restart
*/
- outb(SEQRESET | FASTMODE, p->base + SEQCTL);
-
- unpause_sequencer(p, /*unpause_always*/ TRUE);
+ aic_outb(p, SEQRESET | FASTMODE, SEQCTL);
+ pause_sequencer(p);
}
aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
{
unsigned char opcode;
- struct ins_format3 *instr;
+ struct ins_format3 instr;
+ unsigned char dconsts[4] = { 0, 0, 0, 0 };
- instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+ instr = *(struct ins_format3 *) &seqprog[instrptr * 4];
/* Pull the opcode */
- opcode = instr->opcode_addr >> 1;
+ opcode = (instr.opcode_addr & ~DOWNLOAD_CONST_IMMEDIATE) >> 1;
switch (opcode)
{
case AIC_OP_JMP:
case AIC_OP_JZ:
{
int address_offset;
- struct ins_format3 new_instr;
unsigned int address;
struct patch *patch;
int i;
address_offset = 0;
- new_instr = *instr; /* Strucure copy */
- address = new_instr.address;
- address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+ address = instr.address;
+ address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8;
for (i = 0; i < NUMBER(patches); i++)
{
patch = &patches[i];
}
}
address -= address_offset;
- new_instr.address = address &0xFF;
- new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
- new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
- outsb(p->base + SEQRAM, &new_instr.immediate, 4);
- break;
+ instr.address = address & 0xFF;
+ instr.opcode_addr &= ~ADDR_HIGH_BIT;
+ instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
}
-
+ /* Fall through */
case AIC_OP_OR:
case AIC_OP_AND:
case AIC_OP_XOR:
case AIC_OP_ADD:
case AIC_OP_ADC:
+ if (instr.opcode_addr & DOWNLOAD_CONST_IMMEDIATE)
+ {
+ instr.immediate = dconsts[instr.immediate];
+ }
+ instr.opcode_addr &= ~DOWNLOAD_CONST_IMMEDIATE;
case AIC_OP_ROL:
- outsb(p->base + SEQRAM, &instr->immediate, 4);
+ aic_outsb(p, SEQRAM, &instr.immediate, 4);
break;
default:
int i;
int downloaded;
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_PROBE)
{
- printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+ printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
}
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("\n");
options = 1; /* Code for all options. */
downloaded = 0;
- if ((p->flags & ULTRA_ENABLED) != 0)
+ if (p->type & AHC_ULTRA)
+ {
options |= ULTRA;
- if (p->bus_type == AIC_TWIN)
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Will download code for option ULTRA\n",
+ p->host_no);
+ }
+ if (p->type & AHC_TWIN)
+ {
options |= TWIN_CHANNEL;
- if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Will download code for option "
+ "TWIN_CHANNEL\n", p->host_no);
+ }
+ if (p->type & AHC_WIDE)
+ {
+ options |= WIDE;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Will download code for option WIDE\n",
+ p->host_no);
+ }
+ /* if (p->scb_data->maxscbs > p->scb_data->maxhscbs) this should always
+ be true, don't test,
+ just do. */
+ {
options |= SCB_PAGING;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Will download code for option SCB_PAGING\n",
+ p->host_no);
+ }
+ /* We don't actually support target mode yet, so leave this out
+ if (p->flags & AHC_TARGETMODE)
+ options |= TARGET_MODE; */
+
+ if ( (options & ~(ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01)) )
+ {
+ printk(KERN_INFO "(scsi%d) Unknown bits set in the options field, "
+ "correcting.\n", p->host_no);
+ options &= ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01;
+ }
+
cur_patch = patches;
- outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
- outb(0, p->base + SEQADDR0);
- outb(0, p->base + SEQADDR1);
+ aic_outb(p, PERRORDIS | LOADRAM, SEQCTL);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
for (i = 0; i < sizeof(seqprog) / 4; i++)
{
downloaded++;
}
- outb(FASTMODE, p->base + SEQCTL);
- outb(0, p->base + SEQADDR0);
- outb(0, p->base + SEQADDR1);
+ aic_outb(p, FASTMODE, SEQCTL);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Download complete,", p->host_no);
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_PROBE)
{
- printk(" %d instructions downloaded\n", downloaded);
+ printk(" %d instructions downloaded\n", downloaded);
}
}
static void
aic7xxx_delay(int seconds)
{
- int i;
+ unsigned int i;
/*
* Call udelay() for 1 millisecond inside a loop for
/*+F*************************************************************************
* Function:
- * rcs_version
+ * aic7xxx_info
*
* Description:
- * Return a string containing just the RCS version number from either
- * an Id or Revision RCS clause.
+ * Return a string describing the driver.
*-F*************************************************************************/
const char *
-rcs_version(const char *version_info)
+aic7xxx_info(struct Scsi_Host *dooh)
{
- static char buf[10];
- char *bp, *ep;
-
- bp = NULL;
- strcpy(buf, "????");
- if (!strncmp(version_info, "$Id: ", 5))
- {
- if ((bp = strchr(version_info, ' ')) != NULL)
- {
- bp++;
- if ((bp = strchr(bp, ' ')) != NULL)
- {
- bp++;
- }
- }
- }
- else
- {
- if (!strncmp(version_info, "$Revision: ", 11))
- {
- if ((bp = strchr(version_info, ' ')) != NULL)
- {
- bp++;
- }
- }
- }
-
- if (bp != NULL)
- {
- if ((ep = strchr(bp, ' ')) != NULL)
- {
- register int len = ep - bp;
-
- strncpy(buf, bp, len);
- buf[len] = '\0';
- }
- }
+ static char buffer[256];
+ char *bp;
+ struct aic7xxx_host *p;
- return buf;
+ bp = &buffer[0];
+ p = (struct aic7xxx_host *)dooh->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+ strcat(bp, AIC7XXX_C_VERSION);
+ strcat(bp, "/");
+ strcat(bp, AIC7XXX_H_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, board_names[p->board_name_index]);
+ strcat(bp, ">");
+
+ return(bp);
}
+
/*+F*************************************************************************
* Function:
- * aic7xxx_info
+ * aic7xxx_scsirate
*
* Description:
- * Return a string describing the driver.
+ * Look up the valid period to SCSIRATE conversion in our table
*-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *notused)
+static unsigned char
+aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
+ unsigned char *period, unsigned char *offset, int target, int channel,
+ int set)
{
- static char buffer[128];
+ int i = num_aic7xxx_syncrates;
+ unsigned char response_period;
+ unsigned char tindex;
+ unsigned short target_mask;
+ unsigned char lun;
- strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
- strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
- strcat(buffer, "/");
- strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
-#if 0
- strcat(buffer, "/");
- strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
-#endif
+ tindex = target | (channel << 3);
+ target_mask = 0x01 << tindex;
+ lun = aic_inb(p, SCB_TCL) & 0x07;
- return buffer;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_length
- *
- * Description:
- * How much data should be transferred for this SCSI command? Assume
- * all segments are to be transferred except for the last sg_last
- * segments. This will allow us to compute underflow easily. To
- * calculate the total length of the command, use sg_last = 0. To
- * calculate the length of all but the last 2 SG segments, use
- * sg_last = 2.
- *-F*************************************************************************/
-static unsigned
-aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
-{
- int i, segments;
- unsigned length;
- struct scatterlist *sg;
-
- segments = cmd->use_sg - sg_last;
- sg = (struct scatterlist *) cmd->request_buffer;
-
- if (cmd->use_sg)
- {
- for (i = length = 0; i < segments; i++)
- {
- length += sg[i].length;
- }
- }
- else
- {
- length = cmd->request_bufflen;
- }
-
- return (length);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_scsirate
- *
- * Description:
- * Look up the valid period to SCSIRATE conversion in our table
- *-F*************************************************************************/
-static void
-aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
- unsigned char *period, unsigned char *offset, int target, char channel)
-{
- int i = num_aic7xxx_syncrates;
- unsigned long ultra_enb_addr;
- unsigned char ultra_enb, sxfrctl0;
+ response_period = *period;
/*
* If the offset is 0, then the device is requesting asynchronous
* Watch out for Ultra speeds when ultra is not enabled and
* vice-versa.
*/
- if (!(p->flags & ULTRA_ENABLED) &&
+ if (!(p->type & AHC_ULTRA) &&
(aic7xxx_syncrates[i].rate & ULTRA_SXFR))
{
/*
*scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
*period = aic7xxx_syncrates[i].period;
- if (aic7xxx_verbose)
+ /*
+ * When responding to a target that requests
+ * sync, that rate may fall between two rates
+ * that we can output, but still be a rate
+ * that we can receive. Because of this,
+ * we may want to respond to the target with
+ * the same rate that it sent to us even
+ * if the period we use to send data to it
+ * is lower. Only lower the response period
+ * if we must.
+ */
+ if ((i == 0) ||
+ ((aic7xxx_syncrates[i-1].rate & ULTRA_SXFR) != 0
+ && (p->type & AHC_ULTRA) == 0))
+ {
+ response_period = *period;
+ }
+
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
{
- printk("(scsi%d:%d:%d:%d) Synchronous at %sMHz, "
- "offset %d.\n", p->host_no, CHAN_TO_INT(channel), target, 0,
+ printk(INFO_LEAD "Synchronous at %sMHz, "
+ "offset %d.\n", p->host_no, channel, target, lun,
aic7xxx_syncrates[i].english, *offset);
+ p->dev_flags[tindex] &= ~ DEVICE_PRINT_SDTR;
}
break;
}
*scsirate = 0;
*period = 0;
*offset = 0;
- if (aic7xxx_verbose)
+ response_period = 0;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
{
- printk("(scsi%d:%d:%d:%d) Using asynchronous transfers.\n",
- p->host_no, CHAN_TO_INT(channel), target, 0);
+ printk(INFO_LEAD "Using asynchronous transfers.\n",
+ p->host_no, channel, target, lun);
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
}
}
/*
* Ensure Ultra mode is set properly for this target.
*/
- ultra_enb_addr = ULTRA_ENB;
- if ((channel == 'B') || (target > 7))
+ if ( (*scsirate != 0) &&
+ (aic7xxx_syncrates[i].rate & ULTRA_SXFR) )
{
- ultra_enb_addr++;
+ p->ultraenb |= target_mask;
}
- ultra_enb = inb(p->base + ultra_enb_addr);
- sxfrctl0 = inb(p->base + SXFRCTL0);
- if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
+ else
{
- ultra_enb |= 0x01 << (target & 0x07);
- sxfrctl0 |= FAST20;
+ p->ultraenb &= ~target_mask;
}
- else
+ if (set)
{
- ultra_enb &= ~(0x01 << (target & 0x07));
+ unsigned char sxfrctl0;
+
+ sxfrctl0 = aic_inb(p, SXFRCTL0);
sxfrctl0 &= ~FAST20;
+ if (p->ultraenb & target_mask)
+ {
+ sxfrctl0 |= FAST20;
+ }
+ aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+ aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1);
+ aic_outb(p, sxfrctl0, SXFRCTL0);
}
- outb(ultra_enb, p->base + ultra_enb_addr);
- outb(sxfrctl0, p->base + SXFRCTL0);
+ return(response_period);
}
/*+F*************************************************************************
*
*-F*************************************************************************/
static inline void
-scbq_init(scb_queue_type *queue)
+scbq_init(volatile scb_queue_type *queue)
{
queue->head = NULL;
queue->tail = NULL;
*
*-F*************************************************************************/
static inline void
-scbq_insert_head(scb_queue_type *queue, struct aic7xxx_scb *scb)
+scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
{
- unsigned long processor_flags;
- save_flags(processor_flags);
- cli();
scb->q_next = queue->head;
queue->head = scb;
if (queue->tail == NULL) /* If list was empty, update tail. */
queue->tail = queue->head;
- restore_flags(processor_flags);
}
/*+F*************************************************************************
* Remove an SCB from the head of the list.
*
*-F*************************************************************************/
-static inline void
-scbq_remove_head(scb_queue_type *queue)
+static __inline struct aic7xxx_scb *
+scbq_remove_head(volatile scb_queue_type *queue)
{
- unsigned long processor_flags;
- save_flags(processor_flags);
- cli();
+ struct aic7xxx_scb * scbp;
+ scbp = queue->head;
if (queue->head != NULL)
queue->head = queue->head->q_next;
if (queue->head == NULL) /* If list is now empty, update tail. */
queue->tail = NULL;
- restore_flags(processor_flags);
+ return(scbp);
}
/*+F*************************************************************************
*
*-F*************************************************************************/
static inline void
-scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
{
- unsigned long processor_flags;
- save_flags(processor_flags);
- cli();
if (queue->head == scb)
{
/* At beginning of queue, remove from head. */
}
}
}
- restore_flags(processor_flags);
}
/*+F*************************************************************************
*
*-F*************************************************************************/
static inline void
-scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
+scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
{
- unsigned long processor_flags;
- save_flags(processor_flags);
- cli();
scb->q_next = NULL;
if (queue->tail != NULL) /* Add the scb at the end of the list. */
queue->tail->q_next = scb;
-
queue->tail = scb; /* Update the tail. */
if (queue->head == NULL) /* If list was empty, update head. */
queue->head = queue->tail;
- restore_flags(processor_flags);
}
/*+F*************************************************************************
* to be reset and all devices on that channel must be aborted.
*-F*************************************************************************/
static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
- int lun, unsigned char tag)
+aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ int target, int channel, int lun, unsigned char tag)
{
int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
- char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
int slun = scb->hscb->target_channel_lun & 0x07;
int match;
if (match != 0)
match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
- if (aic7xxx_verbose > 4)
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
{
- if (match)
- {
- printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) matches search criteria"
- " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
- CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
- CHAN_TO_INT(channel), target, lun, tag);
- }
- else
- {
- printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) doesn't match search criteria"
- " (scsi%d:%d:%d:%d:tag%d)\n", scb->cmd->device->host->host_no,
- CTL_OF_SCB(scb), scb->hscb->tag, scb->cmd->device->host->host_no,
- CHAN_TO_INT(channel), target, lun, tag);
- }
+ printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) %s search criteria"
+ " (scsi%d:%d:%d:%d:tag%d)\n", p->host_no, CTL_OF_SCB(scb),
+ scb->hscb->tag, (match) ? "matches" : "doesn't match",
+ p->host_no, channel, target, lun, tag);
}
return (match);
* Invalidate the tag so that aic7xxx_find_scb doesn't think
* it's active
*/
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
- outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
- outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+ aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
+ aic_outb(p, aic_inb(p, SCBPTR), FREE_SCBH);
}
/*+F*************************************************************************
unsigned char next;
unsigned char prev;
- outb(scbptr, p->base + SCBPTR);
- next = inb(p->base + SCB_NEXT);
- prev = inb(p->base + SCB_PREV);
-
- outb(0, p->base + SCB_CONTROL);
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
-
+ aic_outb(p, scbptr, SCBPTR);
+ next = aic_inb(p, SCB_NEXT);
+ prev = aic_inb(p, SCB_PREV);
aic7xxx_add_curscb_to_free_list(p);
if (prev != SCB_LIST_NULL)
{
- outb(prev, p->base + SCBPTR);
- outb(next, p->base + SCB_NEXT);
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
}
else
{
- outb(next, p->base + DISCONNECTED_SCBH);
+ aic_outb(p, next, DISCONNECTED_SCBH);
}
if (next != SCB_LIST_NULL)
{
- outb(next, p->base + SCBPTR);
- outb(prev, p->base + SCB_PREV);
+ aic_outb(p, next, SCBPTR);
+ aic_outb(p, prev, SCB_PREV);
}
return next;
}
* Description:
* Set the specified target busy.
*-F*************************************************************************/
-static void
-aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
- char channel, unsigned char scbid)
+static __inline void
+aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- unsigned char active_scb;
- unsigned char info_scb;
- unsigned int scb_offset;
-
- info_scb = target / 4;
- if (channel == 'B')
- info_scb = info_scb + 2;
-
- active_scb = inb(p->base + SCBPTR);
- outb(info_scb, p->base + SCBPTR);
- scb_offset = SCB_BUSYTARGETS + (target & 0x03);
- outb(scbid, p->base + scb_offset);
- outb(active_scb, p->base + SCBPTR);
+ p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
}
/*+F*************************************************************************
* Returns the index of the busy target, and optionally sets the
* target inactive.
*-F*************************************************************************/
-static unsigned char
-aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
- char channel, int unbusy)
+static __inline unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
+ int unbusy)
{
- unsigned char active_scb;
- unsigned char info_scb;
unsigned char busy_scbid;
- unsigned int scb_offset;
- info_scb = target / 4;
- if (channel == 'B')
- info_scb = info_scb + 2;
-
- active_scb = inb(p->base + SCBPTR);
- outb(info_scb, p->base + SCBPTR);
- scb_offset = SCB_BUSYTARGETS + (target & 0x03);
- busy_scbid = inb(p->base + scb_offset);
+ busy_scbid = p->untagged_scbs[tcl];
if (unbusy)
{
- outb(SCB_LIST_NULL, p->base + scb_offset);
+ p->untagged_scbs[tcl] = SCB_LIST_NULL;
}
- outb(active_scb, p->base + SCBPTR);
return (busy_scbid);
}
unsigned char saved_scbptr;
unsigned char curindex;
- saved_scbptr = inb(p->base + SCBPTR);
+ saved_scbptr = aic_inb(p, SCBPTR);
curindex = 0;
for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
{
- outb(curindex, p->base + SCBPTR);
- if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+ aic_outb(p, curindex, SCBPTR);
+ if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
{
break;
}
}
- outb(saved_scbptr, p->base + SCBPTR);
+ aic_outb(p, saved_scbptr, SCBPTR);
if (curindex >= p->scb_data->maxhscbs)
{
curindex = SCB_LIST_NULL;
* Get an SCB from the free list or by allocating a new one.
*-F*************************************************************************/
static struct aic7xxx_scb *
-aic7xxx_allocate_scb(struct aic7xxx_host *p)
+aic7xxx_allocate_scb(struct aic7xxx_host *p, int force_alloc)
{
struct aic7xxx_scb *scbp = NULL;
- struct aic7xxx_hwscb *hscbp = NULL;
+ int scb_size = sizeof(struct aic7xxx_scb) +
+ sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
+ int i;
+ unsigned long scb_count = 0;
+ struct hw_scatterlist *hsgp;
+ struct aic7xxx_scb *scb_ap;
- scbp = p->scb_data->free_scbs.head;
- if (scbp != NULL)
+
+ if (force_alloc == FALSE)
{
- scbq_remove_head(&p->scb_data->free_scbs);
+ scbp = scbq_remove_head(&p->scb_data->free_scbs);
+ if (scbp != NULL)
+ return(scbp);
}
- else
+ /*
+ * Either there wasn't an SCB or this is a strictly allocation call
+ */
+
+ if (p->scb_data->numscbs < p->scb_data->maxscbs)
{
- if (p->scb_data->numscbs < p->scb_data->maxscbs)
- {
- int scb_index = p->scb_data->numscbs;
- int scb_size = sizeof(struct aic7xxx_scb) +
- sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
- scbp = kmalloc(scb_size, GFP_ATOMIC);
- if (scbp != NULL)
+ /*
+ * Optimize for 30 scbs at a time, but allow a final allocation of
+ * fewer than 30 scbs. Except on 64 bit platforms, we optimize for
+ * 29 SCBs at a time because a pointer is 4 bytes larger and we don't
+ * want to overrun this suppossedly 32K allocation to 64K and waste
+ * tons of space.
+ */
+ if( sizeof(void *) == sizeof(int) )
+ scb_count = MIN(30, p->scb_data->maxscbs - p->scb_data->numscbs);
+ else
+ scb_count = MIN(29, p->scb_data->maxscbs - p->scb_data->numscbs);
+
+ scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC);
+ if (scb_ap != NULL)
+ {
+ if (aic7xxx_verbose & VERBOSE_QUEUE)
+ {
+ if (p->scb_data->numscbs == 0)
+ printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
+ p->host_no, -1, -1, -1, scb_count);
+ else
+ printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
+ p->host_no, -1, -1, -1, scb_count);
+ }
+ memset(scb_ap, 0, scb_count * scb_size);
+ hsgp = (struct hw_scatterlist *) &scb_ap[scb_count];
+ for (i=0; i < scb_count; i++)
{
- memset(scbp, 0, sizeof(struct aic7xxx_scb));
- hscbp = &p->scb_data->hscbs[scb_index];
- scbp->hscb = hscbp;
- scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
- memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
- hscbp->tag = scb_index;
- p->scb_data->numscbs++;
+ scbp = &scb_ap[i];
+ scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
+ scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
+ memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
+ scbp->hscb->tag = p->scb_data->numscbs;
/*
* Place in the scb array; never is removed
*/
- p->scb_data->scb_array[scb_index] = scbp;
+ p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
+ scbq_insert_head(&p->scb_data->free_scbs, scbp);
}
}
+ else
+ {
+ return(NULL);
+ }
+ }
+ if (force_alloc == TRUE)
+ {
+ return((struct aic7xxx_scb *)scb_count);
+ }
+ else
+ {
+ return(scbq_remove_head(&p->scb_data->free_scbs));
}
-
- return (scbp);
}
/*+F*************************************************************************
static inline void
aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
- unsigned int flags;
- save_flags(flags);
- cli();
cmd->host_scribble = (char *)p->completeq.head;
p->completeq.head = cmd;
- restore_flags(flags);
}
/*+F*************************************************************************
aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
{
Scsi_Cmnd *cmd;
- unsigned int processor_flags;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned int cpu_flags = 0;
+#endif
- save_flags(processor_flags);
- cli();
+ DRIVER_LOCK
while (p->completeq.head != NULL)
{
cmd = p->completeq.head;
p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
cmd->host_scribble = NULL;
- restore_flags(processor_flags);
+ DRIVER_UNLOCK
cmd->scsi_done(cmd);
- cli();
+ DRIVER_LOCK
}
- restore_flags(processor_flags);
+ DRIVER_UNLOCK
}
/*+F*************************************************************************
static void
aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- struct aic7xxx_hwscb *hscb;
-
- hscb = scb->hscb;
scb->flags = SCB_FREE;
scb->cmd = NULL;
- hscb->control = 0;
- hscb->target_status = 0;
+ scb->sg_count = 0;
+ scb->sg_length = 0;
+ scb->tag_action = 0;
+ scb->hscb->control = 0;
+ scb->hscb->target_status = 0;
+ scb->hscb->target_channel_lun = SCB_LIST_NULL;
scbq_insert_head(&p->scb_data->free_scbs, scb);
}
if (scb->flags & SCB_RECOVERY_SCB)
{
- p->flags &= ~ABORT_PENDING;
+ p->flags &= ~AHC_ABORT_PENDING;
}
if (scb->flags & SCB_RESET)
{
cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) |
- (cmd->result & 0xffff);
+ (cmd->result & 0xffff);
}
else if (scb->flags & SCB_ABORT)
{
cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) |
- (cmd->result & 0xffff);
+ (cmd->result & 0xffff);
}
if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
{
unsigned short mask;
int message_error = FALSE;
- mask = 0x01 << TARGET_INDEX(scb->cmd);
+ mask = 0x01 << tindex;
/*
* Check to see if we get an invalid message or a message error
p->wdtr_pending &= ~mask;
if (message_error)
{
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Wide Negotiation "
+ "processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+ }
p->needwdtr &= ~mask;
p->needwdtr_copy &= ~mask;
}
p->sdtr_pending &= ~mask;
if (message_error)
{
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Sync Negotiation "
+ "processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+ }
p->needsdtr &= ~mask;
p->needsdtr_copy &= ~mask;
}
}
}
- queue_depth = (p->device_status[tindex].timer.expires) ?
- p->device_status[tindex].temp_queue_depth :
- p->device_status[tindex].max_queue_depth;
- scbp = p->device_status[tindex].delayed_scbs.head;
- if ( (scbp != NULL) &&
- (queue_depth > p->device_status[tindex].active_cmds) )
+ queue_depth = p->dev_temp_queue_depth[tindex];
+ if (queue_depth >= p->dev_active_cmds[tindex])
{
- scbq_remove_head(&p->device_status[tindex].delayed_scbs);
- scbq_insert_tail(&p->waiting_scbs, scbp);
- scbp = p->device_status[tindex].delayed_scbs.head;
- if ( (scbp != NULL) &&
- (queue_depth > (p->device_status[tindex].active_cmds + 1)) )
- {
- scbq_remove_head(&p->device_status[tindex].delayed_scbs);
+ scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+ if (scbp)
scbq_insert_tail(&p->waiting_scbs, scbp);
+ if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp)
+ {
+ scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+ if (scbp)
+ scbq_insert_tail(&p->waiting_scbs, scbp);
}
}
- if ( (p->device_status[tindex].timer.expires) &&
- ((p->device_status[tindex].active_cmds == 1) ||
- (p->device_status[tindex].max_queue_depth ==
- p->device_status[tindex].temp_queue_depth)) )
+ if ( (p->dev_timer[tindex].expires) &&
+ ((p->dev_active_cmds[tindex] == 1) ||
+ (p->dev_max_queue_depth[tindex] ==
+ p->dev_temp_queue_depth[tindex])) )
{
- del_timer(&p->device_status[tindex].timer);
- p->device_status[tindex].timer.expires = 0;
- p->device_status[tindex].temp_queue_depth =
- p->device_status[tindex].max_queue_depth;
+ del_timer(&p->dev_timer[tindex]);
+ p->dev_timer[tindex].expires = 0;
+ p->dev_temp_queue_depth[tindex] =
+ p->dev_max_queue_depth[tindex];
+ }
+ p->dev_active_cmds[tindex]--;
+ p->activescbs--;
+
+ /*
+ * If this was an untagged I/O, unbusy the target so the sequencer won't
+ * mistake things later
+ */
+ if (aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, FALSE) ==
+ scb->hscb->tag)
+ {
+ aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
}
- p->device_status[tindex].active_cmds--;
- aic7xxx_free_scb(p, scb);
- aic7xxx_queue_cmd_complete(p, cmd);
#ifdef AIC7XXX_PROC_STATS
- if ( (cmd->cmnd[0] != TEST_UNIT_READY) &&
- (cmd->cmnd[0] != INQUIRY) )
{
int actual;
/*
* XXX: we should actually know how much actually transferred
* XXX: for each command, but apparently that's too difficult.
+ *
+ * We set a lower limit of 512 bytes on the transfer length. We
+ * ignore anything less than this because we don't have a real
+ * reason to count it. Read/Writes to tapes are usually about 20K
+ * and disks are a minimum of 512 bytes unless you want to count
+ * non-read/write commands (such as TEST_UNIT_READY) which we don't
*/
- actual = aic7xxx_length(cmd, 0);
- if ((actual > 0) && (((cmd->result >> 16) & 0xf) == DID_OK))
+ actual = scb->sg_length;
+ if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
{
struct aic7xxx_xferstats *sp;
long *ptr;
sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
sp->xfers++;
- if (cmd->request.cmd == WRITE)
+ /*
+ * For block devices, cmd->request.cmd is always == either READ or
+ * WRITE. For character devices, this isn't always set properly, so
+ * we check data_cmnd[0]. This catches the conditions for st.c, but
+ * I'm still not sure if request.cmd is valid for sg devices.
+ */
+ if ( (cmd->request.cmd == WRITE) || (cmd->data_cmnd[0] == WRITE_6) ||
+ (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
{
sp->w_total++;
sp->w_total512 += (actual >> 9);
}
}
#endif /* AIC7XXX_PROC_STATS */
+
+ aic7xxx_free_scb(p, scb);
+ aic7xxx_queue_cmd_complete(p, cmd);
+
}
/*+F*************************************************************************
scb = p->scb_data->scb_array[i];
if (scb->flags & SCB_QUEUED_FOR_DONE)
{
- if (aic7xxx_verbose > 3)
- printk("(scsi%d:%d:%d:%d) Aborting scb %d\n",
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Aborting scb %d\n",
p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
found++;
aic7xxx_done(p, scb);
}
}
- if (aic7xxx_verbose > 1)
+ if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
{
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) %d commands found and queued for "
- "completion.\n", p->host_no, found);
+ printk(INFO_LEAD "%d commands found and queued for "
+ "completion.\n", p->host_no, -1, -1, -1, found);
}
if (complete)
{
/*
* Select the SCB we want to abort and pull the next pointer out of it.
*/
- curscb = inb(p->base + SCBPTR);
- outb(scbpos, p->base + SCBPTR);
- next = inb(p->base + SCB_NEXT);
-
- /*
- * Clear the necessary fields
- */
- outb(0, p->base + SCB_CONTROL);
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ curscb = aic_inb(p, SCBPTR);
+ aic_outb(p, scbpos, SCBPTR);
+ next = aic_inb(p, SCB_NEXT);
aic7xxx_add_curscb_to_free_list(p);
/*
* First in the list
*/
- outb(next, p->base + WAITING_SCBH);
+ aic_outb(p, next, WAITING_SCBH);
}
else
{
/*
* Select the scb that pointed to us and update its next pointer.
*/
- outb(prev, p->base + SCBPTR);
- outb(next, p->base + SCB_NEXT);
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
}
/*
* Point us back at the original scb position and inform the SCSI
* system that the command has been aborted.
*/
- outb(curscb, p->base + SCBPTR);
- scb->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- scb->flags &= ~SCB_ACTIVE;
-
+ aic_outb(p, curscb, SCBPTR);
return (next);
}
* requeue. Returns the number of matching SCBs.
*-F*************************************************************************/
static int
-aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
- int lun, unsigned char tag, int flags, int requeue, scb_queue_type *queue)
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, int channel,
+ int lun, unsigned char tag, int flags, int requeue,
+ volatile scb_queue_type *queue)
{
- unsigned char saved_queue[AIC7XXX_MAXSCB];
- int queued = inb(p->base + QINCNT) & p->qcntmask;
- int i;
int found;
+ unsigned char qinpos, qintail;
struct aic7xxx_scb *scbp;
- scb_queue_type removed_scbs;
found = 0;
- scbq_init (&removed_scbs);
- for (i = 0; i < (queued - found); i++)
+ qinpos = aic_inb(p, QINPOS);
+ qintail = p->qinfifonext;
+
+ p->qinfifonext = qinpos;
+
+ while (qinpos != qintail)
{
- saved_queue[i] = inb(p->base + QINFIFO);
- scbp = p->scb_data->scb_array[saved_queue[i]];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
{
/*
* We found an scb that needs to be removed.
*/
- if (requeue)
+ if (requeue && (queue != NULL))
+ {
+ if ( !(scbp->flags & SCB_WAITINGQ) )
+ {
+ scbq_insert_tail(queue, scbp);
+ p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]--;
+ p->activescbs--;
+ scbp->flags |= SCB_WAITINGQ;
+ }
+ if ( !(scbp->tag_action & TAG_ENB) )
+ {
+ aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ TRUE);
+ }
+ }
+ else if (requeue)
{
- scbq_insert_head(&removed_scbs, scbp);
+ p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
}
else
{
+ /*
+ * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
+ * flags we were called with, presumeably so aic7xxx_run_done_queue
+ * can find this scb
+ */
scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
+ if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ FALSE) == scbp->hscb->tag)
+ {
+ aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ TRUE);
+ }
}
- i--;
found++;
}
- }
- /* Now put the saved scbs back. */
- for (queued = 0; queued < i; queued++)
- outb(saved_queue[queued], p->base + QINFIFO);
-
- if (requeue)
- {
- scbp = removed_scbs.head;
- while (scbp != NULL)
+ else
{
- scbq_remove_head(&removed_scbs);
- /*
- * XXX - Shouldn't we be adding this to the free list?
- */
- if ( !(scbp->flags & SCB_WAITINGQ) )
- { /* OK...we aren't already on a queue, so put us on there */
- p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds--;
- scbq_insert_head(queue, scbp);
- scbp->flags |= SCB_WAITINGQ;
- }
- scbp = removed_scbs.head;
+ p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
}
}
+ /*
+ * Now that we've done the work, clear out any left over commands in the
+ * qinfifo and update the KERNEL_QINPOS down on the card.
+ *
+ * NOTE: This routine expect the sequencer to already be paused when
+ * it is run....make sure it's that way!
+ */
+ qinpos = p->qinfifonext;
+ while(qinpos != qintail)
+ {
+ p->qinfifo[qinpos++] = SCB_LIST_NULL;
+ }
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
return (found);
}
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_scb_on_qoutfifo
+ *
+ * Description:
+ * Is the scb that was passed to us currently on the qoutfifo?
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int i=0;
+
+ while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
+ {
+ if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
+ return TRUE;
+ else
+ i++;
+ }
+ return FALSE;
+}
+
+
/*+F*************************************************************************
* Function:
* aic7xxx_reset_device
* them.
*-F*************************************************************************/
static void
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
int lun, unsigned char tag)
{
struct aic7xxx_scb *scbp;
- unsigned char active_scb;
+ unsigned char active_scb, tcl;
int i = 0, j, init_lists = FALSE;
/*
* Restore this when we're done
*/
- active_scb = inb(p->base + SCBPTR);
+ active_scb = aic_inb(p, SCBPTR);
- if (aic7xxx_verbose > 2)
- printk("(scsi%d:%d:%d:%d) Reset device, active_scb %d\n",
- p->host_no, CHAN_TO_INT(channel), target, lun, active_scb);
+ if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+ printk(INFO_LEAD "Reset device, active_scb %d\n",
+ p->host_no, channel, target, lun, active_scb);
/*
* Deal with the busy target and linked next issues.
*/
{
int min_target, max_target;
- unsigned char busy_scbid;
struct aic7xxx_scb *scbp, *prev_scbp;
/* Make all targets 'relative' to bus A. */
{
switch (channel)
{
- case 'A':
- min_target = 0;
- max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
- break;
- case 'B':
- min_target = 8;
- max_target = 15;
- break;
+ case 0:
+ min_target = 0;
+ max_target = (p->type & AHC_WIDE) ? 15 : 7;
+ break;
+ case 1:
+ min_target = 8;
+ max_target = 15;
+ break;
case ALL_CHANNELS:
default:
- min_target = 0;
- max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
- break;
+ min_target = 0;
+ max_target = (p->type & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
+ break;
}
}
else
{
- min_target = target + ((channel == 'B') ? 8 : 0);
+ min_target = target | (channel << 3);
max_target = min_target;
}
for (i = min_target; i <= max_target; i++)
{
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning up status information "
- "and delayed_scbs.\n", p->host_no, CHAN_TO_INT(channel), i, lun);
- busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/ TRUE);
- p->device_status[i].flags &= ~BUS_DEVICE_RESET_PENDING;
- p->device_status[i].last_reset = jiffies;
- p->device_status[i].last_queue_full_count = 0;
- p->device_status[i].last_queue_full = 0;
- p->device_status[i].temp_queue_depth =
- p->device_status[i].max_queue_depth;
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning up status information "
+ "and delayed_scbs.\n", p->host_no, channel, i, lun);
+ if ( !(p->dev_flags[i] & DEVICE_TAGGED_SUCCESS) &&
+ (p->dev_active_cmds[i]) &&
+ (p->tagenable & (0x01 << i)) )
+ {
+ printk(INFO_LEAD "Device appears to be choking on tagged commands.\n",
+ p->host_no, channel, i, lun);
+ printk(INFO_LEAD "Will use untagged I/O instead.\n", p->host_no,
+ channel, i, lun);
+ p->dev_max_queue_depth[i] = 1;
+ p->dev_temp_queue_depth[i] = 1;
+ p->tagenable &= ~(0x01 << i);
+ p->orderedtag &= ~(0x01 << i);
+ }
+ p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
+ if ( tag == SCB_LIST_NULL )
+ {
+ p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+ p->dev_last_reset[i] = jiffies;
+ p->dev_last_queue_full_count[i] = 0;
+ p->dev_last_queue_full[i] = 0;
+ p->dev_temp_queue_depth[i] =
+ p->dev_max_queue_depth[i];
+ /*
+ * In case this isn't a full bus reset, we want to add a 4 second timer in
+ * here so that we can delay all re-sent commands for this device for the
+ * 4 seconds and then have our timer routine pick them back up.
+ */
+ if( (p->dev_timer[i].prev != NULL) ||
+ (p->dev_timer[i].next != NULL) )
+ {
+ del_timer(&p->dev_timer[i]);
+ }
+ p->dev_timer[i].expires = jiffies + (3 * HZ);
+ add_timer(&p->dev_timer[i]);
+ }
+ for(j=0; j<MAX_LUNS; j++)
+ {
+ if (channel == 1)
+ tcl = ((i << 4) & 0x70) | (channel << 3) | j;
+ else
+ tcl = (i << 4) | (channel << 3) | j;
+ if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
+ (tag == SCB_LIST_NULL) )
+ aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
+ }
j = 0;
prev_scbp = NULL;
- scbp = p->device_status[i].delayed_scbs.head;
+ scbp = p->delayed_scbs[i].head;
while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
{
prev_scbp = scbp;
scbp = scbp->q_next;
if ( prev_scbp == scbp )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! scb->q_next == scb "
- "in the delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
- i, lun);
- scbp = NULL;
- prev_scbp->q_next = NULL;
- p->device_status[i].delayed_scbs.tail = prev_scbp;
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+ "in the delayed_scbs queue!\n", p->host_no, channel, i, lun);
+ scbp = NULL;
+ prev_scbp->q_next = NULL;
+ p->delayed_scbs[i].tail = prev_scbp;
}
- if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
+ if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
{
- scbq_remove(&p->device_status[i].delayed_scbs, prev_scbp);
- if ( prev_scbp->flags & SCB_WAITINGQ )
- p->device_status[i].active_cmds++;
- prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbq_remove(&p->delayed_scbs[i], prev_scbp);
+ if ( !(prev_scbp->flags & SCB_QUEUED_ABORT) )
+ {
+ p->dev_active_cmds[i]++;
+ p->activescbs++;
+ }
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
}
}
if ( j > p->scb_data->numscbs )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Yikes!! There's a loop in the "
- "delayed_scbs queue!\n", p->host_no, CHAN_TO_INT(channel),
- i, lun);
- scbq_init(&p->device_status[i].delayed_scbs);
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! There's a loop in the "
+ "delayed_scbs queue!\n", p->host_no, channel, i, lun);
+ scbq_init(&p->delayed_scbs[i]);
}
- if ( (p->device_status[i].delayed_scbs.head == NULL) &&
- (p->device_status[i].timer.expires) )
+ if ( (p->delayed_scbs[i].head == NULL) &&
+ (p->dev_timer[i].expires) )
{
- del_timer(&p->device_status[i].timer);
- p->device_status[i].timer.expires = 0;
+ del_timer(&p->dev_timer[i]);
+ p->dev_timer[i].expires = 0;
}
}
}
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning QINFIFO.\n", p->host_no,
- CHAN_TO_INT(channel), target, lun );
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
aic7xxx_search_qinfifo(p, target, channel, lun, tag,
SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
* Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
* ABORT/RESET commands.
*/
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting_scbs.\n",
- p->host_no, CHAN_TO_INT(channel), target, lun );
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
+ target, lun );
{
struct aic7xxx_scb *scbp, *prev_scbp;
scbp = scbp->q_next;
if ( prev_scbp == scbp )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! scb->q_next == scb "
- "in the waiting_scbs queue!\n", p->host_no);
- scbp = NULL;
- prev_scbp->q_next = NULL;
- p->waiting_scbs.tail = prev_scbp;
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+ "in the waiting_scbs queue!\n", p->host_no, CTL_OF_SCB(scbp));
+ scbp = NULL;
+ prev_scbp->q_next = NULL;
+ p->waiting_scbs.tail = prev_scbp;
}
- if (aic7xxx_match_scb(prev_scbp, target, channel, lun, tag))
+ if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
{
- scbq_remove(&p->waiting_scbs, prev_scbp);
- if ( prev_scbp->flags & SCB_WAITINGQ )
- p->device_status[TARGET_INDEX(prev_scbp->cmd)].active_cmds++;
- prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ if ( !(prev_scbp->flags & SCB_QUEUED_ABORT) )
+ {
+ p->dev_active_cmds[TARGET_INDEX(prev_scbp->cmd)]++;
+ p->activescbs++;
+ }
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
}
}
if ( j > p->scb_data->numscbs )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There's a loop in the "
- "waiting_scbs queue!\n", p->host_no);
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! There's a loop in the "
+ "waiting_scbs queue!\n", p->host_no, channel, target, lun);
scbq_init(&p->waiting_scbs);
}
}
/*
* Search waiting for selection list.
*/
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Cleaning waiting for selection "
- "list.\n", p->host_no, CHAN_TO_INT(channel), target, lun);
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning waiting for selection "
+ "list.\n", p->host_no, channel, target, lun);
{
unsigned char next, prev, scb_index;
- next = inb(p->base + WAITING_SCBH); /* Start at head of list. */
+ next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */
prev = SCB_LIST_NULL;
j = 0;
while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
{
- outb(next, p->base + SCBPTR);
- scb_index = inb(p->base + SCB_TAG);
+ aic_outb(p, next, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
if (scb_index >= p->scb_data->numscbs)
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
- "SCB index=%d, numscbs=%d\n", p->host_no,
- CHAN_TO_INT(channel), target, lun, scb_index,
- p->scb_data->numscbs);
- next = inb(p->base + SCB_NEXT);
- aic7xxx_add_curscb_to_free_list(p);
+ /*
+ * No aic7xxx_verbose check here.....we want to see this since it
+ * means either the kernel driver or the sequencer screwed things up
+ */
+ printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+ "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic_inb(p, SCB_NEXT);
+ aic7xxx_add_curscb_to_free_list(p);
}
else
{
scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
{
next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
- scbp->flags &= ~SCB_ACTIVE;
- scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ if (prev == SCB_LIST_NULL)
+ {
+ /*
+ * This is either the first scb on the waiting list, or we
+ * have already yanked the first and haven't left any behind.
+ * Either way, we need to turn off the selection hardware if
+ * it isn't already off.
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ }
}
else
{
prev = next;
- next = inb(p->base + SCB_NEXT);
+ next = aic_inb(p, SCB_NEXT);
}
}
}
if ( j > p->scb_data->maxhscbs )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There is a loop in the "
- "waiting for selection list!\n", p->host_no);
+ printk(WARN_LEAD "Yikes!! There is a loop in the waiting for "
+ "selection list!\n", p->host_no, channel, target, lun);
init_lists = TRUE;
}
}
{
unsigned char next, prev, scb_index;
- next = inb(p->base + DISCONNECTED_SCBH);
+ next = aic_inb(p, DISCONNECTED_SCBH);
prev = SCB_LIST_NULL;
j = 0;
while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
{
- outb(next, p->base + SCBPTR);
- scb_index = inb(p->base + SCB_TAG);
+ aic_outb(p, next, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
if (scb_index > p->scb_data->numscbs)
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Waiting List inconsistency; "
- "SCB index=%d, numscbs=%d\n", p->host_no,
- CHAN_TO_INT(channel), target, lun, scb_index,
- p->scb_data->numscbs);
- next = aic7xxx_rem_scb_from_disc_list(p, next);
+ printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+ "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
}
else
{
scbp = p->scb_data->scb_array[scb_index];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
{
next = aic7xxx_rem_scb_from_disc_list(p, next);
- scbp->flags = SCB_RESET | SCB_QUEUED_FOR_DONE;
- scbp->hscb->control = 0;
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->hscb->control = 0;
}
else
{
prev = next;
- next = inb(p->base + SCB_NEXT);
+ next = aic_inb(p, SCB_NEXT);
}
}
}
if ( j > p->scb_data->maxhscbs )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There is a loop in the "
- "disconnected list!\n", p->host_no);
+ printk(WARN_LEAD "Yikes!! There is a loop in the disconnected list!\n",
+ p->host_no, channel, target, lun);
init_lists = TRUE;
}
}
unsigned char next;
j = 0;
- next = inb(p->base + FREE_SCBH);
+ next = aic_inb(p, FREE_SCBH);
while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
{
- outb(next, p->base + SCBPTR);
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
- outb(0, p->base + SCB_CONTROL);
- next = inb(p->base + SCB_NEXT);
+ aic_outb(p, next, SCBPTR);
+ if ( aic_inb(p, SCB_TAG) < p->scb_data->numscbs )
+ {
+ printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
+ target, lun);
+ init_lists = TRUE;
+ next = SCB_LIST_NULL;
+ }
+ else
+ {
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ next = aic_inb(p, SCB_NEXT);
+ }
}
if ( j > p->scb_data->maxhscbs )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Yikes!! There is a loop in the "
- "free list!\n", p->host_no);
+ printk(WARN_LEAD "Yikes!! There is a loop in the free list!\n",
+ p->host_no, channel, target, lun);
init_lists = TRUE;
}
}
*/
if (init_lists)
{
- outb(SCB_LIST_NULL, p->base + FREE_SCBH);
- outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
- outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+ aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
}
for (i = p->scb_data->maxhscbs; i >= 0; --i)
{
unsigned char scbid;
+ aic_outb(p, i, SCBPTR);
if (init_lists)
{
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
- outb(SCB_LIST_NULL, p->base + SCB_NEXT);
- outb(SCB_LIST_NULL, p->base + SCB_PREV);
- outb(SCB_LIST_NULL, p->base + SCB_LINKED_NEXT);
- outb(0, p->base + SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+ aic_outb(p, SCB_LIST_NULL, SCB_PREV);
+ aic_outb(p, 0, SCB_CONTROL);
aic7xxx_add_curscb_to_free_list(p);
}
else
{
- outb(i, p->base + SCBPTR);
- scbid = inb(p->base + SCB_TAG);
+ scbid = aic_inb(p, SCB_TAG);
if (scbid < p->scb_data->numscbs)
{
scbp = p->scb_data->scb_array[scbid];
- if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
{
- outb(0, p->base + SCB_CONTROL);
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
aic7xxx_add_curscb_to_free_list(p);
}
}
* Go through the entire SCB array now and look for commands for
* for this target that are stillactive. These are other (most likely
* tagged) commands that were disconnected when the reset occurred.
+ * Any commands we find here we know this about, it wasn't on any queue,
+ * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
+ * lists, so it really must have been a paged out SCB. In that case,
+ * we shouldn't need to bother with updating any counters, just mark
+ * the correct flags and go on.
*/
for (i = 0; i < p->scb_data->numscbs; i++)
{
scbp = p->scb_data->scb_array[i];
- if (((scbp->flags & SCB_ACTIVE) != 0) &&
- aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ if ((scbp->flags & SCB_ACTIVE) &&
+ aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
+ !aic7xxx_scb_on_qoutfifo(p, scbp))
{
scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
- scbp->flags &= ~SCB_ACTIVE;
-
- if ((scbp->flags & SCB_WAITINGQ) != 0)
- {
- scbq_remove(&p->waiting_scbs, scbp);
- scbq_remove(&p->device_status[TARGET_INDEX(scbp->cmd)].delayed_scbs,
- scbp);
- scbp->flags &= ~SCB_WAITINGQ;
- p->device_status[TARGET_INDEX(scbp->cmd)].active_cmds++;
- }
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
}
}
- outb(active_scb, p->base + SCBPTR);
+ aic_outb(p, active_scb, SCBPTR);
}
aic7xxx_clear_intstat(struct aic7xxx_host *p)
{
/* Clear any interrupt conditions this may have caused. */
- outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
- outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
- CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
- outb(CLRSCSIINT, p->base + CLRINT);
+ aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
+ aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+ CLRPHASECHG | CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT, CLRINT);
}
/*+F*************************************************************************
unsigned char scsiseq;
/* Disable reset interrupts. */
- outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
/* Turn on the bus reset. */
- scsiseq = inb(p->base + SCSISEQ);
- outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+ scsiseq = aic_inb(p, SCSISEQ);
+ aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ);
udelay(1000);
/* Turn off the bus reset. */
- outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+ aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ);
aic7xxx_clear_intstat(p);
/* Re-enable reset interrupts. */
- outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+ aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
udelay(1000);
}
* Reset the channel.
*-F*************************************************************************/
static void
-aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
+aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
{
unsigned long offset, offset_max;
unsigned char sblkctl;
- char cur_channel;
+ int cur_channel;
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:-1:-1) Reset channel called, %s initiate "
- "reset.\n", p->host_no, CHAN_TO_INT(channel),
- (initiate_reset == TRUE) ? "will" : "won't" );
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
+ p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
- if (channel == 'B')
+ if (channel == 1)
{
p->needsdtr |= (p->needsdtr_copy & 0xFF00);
p->sdtr_pending &= 0x00FF;
}
else
{
- if (p->bus_type == AIC_WIDE)
+ if (p->type & AHC_WIDE)
{
p->needsdtr = p->needsdtr_copy;
p->needwdtr = p->needwdtr_copy;
*/
u_char targ_scratch;
- targ_scratch = inb(p->base + offset);
+ targ_scratch = aic_inb(p, offset);
targ_scratch &= SXFR;
- outb(targ_scratch, p->base + offset);
- offset++;
+ aic_outb(p, targ_scratch, offset++);
}
/*
* Reset the bus and unpause/restart the controller
*/
- sblkctl = inb(p->base + SBLKCTL);
- cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
+ sblkctl = aic_inb(p, SBLKCTL);
+ cur_channel = (sblkctl & SELBUSB) >> 3;
if (cur_channel != channel)
{
/*
* Case 1: Command for another bus is active
*/
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:-1:-1) Stealthily resetting idle "
- "channel.\n", p->host_no, CHAN_TO_INT(channel));
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
+ channel, -1, -1);
/*
* Stealthily reset the other bus without upsetting the current bus.
*/
- outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
- outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+ aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
if (initiate_reset)
{
aic7xxx_reset_current_bus(p);
}
- outb(0, p->base + SCSISEQ);
+ aic_outb(p, 0, SCSISEQ);
aic7xxx_clear_intstat(p);
- outb(sblkctl, p->base + SBLKCTL);
+ aic_outb(p, sblkctl, SBLKCTL);
}
else
{
/*
* Case 2: A command from this bus is active or we're idle.
*/
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:-1:-1) Resetting currently active "
- "channel.\n", p->host_no, CHAN_TO_INT(channel));
- outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
+ channel, -1, -1);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+ SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_len = 0;
if (initiate_reset)
{
aic7xxx_reset_current_bus(p);
}
- outb(0, p->base + SCSISEQ);
+ aic_outb(p, 0, SCSISEQ);
aic7xxx_clear_intstat(p);
}
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:-1:-1) Channel reset\n",
- p->host_no, CHAN_TO_INT(channel));
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
/*
* Clean up all the state information for the pending transactions
* on this bus.
*/
aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
- if ( p->bus_type != AIC_TWIN )
+ if ( !(p->type & AHC_TWIN) )
{
restart_sequencer(p);
- pause_sequencer(p);
}
- p->host->last_reset = jiffies + (HZ * AIC7XXX_RESET_DELAY);
-
/*
* Now loop through all the SCBs that have been marked for abortion,
* and call the scsi_done routines.
*/
- aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+ if(!(p->flags & AHC_IN_ISR))
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
return;
}
{
struct aic7xxx_scb *scb;
int tindex;
+ int sent;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
if (p->waiting_scbs.head == NULL)
return;
+ sent = 0;
+
/*
* First handle SCBs that are waiting but have been assigned a slot.
*/
- pause_sequencer(p);
- scb = p->waiting_scbs.head;
- while (scb != NULL)
+ DRIVER_LOCK
+ while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
{
- if (p->curqincnt >= p->qfullcount)
- {
- p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
- if (p->curqincnt >= p->qfullcount)
- {
- break;
- }
- }
-
- /*
- * We have some space.
- */
- scbq_remove_head(&p->waiting_scbs);
tindex = TARGET_INDEX(scb->cmd);
- if ( p->device_status[tindex].active_cmds >=
- p->device_status[tindex].temp_queue_depth )
+ if ( (p->dev_active_cmds[tindex] >=
+ p->dev_temp_queue_depth[tindex]) ||
+ (p->dev_last_reset[tindex] >= (jiffies + (3 * HZ))) )
{
- scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
+ scbq_insert_tail(&p->delayed_scbs[tindex], scb);
}
else
{
scb->flags &= ~SCB_WAITINGQ;
- p->device_status[tindex].active_cmds++;
- outb(scb->hscb->tag, p->base + QINFIFO);
- p->curqincnt++;
+ if ( !(scb->flags & SCB_QUEUED_ABORT) )
+ {
+ p->dev_active_cmds[tindex]++;
+ p->activescbs++;
+ }
+ if ( !(scb->tag_action) )
+ {
+ aic7xxx_busy_target(p, scb);
+ }
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ sent++;
}
- scb = p->waiting_scbs.head;
}
- unpause_sequencer(p, FALSE);
+ if (sent)
+ {
+ if(p->type & AHC_AIC78x0)
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ else
+ {
+ pause_sequencer(p);
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ unpause_sequencer(p, FALSE);
+ }
+ if (p->activescbs > p->max_activescbs)
+ p->max_activescbs = p->activescbs;
+ }
+ DRIVER_UNLOCK
}
static void
aic7xxx_timer(struct aic7xxx_host *p)
{
- int i;
- unsigned long processor_flags;
- struct aic7xxx_scb *scb;
-
- save_flags(processor_flags);
- cli();
- if (aic7xxx_verbose > 3)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Timer running.\n", p->host_no);
- for(i=0; i<MAX_TARGETS; i++)
- {
- if ( (p->device_status[i].timer.expires) &&
- (p->device_status[i].timer.expires <= jiffies) )
- {
- p->device_status[i].timer.expires = 0;
- if ( (p->device_status[i].timer.prev != NULL) ||
- (p->device_status[i].timer.next != NULL) )
- {
- del_timer(&p->device_status[i].timer);
- }
- p->device_status[i].temp_queue_depth =
- p->device_status[i].max_queue_depth;
- scb = p->device_status[i].delayed_scbs.head;
- while ( scb != NULL )
- {
- scbq_remove_head(&p->device_status[i].delayed_scbs);
- scbq_insert_tail(&p->waiting_scbs, scb);
- scb = p->device_status[i].delayed_scbs.head;
- }
- }
- }
- aic7xxx_run_waiting_queues(p);
- unpause_sequencer(p, FALSE);
- restore_flags(processor_flags);
+ int i;
+ unsigned long cpu_flags = 0;
+ struct aic7xxx_scb *scb;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ DRIVER_LOCK
+#else
+ spin_lock_irqsave(&io_request_lock, cpu_flags);
+#endif
+ for(i=0; i<MAX_TARGETS; i++)
+ {
+ if ( (p->dev_timer[i].expires) &&
+ (p->dev_timer[i].expires <= jiffies) )
+ {
+ p->dev_timer[i].expires = 0;
+ if ( (p->dev_timer[i].prev != NULL) ||
+ (p->dev_timer[i].next != NULL) )
+ {
+ del_timer(&p->dev_timer[i]);
+ }
+ p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i];
+ while ( (scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL )
+ {
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ }
+ }
+ }
+ aic7xxx_run_waiting_queues(p);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ DRIVER_UNLOCK
+#else
+ spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+#endif
}
-
/*+F*************************************************************************
* Function:
* buffer on the sequencer.
*-F*************************************************************************/
static void
-aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
- unsigned char period, unsigned char offset)
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, unsigned char period,
+ unsigned char offset)
{
- outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte);
- outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
- outb(MSG_EXT_SDTR, p->base + MSG_OUT + 2 + start_byte);
- outb(period, p->base + MSG_OUT + 3 + start_byte);
- outb(offset, p->base + MSG_OUT + 4 + start_byte);
- outb(start_byte + 5, p->base + MSG_LEN);
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
+ p->msg_buf[p->msg_index++] = period;
+ p->msg_buf[p->msg_index++] = offset;
+ p->msg_len += 5;
}
/*+F*************************************************************************
* on the sequencer.
*-F*************************************************************************/
static void
-aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
- unsigned char bus_width)
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
{
- outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte);
- outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
- outb(MSG_EXT_WDTR, p->base + MSG_OUT + 2 + start_byte);
- outb(bus_width, p->base + MSG_OUT + 3 + start_byte);
- outb(start_byte + 4, p->base + MSG_LEN);
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
+ p->msg_buf[p->msg_index++] = bus_width;
+ p->msg_len += 4;
}
/*+F*************************************************************************
{
struct aic7xxx_hwscb *hscb;
Scsi_Cmnd *cmd;
- int actual;
+ int actual, i;
cmd = scb->cmd;
hscb = scb->hscb;
* and cmd->underflow seems to be set rather half-
* heartedly in the higher-level SCSI code.
*/
- actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
-
+ actual = scb->sg_length;
+ for (i=1; i < hscb->residual_SG_segment_count; i++)
+ {
+ actual -= scb->sg_list[scb->sg_count - i].length;
+ }
actual -= (hscb->residual_data_count[2] << 16) |
(hscb->residual_data_count[1] << 8) |
hscb->residual_data_count[0];
- if ( (actual < cmd->underflow) && (aic7xxx_verbose) )
+ if (actual < cmd->underflow)
{
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Underflow - "
- "Wanted at least %u, got %u, residual SG count %d.\n",
- p->host_no, CTL_OF_SCB(scb), cmd->underflow, actual,
- hscb->residual_SG_segment_count);
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
+ "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
+ (cmd->request.cmd == WRITE) ? "wrote" : "read", actual,
+ hscb->residual_SG_segment_count);
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
aic7xxx_status(cmd) = hscb->target_status;
}
* Interrupt handler for sequencer interrupts (SEQINT).
*-F*************************************************************************/
static void
-aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
{
unsigned short targ_mask;
unsigned char targ_scratch;
int scratch_offset = target;
- if (channel == 'B')
- {
- scratch_offset += 8;
- }
+ scratch_offset += channel << 3;
+
targ_mask = (0x01 << scratch_offset);
/*
* Go back to async/narrow transfers and renegotiate.
*/
- p->needsdtr |= p->needsdtr_copy & targ_mask;
- p->needwdtr |= p->needwdtr_copy & targ_mask;
+ p->needsdtr |= (p->needsdtr_copy & targ_mask);
+ p->needwdtr |= (p->needwdtr_copy & targ_mask);
p->sdtr_pending &= ~targ_mask;
p->wdtr_pending &= ~targ_mask;
- targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
targ_scratch &= SXFR;
- outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:-1) Bus Device Reset delivered.\n",
- p->host_no, CHAN_TO_INT(channel), target);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
+ target, -1);
aic7xxx_run_done_queue(p, /*complete*/ TRUE);
}
unsigned char queue_flag = FALSE;
char channel;
- if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
- {
- target = (inb(p->base + SELID) >> 4) & 0x0F;
- }
- else
- {
- target = (inb(p->base + SCSIID) >> 4) & 0x0F;
- }
- scratch_offset = target;
- channel = 'A';
- lun = inb(p->base + SAVED_TCL) & 0x07;
- if (inb(p->base + SBLKCTL) & SELBUSB)
- {
- channel = 'B';
- scratch_offset += 8;
- }
+ target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
+ channel = (aic_inb(p, SBLKCTL) >> 3) & 0x01;
+ scratch_offset = target + (channel << 3);
+ lun = aic_inb(p, SAVED_TCL) & 0x07;
target_mask = (0x01 << scratch_offset);
switch (intstat & SEQINT_MASK)
{
case NO_MATCH:
{
- /*
- * This could be for a normal abort request. Figure out
- * which SCB we were trying to find and only give an error
- * if we didn't ask for this to happen.
- */
- unsigned char scb_index;
- unsigned char busy_scbid;
- unsigned char arg1;
-
- busy_scbid = aic7xxx_index_busy_target(p, target, channel,
- /*unbusy*/ FALSE);
- arg1 = inb(p->base + ARG_1);
-
- if (arg1 == SCB_LIST_NULL)
- {
- /* untagged request */
- scb_index = busy_scbid;
- }
- else
- {
- scb_index = arg1;
- }
-
- if (scb_index < p->scb_data->numscbs)
- {
- scb = p->scb_data->scb_array[scb_index];
- if (scb->hscb->control & ABORT_SCB)
- {
- /*
- * We expected this. Let the busfree handler take care
- * of this when we the abort is finally sent. Set
- * IDENTIFY_SEEN so that the busfree handler knows that
- * there is an SCB to cleanup.
- */
- outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reconnect SCB abort "
- "successful\n", p->host_no, CTL_OF_SCB(scb));
- break;
- }
- }
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) No active SCB for reconnecting "
- "target - Issuing BUS DEVICE RESET.\n",
- p->host_no, CHAN_TO_INT(channel), target, lun);
-
- printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
- inb(p->base + SAVED_TCL), arg1,
- (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
- aic7xxx_handle_device_reset(p, target, channel);
+ printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
+ "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
+ printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+ p->host_no, channel, target, lun,
+ aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
}
break;
- case NO_MATCH_BUSY:
+ case SEND_REJECT:
{
- /*
- * XXX - Leave this as a panic for the time being since it
- * indicates a bug in the timeout code for this to happen.
- */
- unsigned char scb_index;
-
- scb_index = inb(p->base + CUR_SCBID);
- scb = p->scb_data->scb_array[scb_index];
-
- panic("(scsi%d:%d:%d:%d) Target busy link failure, "
- "but busy SCB exists!\n",
- p->host_no, CHAN_TO_INT(channel), target, lun );
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
+ "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
+ aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
}
break;
- case SEND_REJECT:
- {
- unsigned char rej_byte;
-
- rej_byte = inb(p->base + REJBYTE);
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting unknown message "
- "(0x%x) received from target, SEQ_FLAGS=0x%x\n",
- p->host_no, CHAN_TO_INT(channel), target, lun,
- rej_byte, inb(p->base + SEQ_FLAGS));
- }
- break;
-
- case NO_IDENT:
+ case NO_IDENT:
{
/*
* The reconnecting target either did not send an identify
- * message, or did, but we didn't find and SCB to match and
+ * message, or did, but we didn't find an SCB to match and
* before it could respond to our ATN/abort, it hit a dataphase.
* The only safe thing to do is to blow it away with a bus
* reset.
*/
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target did not send an "
- "IDENTIFY message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
- p->host_no, CHAN_TO_INT(channel), target, lun,
- inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+ if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
+ printk(INFO_LEAD "Target did not send an IDENTIFY message; "
+ "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
+ lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
break;
case BAD_PHASE:
- if (inb(p->base + LASTPHASE) == P_BUSFREE)
+ if (aic_inb(p, LASTPHASE) == P_BUSFREE)
{
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Missed busfree.\n",
- p->host_no, CHAN_TO_INT(channel), target, lun);
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
+ target, lun);
restart_sequencer(p);
}
else
{
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unknown scsi bus phase, "
- "continuing\n", p->host_no, CHAN_TO_INT(channel), target, lun);
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
+ channel, target, lun);
}
break;
case EXTENDED_MSG:
{
- unsigned char message_length;
- unsigned char message_code;
- unsigned char scb_index;
+ p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ p->msg_len = 0;
+ p->msg_index = 0;
- message_length = inb(p->base + MSGIN_EXT_LEN);
- message_code = inb(p->base + MSGIN_EXT_OPCODE);
- scb_index = inb(p->base + SCB_TAG);
- scb = p->scb_data->scb_array[scb_index];
+ /*
+ * We have to clear the SEQINT *BEFORE* we set the REQINIT handler
+ * active or else VLB and edge triggered EISA cards could loose the
+ * first REQINIT and cause a bus hang/reset cycle.
+ */
+ aic_outb(p, CLRSEQINT, CLRINT);
- switch (message_code)
- {
- case MSG_EXT_SDTR:
- {
- unsigned char period;
- unsigned char offset;
- unsigned char saved_offset;
- unsigned char targ_scratch;
- unsigned char max_offset;
- unsigned char rate;
-
- if (message_length != MSG_EXT_SDTR_LEN)
- {
- outb(SEND_REJ, p->base + RETURN_1);
- break;
- }
+ /*
+ * To actually receive the message, simply turn on
+ * REQINIT interrupts and let our interrupt handler
+ * do the rest (REQINIT should already be true).
+ */
+ p->flags |= AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
- period = inb(p->base + MSGIN_EXT_BYTES);
- saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
- targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ /*
+ * We don't want the sequencer unpaused yet so we return early
+ */
+ return;
+ }
- if (targ_scratch & WIDEXFER)
- max_offset = MAX_OFFSET_16BIT;
- else
- max_offset = MAX_OFFSET_8BIT;
- offset = MIN(saved_offset, max_offset);
+ case REJECT_MSG:
+ {
+ /*
+ * What we care about here is if we had an outstanding SDTR
+ * or WDTR message for this target. If we did, this is a
+ * signal that the target is refusing negotiation.
+ */
+ unsigned char targ_scratch;
+ unsigned char scb_index;
+ unsigned char last_msg;
- aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+ scb_index = aic_inb(p, SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
+ last_msg = aic_inb(p, LAST_MSG);
+ if ( (last_msg == MSG_IDENTIFYFLAG) &&
+ (scb->tag_action != 0 ) &&
+ !(p->flags & AHC_HANDLING_REQINITS) )
+ {
+ if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
+ (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS))
+ {
/*
- * Preserve the WideXfer flag.
+ * OK...the device seems able to accept tagged commands, but
+ * not ordered tag commands, only simple tag commands. So, we
+ * disable ordered tag commands and go on with life just like
+ * normal.
*/
- targ_scratch = rate | (targ_scratch & WIDEXFER);
-
+ p->orderedtag &= ~target_mask;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ scb->hscb->control &= ~SCB_TAG_TYPE;
+ scb->hscb->control |= MSG_SIMPLE_Q_TAG;
+ aic_outb(p, scb->hscb->control, SCB_CONTROL);
/*
- * Update both the target scratch area and current SCSIRATE.
+ * OK..we set the tag type to simple tag command, now we re-assert
+ * ATNO and hope this will take us into the identify phase again
+ * so we can resend the tag type and info to the device.
*/
- outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
- outb(targ_scratch, p->base + SCSIRATE);
-
+ }
+ else
+ {
+ unsigned char i, reset = 0;
+ struct aic7xxx_scb *scbp;
+ int old_verbose;
/*
- * See if we initiated Sync Negotiation and didn't have
- * have to fall down to async transfers.
+ * Hmmmm....the device is flaking out on tagged commands. The
+ * bad thing is that we already have tagged commands enabled in
+ * the device struct in the mid level code. We also have a queue
+ * set according to the tagged queue depth. Gonna have to live
+ * with it by controlling our queue depth internally and making
+ * sure we don't set the tagged command flag any more.
*/
- if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
- {
- /* We started it. */
- if (saved_offset == offset)
- {
- /*
- * Don't send an SDTR back to the target.
- */
- outb(0, p->base + RETURN_1);
- }
- else
- {
- /*
- * We went too low - force async and disable future
- * sync negotiation
- */
- p->needsdtr_copy &= ~target_mask;
- outb(SEND_REJ, p->base + RETURN_1);
- }
- }
- else if ( offset != 0 )
- {
- /*
- * Send our own SDTR in reply.
- *
- * We want to see this message as we don't expect a target
- * to send us a SDTR request first.
- */
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Sending reply SDTR.\n",
- p->host_no, CTL_OF_SCB(scb));
- aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
- outb(SEND_MSG, p->base + RETURN_1);
- }
- else
- {
- /*
- * The incoming SDTR was too low, reject it.
- */
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Rejecting SDTR request.\n",
- p->host_no, CTL_OF_SCB(scb));
- outb(SEND_REJ, p->base + RETURN_1);
- p->needsdtr_copy &= ~target_mask;
- }
+ p->tagenable &= ~target_mask;
+ p->orderedtag &= ~target_mask;
+ p->dev_max_queue_depth[scratch_offset] =
+ p->dev_temp_queue_depth[scratch_offset] = 1;
/*
- * Clear the flags.
+ * We set this command up as a bus device reset. However, we have
+ * to clear the tag type as it's causing us problems. We shouldnt
+ * have to worry about any other commands being active, since if
+ * the device is refusing tagged commands, this should be the
+ * first tagged command sent to the device, however, we do have
+ * to worry about any other tagged commands that may already be
+ * in the qinfifo. The easiest way to do this, is to issue a BDR,
+ * send all the commands back to the mid level code, then let them
+ * come back and get rebuilt as untagged commands.
*/
- p->needsdtr &= ~target_mask;
- p->sdtr_pending &= ~target_mask;
- break;
- }
-
- case MSG_EXT_WDTR:
- {
- unsigned char scratch, bus_width;
-
- if (message_length != MSG_EXT_WDTR_LEN)
- {
- outb(SEND_REJ, p->base + RETURN_1);
- break;
- }
-
- bus_width = inb(p->base + MSGIN_EXT_BYTES);
- scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
-
- if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
- {
- /*
- * Don't send an WDTR back to the target, since we asked first.
- */
- outb(0, p->base + RETURN_1);
- switch (bus_width)
- {
- case BUS_8_BIT:
- p->needwdtr_copy &= ~target_mask;
- scratch &= 0x7F;
- break;
-
- case BUS_16_BIT:
- if (aic7xxx_verbose)
- {
- printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
- "transfers.\n", p->host_no, CTL_OF_SCB(scb));
- }
- scratch |= WIDEXFER;
- break;
-
- case BUS_32_BIT:
- outb(SEND_REJ, p->base + RETURN_1);
- /* No verbose here! We want to see this condition. */
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) "
- "requesting 32 bit transfers, rejecting...\n",
- p->host_no, CTL_OF_SCB(scb));
- break;
-
- default:
- break;
- }
- }
- else
+ scb->tag_action = 0;
+ scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
+ scb->hscb->control |= MK_MESSAGE;
+ aic_outb(p, scb->hscb->control, SCB_CONTROL);
+
+ old_verbose = aic7xxx_verbose;
+ aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
+ for (i=0; i!=p->scb_data->numscbs; i++)
{
- int send_reject = FALSE;
-
- /*
- * Send our own WDTR in reply.
- */
- switch (bus_width)
- {
- case BUS_8_BIT:
- p->needwdtr_copy &= ~target_mask;
- scratch &= 0x7F;
- break;
-
- case BUS_32_BIT:
- case BUS_16_BIT:
- if (p->bus_type == AIC_WIDE)
- {
- printk(KERN_INFO "(scsi%d:%d:%d:%d) Using 16 bit (Wide)"
- "transfers.\n", p->host_no, CTL_OF_SCB(scb));
- bus_width = BUS_16_BIT;
- scratch |= WIDEXFER;
- }
- else
- {
- bus_width = BUS_8_BIT;
- scratch &= 0x7F; /* XXX - FreeBSD doesn't do this. */
- send_reject = TRUE;
- }
- break;
-
- default:
- break;
- }
- if (send_reject)
- {
- outb(SEND_REJ, p->base + RETURN_1);
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target initiated "
- "wide negotiation on a narrow bus - rejecting!",
- p->host_no, CTL_OF_SCB(scb));
- }
- else
+ scbp = p->scb_data->scb_array[i];
+ if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
{
- aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
- outb(SEND_MSG, p->base + RETURN_1);
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
+ {
+ aic7xxx_reset_device(p, target, channel, lun, i);
+ reset++;
+ }
}
}
- p->needwdtr &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
- outb(scratch, p->base + SCSIRATE);
- break;
- } /* case MSG_EXT_WDTR */
-
- default:
+ aic7xxx_verbose = old_verbose;
/*
- * Unknown extended message - reject it.
+ * Wait until after the for loop to set the busy index since
+ * aic7xxx_reset_device will clear the busy index during its
+ * operation.
*/
- outb(SEND_REJ, p->base + RETURN_1);
- break;
- } /* switch (message_code) */
- } /* case EXTENDED_MSG */
- break;
-
- case REJECT_MSG:
- {
- /*
- * What we care about here is if we had an outstanding SDTR
- * or WDTR message for this target. If we did, this is a
- * signal that the target is refusing negotiation.
- */
- unsigned char targ_scratch;
- unsigned char scb_index;
-
- scb_index = inb(p->base + SCB_TAG);
- scb = p->scb_data->scb_array[scb_index];
- targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
-
- if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
- {
+ aic7xxx_busy_target(p, scb);
+ printk(INFO_LEAD "Device is refusing tagged commands, using "
+ "untagged I/O.\n", p->host_no, channel, target, lun);
+ }
+ aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ else if ( (last_msg == MSG_IDENTIFYFLAG) &&
+ (scb->flags & SCB_MSGOUT_WDTR) )
+ {
/*
* note 8bit xfers and clear flag
*/
p->needwdtr &= ~target_mask;
p->needwdtr_copy &= ~target_mask;
p->wdtr_pending &= ~target_mask;
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing WIDE "
- "negotiation; using 8 bit transfers.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
- else
- {
- if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
{
- /*
- * note asynch xfers and clear flag
- */
- targ_scratch &= 0xF0;
- p->needsdtr &= ~target_mask;
- p->needsdtr_copy &= ~target_mask;
- p->sdtr_pending &= ~target_mask;
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Refusing "
- "synchronous negotiation; using asynchronous transfers.\n",
- p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Refusing WIDE negotiation; using 8 bit "
+ "transfers.\n", p->host_no, CTL_OF_SCB(scb));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
}
+ scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+ p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT;
+ if (p->needsdtr & target_mask)
+ {
+ p->sdtr_pending |= target_mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ /*
+ * note asynch xfers and clear flag
+ */
+ targ_scratch &= 0xF0;
+ p->needsdtr &= ~target_mask;
+ p->needsdtr_copy &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
+ p->syncinfo[scratch_offset].period = 0;
+ p->syncinfo[scratch_offset].offset = 0;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_SDTR) )
+ {
+ printk(INFO_LEAD "Refusing synchronous negotiation; using "
+ "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR;
+ }
+ }
+ else if (aic7xxx_verbose & VERBOSE_SEQINT)
+ {
/*
* Otherwise, we ignore it.
*/
- }
- outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
- outb(targ_scratch, p->base + SCSIRATE);
+ printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. "
+ "Ignoring.\n", p->host_no, channel, target, lun);
+ }
+ aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
+ aic_outb(p, targ_scratch, SCSIRATE);
}
break;
case BAD_STATUS:
{
- unsigned char scb_index;
- struct aic7xxx_hwscb *hscb;
- Scsi_Cmnd *cmd;
-
- /* The sequencer will notify us when a command has an error that
- * would be of interest to the kernel. This allows us to leave
- * the sequencer running in the common case of command completes
- * without error. The sequencer will have DMA'd the SCB back
- * up to us, so we can reference the drivers SCB array.
- */
- scb_index = inb(p->base + SCB_TAG);
- scb = p->scb_data->scb_array[scb_index];
- hscb = scb->hscb;
-
- /*
- * Set the default return value to 0 indicating not to send
- * sense. The sense code will change this if needed and this
- * reduces code duplication.
- */
- outb(0, p->base + RETURN_1);
- if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Invalid SCB during "
- "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
- CHAN_TO_INT(channel), target, lun, intstat, scb_index,
- scb->flags, (unsigned int) scb->cmd);
- }
- else
- {
+ unsigned char scb_index;
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+
+ /* The sequencer will notify us when a command has an error that
+ * would be of interest to the kernel. This allows us to leave
+ * the sequencer running in the common case of command completes
+ * without error. The sequencer will have DMA'd the SCB back
+ * up to us, so we can reference the drivers SCB array.
+ */
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ aic_outb(p, 0, RETURN_1);
+ printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
+ p->host_no, channel, target, lun, intstat, scb_index);
+ break;
+ }
+ scb = p->scb_data->scb_array[scb_index];
+ hscb = scb->hscb;
+
+ /*
+ * Set the default return value to 0 indicating not to send
+ * sense. The sense code will change this if needed and this
+ * reduces code duplication.
+ */
+ aic_outb(p, 0, RETURN_1);
+ if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
+ " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
+ scb_index, scb->flags, (unsigned long) scb->cmd);
+ }
+ else
+ {
cmd = scb->cmd;
- hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+ hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
aic7xxx_status(cmd) = hscb->target_status;
- cmd->result |= hscb->target_status;
+ cmd->result = hscb->target_status;
switch (status_byte(hscb->target_status))
{
case GOOD:
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Interrupted for status of "
- "GOOD???\n", p->host_no, CTL_OF_SCB(scb));
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Interrupted for status of GOOD???\n",
+ p->host_no, CTL_OF_SCB(scb));
break;
+ case COMMAND_TERMINATED:
case CHECK_CONDITION:
- if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+ if ( !(scb->flags & SCB_SENSE) )
{
- unsigned int addr; /* must be 32 bits */
- /*
- * XXX - How do we save the residual (if there is one).
- */
- aic7xxx_calculate_residual(p, scb);
-
- /*
- * Send a sense command to the requesting target.
- * XXX - revisit this and get rid of the memcopys.
- */
- memcpy((void *) scb->sense_cmd, (void *) generic_sense,
- sizeof(generic_sense));
-
- scb->sense_cmd[1] = (cmd->lun << 5);
- scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
- scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer[0]);
- scb->sg_list[0].length = sizeof(cmd->sense_buffer);
- cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+ /*
+ * XXX - How do we save the residual (if there is one).
+ */
+ if ( hscb->residual_SG_segment_count != 0 )
+ aic7xxx_calculate_residual(p, scb);
+
+ /*
+ * Send a sense command to the requesting target.
+ * XXX - revisit this and get rid of the memcopys.
+ */
+ memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+ sizeof(generic_sense));
+
+ scb->sense_cmd[1] = (cmd->lun << 5);
+ scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+ scb->sg_list[0].address =
+ cpu_to_le32(VIRT_TO_BUS(&cmd->sense_buffer[0]));
+ scb->sg_list[0].length =
+ cpu_to_le32(sizeof(cmd->sense_buffer));
/*
* XXX - We should allow disconnection, but can't as it
* might allow overlapped tagged commands.
*/
- /* hscb->control &= DISCENB; */
+ /* hscb->control &= DISCENB; */
hscb->control = 0;
- hscb->target_status = 0;
- hscb->SG_segment_count = 1;
-
- addr = VIRT_TO_BUS(&scb->sg_list[0]);
- memcpy(&hscb->SG_list_pointer, &addr,
- sizeof(hscb->SG_list_pointer));
-
- memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
- sizeof(hscb->data_pointer));
- /* Maintain SCB_LINKED_NEXT */
- hscb->data_count &= 0xFF000000;
- hscb->data_count |= scb->sg_list[0].length;
-
- addr = VIRT_TO_BUS(&scb->sense_cmd[0]);
- memcpy(&hscb->SCSI_cmd_pointer, &addr,
- sizeof(hscb->SCSI_cmd_pointer));
- hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+ hscb->target_status = 0;
+ hscb->SG_list_pointer =
+ cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0]));
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->data_count = scb->sg_list[0].length;
+ hscb->SCSI_cmd_pointer =
+ cpu_to_le32(VIRT_TO_BUS(&scb->sense_cmd[0]));
+ hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
hscb->residual_SG_segment_count = 0;
hscb->residual_data_count[0] = 0;
hscb->residual_data_count[1] = 0;
hscb->residual_data_count[2] = 0;
- scb->sg_count = hscb->SG_segment_count;
- scb->flags |= SCB_SENSE;
+ scb->sg_count = hscb->SG_segment_count = 1;
+ scb->sg_length = sizeof(cmd->sense_buffer);
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->tag_action = 0;
+ /*
+ * This problem could be caused if the target has lost power
+ * or found some other way to loose the negotiation settings,
+ * so if needed, we'll re-negotiate while doing the sense cmd.
+ * However, if this SCB already was attempting to negotiate,
+ * then we assume this isn't the problem and skip this part.
+ */
+ if ( !(scb->flags & SCB_MSGOUT_BITS) )
+ {
+ if ( p->needwdtr_copy & target_mask )
+ {
+ p->needwdtr |= target_mask;
+ p->wdtr_pending |= target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+ }
+ if ( p->needsdtr_copy & target_mask )
+ {
+ p->needsdtr |= target_mask;
+ if ((hscb->control & MK_MESSAGE) == 0)
+ {
+ p->sdtr_pending |= target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ }
+ }
+
+ scb->flags |= SCB_SENSE;
/*
* Ensure the target is busy since this will be an
* an untagged request.
*/
- aic7xxx_busy_target(p, target, channel, hscb->tag);
- outb(SEND_SENSE, p->base + RETURN_1);
+ aic7xxx_busy_target(p, scb);
+ aic_outb(p, SEND_SENSE, RETURN_1);
+ aic7xxx_error(cmd) = DID_OK;
+ break;
} /* first time sense, no errors */
- else
- {
- if (aic7xxx_error(cmd) == 0)
- {
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
- }
+ aic7xxx_error(cmd) = DID_OK;
+ scb->flags &= ~SCB_SENSE;
break;
case QUEUE_FULL:
case BUSY: /* drop through to here */
{
struct aic7xxx_scb *next_scbp, *prev_scbp;
- int ti = TARGET_INDEX(scb->cmd);
-
+ unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
+ /*
+ * We have to look three places for queued commands:
+ * 1: QINFIFO
+ * 2: p->waiting_scbs queue
+ * 3: WAITING_SCBS list on card (for commands that are started
+ * but haven't yet made it to the device)
+ */
aic7xxx_search_qinfifo(p, target, channel, lun,
SCB_LIST_NULL, 0, TRUE,
- &p->device_status[ti].delayed_scbs);
+ &p->delayed_scbs[scratch_offset]);
next_scbp = p->waiting_scbs.head;
while ( next_scbp != NULL )
{
prev_scbp = next_scbp;
next_scbp = next_scbp->q_next;
- if ( aic7xxx_match_scb(prev_scbp, target, channel, lun,
+ if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
SCB_LIST_NULL) )
{
scbq_remove(&p->waiting_scbs, prev_scbp);
- scbq_insert_tail(&p->device_status[ti].delayed_scbs,
+ scbq_insert_tail(&p->delayed_scbs[scratch_offset],
prev_scbp);
}
}
- scbq_insert_head(&p->device_status[ti].delayed_scbs, scb);
- p->device_status[ti].active_cmds--;
- scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
+ next_scbp = NULL;
+ active_hscb = aic_inb(p, SCBPTR);
+ prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
+ next_hscb = aic_inb(p, WAITING_SCBH);
+ while (next_hscb != SCB_LIST_NULL)
+ {
+ aic_outb(p, next_hscb, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ next_scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
+ SCB_LIST_NULL) )
+ {
+ scbq_insert_head(&p->delayed_scbs[scratch_offset],
+ next_scbp);
+ next_scbp->flags |= SCB_WAITINGQ;
+ p->dev_active_cmds[scratch_offset]--;
+ p->activescbs--;
+ next_hscb = aic_inb(p, SCB_NEXT);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ if (prev_hscb == SCB_LIST_NULL)
+ {
+ aic_outb(p, 0, SCSISEQ); /* We were first on the list,
+ * so we kill the selection
+ * hardware. Let the sequencer
+ * re-init the hardware itself
+ */
+ aic_outb(p, next_hscb, WAITING_SCBH);
+ }
+ else
+ {
+ aic_outb(p, prev_hscb, SCBPTR);
+ aic_outb(p, next_hscb, SCB_NEXT);
+ }
+ }
+ else
+ {
+ prev_hscb = next_hscb;
+ next_hscb = aic_inb(p, SCB_NEXT);
+ }
+ }
+ aic_outb(p, active_hscb, SCBPTR);
+ scbq_insert_head(&p->delayed_scbs[scratch_offset], scb);
+ p->dev_active_cmds[scratch_offset]--;
+ p->activescbs--;
+ scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
- if (p->device_status[ti].timer.expires == 0)
+ if (p->dev_timer[scratch_offset].expires == 0)
{
- if ( p->device_status[ti].active_cmds )
- {
- p->device_status[ti].timer.expires = jiffies + (HZ * 2);
- add_timer(&p->device_status[ti].timer);
- }
- else
- {
- p->device_status[ti].timer.expires = jiffies + (HZ / 2);
- add_timer(&p->device_status[ti].timer);
- }
+ if ( p->dev_active_cmds[scratch_offset] )
+ {
+ p->dev_timer[scratch_offset].expires = jiffies + (HZ * 2);
+ add_timer(&p->dev_timer[scratch_offset]);
+ }
+ else
+ {
+ p->dev_timer[scratch_offset].expires = jiffies + (HZ / 2);
+ add_timer(&p->dev_timer[scratch_offset]);
+ }
}
- if (aic7xxx_verbose > 1)
+ if (aic7xxx_verbose & VERBOSE_QUEUE_FULL)
{
if (queue_flag)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue full received; "
- "queue depth %d, active %d\n", p->host_no,
- CTL_OF_SCB(scb), p->device_status[ti].max_queue_depth,
- p->device_status[ti].active_cmds);
+ printk(INFO_LEAD "Queue full received; queue depth %d, "
+ "active %d\n", p->host_no, CTL_OF_SCB(scb),
+ p->dev_max_queue_depth[scratch_offset],
+ p->dev_active_cmds[scratch_offset]);
else
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Target busy\n",
- p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
}
if (queue_flag)
{
- p->device_status[ti].temp_queue_depth =
- p->device_status[ti].active_cmds;
- if ( (p->device_status[ti].last_queue_full <
- (p->device_status[ti].active_cmds - 1)) ||
- (p->device_status[ti].last_queue_full >
- (p->device_status[ti].active_cmds + 1)) )
+ p->dev_temp_queue_depth[scratch_offset] =
+ p->dev_active_cmds[scratch_offset];
+ if ( p->dev_last_queue_full[scratch_offset] !=
+ p->dev_active_cmds[scratch_offset] )
{
- p->device_status[ti].last_queue_full =
- p->device_status[ti].active_cmds;
- p->device_status[ti].last_queue_full_count = 0;
+ p->dev_last_queue_full[scratch_offset] =
+ p->dev_active_cmds[scratch_offset];
+ p->dev_last_queue_full_count[scratch_offset] = 0;
}
else
{
- p->device_status[ti].last_queue_full_count++;
+ p->dev_last_queue_full_count[scratch_offset]++;
}
- if ( (p->device_status[ti].last_queue_full_count > 14) &&
- (p->device_status[ti].active_cmds > 4) )
+ if ( (p->dev_last_queue_full_count[scratch_offset] > 14) &&
+ (p->dev_active_cmds[scratch_offset] > 4) )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queue depth reduced "
- "to %d\n", p->host_no, CTL_OF_SCB(scb),
- p->device_status[ti].active_cmds);
- p->device_status[ti].max_queue_depth =
- p->device_status[ti].active_cmds;
- p->device_status[ti].last_queue_full = 0;
- p->device_status[ti].last_queue_full_count = 0;
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+ printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
+ CTL_OF_SCB(scb), p->dev_active_cmds[scratch_offset]);
+ p->dev_max_queue_depth[scratch_offset] =
+ p->dev_active_cmds[scratch_offset];
+ p->dev_last_queue_full[scratch_offset] = 0;
+ p->dev_last_queue_full_count[scratch_offset] = 0;
}
}
break;
}
default:
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Unexpected target "
- "status 0x%x.\n", p->host_no,
- CTL_OF_SCB(scb), scb->hscb->target_status);
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->target_status);
if (!aic7xxx_error(cmd))
{
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
}
break;
} /* end switch */
- } /* end else of */
+ } /* end else of */
}
break;
case AWAITING_MSG:
{
- unsigned char scb_index;
- unsigned char message_offset;
-
- scb_index = inb(p->base + SCB_TAG);
- scb = p->scb_data->scb_array[scb_index];
-
- /*
- * This SCB had a MK_MESSAGE set in its control byte informing
- * the sequencer that we wanted to send a special message to
- * this target.
- */
- message_offset = inb(p->base + MSG_LEN);
- if (scb->flags & SCB_DEVICE_RESET)
- {
- outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
- outb(1, p->base + MSG_LEN);
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset mailed.\n",
- p->host_no, CTL_OF_SCB(scb));
- }
+ unsigned char scb_index;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ p->msg_index = p->msg_len = 0;
+ /*
+ * This SCB had a MK_MESSAGE set in its control byte informing
+ * the sequencer that we wanted to send a special message to
+ * this target.
+ */
+
+ if (scb->flags & SCB_DEVICE_RESET)
+ {
+ p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
+ p->msg_len++;
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset mailed.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
else if (scb->flags & SCB_ABORT)
{
- if ((scb->hscb->control & TAG_ENB) != 0)
+ if (scb->hscb->control & TAG_ENB)
{
- outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+ if (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG)
+ {
+ p->msg_buf[p->msg_index++] = scb->tag_action;
+ p->msg_buf[p->msg_index++] = scb->hscb->tag;
+ p->msg_len += 2;
+ }
+ p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
}
else
{
- outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+ p->msg_buf[p->msg_index++] = MSG_ABORT;
}
- outb(message_offset + 1, p->base + MSG_LEN);
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort message mailed.\n",
- p->host_no, CTL_OF_SCB(scb));
+ p->msg_len++;
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
+ CTL_OF_SCB(scb));
}
- else if (scb->flags & SCB_MSGOUT_WDTR)
- {
- aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+ else if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT));
}
else if (scb->flags & SCB_MSGOUT_SDTR)
{
- unsigned char target_scratch;
- unsigned short ultra_enable;
- int i, sxfr;
+ unsigned char period, offset;
/*
* Pull the user defined setting from scratch RAM.
*/
- target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
- sxfr = target_scratch & SXFR;
- ultra_enable = inb(p->base + ULTRA_ENB) |
- (inb(p->base + ULTRA_ENB + 1) << 8);
- if (ultra_enable & target_mask)
- {
- sxfr |= 0x100;
- }
- for (i = 0; i < num_aic7xxx_syncrates; i++)
+ period = p->syncinfo[scratch_offset].period;
+ offset = p->syncinfo[scratch_offset].offset;
+ if ( (p->needsdtr_copy & target_mask) == 0)
{
- if (sxfr == aic7xxx_syncrates[i].rate)
- break;
+ period = 0;
+ offset = 0;
}
- aic7xxx_construct_sdtr(p, message_offset,
- aic7xxx_syncrates[i].period,
- target_scratch & WIDEXFER ?
- MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
+ aic7xxx_construct_sdtr(p, period, offset);
}
else
{
panic("aic7xxx: AWAITING_MSG for an SCB that does "
- "not have a waiting message.");
- }
+ "not have a waiting message.\n");
+ }
+ /*
+ * We've set everything up to send our message, now to actually do
+ * so we need to enable reqinit interrupts and let the interrupt
+ * handler do the rest. We don't want to unpause the sequencer yet
+ * though so we'll return early. We also have to make sure that
+ * we clear the SEQINT *BEFORE* we set the REQINIT handler active
+ * or else it's possible on VLB cards to loose the first REQINIT
+ * interrupt. Edge triggered EISA cards could also loose this
+ * interrupt, although PCI and level triggered cards should not
+ * have this problem since they continually interrupt the kernel
+ * until we take care of the situation.
+ */
+ aic_outb(p, CLRSEQINT, CLRINT);
+ p->msg_index = 0;
+ p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ p->flags |= AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+ return;
}
break;
case DATA_OVERRUN:
{
- unsigned char scb_index = inb(p->base + SCB_TAG);
- unsigned char lastphase = inb(p->base + LASTPHASE);
- unsigned int i, overrun;
-
- scb = (p->scb_data->scb_array[scb_index]);
- overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
- (inb(p->base + STCNT + 2) << 16);
- overrun = 0x00FFFFFF - overrun;
- if (aic7xxx_verbose)
+ unsigned char scb_index = aic_inb(p, SCB_TAG);
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned int i;
+
+ scb = (p->scb_data->scb_array[scb_index]);
+ /*
+ * XXX - What do we really want to do on an overrun? The
+ * mid-level SCSI code should handle this, but for now,
+ * we'll just indicate that the command should retried.
+ * If we retrieved sense info on this target, then the
+ * base SENSE info should have been saved prior to the
+ * overrun error. In that case, we return DID_OK and let
+ * the mid level code pick up on the sense info. Otherwise
+ * we return DID_ERROR so the command will get retried.
+ */
+ if ( !(scb->flags & SCB_SENSE) )
{
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Data overrun of %d bytes "
- "detected in %s phase, tag %d; forcing a retry.\n",
- p->host_no, CTL_OF_SCB(scb), overrun,
- lastphase == P_DATAIN ? "Data-In" : "Data-Out",
- scb->hscb->tag);
- printk(KERN_WARNING "%s seen Data Phase. Length=%d, NumSGs=%d.\n",
- inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
- aic7xxx_length(scb->cmd, 0), scb->sg_count);
+ printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
+ p->host_no, CTL_OF_SCB(scb),
+ (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
+ printk(KERN_WARNING " %s seen Data Phase. Length=%d, NumSGs=%d.\n",
+ (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
+ scb->sg_length, scb->sg_count);
for (i = 0; i < scb->sg_count; i++)
{
printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n",
- i, scb->sg_list[i].address, scb->sg_list[i].length);
+ i,
+ le32_to_cpu(scb->sg_list[i].address),
+ le32_to_cpu(scb->sg_list[i].length) );
}
+ aic7xxx_error(scb->cmd) = DID_ERROR;
}
- /*
- * XXX - What do we really want to do on an overrun? The
- * mid-level SCSI code should handle this, but for now,
- * we'll just indicate that the command should retried.
- */
- aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
+ else
+ printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ break;
+
+ case TRACEPOINT:
+ {
+ printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
+ target, lun);
+ }
+ break;
+
+ case TRACEPOINT2:
+ {
+ printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no, channel,
+ target, lun);
}
break;
-/* #if AIC7XXX_NOT_YET */
+#if AIC7XXX_NOT_YET
/* XXX Fill these in later */
case MSG_BUFFER_BUSY:
printk("aic7xxx: Message buffer busy.\n");
case MSGIN_PHASEMIS:
printk("aic7xxx: Message-in phasemis.\n");
break;
-/*#endif */
-
- case ABORT_CMDCMPLT:
- /* This interrupt serves to pause the sequencer until we can clean
- * up the QOUTFIFO allowing us to handle any abort SCBs that may
- * completed yet still have an SCB in the QINFIFO or waiting for
- * selection queue. By the time we get here, we should have
- * already cleaned up the queues, so all we need to do is unpause
- * the sequencer.
- */
- break;
+#endif
- default: /* unknown */
- printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
- p->host_no, intstat, inb(p->base + SCSISIGI));
+ default: /* unknown */
+ printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+ p->host_no, channel, target, lun, intstat,
+ aic_inb(p, SCSISIGI));
break;
}
/*
* Clear the sequencer interrupt and unpause the sequencer.
*/
- outb(CLRSEQINT, p->base + CLRINT);
+ aic_outb(p, CLRSEQINT, CLRINT);
unpause_sequencer(p, /* unpause always */ TRUE);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_handle_scsiint
+ * aic7xxx_parse_msg
*
* Description:
- * Interrupt handler for SCSI interrupts (SCSIINT).
- *-F*************************************************************************/
-static void
-aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+ * Parses incoming messages into actions on behalf of
+ * aic7xxx_handle_reqinit
+ *_F*************************************************************************/
+static int
+aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- unsigned char scb_index;
- unsigned char status;
- struct aic7xxx_scb *scb;
+ int reject, done;
+ unsigned char target_scratch, scratch_offset;
+ unsigned short target_mask;
- scb_index = inb(p->base + SCB_TAG);
- status = inb(p->base + SSTAT1);
+ reject = done = FALSE;
+ scratch_offset = TARGET_INDEX(scb->cmd);
+ target_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
+ target_mask = (0x01 << scratch_offset);
- if (scb_index < p->scb_data->numscbs)
- {
- scb = p->scb_data->scb_array[scb_index];
- if ((scb->flags & SCB_ACTIVE) == 0)
- {
- scb = NULL;
- }
- }
- else
- {
- scb = NULL;
- }
+ /*
+ * Parse as much of the message as is availible,
+ * rejecting it if we don't support it. When
+ * the entire message is availible and has been
+ * handled, return TRUE indicating that we have
+ * parsed an entire message.
+ */
- if ((status & SCSIRSTI) != 0)
+ if (p->msg_buf[0] != MSG_EXTENDED)
{
- char channel;
-
- channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
-
- printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
- p->host_no, channel);
- /*
- * Go through and abort all commands for the channel, but do not
- * reset the channel again.
- */
- aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
- scb = NULL;
+ reject = TRUE;
}
- else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
- {
- /*
- * First look at what phase we were last in. If it's message-out,
- * chances are pretty good that the bus free was in response to
- * one of our abort requests.
- */
- unsigned char lastphase = inb(p->base + LASTPHASE);
- unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
- char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
- int printerror = TRUE;
-
- outb(0, p->base + SCSISEQ);
- if (lastphase == P_MESGOUT)
- {
- unsigned char sindex;
- unsigned char message;
- sindex = inb(p->base + SINDEX);
- message = inb(p->base + sindex - 1);
+ /*
+ * Just accept the length byte outright and perform
+ * more checking once we know the message type.
+ */
- if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
- {
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB %d abort delivered.\n",
- p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
- aic7xxx_reset_device(p, target, channel, ALL_LUNS,
- (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
- aic7xxx_run_done_queue(p, FALSE);
- scb = NULL;
- printerror = 0;
- }
- else if (message == MSG_BUS_DEV_RESET)
- {
- aic7xxx_handle_device_reset(p, target, channel);
- scb = NULL;
- printerror = 0;
- }
- }
- if (printerror != 0)
+ if ( !reject && (p->msg_len > 2) )
+ {
+ switch(p->msg_buf[2])
{
- if (scb != NULL)
+ case MSG_EXT_SDTR:
{
- unsigned char tag;
+ unsigned char period, response_period, offset;
+ unsigned char max_offset, saved_offset, rate;
- if ((scb->hscb->control & TAG_ENB) != 0)
+ if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
{
- tag = scb->hscb->tag;
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
+ {
+ break;
+ }
+
+ period = p->msg_buf[3];
+ saved_offset = p->msg_buf[4];
+
+ if (target_scratch & WIDEXFER)
+ {
+ max_offset = MAX_OFFSET_16BIT;
+ }
+ else
+ {
+ max_offset = MAX_OFFSET_8BIT;
+ }
+ offset = MIN(saved_offset, max_offset);
+ response_period = aic7xxx_scsirate(p, &rate, &period,
+ &offset, scb->cmd->target, scb->cmd->channel, /* set */ TRUE);
+ /* Preserve the WideXfer flag */
+ target_scratch = rate | (target_scratch & WIDEXFER);
+
+ /*
+ * Update the TARGET_SCRATCH, the SCSIRATE, and our syncinfo
+ * areas.
+ */
+ aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
+ aic_outb(p, target_scratch, SCSIRATE);
+ p->syncinfo[scratch_offset].period = response_period;
+ p->syncinfo[scratch_offset].offset = offset;
+
+ /*
+ * Did we start this, if not, or if we went to low and had to
+ * go async, then send an SDTR back to the target
+ */
+ p->needsdtr &= ~target_mask;
+ if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ if (saved_offset != offset)
+ {
+ p->needsdtr_copy &= ~target_mask;
+ reject = TRUE;
+ }
+ scb->flags &= ~SCB_MSGOUT_SDTR;
+ p->sdtr_pending &= ~target_mask;
+ }
+ else
+ {
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ p->sdtr_pending |= target_mask;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ done = TRUE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ unsigned char bus_width;
+
+ if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
+ {
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
+ {
+ break;
+ }
+
+ bus_width = p->msg_buf[3];
+ p->needwdtr &= ~target_mask;
+ if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ switch(bus_width)
+ {
+ default:
+ {
+ reject = TRUE;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+ p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+ }
+ } /* We fall through on purpose */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ p->needwdtr_copy &= ~target_mask;
+ target_scratch &= 0x7f;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+ }
+ break;
+ }
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+ }
+ target_scratch |= WIDEXFER;
+ break;
+ }
+ }
+ scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+ p->wdtr_pending &= ~target_mask;
+ }
+ else
+ {
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ switch(bus_width)
+ {
+ default:
+ {
+ if (p->type & AHC_WIDE)
+ {
+ bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ p->needwdtr_copy |= target_mask;
+ scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+ target_scratch |= WIDEXFER;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+ }
+ break;
+ }
+ } /* Fall through if we aren't a wide card */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ p->needwdtr_copy &= ~target_mask;
+ scb->flags |= SCB_MSGOUT_WDTR_8BIT;
+ target_scratch &= 0x7f;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+ }
+ break;
+ }
+ }
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ p->wdtr_pending |= target_mask;
+ }
+ aic_outb(p, target_scratch, SCSIRATE);
+ aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
+ p->syncinfo[scratch_offset].offset =
+ (bus_width == MSG_EXT_WDTR_BUS_8_BIT) ?
+ MAX_OFFSET_8BIT : MAX_OFFSET_16BIT;
+ if ( !(p->wdtr_pending & target_mask) && !reject)
+ {
+ /*
+ * We've successfully completed the wide negotiation, so let's start
+ * up the sync negotiation now.
+ */
+ scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+ if ((p->needsdtr & target_mask) && !(p->sdtr_pending & target_mask))
+ {
+ p->sdtr_pending |= target_mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ }
+ done = TRUE;
+ break;
+ }
+ default:
+ {
+ reject = TRUE;
+ break;
+ }
+ } /* end of switch(p->msg_type) */
+ } /* end of if (!reject && (p->msg_len > 2)) */
+
+ if (reject)
+ {
+ aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ return(done);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_reqinit
+ *
+ * Description:
+ * Interrupt handler for REQINIT interrupts (used to transfer messages to
+ * and from devices).
+ *_F*************************************************************************/
+static void
+aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ unsigned char lastbyte;
+ unsigned char phasemis;
+ int done;
+
+ switch(p->msg_type)
+ {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ if (p->msg_len == 0)
+ panic("aic7xxx: REQINIT with no active message!\n");
+
+ lastbyte = (p->msg_index == (p->msg_len - 1));
+ phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
+
+ if (lastbyte || phasemis)
+ {
+ /* Time to end the message */
+ p->msg_len = 0;
+ p->msg_type = MSG_TYPE_NONE;
+ /*
+ * NOTE-TO-MYSELF: If you clear the REQINIT after you
+ * disable REQINITs, then cases of REJECT_MSG stop working
+ * and hang the bus
+ */
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+
+ if (phasemis == 0)
+ {
+ aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
+ aic_outb(p, 0, RETURN_1);
+ }
+ else
+ {
+ aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+ }
+ unpause_sequencer(p, TRUE);
+ }
+ else
+ {
+ /*
+ * Present the byte on the bus (clearing REQINIT) but don't
+ * unpause the sequencer.
+ */
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ aic_outb(p, p->msg_buf[p->msg_index++], SCSIDATL);
+ }
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
+
+ if (phasemis == 0)
+ {
+ p->msg_len++;
+ /* Pull the byte in without acking it */
+ p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
+ done = aic7xxx_parse_msg(p, scb);
+ /* Ack the byte */
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ aic_inb(p, SCSIDATL);
+ p->msg_index++;
+ }
+ if (phasemis || done)
+ {
+ /* Time to end our message session */
+ p->msg_len = 0;
+ p->msg_type = MSG_TYPE_NONE;
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, TRUE);
+ }
+ break;
+ }
+ default:
+ {
+ panic("aic7xxx: Unknown REQINIT message type.\n");
+ break;
+ }
+ } /* End of switch(p->msg_type) */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_scsiint
+ *
+ * Description:
+ * Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ unsigned char scb_index;
+ unsigned char status;
+ struct aic7xxx_scb *scb;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ status = aic_inb(p, SSTAT1);
+
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
+ }
+ }
+ else
+ {
+ scb = NULL;
+ }
+
+
+ if ( (p->flags & AHC_HANDLING_REQINITS) && (status & REQINIT) )
+ {
+ if (scb)
+ {
+ aic7xxx_handle_reqinit(p, scb);
+ }
+ else
+ {
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_index = 0;
+ p->msg_len = 0;
+ }
+ return;
+ }
+
+ if ((status & SCSIRSTI) != 0)
+ {
+ int channel;
+
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+
+ if (aic7xxx_verbose & VERBOSE_RESET)
+ printk(WARN_LEAD "Someone else reset the channel!!\n",
+ p->host_no, channel, -1, -1);
+ /*
+ * Go through and abort all commands for the channel, but do not
+ * reset the channel again.
+ */
+ aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+ scb = NULL;
+ }
+ else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
+ {
+ /*
+ * First look at what phase we were last in. If it's message-out,
+ * chances are pretty good that the bus free was in response to
+ * one of our abort requests.
+ */
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
+ unsigned char target = (saved_tcl >> 4) & 0x0F;
+ int channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ int printerror = TRUE;
+
+ aic_outb(p, 0, SCSISEQ);
+ if (lastphase == P_MESGOUT)
+ {
+ unsigned char message;
+
+ message = aic_inb(p, SINDEX);
+
+ if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+ (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+ aic7xxx_run_done_queue(p, FALSE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_BUS_DEV_RESET)
+ {
+ aic7xxx_handle_device_reset(p, target, channel);
+ scb = NULL;
+ printerror = 0;
+ }
+ }
+ if (printerror != 0)
+ {
+ if (scb != NULL)
+ {
+ unsigned char tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ tag = scb->hscb->tag;
}
else
{
tag = SCB_LIST_NULL;
}
aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
- aic7xxx_run_done_queue(p, FALSE);
+ aic7xxx_run_done_queue(p, FALSE);
}
else
{ /* Since we don't really know what happened here, we'll wait */
- /* for the commands to timeout and get aborted if need be */
+ /* for the commands to timeout and get aborted if need be */
aic7xxx_add_curscb_to_free_list(p);
}
- printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
- "SEQADDR = 0x%x\n", p->host_no, lastphase,
- (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
+ "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
scb = NULL;
}
- outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
- outb(CLRBUSFREE, p->base + CLRSINT1);
- outb(CLRSCSIINT, p->base + CLRINT);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+ SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, CLRBUSFREE | CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
restart_sequencer(p);
+ unpause_sequencer(p, TRUE);
}
else if ((status & SELTO) != 0)
{
unsigned char nextscb;
Scsi_Cmnd *cmd;
- scbptr = inb(p->base + WAITING_SCBH);
- outb(scbptr, p->base + SCBPTR);
- scb_index = inb(p->base + SCB_TAG);
+ scbptr = aic_inb(p, WAITING_SCBH);
+ if (scbptr >= p->scb_data->maxhscbs)
+ {
+ scb_index = SCB_LIST_NULL;
+ printk(WARN_LEAD "Bad scbptr %d during SELTO.\n",
+ p->host_no, -1, -1, -1, scbptr);
+ }
+ else
+ {
+ aic_outb(p, scbptr, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ }
scb = NULL;
if (scb_index < p->scb_data->numscbs)
}
if (scb == NULL)
{
- printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
- p->host_no, scb_index);
+ printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
+ p->host_no, -1, -1, -1, scb_index);
printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
- "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
- inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
- inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+ "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
}
else
{
- /*
- * XXX - If we queued an abort tag, go clean up the disconnected list.
- */
cmd = scb->cmd;
cmd->result = (DID_TIME_OUT << 16);
/*
- * Clear an pending messages for the timed out
- * target and mark the target as free.
+ * Clear out this hardware SCB
+ */
+ aic_outb(p, 0, SCB_CONTROL);
+
+ /*
+ * Clear out a few values in the card that are in an undetermined
+ * state.
*/
- outb(0, p->base + MSG_LEN);
- aic7xxx_index_busy_target(p, cmd->target,
- cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
- outb(0, p->base + SCB_CONTROL);
+ aic_outb(p, MSG_NOOP, MSG_OUT);
/*
* Shift the waiting for selection queue forward
*/
- nextscb = inb(p->base + SCB_NEXT);
- outb(nextscb, p->base + WAITING_SCBH);
+ nextscb = aic_inb(p, SCB_NEXT);
+ aic_outb(p, nextscb, WAITING_SCBH);
/*
* Put this SCB back on the free list.
*/
aic7xxx_add_curscb_to_free_list(p);
+ /*
+ * XXX - If we queued an abort tag, go clean up the disconnected list.
+ * We know that this particular SCB had to be the queued abort since
+ * the disconnected SCB would have gotten a reconnect instead.
+ * However, if this is an abort command, then DID_TIMEOUT isn't
+ * appropriate, neither is returning the command for that matter.
+ * What we need to do then is to let the command timeout again so
+ * we get a reset since this abort just failed.
+ */
+ if (p->flags & SCB_QUEUED_ABORT)
+ {
+ cmd->result = 0;
+ scb->flags &= ~SCB_QUEUED_ABORT;
+ scb = NULL;
+ }
}
/*
* Stop the selection.
*/
- outb(0, p->base + SCSISEQ);
- outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
- outb(CLRSCSIINT, p->base + CLRINT);
+ aic_outb(p, 0, SCSISEQ);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
restart_sequencer(p);
+ unpause_sequencer(p, TRUE);
}
else if (scb == NULL)
{
- printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+ printk(WARN_LEAD "aic7xxx_isr - referenced scb not valid "
"during scsiint 0x%x scb(%d)\n"
" SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
- p->host_no, status, scb_index, inb(p->base + SIMODE0),
- inb(p->base + SIMODE1), inb(p->base + SSTAT0),
- (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ p->host_no, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
+ aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
/*
* Turn off the interrupt and set status to zero, so that it
* falls through the rest of the SCSIINT code.
*/
- outb(status, p->base + CLRSINT1);
- outb(CLRSCSIINT, p->base + CLRINT);
+ aic_outb(p, status, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
unpause_sequencer(p, /* unpause always */ TRUE);
scb = NULL;
}
char *phase;
Scsi_Cmnd *cmd;
unsigned char mesg_out = MSG_NOOP;
- unsigned char lastphase = inb(p->base + LASTPHASE);
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
cmd = scb->cmd;
switch (lastphase)
* A parity error has occurred during a data
* transfer phase. Flag it and continue.
*/
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Parity error during phase %s.\n",
+ printk(WARN_LEAD "Parity error during phase %s.\n",
p->host_no, CTL_OF_SCB(scb), phase);
/*
*/
if (mesg_out != MSG_NOOP)
{
- outb(mesg_out, p->base + MSG_OUT);
- outb(1, p->base + MSG_LEN);
+ aic_outb(p, mesg_out, MSG_OUT);
scb = NULL;
}
- else
- {
- /*
- * Should we allow the target to make this decision for us?
- */
- cmd->result = DID_RETRY_COMMAND << 16;
- }
- outb(CLRSCSIPERR, p->base + CLRSINT1);
- outb(CLRSCSIINT, p->base + CLRINT);
+ aic_outb(p, CLRSCSIPERR, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
unpause_sequencer(p, /* unpause_always */ TRUE);
}
else
* We don't know what's going on. Turn off the
* interrupt source and try to continue.
*/
- printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
- outb(status, p->base + CLRSINT1);
- outb(CLRSCSIINT, p->base + CLRINT);
+ if (aic7xxx_verbose & VERBOSE_SCSIINT)
+ printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
+ p->host_no, -1, -1, -1, status);
+ aic_outb(p, status, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
unpause_sequencer(p, /* unpause always */ TRUE);
scb = NULL;
}
}
}
+#ifdef CONFIG_PCI
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_pci_intr
+ *
+ * Description:
+ * Check the scsi card for PCI errors and clear the interrupt
+ *
+ * NOTE: If you don't have this function and a 2940 card encounters
+ * a PCI error condition, the machine will end up locked as the
+ * interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
+static void
+aic7xxx_pci_intr(struct aic7xxx_host *p)
+{
+ unsigned char status1;
+ int error;
+
+ error = 0;
+ error = pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+ PCI_STATUS, &status1);
+
+ if (error == 0)
+ {
+ if (status1 & DPE)
+ printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+ "phase.\n", p->host_no, -1, -1, -1);
+ if (status1 & SSE)
+ printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+ -1, -1, -1);
+ if (status1 & RMA)
+ printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+ -1, -1, -1);
+ if (status1 & RTA)
+ printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+ -1, -1, -1);
+ if (status1 & STA)
+ printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+ -1, -1, -1);
+ if (status1 & DPR)
+ printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+ "PERR#\n", p->host_no, -1, -1, -1);
+ }
+ else
+ {
+ printk(WARN_LEAD "Error reading PCI config register during PCI ERROR"
+ "interrupt.\n", p->host_no, -1, -1, -1);
+ aic_outb(p, CLRPARERR, CLRINT);
+ return;
+ }
+
+ pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+ PCI_STATUS, status1);
+ if (status1 & (DPR|RMA|RTA))
+ aic_outb(p, CLRPARERR, CLRINT);
+
+}
+#endif
+
/*+F*************************************************************************
* Function:
* aic7xxx_isr
*
* Description:
* SCSI controller interrupt handler.
- *
- * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- * be disabled all through this function unless we say otherwise.
*-F*************************************************************************/
static void
aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
{
struct aic7xxx_host *p;
unsigned char intstat;
- unsigned long flags;
- unsigned int interrupts_cleared = 0;
- p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+ p = (struct aic7xxx_host *)dev_id;
/*
- * Search for the host with a pending interrupt. If we can't find
- * one, then we've encountered a spurious interrupt.
+ * Just a few sanity checks. Make sure p != NULL, that we have an
+ * interrupt pending, and that we aren't already in our int handler.
+ * Also, if PCI, then we are going to check for a PCI bus error status
+ * should we get too many spurious interrupts.
*/
- while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
+ if (p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: ISR routine called with NULL dev_id\n");
+ return;
+ }
+ else if (!(aic_inb(p, INTSTAT) & INT_PEND))
{
- if (p->next == NULL)
+#ifdef CONFIG_PCI
+ if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500))
{
- p = NULL;
+ if ( aic_inb(p, ERROR) & PCIERRSTAT )
+ {
+ aic7xxx_pci_intr(p);
+ }
+ p->spurious_int = 0;
}
else
{
- p = (struct aic7xxx_host *) p->next->hostdata;
+ p->spurious_int++;
}
+#endif
+ return;
}
-
- if (p == NULL)
+ else if (p->flags & AHC_IN_ISR)
+ {
return;
+ }
/*
* Handle all the interrupt sources - especially for SCSI
* interrupts, we won't get a second chance at them.
*/
- intstat = inb(p->base + INTSTAT);
+ intstat = aic_inb(p, INTSTAT);
+ p->spurious_int = 0;
/*
* Keep track of interrupts for /proc/scsi
*/
p->isr_count++;
-
- if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
- {
- /*
- * We must only have one card at this IRQ and it must have been
- * added to the board data before the spurious interrupt occurred.
- * It is sufficient that we check isr_count and not the spurious
- * interrupt count.
- */
- printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
- if (intstat)
- {
- /* Try clearing all interrupts. */
- outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
- }
- return;
- }
-
- if (p->flags & IN_ISR)
- {
- panic(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
- p->host_no);
- return;
- }
+ p->flags |= AHC_IN_ISR;
/*
* Indicate that we're in the interrupt handler.
*/
- save_flags(flags);
- p->flags |= IN_ISR;
-
if (intstat & CMDCMPLT)
{
struct aic7xxx_scb *scb = NULL;
Scsi_Cmnd *cmd;
- unsigned char qoutcnt;
unsigned char scb_index;
- int i;
+ /*
+ * Clear interrupt status before running the completion loop.
+ * This eliminates a race condition whereby a command could
+ * complete between the last check of qoutfifo and the
+ * CLRCMDINT statement. This would result in us thinking the
+ * qoutfifo was empty when it wasn't, and in actuality be a lost
+ * completion interrupt. With multiple devices or tagged queueing
+ * this could be very bad if we caught all but the last completion
+ * and no more are imediately sent.
+ */
+ aic_outb(p, CLRCMDINT, CLRINT);
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
- cli();
- qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
-#if 1
- if (qoutcnt >= p->qfullcount - 1)
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) Command complete near Qfull count, "
- "qoutcnt = %d.\n", p->host_no, qoutcnt);
-#endif
- while (qoutcnt > 0)
+ while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
{
- if (p->flags & PAGE_ENABLED)
+ scb_index = p->qoutfifo[p->qoutfifonext];
+ p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
+ if ( scb_index >= p->scb_data->numscbs )
+ scb = NULL;
+ else
+ scb = p->scb_data->scb_array[scb_index];
+ if (scb == NULL)
{
- p->cmdoutcnt += qoutcnt;
- if ( p->cmdoutcnt >= p->qfullcount )
- {
- outb(0, p->base + CMDOUTCNT);
- p->cmdoutcnt = 0;
- }
+ printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
+ -1, -1, -1, scb_index);
+ continue;
}
- for (i = 0; i < qoutcnt; i++)
+ else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
- scb_index = inb(p->base + QOUTFIFO);
- if ( scb_index >= p->scb_data->numscbs )
- scb = NULL;
- else
- scb = p->scb_data->scb_array[scb_index];
- if (scb == NULL)
- {
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) CMDCMPLT with invalid SCB "
- "index %d, QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
- inb(p->base + QOUTCNT), inb(p->base + QINCNT));
- continue;
- }
- else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "(scsi%d:-1:-1:-1) CMDCMPLT without command for "
- "SCB %d, QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
- p->host_no, scb_index, inb(p->base + QOUTCNT),
- inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
- continue;
- }
- switch (status_byte(scb->hscb->target_status))
- {
- case QUEUE_FULL:
- case BUSY:
- scb->hscb->target_status = 0;
- scb->cmd->result = 0;
- aic7xxx_error(scb->cmd) = DID_OK;
- break;
- default:
- cmd = scb->cmd;
- if (scb->hscb->residual_SG_segment_count != 0)
- {
- aic7xxx_calculate_residual(p, scb);
- }
- cmd->result |= (aic7xxx_error(cmd) << 16);
- p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
- aic7xxx_done(p, scb);
- }
+ printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
+ "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
+ (unsigned long) scb->cmd);
+ continue;
}
- /*
- * Clear interrupt status before checking the output queue again.
- * This eliminates a race condition whereby a command could
- * complete between the queue poll and the interrupt clearing,
- * so notification of the command being complete never made it
- * back up to the kernel.
- */
- outb(CLRCMDINT, p->base + CLRINT);
- interrupts_cleared++;
- qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
- }
-
- if (interrupts_cleared == 0)
- {
- outb(CLRCMDINT, p->base + CLRINT);
+ switch (status_byte(scb->hscb->target_status))
+ {
+ case QUEUE_FULL:
+ case BUSY:
+ scb->hscb->target_status = 0;
+ scb->cmd->result = 0;
+ aic7xxx_error(scb->cmd) = DID_OK;
+ break;
+ default:
+ cmd = scb->cmd;
+ if (scb->hscb->residual_SG_segment_count != 0)
+ {
+ aic7xxx_calculate_residual(p, scb);
+ }
+ cmd->result |= (aic7xxx_error(cmd) << 16);
+ if (scb->tag_action)
+ p->dev_flags[TARGET_INDEX(cmd)] |=
+ DEVICE_TAGGED_SUCCESS | DEVICE_SUCCESS | DEVICE_PRESENT;
+ else
+ p->dev_flags[TARGET_INDEX(cmd)] |=
+ DEVICE_SUCCESS | DEVICE_PRESENT;
+ aic7xxx_done(p, scb);
+ break;
+ }
}
- restore_flags(flags);
}
if (intstat & BRKADRINT)
{
int i;
- unsigned char errno = inb(p->base + ERROR);
+ unsigned char errno = aic_inb(p, ERROR);
- printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+ printk(KERN_ERR "(scsi%d) BRKADRINT error(0x%x):\n", p->host_no, errno);
for (i = 0; i < NUMBER(hard_error); i++)
{
if (errno & hard_error[i].errno)
printk(KERN_ERR " %s\n", hard_error[i].errmesg);
}
}
- aic7xxx_reset_channel(p, 'A', TRUE);
- if ( p->bus_type == AIC_TWIN )
+ printk(KERN_ERR "(scsi%d) LINE=%d\n", p->host_no,
+ (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
+ aic7xxx_reset_channel(p, 0, TRUE);
+ if ( p->type & AHC_TWIN )
{
- aic7xxx_reset_channel(p, 'B', TRUE);
+ aic7xxx_reset_channel(p, 1, TRUE);
restart_sequencer(p);
- pause_sequencer(p);
}
- outb(CLRBRKADRINT, p->base + CLRINT);
+ aic_outb(p, CLRBRKADRINT, CLRINT);
}
if (intstat & SEQINT)
{
- cli();
aic7xxx_handle_seqint(p, intstat);
- restore_flags(flags);
}
if (intstat & SCSIINT)
{
- cli();
aic7xxx_handle_scsiint(p, intstat);
- restore_flags(flags);
}
-
- aic7xxx_done_cmds_complete(p);
- cli();
- aic7xxx_run_waiting_queues(p);
- unpause_sequencer(p, TRUE);
- p->flags &= ~IN_ISR;
- restore_flags(flags);
+ if(!(p->flags & (AHC_IN_ABORT | AHC_IN_RESET)))
+ {
+ aic7xxx_done_cmds_complete(p);
+ aic7xxx_run_waiting_queues(p);
+ }
+ p->flags &= ~AHC_IN_ISR;
}
+/*+F*************************************************************************
+ * Function:
+ * do_aic7xxx_isr
+ *
+ * Description:
+ * This is a gross hack to solve a problem in linux kernels 2.1.85 and
+ * above. Please, children, do not try this at home, and if you ever see
+ * anything like it, please inform the Gross Hack Police immediately
+ *-F*************************************************************************/
+static void
+do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long cpu_flags;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95)
+
+ spin_lock_irqsave(&io_request_lock, cpu_flags);
+ aic7xxx_isr(irq, dev_id, regs);
+ spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+#else
+ DRIVER_LOCK
+ aic7xxx_isr(irq, dev_id, regs);
+ DRIVER_UNLOCK
+#endif
+}
/*+F*************************************************************************
* Function:
static void
aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
{
- int default_depth = 2;
+ int default_depth = 3;
unsigned char tindex;
+ unsigned short target_mask;
tindex = device->id | (device->channel << 3);
+ target_mask = (1 << tindex);
device->queue_depth = default_depth;
-#ifdef AIC7XXX_TAGGED_QUEUEING
+ p->dev_mid_level_queue_depth[tindex] = 3;
+ p->dev_temp_queue_depth[tindex] = 1;
+ p->dev_max_queue_depth[tindex] = 1;
+ p->tagenable &= ~target_mask;
+
if (device->tagged_supported)
{
- unsigned short target_mask;
int tag_enabled = TRUE;
- target_mask = (1 << (device->id | (device->channel << 3)));
-
#ifdef AIC7XXX_CMDS_PER_LUN
default_depth = AIC7XXX_CMDS_PER_LUN;
#else
- if (p->scb_data->maxhscbs <= 4)
- {
- default_depth = 4; /* Not many SCBs to work with. */
- }
- else
- {
- default_depth = 8;
- }
+ default_depth = 8; /* Not many SCBs to work with. */
#endif
if (!(p->discenable & target_mask))
{
- printk(KERN_INFO "(scsi%d:%d:%d:%d) Disconnection disabled, unable to "
+ if (aic7xxx_verbose & VERBOSE_QUEUE)
+ printk(INFO_LEAD "Disconnection disabled, unable to "
"enable tagged queueing.\n",
p->host_no, device->channel, device->id, device->lun);
}
else
{
-#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
- device->queue_depth = default_depth;
-#else
if (p->instance >= NUMBER(aic7xxx_tag_info))
{
+ static int print_warning = TRUE;
+ if(print_warning)
+ {
+ printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
+ " installed controllers.\n");
+ printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
+ " the aic7xxx.c source file.\n");
+ print_warning = FALSE;
+ }
device->queue_depth = default_depth;
}
else
{
- if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
+ if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
{
tag_enabled = FALSE;
- device->queue_depth = 2; /* Tagged queueing is disabled. */
+ device->queue_depth = 3; /* Tagged queueing is disabled. */
}
else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
{
aic7xxx_tag_info[p->instance].tag_commands[tindex];
}
}
-#endif
if ((device->tagged_queue == 0) && tag_enabled)
{
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_QUEUE)
{
- printk(KERN_INFO "(scsi%d:%d:%d:%d) Enabled tagged queuing, "
- "queue depth %d.\n", p->host_no,
- device->channel, device->id, device->lun, device->queue_depth);
+ printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
+ p->host_no, device->channel, device->id,
+ device->lun, device->queue_depth);
}
- p->device_status[tindex].max_queue_depth = device->queue_depth;
- p->device_status[tindex].temp_queue_depth = device->queue_depth;
+ p->dev_max_queue_depth[tindex] = device->queue_depth;
+ p->dev_temp_queue_depth[tindex] = device->queue_depth;
+ p->dev_mid_level_queue_depth[tindex] = device->queue_depth;
+ p->tagenable |= target_mask;
+ p->orderedtag |= target_mask;
device->tagged_queue = 1;
device->current_tag = SCB_LIST_NULL;
}
}
}
-#endif
}
/*+F*************************************************************************
{
Scsi_Device *device;
struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+ int scbnum;
+ scbnum = 0;
for (device = scsi_devs; device != NULL; device = device->next)
{
if (device->host == host)
{
aic7xxx_device_queue_depth(p, device);
+ scbnum += device->queue_depth;
}
}
+ while (scbnum > p->scb_data->numscbs)
+ {
+ /*
+ * Pre-allocate the needed SCBs to get around the possibility of having
+ * to allocate some when memory is more or less exhausted and we need
+ * the SCB in order to perform a swap operation (possible deadlock)
+ */
+ if ( aic7xxx_allocate_scb(p, TRUE) == NULL )
+ return;
+ }
}
/*+F*************************************************************************
* The fourth byte's lowest bit seems to be an enabled/disabled
* flag (rest of the bits are reserved?).
*-F*************************************************************************/
-static aha_chip_type
-aic7xxx_probe(int slot, int base, aha_status_type *bios)
+static ahc_type
+aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
{
int i;
unsigned char buf[4];
static struct {
int n;
unsigned char signature[sizeof(buf)];
- aha_chip_type type;
+ ahc_type type;
int bios_disabled;
} AIC7xxx[] = {
- { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
- { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_7770, FALSE }, /* motherboard 7770 */
- { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x, FALSE }, /* 284x BIOS enabled */
- { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x, TRUE } /* 284x BIOS disabled */
+ { 4, { 0x04, 0x90, 0x77, 0x71 }, AHC_274, FALSE }, /* host adapter 274x */
+ { 4, { 0x04, 0x90, 0x77, 0x70 }, AHC_AIC7770, FALSE }, /* mb 7770 */
+ { 4, { 0x04, 0x90, 0x77, 0x56 }, AHC_284, FALSE }, /* 284x BIOS enabled */
+ { 4, { 0x04, 0x90, 0x77, 0x57 }, AHC_284, TRUE } /* 284x BIOS disabled */
};
/*
{
if (AIC7xxx[i].bios_disabled)
{
- *bios = AIC_DISABLED;
+ *flags |= AHC_USEDEFAULTS;
}
else
{
- *bios = AIC_ENABLED;
+ *flags |= AHC_BIOS_ENABLED;
}
- return (AIC7xxx[i].type);
+ return (AIC7xxx[i].type);
}
printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
}
}
- return (AIC_NONE);
+ return (AHC_NONE);
}
/*+F*************************************************************************
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \
- { \
- ; /* Do nothing */ \
- } \
- (void) inb(p->base + SEECTL_2840);
+ while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0) \
+ { \
+ ; /* Do nothing */ \
+ } \
+ (void) aic_inb(p, SEECTL_2840);
/*
* Read the first 32 registers of the seeprom. For the 2840,
/*
* Send chip select for one clock cycle.
*/
- outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+ aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
CLOCK_PULSE(p);
/*
for (i = 0; i < seeprom_read.len; i++)
{
temp = CS_2840 | seeprom_read.bits[i];
- outb(temp, p->base + SEECTL_2840);
+ aic_outb(p, temp, SEECTL_2840);
CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- outb(temp, p->base + SEECTL_2840);
+ aic_outb(p, temp, SEECTL_2840);
CLOCK_PULSE(p);
}
/*
temp = k;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = CS_2840 | temp;
- outb(temp, p->base + SEECTL_2840);
+ aic_outb(p, temp, SEECTL_2840);
CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- outb(temp, p->base + SEECTL_2840);
+ aic_outb(p, temp, SEECTL_2840);
CLOCK_PULSE(p);
}
for (i = 0; i <= 16; i++)
{
temp = CS_2840;
- outb(temp, p->base + SEECTL_2840);
+ aic_outb(p, temp, SEECTL_2840);
CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
- outb(temp, p->base + SEECTL_2840);
+ seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
+ aic_outb(p, temp, SEECTL_2840);
CLOCK_PULSE(p);
}
/*
/*
* Reset the chip select for the next command cycle.
*/
- outb(0, p->base + SEECTL_2840);
+ aic_outb(p, 0, SEECTL_2840);
CLOCK_PULSE(p);
- outb(CK_2840, p->base + SEECTL_2840);
+ aic_outb(p, CK_2840, SEECTL_2840);
CLOCK_PULSE(p);
- outb(0, p->base + SEECTL_2840);
+ aic_outb(p, 0, SEECTL_2840);
CLOCK_PULSE(p);
}
* is needed. Reason: after the 7870 chip reset, there
* should be no contention.
*/
- outb(SEEMS, p->base + SEECTL);
+ aic_outb(p, SEEMS, SEECTL);
wait = 1000; /* 1000 msec = 1 second */
- while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+ while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0))
{
wait--;
udelay(1000); /* 1 msec */
}
- if ((inb(p->base + SEECTL) & SEERDY) == 0)
+ if ((aic_inb(p, SEECTL) & SEERDY) == 0)
{
- outb(0, p->base + SEECTL);
+ aic_outb(p, 0, SEECTL);
return (0);
}
return (1);
static inline void
release_seeprom(struct aic7xxx_host *p)
{
- outb(0, p->base + SEECTL);
+ aic_outb(p, 0, SEECTL);
}
/*+F*************************************************************************
* this case, has no implied timing.
*-F*************************************************************************/
static int
-read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
- unsigned int len, seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset,
+ unsigned short *scarray, unsigned int len, seeprom_chip_type chip)
{
int i = 0, k;
unsigned char temp;
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(p->base + SEECTL) & SEERDY) == 0) \
- { \
- ; /* Do nothing */ \
+ while ((aic_inb(p, SEECTL) & SEERDY) == 0) \
+ { \
+ ; /* Do nothing */ \
}
/*
/*
* Send chip select for one clock cycle.
*/
- outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+ aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
CLOCK_PULSE(p);
/*
for (i = 0; i < seeprom_read.len; i++)
{
temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
- outb(temp, p->base + SEECTL);
+ aic_outb(p, temp, SEECTL);
CLOCK_PULSE(p);
temp = temp ^ SEECK;
- outb(temp, p->base + SEECTL);
+ aic_outb(p, temp, SEECTL);
CLOCK_PULSE(p);
}
/*
temp = k + offset;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = SEEMS | SEECS | (temp << 1);
- outb(temp, p->base + SEECTL);
+ aic_outb(p, temp, SEECTL);
CLOCK_PULSE(p);
temp = temp ^ SEECK;
- outb(temp, p->base + SEECTL);
+ aic_outb(p, temp, SEECTL);
CLOCK_PULSE(p);
}
for (i = 0; i <= 16; i++)
{
temp = SEEMS | SEECS;
- outb(temp, p->base + SEECTL);
+ aic_outb(p, temp, SEECTL);
CLOCK_PULSE(p);
temp = temp ^ SEECK;
- scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
- outb(temp, p->base + SEECTL);
+ scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
+ aic_outb(p, temp, SEECTL);
CLOCK_PULSE(p);
}
/*
* Reset the chip select for the next command cycle.
*/
- outb(SEEMS, p->base + SEECTL);
+ aic_outb(p, SEEMS, SEECTL);
CLOCK_PULSE(p);
- outb(SEEMS | SEECK, p->base + SEECTL);
+ aic_outb(p, SEEMS | SEECK, SEECTL);
CLOCK_PULSE(p);
- outb(SEEMS, p->base + SEECTL);
+ aic_outb(p, SEEMS, SEECTL);
CLOCK_PULSE(p);
}
}
printk("\n");
#endif
-
if (checksum != scarray[len - 1])
{
return (0);
unsigned char brdctl;
brdctl = BRDCS | BRDSTB;
- outb(brdctl, p->base + BRDCTL);
+ aic_outb(p, brdctl, BRDCTL);
brdctl |= value;
- outb(brdctl, p->base + BRDCTL);
+ aic_outb(p, brdctl, BRDCTL);
brdctl &= ~BRDSTB;
- outb(brdctl, p->base + BRDCTL);
+ aic_outb(p, brdctl, BRDCTL);
brdctl &= ~BRDCS;
- outb(brdctl, p->base + BRDCTL);
+ aic_outb(p, brdctl, BRDCTL);
}
/*+F*************************************************************************
* Description:
* Reads the BRDCTL register.
*-F*************************************************************************/
-static inline unsigned char
-read_brdctl(struct aic7xxx_host *p)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
+{
+ aic_outb(p, BRDRW | BRDCS, BRDCTL);
+ return (aic_inb(p, BRDCTL));
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic785x_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on aic785x class controller chips
+ *-F*************************************************************************/
+static void
+aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
+ int *ext_present, int *eeprom)
+{
+ unsigned char brdctl;
+
+ aic_outb(p, BRDRW | BRDCS, BRDCTL);
+ aic_outb(p, 0, BRDCTL);
+ brdctl = aic_inb(p, BRDCTL);
+ *int_50 = !(brdctl & BRDDAT5);
+ *ext_present = !(brdctl & BRDDAT6);
+ *eeprom = ( aic_inb(p, SPIOCAP) & EEPROM ) != 0;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic787x_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on aic787x class controller chips
+ *
+ * NOTE: This functions assumes the SEEPROM will have already been aquired
+ * prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
+ int *ext_present, int *eeprom)
{
- outb(BRDRW | BRDCS, p->base + BRDCTL);
- return (inb(p->base + BRDCTL));
+ unsigned char brdctl;
+
+ /*
+ * First read the status of our cables. Set the rom bank to
+ * 0 since the bank setting serves as a multiplexor for the
+ * cable detection logic. BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(p, 0);
+
+ /*
+ * Now we read the state of the two internal connectors. BRDDAT6
+ * is internal 50, BRDDAT7 is internal 68. For each, the cable is
+ * present if the bit is 0
+ */
+ brdctl = read_brdctl(p);
+ *int_50 = !(brdctl & BRDDAT6);
+ *int_68 = !(brdctl & BRDDAT7);
+
+ /*
+ * Set the bank bit in brdctl and then read the external cable state
+ * and the EEPROM status
+ */
+ write_brdctl(p, BRDDAT5);
+ brdctl = read_brdctl(p);
+
+ *ext_present = !(brdctl & BRDDAT6);
+ *eeprom = (brdctl & BRDDAT7);
+
+ /*
+ * We're done, the calling function will release the SEEPROM for us
+ */
+
}
/*+F*************************************************************************
configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
unsigned short adapter_control, unsigned char max_targ)
{
- unsigned char brdctl_int, brdctl_ext;
int internal50_present;
int internal68_present = 0;
int external_present = 0;
int eprom_present;
int high_on;
int low_on;
- int old_verbose;
if (acquire_seeprom(p))
{
if (adapter_control & CFAUTOTERM)
{
- old_verbose = aic7xxx_verbose;
- printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please "
- "verify driver\n");
- printk(KERN_INFO " detected settings and use manual termination "
- "if necessary.\n");
-
+ printk(KERN_INFO "aic7xxx: Warning - detected auto-termination on "
+ "controller:\n");
+ printk(KERN_INFO "aic7xxx: <%s> at ", board_names[p->board_name_index]);
+ switch(p->type & 0x1ff1)
+ {
+ case AHC_AIC7770:
+ case AHC_274:
+ printk("EISA slot %d\n", p->pci_device_fn);
+ break;
+ case AHC_284:
+ printk("VLB slot %d\n", p->pci_device_fn);
+ break;
+ default:
+ printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
+ PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+ printk(KERN_INFO "aic7xxx: Please verify driver detected settings are "
+ "correct.\n");
+ printk(KERN_INFO "aic7xxx: If not, then please properly set the device "
+ "termination\n");
+ printk(KERN_INFO "aic7xxx: in the Adaptec SCSI BIOS by hitting CTRL-A "
+ "when prompted\n");
+ printk(KERN_INFO "aic7xxx: during machine bootup.\n");
/* Configure auto termination. */
- outb(SEECS | SEEMS, p->base + SEECTL);
-
- /*
- * First read the status of our cables. Set the rom bank to
- * 0 since the bank setting serves as a multiplexor for the
- * cable detection logic. BRDDAT5 controls the bank switch.
- */
- write_brdctl(p, 0);
+ aic_outb(p, SEECS | SEEMS, SEECTL);
- /*
- * Now read the state of the internal connectors. The
- * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
- * set when cables are not present (BRDDAT6 is INT50 and
- * BRDDAT7 is INT68).
- */
- brdctl_int = read_brdctl(p);
- internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+ if ( (p->type & AHC_AIC7860) == AHC_AIC7860 )
+ {
+ aic785x_cable_detect(p, &internal50_present, &external_present,
+ &eprom_present);
+ }
+ else
+ {
+ aic787x_cable_detect(p, &internal50_present, &internal68_present,
+ &external_present, &eprom_present);
+ }
if (max_targ > 8)
{
- internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
+ printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+ "Ext-68 %s)\n",
+ internal50_present ? "YES" : "NO",
+ internal68_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
}
-
- /*
- * Set the rom bank to 1 and determine
- * the other signals.
- */
- write_brdctl(p, BRDDAT5);
-
- /*
- * Now read the state of the external connectors. BRDDAT6 is
- * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
- * set when the eprom is present.
- */
- brdctl_ext = read_brdctl(p);
- external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
- eprom_present = brdctl_ext & BRDDAT7;
- if (aic7xxx_verbose)
+ else
{
- if (max_targ > 8)
- {
- printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
- "Ext-68 %s)\n",
- internal50_present ? "YES" : "NO",
- internal68_present ? "YES" : "NO",
- external_present ? "YES" : "NO");
- }
- else
- {
- printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
- internal50_present ? "YES" : "NO",
- external_present ? "YES" : "NO");
- }
- printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
- "brdctl_ext=0x%x\n",
- eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
+ printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+ internal50_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
+ internal68_present = 0;
}
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "aic7xxx: EEPROM %s present.\n",
+ eprom_present ? "is" : "is not");
/*
* Now set the termination based on what we found. BRDDAT6
high_on = TRUE;
}
- if ((internal50_present + internal68_present + external_present) <= 1)
+ if ( ( (internal50_present ? 1 : 0) +
+ (internal68_present ? 1 : 0) +
+ (external_present ? 1 : 0) ) <= 1)
{
low_on = TRUE;
}
if (internal50_present && internal68_present && external_present)
{
- printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
- " Only two connectors on the adapter may be "
- "used at a time!\n");
+ printk(KERN_INFO "aic7xxx: Illegal cable configuration!! Only two\n");
+ printk(KERN_INFO "aic7xxx: connectors on the SCSI controller may be "
+ "in use at a time!\n");
}
if (high_on == TRUE)
if (low_on == TRUE)
*sxfrctl1 |= STPWEN;
- if (aic7xxx_verbose)
+ if (max_targ > 8)
{
- if (max_targ > 8)
- {
- printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
- low_on ? "ON" : "OFF",
- high_on ? "ON" : "OFF");
- }
- else
- {
- printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
- }
+ printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+ low_on ? "ON" : "OFF", high_on ? "ON" : "OFF");
+ }
+ else
+ {
+ printk(KERN_INFO "aic7xxx: Termination %s\n",
+ low_on ? "Enabled" : "Disabled");
}
- aic7xxx_verbose = old_verbose;
}
else
{
if (adapter_control & CFSTERM)
- {
*sxfrctl1 |= STPWEN;
- }
- outb(SEEMS | SEECS, p->base + SEECTL);
+
+ aic_outb(p, SEEMS | SEECS, SEECTL);
/*
* Configure high byte termination.
*/
{
write_brdctl(p, 0);
}
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
{
printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
(adapter_control & CFSTERM) ? "ON" : "OFF",
detect_maxscb(struct aic7xxx_host *p)
{
int i;
- unsigned char max_scbid = 255;
/*
* It's possible that we've already done this for multichannel
* We haven't initialized the SCB settings yet. Walk the SCBs to
* determince how many there are.
*/
- outb(0, p->base + FREE_SCBH);
+ aic_outb(p, 0, FREE_SCBH);
for (i = 0; i < AIC7XXX_MAXSCB; i++)
{
- outb(i, p->base + SCBPTR);
- outb(i, p->base + SCB_CONTROL);
- if (inb(p->base + SCB_CONTROL) != i)
+ aic_outb(p, i, SCBPTR);
+ aic_outb(p, i, SCB_CONTROL);
+ if (aic_inb(p, SCB_CONTROL) != i)
break;
- outb(0, p->base + SCBPTR);
- if (inb(p->base + SCB_CONTROL) != 0)
+ aic_outb(p, 0, SCBPTR);
+ if (aic_inb(p, SCB_CONTROL) != 0)
break;
- outb(i, p->base + SCBPTR);
- outb(0, p->base + SCB_CONTROL); /* Clear the control byte. */
- outb(i + 1, p->base + SCB_NEXT); /* Set the next pointer. */
- outb(SCB_LIST_NULL, p->base + SCB_TAG); /* Make the tag invalid. */
-
- /* Make the non-tagged targets not busy. */
- outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
- outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
- outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
- outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+ aic_outb(p, i, SCBPTR);
+ aic_outb(p, 0, SCB_CONTROL); /* Clear the control byte. */
+ aic_outb(p, i + 1, SCB_NEXT); /* Set the next pointer. */
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG); /* Make the tag invalid. */
}
/* Make sure the last SCB terminates the free list. */
- outb(i - 1, p->base + SCBPTR);
- outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+ aic_outb(p, i - 1, SCBPTR);
+ aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
/* Ensure we clear the first (0) SCBs control byte. */
- outb(0, p->base + SCBPTR);
- outb(0, p->base + SCB_CONTROL);
+ aic_outb(p, 0, SCBPTR);
+ aic_outb(p, 0, SCB_CONTROL);
p->scb_data->maxhscbs = i;
}
- if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
- {
- /* Determine the number of valid bits in the FIFOs. */
- outb(max_scbid, p->base + QINFIFO);
- max_scbid = inb(p->base + QINFIFO);
- p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
- }
- else
- {
- p->scb_data->maxscbs = p->scb_data->maxhscbs;
- }
- if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
- {
- /*
- * Disable paging if the QINFIFO doesn't allow more SCBs than
- * we have in hardware.
- */
- p->flags &= ~PAGE_ENABLED;
- }
-
- /*
- * Set the Queue Full Count. Some cards have more queue space than
- * SCBs.
- */
- switch (p->chip_class)
- {
- case AIC_777x:
- p->qfullcount = 4;
- p->qcntmask = 0x07;
- break;
- case AIC_785x:
- case AIC_786x:
- p->qfullcount = 8;
- p->qcntmask = 0x0f;
- break;
- case AIC_787x:
- case AIC_788x:
- if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
- {
- p->qfullcount = AIC7XXX_MAXSCB;
- p->qcntmask = 0xFF;
- }
- else
- {
- p->qfullcount = 16;
- p->qcntmask = 0x1F;
- }
- break;
- }
}
/*+F*************************************************************************
* Register a Adaptec aic7xxx chip SCSI controller with the kernel.
*-F*************************************************************************/
static int
-aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
+ int reset_delay)
{
- int i;
- unsigned char sblkctl, flags = 0;
+ int i, result;
int max_targets;
int found = 1;
- char channel_ids[] = {'A', 'B', 'C'};
unsigned char target_settings;
- unsigned char scsi_conf, sxfrctl1;
+ unsigned char term, scsi_conf, sxfrctl1;
unsigned short ultraenable = 0;
struct Scsi_Host *host;
*/
request_region(p->base, MAXREG - MINREG, "aic7xxx");
- /*
- * Read the bus type from the SBLKCTL register. Set the FLAGS
- * register in the sequencer for twin and wide bus cards.
- */
- sblkctl = inb(p->base + SBLKCTL);
- if (p->flags & PAGE_ENABLED)
- flags = PAGESCBS;
-
- switch (sblkctl & SELBUS_MASK)
- {
- case SELNARROW: /* narrow/normal bus */
- p->scsi_id = inb(p->base + SCSICONF) & 0x07;
- p->bus_type = AIC_SINGLE;
- p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
- if (p->flags & MULTI_CHANNEL)
- {
- printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
- channel_ids[p->chan_num], p->scsi_id);
- }
- else
- {
- printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
- p->scsi_id);
- }
- outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
- break;
-
- case SELWIDE: /* Wide bus */
- p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
- p->bus_type = AIC_WIDE;
- p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
- if (p->flags & MULTI_CHANNEL)
- {
- printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
- channel_ids[p->chan_num], p->scsi_id);
- }
- else
- {
- printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
- p->scsi_id);
- }
- outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
- break;
-
- case SELBUSB: /* Twin bus */
- p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
- p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
- p->bus_type = AIC_TWIN;
- printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
- p->scsi_id, p->scsi_id_b);
- outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
- break;
-
- default:
- printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
- "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
- outb(0, p->base + SEQ_FLAGS);
- return (0);
- }
-
- /*
- * Detect SCB parameters and initialize the SCB array.
- */
- detect_maxscb(p);
- printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
- p->scb_data->maxhscbs, p->scb_data->maxscbs,
- p->qfullcount, p->qcntmask);
host = p->host;
- host->can_queue = p->scb_data->maxscbs;
- host->cmd_per_lun = 2;
+ p->scb_data->maxscbs = AIC7XXX_MAXSCB;
+ host->can_queue = AIC7XXX_MAXSCB;
+ host->cmd_per_lun = 3;
host->sg_tablesize = AIC7XXX_MAX_SG;
host->select_queue_depths = aic7xxx_select_queue_depth;
host->this_id = p->scsi_id;
host->n_io_port = 0xFF;
host->base = (unsigned char *) p->mbase;
host->irq = p->irq;
- if (p->bus_type == AIC_WIDE)
+ if (p->type & AHC_WIDE)
{
host->max_id = 16;
}
- if (p->bus_type == AIC_TWIN)
+ if (p->type & AHC_TWIN)
{
host->max_channel = 1;
}
scbq_init(&p->scb_data->free_scbs);
scbq_init(&p->waiting_scbs);
- for (i = 0; i < NUMBER(p->device_status); i++)
- {
- p->device_status[i].commands_sent = 0;
- p->device_status[i].flags = 0;
- p->device_status[i].active_cmds = 0;
- p->device_status[i].last_reset = 0;
- p->device_status[i].last_queue_full = 0;
- p->device_status[i].last_queue_full_count = 0;
- p->device_status[i].max_queue_depth = 2;
- p->device_status[i].temp_queue_depth = 2;
- scbq_init(&p->device_status[i].delayed_scbs);
- init_timer(&p->device_status[i].timer);
- p->device_status[i].timer.expires = 0;
- p->device_status[i].timer.data = (unsigned long)p;
- p->device_status[i].timer.function = (void *)aic7xxx_timer;
- }
- if (aic7xxx_boards[p->irq] == NULL)
- {
- int result;
- int irq_flags = 0;
-
-#ifdef AIC7XXX_OLD_ISR_TYPE
- irq_flags = SA_INTERRUPT;
-#endif
- /*
- * Warning! This must be done before requesting the irq. It is
- * possible for some boards to raise an interrupt as soon as
- * they are enabled. So when we request the irq from the Linux
- * kernel, an interrupt is triggered immediately. Therefore, we
- * must ensure the board data is correctly set before the request.
- */
- aic7xxx_boards[p->irq] = host;
+ for (i = 0; i < NUMBER(p->untagged_scbs); i++)
+ {
+ p->untagged_scbs[i] = SCB_LIST_NULL;
+ p->qinfifo[i] = SCB_LIST_NULL;
+ p->qoutfifo[i] = SCB_LIST_NULL;
+ }
+ /*
+ * We currently have no commands of any type
+ */
+ p->qinfifonext = 0;
+ p->qoutfifonext = 0;
+
+ for (i = 0; i < MAX_TARGETS; i++)
+ {
+ p->dev_commands_sent[i] = 0;
+ p->dev_flags[i] = DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+ p->dev_active_cmds[i] = 0;
+ p->dev_last_reset[i] = 0;
+ p->dev_last_queue_full[i] = 0;
+ p->dev_last_queue_full_count[i] = 0;
+ p->dev_max_queue_depth[i] = 1;
+ p->dev_temp_queue_depth[i] = 1;
+ p->dev_mid_level_queue_depth[i] = 3;
+ scbq_init(&p->delayed_scbs[i]);
+ init_timer(&p->dev_timer[i]);
+ p->dev_timer[i].expires = 0;
+ p->dev_timer[i].data = (unsigned long)p;
+ p->dev_timer[i].function = (void *)aic7xxx_timer;
+ p->syncinfo[i].period = 0;
+ p->syncinfo[i].offset = 0;
+ }
+
+ printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
+ board_names[p->board_name_index]);
+ switch(p->type & 0x1ff1)
+ {
+ case AHC_AIC7770:
+ case AHC_274:
+ printk("EISA slot %d\n", p->pci_device_fn);
+ break;
+ case AHC_284:
+ printk("VLB slot %d\n", p->pci_device_fn);
+ break;
+ default:
+ printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
+ PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+ if (p->type & AHC_TWIN)
+ {
+ printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+ p->host_no, p->scsi_id, p->scsi_id_b);
+ }
+ else
+ {
+ char *channel;
- /*
- * Register IRQ with the kernel. Only allow sharing IRQs with
- * PCI devices.
- */
- if (p->chip_class == AIC_777x)
+ channel = "";
+
+ if ((p->type & AHC_39x) != 0)
+ {
+ channel = " A";
+
+ if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
+ {
+ channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+ }
+ }
+ if (p->type & AHC_WIDE)
{
- result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+ printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
}
else
{
- result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
- "aic7xxx", NULL));
- if (result < 0)
- {
- irq_flags = (irq_flags & SA_INTERRUPT) ? 0 : SA_INTERRUPT;
- result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
- "aic7xxx", NULL));
- }
+ printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
}
+ printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
+ }
+ aic_outb(p, 0, SEQ_FLAGS);
+
+ /*
+ * Detect SCB parameters and initialize the SCB array.
+ */
+ detect_maxscb(p);
+ printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
+ printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+ p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+ p->base, p->irq);
+ printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
+ p->host_no, p->mbase, (unsigned long)p->maddr);
+
+
+
+ /*
+ * Register IRQ with the kernel. Only allow sharing IRQs with
+ * PCI devices.
+ */
+ if ((p->type & AHC_AIC7770) == AHC_AIC7770)
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+ }
+ else
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+ "aic7xxx", p));
if (result < 0)
{
- printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
- p->irq);
- aic7xxx_boards[p->irq] = NULL;
- return (0);
+ result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+ "aic7xxx", p));
}
}
- else
+ if (result < 0)
{
- /*
- * We have found a host adapter sharing an IRQ of a previously
- * registered host adapter. Add this host adapter's Scsi_Host
- * to the beginning of the linked list of hosts at the same IRQ.
- */
- p->next = aic7xxx_boards[p->irq];
- aic7xxx_boards[p->irq] = host;
+ printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring.\n",
+ p->host_no, p->irq);
+ return (0);
}
/*
* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
*/
- if (p->bus_type == AIC_TWIN)
+ if (p->type & AHC_TWIN)
{
/*
* The controller is gated to channel B after a chip reset; set
* bus B values first.
*/
- outb(p->scsi_id_b, p->base + SCSIID);
- scsi_conf = inb(p->base + SCSICONF + 1);
- sxfrctl1 = inb(p->base + SXFRCTL1);
- outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) |
- ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
- outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
- if (p->flags & ULTRA_ENABLED)
+ term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0;
+ aic_outb(p, p->scsi_id_b, SCSIID);
+ scsi_conf = aic_inb(p, SCSICONF + 1);
+ sxfrctl1 = aic_inb(p, SXFRCTL1);
+ aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term |
+ ENSTIMER | ACTNEGEN, SXFRCTL1);
+ aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+ if (p->type & AHC_ULTRA)
{
- outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
+ aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
}
else
{
- outb(DFON | SPIOEN, p->base + SXFRCTL0);
+ aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+ }
+ if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+ {
+ scsi_conf &= ~0x07;
+ scsi_conf |= p->scsi_id_b;
+ aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1);
}
-
if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
{
/* Reset SCSI bus B. */
- if (aic7xxx_verbose)
- printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+ if (aic7xxx_verbose & VERBOSE_PROBE)
+ printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
aic7xxx_reset_current_bus(p);
}
/* Select channel A */
- outb(SELNARROW, p->base + SBLKCTL);
+ aic_outb(p, SELNARROW, SBLKCTL);
}
- outb(p->scsi_id, p->base + SCSIID);
- scsi_conf = inb(p->base + SCSICONF);
- sxfrctl1 = inb(p->base + SXFRCTL1);
- outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) |
- ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
- outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
- if (p->flags & ULTRA_ENABLED)
+ term = ((p->flags & AHC_TERM_ENB_A) != 0) ? STPWEN : 0;
+ aic_outb(p, p->scsi_id, SCSIID);
+ scsi_conf = aic_inb(p, SCSICONF);
+ sxfrctl1 = aic_inb(p, SXFRCTL1);
+ aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term |
+ ENSTIMER | ACTNEGEN, SXFRCTL1);
+ aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+ if (p->type & AHC_ULTRA)
{
- outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
+ aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
}
else
{
- outb(DFON | SPIOEN, p->base + SXFRCTL0);
+ aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+ }
+ if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+ {
+ scsi_conf &= ~0x07;
+ scsi_conf |= p->scsi_id;
+ aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF);
}
+
if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
{
/* Reset SCSI bus A. */
- if (aic7xxx_verbose)
- printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+ if (aic7xxx_verbose & VERBOSE_PROBE)
+ { /* In case we are a 3940, 3985, or 7895, print the right channel */
+ char *channel = "";
+ if (p->flags & AHC_MULTI_CHANNEL)
+ {
+ channel = " A";
+ if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+ channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+ }
+ printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+ }
aic7xxx_reset_current_bus(p);
/*
* Delay for the reset delay.
*/
- aic7xxx_delay(AIC7XXX_RESET_DELAY);
+ if (!reset_delay)
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
}
/*
p->sdtr_pending = 0x0;
p->needwdtr_copy = 0x0;
p->wdtr_pending = 0x0;
- if (p->bus_type == AIC_SINGLE)
+ p->tagenable = 0x0;
+ p->ultraenb = 0x0;
+ p->discenable = 0xffff;
+ if ((p->type & (AHC_TWIN|AHC_WIDE)) == 0)
{
max_targets = 8;
}
/*
* Grab the disconnection disable table and invert it for our needs
*/
- if (p->flags & USE_DEFAULTS)
+ if (p->flags & AHC_USEDEFAULTS)
{
- printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
- "device parameters.\n");
- p->discenable = 0xFFFF;
+ printk(KERN_INFO "(scsi%d) Host adapter BIOS disabled. Using default SCSI "
+ "device parameters.\n", p->host_no);
+ if (p->type & AHC_ULTRA)
+ p->ultraenb = 0xffff;
+ p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
}
else
{
- p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
- inb(p->base + DISC_DSB));
+ p->discenable = ~((aic_inb(p, DISC_DSB + 1) << 8) |
+ aic_inb(p, DISC_DSB));
+ if (p->type & AHC_ULTRA)
+ p->ultraenb = (aic_inb(p, ULTRA_ENB + 1) << 8) |
+ aic_inb(p, ULTRA_ENB);
}
for (i = 0; i < max_targets; i++)
{
- if (p->flags & USE_DEFAULTS)
+ if (p->flags & AHC_USEDEFAULTS)
{
target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */
p->needsdtr_copy |= (0x01 << i);
p->needwdtr_copy |= (0x01 << i);
- if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ if (p->type & AHC_ULTRA)
ultraenable |= (0x01 << i);
}
else
{
- target_settings = inb(p->base + TARG_SCRATCH + i);
+ target_settings = aic_inb(p, TARG_SCRATCH + i);
if (target_settings & 0x0F)
{
p->needsdtr_copy |= (0x01 << i);
*/
target_settings &= 0x7F;
}
- if (p->flags & ULTRA_ENABLED)
+ }
+
+ aic_outb(p, target_settings, TARG_SCRATCH + i);
+ if (p->needsdtr_copy & (0x01 << i))
+ {
+ short sxfr, j;
+
+ sxfr = target_settings & SXFR;
+ if ((p->ultraenb & (1 << i)) != 0)
{
- switch (target_settings & 0x70)
- {
- case 0x00:
- case 0x10:
- case 0x20:
- ultraenable |= (0x01 << i);
- break;
- case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */
- target_settings &= ~(0x70);
- break;
- default:
- break;
- }
+ /* Want an ultra speed in the table */
+ sxfr |= 0x100;
+ }
+ for (j = 0; j < NUMBER(aic7xxx_syncrates); j++)
+ {
+ if (sxfr == aic7xxx_syncrates[j].rate)
+ break;
}
+ p->syncinfo[i].period = aic7xxx_syncrates[j].period;
+ p->syncinfo[i].offset =
+ (p->type & AHC_WIDE) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT;
+ }
+ else
+ {
+ p->syncinfo[i].period = 0;
+ p->syncinfo[i].offset = 0;
}
- outb(target_settings, p->base + TARG_SCRATCH + i);
}
/*
* work on some cards that don't leave these fields cleared
* when BIOS is not installed.
*/
- if (p->bus_type != AIC_WIDE)
+ if ( !(p->type & AHC_WIDE))
{
p->needwdtr_copy = 0;
}
p->needsdtr = p->needsdtr_copy;
p->needwdtr = p->needwdtr_copy;
- p->orderedtag = 0;
- outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
- outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
-
- /*
- * Set the number of available hardware SCBs.
- */
- outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
-
- /*
- * 2s compliment of maximum tag value.
- */
- i = p->scb_data->maxscbs;
- outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
+ aic_outb(p, 0, ULTRA_ENB);
+ aic_outb(p, 0, ULTRA_ENB + 1);
/*
* Allocate enough hardware scbs to handle the maximum number of
p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
if (p->scb_data->hscbs == NULL)
{
- printk("aic7xxx: Unable to allocate hardware SCB array; "
- "failing detection.\n");
+ printk("(scsi%d) Unable to allocate hardware SCB array; "
+ "failing detection.\n", p->host_no);
release_region(p->base, MAXREG - MINREG);
- /*
- * Ensure that we only free the IRQ when there is _not_ another
- * aic7xxx adapter sharing this IRQ. The adapters are always
- * added to the beginning of the list, so we can grab the next
- * pointer and place it back in the board array.
- */
- if (p->next == NULL)
- {
- free_irq(p->irq, aic7xxx_isr);
- }
- aic7xxx_boards[p->irq] = p->next;
+ free_irq(p->irq, p);
return(0);
}
/* At least the control byte of each SCB needs to be 0. */
/* Tell the sequencer where it can find the hardware SCB array. */
hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
- outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
- outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
- outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
- outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
- }
+ aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
+ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
+ aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
+ aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
- /*
- * QCount mask to deal with broken aic7850s that sporadically get
- * garbage in the upper bits of their QCNT registers.
- */
- outb(p->qcntmask, p->base + QCNTMASK);
+ /* Set up the fifo areas at the same time */
+ hscb_physaddr = VIRT_TO_BUS(&p->untagged_scbs[0]);
+ aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
+ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
+ aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
+ aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+
+ /* The Q-FIFOs we just set up are all empty */
+ aic_outb(p, 0, QINPOS);
+ aic_outb(p, 0, KERNEL_QINPOS);
+ aic_outb(p, 0, QOUTPOS);
- /*
- * Set FIFO depth and command out count. These are only used when
- * paging is enabled and should not be touched for AIC-7770 based
- * adapters; FIFODEPTH and CMDOUTCNT overlay SCSICONF and SCSICONF+1
- * which are used to control termination.
- */
- if (p->flags & PAGE_ENABLED)
- {
- outb(p->qfullcount, p->base + FIFODEPTH);
- outb(0, p->base + CMDOUTCNT);
- p->cmdoutcnt = 0;
}
/*
* We don't have any waiting selections or disconnected SCBs.
*/
- outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
- outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
/*
* Message out buffer starts empty
*/
- outb(0, p->base + MSG_LEN);
+ aic_outb(p, MSG_NOOP, MSG_OUT);
/*
* Load the sequencer program, then re-enable the board -
*/
aic7xxx_loadseq(p);
- if (p->chip_class == AIC_777x)
+ if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
{
- outb(ENABLE, p->base + BCTL); /* Enable the boards BUS drivers. */
+ aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */
}
+ /*
+ * Link us into the list of valid hosts
+ */
+ p->next = first_aic7xxx;
+ first_aic7xxx = p;
+
/*
* Unpause the sequencer before returning and enable
* interrupts - we shouldn't get any until the first
* Perform a chip reset on the aic7xxx SCSI controller. The controller
* is paused upon return.
*-F*************************************************************************/
-static void
+int
aic7xxx_chip_reset(struct aic7xxx_host *p)
{
unsigned char hcntrl;
int wait;
/* Retain the IRQ type across the chip reset. */
- hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+ hcntrl = (aic_inb(p, HCNTRL) & IRQMS) | INTEN;
/*
* For some 274x boards, we must clear the CHIPRST bit and pause
* the sequencer. For some reason, this makes the driver work.
*/
- outb(PAUSE | CHIPRST, p->base + HCNTRL);
+ aic_outb(p, PAUSE | CHIPRST, HCNTRL);
/*
* In the future, we may call this function as a last resort for
* error handling. Let's be nice and not do any unecessary delays.
*/
wait = 1000; /* 1 second (1000 * 1000 usec) */
- while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+ while ((wait > 0) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0))
{
udelay(1000); /* 1 msec = 1000 usec */
wait = wait - 1;
}
- if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+ if ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)
{
printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
}
- outb(hcntrl | PAUSE, p->base + HCNTRL);
+ aic_outb(p, hcntrl | PAUSE, HCNTRL);
+
+ switch( aic_inb(p, SBLKCTL) & 0x0a )
+ {
+ case 0: /* normal narrow card */
+ break;
+ case 2: /* Wide card */
+ p->type |= AHC_WIDE;
+ break;
+ case 8: /* Twin card */
+ p->type |= AHC_TWIN;
+ p->flags |= AHC_MULTI_CHANNEL;
+ break;
+ default: /* hmmm...we don't know what this is */
+ printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
+ aic_inb(p, SBLKCTL) & 0x0a);
+ return(-1);
+ }
+ return(0);
}
/*+F*************************************************************************
* and a pointer to a aic7xxx_host struct upon success.
*-F*************************************************************************/
static struct aic7xxx_host *
-aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
- aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp)
{
struct aic7xxx_host *p = NULL;
struct Scsi_Host *host;
memset(p, 0, sizeof(struct aic7xxx_host));
p->host = host;
- if (scb_data != NULL)
+ p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+ if (p->scb_data != NULL)
{
- /*
- * We are sharing SCB data areas; use the SCB data pointer
- * provided.
- */
- p->scb_data = scb_data;
- p->flags |= SHARED_SCBDATA;
+ memset(p->scb_data, 0, sizeof(scb_data_type));
+ scbq_init (&p->scb_data->free_scbs);
}
else
{
/*
- * We are not sharing SCB data; allocate one.
+ * For some reason we don't have enough memory. Free the
+ * allocated memory for the aic7xxx_host struct, and return NULL.
*/
- p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
- if (p->scb_data != NULL)
- {
- memset(p->scb_data, 0, sizeof(scb_data_type));
- scbq_init (&p->scb_data->free_scbs);
- }
- else
- {
- /*
- * For some reason we don't have enough memory. Free the
- * allocated memory for the aic7xxx_host struct, and return NULL.
- */
- scsi_unregister(host);
- p = NULL;
- }
+ scsi_unregister(host);
+ p = NULL;
}
if (p != NULL)
{
p->host_no = host->host_no;
- p->base = base;
- p->mbase = mbase;
- p->maddr = NULL;
- p->flags = flags;
- p->chip_type = chip_type;
- p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
- p->pause = p->unpause | PAUSE;
+ p->base = temp->base;
+ p->mbase = temp->mbase;
+ p->maddr = temp->maddr;
+ p->flags = temp->flags;
+ p->type = temp->type;
+ p->unpause = temp->unpause;
+ p->pause = temp->pause;
+ p->pci_bus = temp->pci_bus;
+ p->pci_device_fn = temp->pci_device_fn;
+ p->bios_address = temp->bios_address;
+ p->irq = temp->irq;
+ p->scsi_id = temp->scsi_id;
+ p->scsi_id_b = temp->scsi_id_b;
+ p->discenable = temp->discenable;
+ p->ultraenb = temp->ultraenb;
+ p->tagenable = 0;
+ p->orderedtag = 0;
+ p->board_name_index = temp->board_name_index;
+ p->adapter_control = temp->adapter_control;
+ p->bios_control = temp->bios_control;
+ DRIVER_LOCK_INIT
}
}
return (p);
* the driver (struct aic7xxx_host *).
*-F*************************************************************************/
static void
-aic7xxx_free (struct aic7xxx_host *p)
+aic7xxx_free(struct aic7xxx_host *p)
{
- int i;
+ int i, jump;
/*
- * We should be careful in freeing the scb_data area. For those
- * adapters sharing external SCB RAM(398x), there will be only one
- * scb_data area allocated. The flag SHARED_SCBDATA indicates if
- * one adapter is sharing anothers SCB RAM.
+ * Free the allocated hardware SCB space.
*/
- if (!(p->flags & SHARED_SCBDATA))
+ if (p->scb_data->hscbs != NULL)
{
- /*
- * Free the allocated hardware SCB space.
- */
- if (p->scb_data->hscbs != NULL)
- {
- kfree(p->scb_data->hscbs);
- }
- /*
- * Free the driver SCBs. These were allocated on an as-need
- * basis.
- */
- for (i = 0; i < p->scb_data->numscbs; i++)
- {
- kfree(p->scb_data->scb_array[i]);
- }
- /*
- * Free the hardware SCBs.
- */
- if (p->scb_data->hscbs != NULL)
- {
- kfree(p->scb_data->hscbs);
- }
-
- /*
- * Free the SCB data area.
- */
- kfree(p->scb_data);
+ kfree(p->scb_data->hscbs);
+ }
+ /*
+ * Free the driver SCBs. These were allocated on an as-need
+ * basis. However, we allocated them 30 at a time up until the
+ * very last allocation (if there was one). So, we need to free
+ * every 30th pointer to free the array (this also frees the
+ * SG_array structs as well).
+ *
+ * Note, on 64 bit machines we allocate 29 at a time instead.
+ */
+ jump = (sizeof(int) == sizeof(void *)) ? 30 : 29;
+ for (i = 0; i < p->scb_data->numscbs; i += jump)
+ {
+ kfree(p->scb_data->scb_array[i]);
}
+ /*
+ * Free the SCB data area.
+ */
+ kfree(p->scb_data);
+
/*
* Free the instance of the device structure.
*/
+
+ /*
+ * XXXXXXXX FIXXXXXMEEEEEE. How do we unmap the I/O range we have mapped
+ * if we are doing MMAPed I/O ?????????? Our biggest concern is the issue
+ * of possibly calling unmap on an area that *might* be used on another
+ * controller as well (aka, the 4096 byte MMAPed area is back to back
+ * with another controller, and the PAGE_SIZE is greater then 4096, allowing
+ * us to remap in a shared page).
+ */
scsi_unregister(p->host);
}
unsigned short scarray[128];
struct seeprom_config *sc = (struct seeprom_config *) scarray;
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
{
printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
}
- switch (p->chip_type)
+ switch (p->type & 0x00001ff1)
{
- case AIC_7770: /* None of these adapters have seeproms. */
- case AIC_7771:
- case AIC_7855:
+ case AHC_AIC7770: /* None of these adapters have seeproms. */
+ case AHC_274:
break;
- case AIC_284x:
+ case AHC_284:
have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
break;
- case AIC_7850: /* The 2910B is a 7850 with a seeprom. */
- case AIC_7861:
- case AIC_7870:
- case AIC_7871:
- case AIC_7872:
- case AIC_7874:
- case AIC_7881:
- case AIC_7882:
- case AIC_7884:
- have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+ case AHC_AIC7850: /* The 2910B is a 7850 with a seeprom. */
+ case AHC_294AU:
+ case AHC_AIC7870: /* For these controllers we try the three possible */
+ case AHC_AIC7895: /* SEEPROM read types. If none works, then we are */
+ case AHC_294: /* SOL. This should catch any SEEPROM variety */
+ case AHC_394: /* Adaptec or some motherboard manufacturer might */
+ case AHC_294U: /* throw at us, and since we perform a checksum */
+ case AHC_394U: /* during the read, we should get bogus seeprom */
+ case AHC_AIC7860: /* reads. */
+ case AHC_AIC7880:
+ case AHC_398:
+ case AHC_398U:
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
scarray, sizeof(*sc)/2, C46);
- break;
-
- case AIC_7860: /* Motherboard Ultra controllers might have RAID port. */
- case AIC_7880:
- have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
- if (!have_seeprom)
- {
- have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
- }
- break;
-
- case AIC_7873: /* The 3985 adapters use the 93c56 serial EEPROM. */
- case AIC_7883:
- have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+ if (!have_seeprom)
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, sizeof(scarray)/2, C46);
+ if (!have_seeprom)
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, sizeof(*sc)/2, C56_66);
+ if (!have_seeprom)
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
scarray, sizeof(scarray)/2, C56_66);
break;
if (!have_seeprom)
{
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
{
printk("\naic7xxx: No SEEPROM available; using defaults.\n");
}
- p->flags |= USE_DEFAULTS;
+ p->flags |= AHC_USEDEFAULTS;
+ p->flags &= ~AHC_BIOS_ENABLED;
}
else
{
- if (aic7xxx_verbose)
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
{
printk("done\n");
}
- p->flags |= HAVE_SEEPROM;
/*
* Update the settings in sxfrctl1 to match the termination settings.
* First process the settings that are different between the VLB
* and PCI adapter seeproms.
*/
- if (p->chip_class == AIC_777x)
+ if (p->type & AHC_284)
{
/* VLB adapter seeproms */
if (sc->bios_control & CF284XEXTEND)
- p->flags |= EXTENDED_TRANSLATION;
+ p->flags |= AHC_EXTEND_TRANS_A;
if (sc->adapter_control & CF284XSTERM)
+ {
*sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_A;
+ }
/*
* The 284x SEEPROM doesn't have a max targets field. We
* set it to 16 to make sure we take care of the 284x-wide
{
/* PCI adapter seeproms */
if (sc->bios_control & CFEXTEND)
- p->flags |= EXTENDED_TRANSLATION;
+ p->flags |= AHC_EXTEND_TRANS_A;
if (sc->adapter_control & CFSTERM)
+ {
*sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_A;
+ }
/* Limit to 16 targets just in case. */
max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
}
+ p->discenable = 0;
+
+ for (i = 0; i < max_targets; i++)
+ {
+ if( (p->type & AHC_ULTRA) &&
+ !(sc->adapter_control & CFULTRAEN) &&
+ (sc->device_flags[i] & CFSYNCHISULTRA) )
+ {
+ p->flags |= AHC_NEWEEPROM_FMT;
+ break;
+ }
+ }
+
for (i = 0; i < max_targets; i++)
{
target_settings = (sc->device_flags[i] & CFXFER) << 4;
if (sc->device_flags[i] & CFSYNCH)
+ {
target_settings |= SOFS;
+ }
if (sc->device_flags[i] & CFWIDEB)
+ {
target_settings |= WIDEXFER;
+ }
if (sc->device_flags[i] & CFDISC)
+ {
p->discenable |= (0x01 << i);
- outb(target_settings, p->base + TARG_SCRATCH + i);
+ }
+ if (p->flags & AHC_NEWEEPROM_FMT)
+ {
+ if (sc->device_flags[i] & CFSYNCHISULTRA)
+ {
+ p->ultraenb |= (0x01 << i);
+ }
+ }
+ else if (sc->adapter_control & CFULTRAEN)
+ {
+ p->ultraenb |= (0x01 << i);
+ }
+ if ( ((target_settings & 0x70) == 0x40) &&
+ (p->ultraenb & (0x01 << i)) )
+ {
+ target_settings &= ~0x70;
+ p->ultraenb &= ~(0x01 << i);
+ }
+ aic_outb(p, target_settings, TARG_SCRATCH + i);
}
- outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
- outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
+ aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+ aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+ aic_outb(p, (p->ultraenb & 0xFF), ULTRA_ENB);
+ aic_outb(p, ((p->ultraenb >> 8) & 0xFF), ULTRA_ENB + 1);
p->scsi_id = sc->brtime_id & CFSCSIID;
+ p->adapter_control = sc->adapter_control;
+ p->bios_control = sc->bios_control;
+
+ if (p->bios_control & CFBIOSEN)
+ {
+ p->flags &= ~AHC_USEDEFAULTS;
+ p->flags |= AHC_BIOS_ENABLED;
+ }
+ else
+ {
+ p->flags &= ~AHC_BIOS_ENABLED;
+ p->flags |= AHC_USEDEFAULTS;
+ }
+
+ if ((p->type & 0x1ff1) == AHC_AIC7895)
+ {
+ if (p->adapter_control & CFBPRIMARY)
+ p->flags |= AHC_CHANNEL_B_PRIMARY;
+ }
scsi_conf = (p->scsi_id & 0x7);
if (sc->adapter_control & CFSPARITY)
* The 7850 controllers with a seeprom, do not honor the CFRESETB
* flag in the seeprom. Assume that we want to reset the SCSI bus.
*/
- if ((sc->adapter_control & CFRESETB) || (p->chip_class == AIC_7850))
+ if (sc->adapter_control & CFRESETB)
scsi_conf |= RESET_SCSI;
-
- if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
- {
- /*
- * We allow the operator to override ultra enable through
- * the boot prompt.
- */
- if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
- {
- /* Treat us as a non-ultra card */
- p->flags &= ~ULTRA_ENABLED;
- }
- }
+ /*
+ * We may be a 2842, if so, preserve the TERM_ENB bit in scsi conf
+ */
+ if ( (p->flags & AHC_TERM_ENB_A) &&
+ ((p->type & AHC_AIC7770) == AHC_AIC7770) )
+ scsi_conf |= TERM_ENB;
+ /*
+ * If this is an Ultra card, is Ultra mode enabled? If not, disable
+ * it in the host struct as well
+ */
+ if ( (p->type & AHC_ULTRA) &&
+ !(sc->adapter_control & CFULTRAEN) &&
+ !(p->flags & AHC_NEWEEPROM_FMT) )
+ p->type &= ~AHC_ULTRA;
/* Set the host ID */
- outb(scsi_conf, p->base + SCSICONF);
+ aic_outb(p, scsi_conf, SCSICONF);
/* In case we are a wide card */
- outb(p->scsi_id, p->base + SCSICONF + 1);
+ aic_outb(p, p->scsi_id, SCSICONF + 1);
- if (p->chip_class != AIC_777x)
+ if ((p->type & AHC_AIC7860) == AHC_AIC7860)
{
+ if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
/*
* Update the settings in sxfrctl1 to match the termination
* settings.
*/
- *sxfrctl1 = 0;
- configure_termination(p, sxfrctl1, sc->adapter_control,
- (unsigned char) sc->max_targets & CFMAXTARG);
+ configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
}
+ else if (have_seeprom && ((p->type & AHC_AIC7770) != AHC_AIC7770))
+ configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
}
return (have_seeprom);
}
int
aic7xxx_detect(Scsi_Host_Template *template)
{
+ struct aic7xxx_host *temp_p = NULL;
+ struct aic7xxx_host *current_p = NULL;
+ struct aic7xxx_host *list_p = NULL;
int found = 0;
- aha_status_type adapter_bios;
- aha_chip_class_type chip_class;
- aha_chip_type chip_type;
- int slot, base;
- int chan_num = 0;
- unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
- int i;
- struct aic7xxx_host *p;
+ ahc_flag_type flags = 0;
+ ahc_type type;
+ unsigned char sxfrctl1;
+#if defined(__i386__) || defined(__alpha__)
+ unsigned char hcntrl, hostconf;
+ unsigned int slot, base;
+#endif
+#ifdef MODULE
/*
- * Since we may allow sharing of IRQs, it is imperative
- * that we "null-out" the aic7xxx_boards array. It is
- * not guaranteed to be initialized to 0 (NULL). We use
- * a NULL entry to indicate that no prior hosts have
- * been found/registered for that IRQ.
+ * If we are called as a module, the aic7xxx pointer may not be null
+ * and it would point to our bootup string, just like on the lilo
+ * command line. IF not NULL, then process this config string with
+ * aic7xxx_setup
*/
- for (i = 0; i < NUMBER(aic7xxx_boards); i++)
- {
- aic7xxx_boards[i] = NULL;
- }
+ if(aic7xxx)
+ aic7xxx_setup(aic7xxx, NULL);
+
+#endif
template->proc_dir = &proc_scsi_aic7xxx;
- template->name = aic7xxx_info(NULL);
template->sg_tablesize = AIC7XXX_MAX_SG;
- /*
- * Initialize the spurious count to 0.
- */
- aic7xxx_spurious_count = 0;
+#if defined(__i386__) || defined(__alpha__)
/*
* EISA/VL-bus card signature probe.
*/
- for (slot = MINSLOT; slot <= MAXSLOT; slot++)
+ slot = MINSLOT;
+ while (slot <= MAXSLOT)
{
base = SLOTBASE(slot) + MINREG;
* Some other driver has staked a
* claim to this i/o region already.
*/
- continue;
+ slot++;
+ continue; /* back to the beginning of the for loop */
}
-
- chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
- if (chip_type != AIC_NONE)
+ flags = 0;
+ type = aic7xxx_probe(slot, base + HID0, &flags);
+ switch (type)
{
-
- switch (chip_type)
- {
- case AIC_7770:
- case AIC_7771:
+ case AHC_AIC7770:
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
printk("aic7xxx: <%s> at EISA %d\n",
- board_names[chip_type], slot);
- break;
- case AIC_284x:
+ board_names[2], slot);
+ break;
+ case AHC_274:
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[3], slot);
+ break;
+ case AHC_284:
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
printk("aic7xxx: <%s> at VLB %d\n",
- board_names[chip_type], slot);
- break;
- default:
- break;
- }
+ board_names[4], slot);
+ break;
+ default:
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
+ temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
+ if (temp_p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
+ /*
+ * Pause the card preserving the IRQ type. Allow the operator
+ * to override the IRQ trigger.
+ */
+ if (aic7xxx_irq_trigger == 1)
+ hcntrl = IRQMS; /* Level */
+ else if (aic7xxx_irq_trigger == 0)
+ hcntrl = 0; /* Edge */
+ else
+ hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
+ memset(temp_p, 0, sizeof(struct aic7xxx_host));
+ temp_p->unpause = hcntrl | INTEN;
+ temp_p->pause = hcntrl | PAUSE | INTEN;
+ temp_p->base = base;
+ temp_p->type = type;
+ temp_p->flags = flags | AHC_PAGESCBS;
+ temp_p->mbase = 0;
+ temp_p->maddr = 0;
+ temp_p->pci_bus = 0;
+ temp_p->pci_device_fn = slot;
+ aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
+ while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+ if (aic7xxx_chip_reset(temp_p) == -1)
+ temp_p->irq = 0;
+ else
+ temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+ switch (temp_p->irq)
+ {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
- /*
- * We found a card, allow 1 spurious interrupt.
- */
- aic7xxx_spurious_count = 1;
+ default:
+ printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+ "level %d, ignoring.\n", temp_p->irq);
+ kfree(temp_p);
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
- /*
- * Pause the card preserving the IRQ type. Allow the operator
- * to override the IRQ trigger.
- */
- if (aic7xxx_irq_trigger == 1)
- hcntrl = IRQMS; /* Level */
- else if (aic7xxx_irq_trigger == 0)
- hcntrl = 0; /* Edge */
- else
- hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
- outb(hcntrl | PAUSE, base + HCNTRL);
- p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
- if (p == NULL)
- {
- printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
- continue;
- }
- aic7xxx_chip_reset(p);
+ /*
+ * We are commited now, everything has been checked and this card
+ * has been found, now we just set it up
+ */
+ /*
+ * Insert our new struct into the list at the end
+ */
+ if (list_p == NULL)
+ {
+ list_p = current_p = temp_p;
+ }
+ else
+ {
+ current_p = list_p;
+ while (current_p->next != NULL)
+ current_p = current_p->next;
+ current_p->next = temp_p;
+ }
+ if (aic7xxx_extended)
+ {
+ temp_p->flags |= AHC_EXTEND_TRANS_A;
+ if (temp_p->flags & AHC_MULTI_CHANNEL)
+ temp_p->flags |= AHC_EXTEND_TRANS_B;
+ }
- irq = inb(INTDEF + base) & 0x0F;
- switch (irq)
+ switch (temp_p->type & 0x1ff1)
+ {
+ case AHC_AIC7770:
+ temp_p->board_name_index = 2;
+ case AHC_274:
{
- case 9:
- case 10:
- case 11:
- case 12:
- case 14:
- case 15:
- break;
+ temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
- default:
- printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
- "level %d, ignoring.\n", irq);
- irq = 0;
- aic7xxx_free(p);
- break;
- }
-
- if (irq != 0)
- {
- p->irq = irq & 0x0F;
- p->chip_class = AIC_777x;
-#ifdef AIC7XXX_PAGE_ENABLE
- p->flags |= PAGE_ENABLED;
-#endif
- p->instance = found;
- if (aic7xxx_extended)
+ /*
+ * Get the primary channel information. Right now we don't
+ * do anything with this, but someday we will be able to inform
+ * the mid-level SCSI code which channel is primary.
+ */
+ if (temp_p->board_name_index == 0)
+ temp_p->board_name_index = 3;
+ if (temp_p->bios_control & CHANNEL_B_PRIMARY)
{
- p->flags |= EXTENDED_TRANSLATION;
+ temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
}
- switch (p->chip_type)
+ if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
+ {
+ temp_p->flags |= AHC_USEDEFAULTS;
+ temp_p->flags &= ~AHC_BIOS_ENABLED;
+ }
+ else
{
- case AIC_7770:
- case AIC_7771:
+ temp_p->flags &= ~AHC_USEDEFAULTS;
+ temp_p->flags |= AHC_BIOS_ENABLED;
+ if ( (temp_p->bios_control & 0x20) == 0 )
{
- unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
-
- /*
- * Get the primary channel information. Right now we don't
- * do anything with this, but someday we will be able to inform
- * the mid-level SCSI code which channel is primary.
- */
- if (biosctrl & CHANNEL_B_PRIMARY)
- {
- p->flags |= FLAGS_CHANNEL_B_PRIMARY;
- }
-
- if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+ switch(temp_p->bios_control & 0x07)
{
- p->flags |= USE_DEFAULTS;
+ case 0x0:
+ temp_p->bios_address = 0xcc000;
+ break;
+ case 0x1:
+ temp_p->bios_address = 0xd0000;
+ break;
+ case 0x2:
+ temp_p->bios_address = 0xd4000;
+ break;
+ case 0x3:
+ temp_p->bios_address = 0xd8000;
+ break;
+ case 0x4:
+ temp_p->bios_address = 0xdc000;
+ break;
+ case 0x5:
+ temp_p->bios_address = 0xe0000;
+ break;
+ case 0x6:
+ temp_p->bios_address = 0xe4000;
+ break;
+ case 0x7:
+ temp_p->bios_address = 0xe8000;
+ break;
+ default:
+ break; /* can't get here */
}
- break;
}
-
- case AIC_284x:
- if (!load_seeprom(p, &sxfrctl1))
+ else
+ {
+ switch(temp_p->bios_control & 0x06)
{
- if (aic7xxx_verbose)
- printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+ case 0x0:
+ temp_p->bios_address = 0xd0000;
+ break;
+ case 0x2:
+ temp_p->bios_address = 0xd8000;
+ break;
+ case 0x4:
+ temp_p->bios_address = 0xe0000;
+ break;
+ case 0x6:
+ temp_p->bios_address = 0xe8000;
+ break;
+ default:
+ break; /* can't get here */
}
- break;
-
- default: /* Won't get here. */
- break;
+ }
}
- printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
- (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
- (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
- /*
- * Check for Rev C or E boards. Rev E boards can supposedly have
- * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
- * It's still not clear extactly what is different about the Rev E
- * boards, but we think it allows 8 bit entries in the QOUTFIFO to
- * support "paging" SCBs (more than 4 commands can be active at once).
- *
- * The Rev E boards have a read/write autoflush bit in the
- * SBLKCTL register, while in the Rev C boards it is read only.
- */
- sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
- outb(sblkctl, p->base + SBLKCTL);
- if (inb(p->base + SBLKCTL) == sblkctl)
+ temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
+ temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
+ if (temp_p->flags & AHC_USEDEFAULTS)
{
- /*
- * We detected a Rev E board, we allow paging on this board.
- */
- printk("Revision >= E\n");
- outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+ temp_p->scsi_id = temp_p->scsi_id_b = 7;
+ temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
}
else
{
- /* Do not allow paging. */
- p->flags &= ~PAGE_ENABLED;
- printk("Revision <= C\n");
+ if ( ((temp_p->adapter_control >> 8) & TERM_ENB) != 0 )
+ temp_p->flags |= AHC_TERM_ENB_A;
+ if ( (temp_p->adapter_control & TERM_ENB) != 0 )
+ temp_p->flags |= AHC_TERM_ENB_B;
+ temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
+ temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
}
+ break;
+ }
- if (aic7xxx_verbose)
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-
- /*
- * Set the FIFO threshold and the bus off time.
- */
- hostconf = inb(p->base + HOSTCONF);
- outb(hostconf & DFTHRSH, p->base + BUSSPD);
- outb((hostconf << 2) & BOFF, p->base + BUSTIME);
-
- /*
- * Try to initialize the card and register it with the kernel.
- */
- if (aic7xxx_register(template, p))
- {
- /*
- * We successfully found a board and registered it.
- */
- found = found + 1;
- }
- else
+ case AHC_284:
+ load_seeprom(temp_p, &sxfrctl1);
+ temp_p->board_name_index = 4;
+ switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
{
- /*
- * Something went wrong; release and free all resources.
- */
- aic7xxx_free(p);
+ case 0x00:
+ temp_p->bios_address = 0xe0000;
+ break;
+ case 0x20:
+ temp_p->bios_address = 0xc8000;
+ break;
+ case 0x40:
+ temp_p->bios_address = 0xd0000;
+ break;
+ case 0x60:
+ temp_p->bios_address = 0xd8000;
+ break;
+ default:
+ break; /* can't get here */
}
- }
- /*
- * Disallow spurious interrupts.
- */
- aic7xxx_spurious_count = 0;
+ break;
+
+ default: /* Won't get here. */
+ break;
+ }
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
+ (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
+ temp_p->irq,
+ (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
}
+
+ /*
+ * Set the FIFO threshold and the bus off time.
+ */
+ hostconf = aic_inb(temp_p, HOSTCONF);
+ aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
+ aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
+ slot++;
+ found++;
}
+#endif /* defined(__i386__) || defined(__alpha__) */
+
#ifdef CONFIG_PCI
/*
* PCI-bus probe.
{
unsigned short vendor_id;
unsigned short device_id;
- aha_chip_type chip_type;
- aha_chip_class_type chip_class;
+ ahc_type type;
+ ahc_flag_type flags;
+ int board_name_index;
} const aic7xxx_pci_devices[] = {
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AIC_7873, AIC_787x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AIC_7874, AIC_787x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AIC_7880, AIC_788x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AIC_7881, AIC_788x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AIC_7882, AIC_788x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AIC_7883, AIC_788x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
+ AHC_FNONE, 1 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
+ AHC_PAGESCBS | AHC_USEDEFAULTS, 5 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
+ AHC_PAGESCBS | AHC_USEDEFAULTS, 6 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 7 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_294AU,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 8 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 9 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_294,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 10 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_394,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 11 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_398,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 12 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_294,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 13 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 14 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_294U,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 15 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_394U,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 16 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_398U,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 17 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_294U,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, 18 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 19 }
};
- int error, flags;
- int done = 0;
- unsigned int iobase, mbase;
- unsigned short index = 0;
- unsigned char pci_bus, pci_device_fn;
- unsigned char ultra_enb = 0;
- unsigned int devconfig, class_revid;
- scb_data_type *shared_scb_data = NULL;
- char rev_id[] = {'B', 'C', 'D'};
+ unsigned short command;
+ unsigned int devconfig, i;
+#ifdef MMAPIO
+ unsigned long page_offset;
+#endif
+ struct aic7xxx_host *first_7895 = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ struct pci_dev *pdev = NULL;
+#else
+ int index;
+ unsigned int piobase, mmapbase;
+ unsigned char pci_bus, pci_devfn;
+#endif
for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
{
- done = FALSE;
- while (!done)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pdev = NULL;
+ while ((pdev = pci_find_device(aic7xxx_pci_devices[i].vendor_id,
+ aic7xxx_pci_devices[i].device_id,
+ pdev)))
+#else
+ index = 0;
+ while (!(pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
+ aic7xxx_pci_devices[i].device_id,
+ index++, &pci_bus, &pci_devfn)) )
+#endif
{
- if (pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
- aic7xxx_pci_devices[i].device_id,
- index, &pci_bus, &pci_device_fn))
- {
- index = 0;
- done = TRUE;
- }
- else /* Found an Adaptec PCI device. */
+ if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
{
- chip_class = aic7xxx_pci_devices[i].chip_class;
- chip_type = aic7xxx_pci_devices[i].chip_type;
- chan_num = 0;
- flags = 0;
- switch (aic7xxx_pci_devices[i].chip_type)
+ if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
{
- case AIC_7855:
- flags |= USE_DEFAULTS;
- break;
-
- case AIC_7872: /* 3940 */
- case AIC_7882: /* 3940-Ultra */
- flags |= MULTI_CHANNEL;
- chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
- number_of_3940s++;
- break;
-
- case AIC_7873: /* 3985 */
- case AIC_7883: /* 3985-Ultra */
- chan_num = number_of_3985s; /* Has 3 controllers */
- flags |= MULTI_CHANNEL;
- number_of_3985s++;
- if (number_of_3985s == 3)
- {
- number_of_3985s = 0;
- shared_scb_data = NULL;
- }
- break;
-
- default:
- break;
+ printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
+ "supported by\n");
+ printk(KERN_INFO " this driver, we are ignoring it.\n");
}
+ }
+ else if ( (temp_p = kmalloc(sizeof(struct aic7xxx_host),
+ GFP_ATOMIC)) != NULL )
+ {
+ memset(temp_p, 0, sizeof(struct aic7xxx_host));
+ temp_p->type = aic7xxx_pci_devices[i].type;
+ temp_p->flags = aic7xxx_pci_devices[i].flags;
+ temp_p->board_name_index = aic7xxx_pci_devices[i].board_name_index;
/*
* Read sundry information from PCI BIOS.
*/
- error = pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &iobase);
- error += pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &irq);
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_1, &mbase);
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- DEVCONFIG, &devconfig);
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- CLASS_PROGIF_REVID, &class_revid);
-
- printk("aic7xxx: <%s> at PCI %d\n",
- board_names[chip_type], PCI_SLOT(pci_device_fn));
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ temp_p->irq = pdev->irq;
+ temp_p->pci_bus = pdev->bus->number;
+ temp_p->pci_device_fn = pdev->devfn;
+ temp_p->base = pdev->base_address[0];
+ temp_p->mbase = pdev->base_address[1];
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ pci_write_config_word(pdev, PCI_COMMAND,
+ command | PCI_COMMAND_MASTER |
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+#else
+ temp_p->pci_bus = pci_bus;
+ temp_p->pci_device_fn = pci_devfn;
+ pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
+ &temp_p->irq);
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+ &piobase);
+ temp_p->base = piobase;
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_1,
+ &mmapbase);
+ temp_p->mbase = mmapbase;
+ pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
+ pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND,
+ command | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_IO);
+#endif
+
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at PCI %d/%d\n",
+ board_names[aic7xxx_pci_devices[i].board_name_index],
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
/*
* The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
* we mask it off.
*/
- iobase &= PCI_BASE_ADDRESS_IO_MASK;
-
- p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
- shared_scb_data);
-
- if (p == NULL)
+ temp_p->base &= PCI_BASE_ADDRESS_IO_MASK;
+ temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
+ temp_p->unpause = (aic_inb(temp_p, HCNTRL) & IRQMS) | INTEN;
+ temp_p->pause = temp_p->unpause | PAUSE;
+
+#ifdef MMAPIO
+ base = temp_p->mbase & PAGE_MASK;
+ page_offset = temp_p->mbase - base;
+ /*
+ * replace the next line with this one if you are using 2.1.x:
+ * temp_p->maddr = ioremap(base, page_offset + 256);
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ temp_p->maddr = ioremap(base, page_offset + 256);
+#else
+ temp_p->maddr = vremap(base, page_offset + 256);
+#endif
+ if(temp_p->maddr)
{
- printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
- continue;
+ temp_p->maddr += page_offset;
}
-
- /* Remember to set the channel number, irq, and chip class. */
- p->chan_num = chan_num;
- p->irq = irq;
- p->chip_class = chip_class;
-#ifdef AIC7XXX_PAGE_ENABLE
- p->flags |= PAGE_ENABLED;
#endif
- p->instance = found;
+
+ aic_outb(temp_p, temp_p->pause, HCNTRL);
+ while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+
+ temp_p->bios_address = 0;
/*
* Remember how the card was setup in case there is no seeprom.
*/
- p->scsi_id = inb(p->base + SCSIID) & OID;
- if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+ /*
+ * Get current termination setting
+ */
+ sxfrctl1 = aic_inb(temp_p, SXFRCTL1) & STPWEN;
+
+ if (aic7xxx_chip_reset(temp_p) == -1)
{
- p->flags |= ULTRA_ENABLED;
- ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+ kfree(temp_p);
+ temp_p = NULL;
+ continue;
}
- sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
-
- aic7xxx_chip_reset(p);
-#ifdef AIC7XXX_USE_EXT_SCBRAM
- if (devconfig & RAMPSM)
+ switch (temp_p->type & 0x1ff1)
{
- printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
- "access.\n");
- /*
- * XXX - Assume 9 bit SRAM and enable parity checking.
- */
- devconfig |= EXTSCBPEN;
+ case AHC_394: /* 3940 */
+ case AHC_394U: /* 3940-Ultra */
+ temp_p->flags |= AHC_MULTI_CHANNEL;
+ switch(PCI_SLOT(temp_p->pci_device_fn))
+ {
+ case 5:
+ temp_p->flags |= AHC_CHNLB;
+ break;
+ default:
+ break;
+ }
+ break;
- /*
- * XXX - Assume fast SRAM and only enable 2 cycle access if we
- * are sharing the SRAM across multiple adapters (398x).
- */
- if ((devconfig & MPORTMODE) == 0)
- {
- devconfig |= EXTSCBTIME;
- }
- devconfig &= ~SCBRAMSEL;
- pcibios_write_config_dword(pci_bus, pci_device_fn,
- DEVCONFIG, devconfig);
+ case AHC_398: /* 3985 */
+ case AHC_398U: /* 3985-Ultra */
+ temp_p->flags |= AHC_MULTI_CHANNEL;
+ switch(PCI_SLOT(temp_p->pci_device_fn))
+ {
+ case 8:
+ temp_p->flags |= AHC_CHNLB;
+ break;
+ case 12:
+ temp_p->flags |= AHC_CHNLC;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case AHC_AIC7895:
+ temp_p->flags |= AHC_MULTI_CHANNEL;
+ if (PCI_FUNC(temp_p->pci_device_fn) != 0)
+ {
+ temp_p->flags |= AHC_CHNLB;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ devconfig |= SCBSIZE32;
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#else
+ pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ &devconfig);
+ devconfig |= SCBSIZE32;
+ pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ devconfig);
+#endif
+ if (aic7xxx_7895_irq_hack != -1)
+ {
+ if (first_7895 == NULL)
+ {
+ printk(KERN_INFO "aic7xxx: Using 7895_irq_hack. Please "
+ "upgrade your motherboard BIOS\n");
+ first_7895 = temp_p;
+ }
+ else if (aic7xxx_7895_irq_hack == 0)
+ {
+ if (temp_p->flags & AHC_CHNLB)
+ temp_p->irq = first_7895->irq;
+ else
+ first_7895->irq = temp_p->irq;
+ first_7895 = NULL;
+ }
+ else
+ {
+ if ( !(temp_p->flags & AHC_CHNLB) )
+ temp_p->irq = first_7895->irq;
+ else
+ first_7895->irq = temp_p->irq;
+ first_7895 = NULL;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Loading of the SEEPROM needs to come after we've set the flags
+ * to indicate possible CHNLB and CHNLC assigments. Otherwise,
+ * on 394x and 398x cards we'll end up reading the wrong settings
+ * for channels B and C
+ */
+ if ( !(load_seeprom(temp_p, &sxfrctl1)) )
+ {
+ temp_p->flags |= AHC_USEDEFAULTS;
+ if (sxfrctl1 & STPWEN)
+ temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
+ temp_p->scsi_id = temp_p->scsi_id_b = 7;
}
-#endif
- if ((p->flags & USE_DEFAULTS) == 0)
+ /*
+ * and then we need another switch based on the type in order to
+ * make sure the channel B primary flag is set properly on 7895
+ * controllers....Arrrgggghhh!!!
+ */
+ switch(temp_p->type & 0x1ff1)
{
- load_seeprom(p, &sxfrctl1);
+ case AHC_AIC7895:
+ current_p = list_p;
+ while(current_p != NULL)
+ {
+ if ( (current_p->pci_bus == temp_p->pci_bus) &&
+ (PCI_SLOT(current_p->pci_device_fn) ==
+ PCI_SLOT(temp_p->pci_device_fn)) )
+ {
+ if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+ temp_p->flags |=
+ (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+ else
+ current_p->flags |=
+ (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+ }
+ current_p = current_p->next;
+ }
+ break;
+ default:
+ break;
}
/*
* Take the LED out of diagnostic mode
*/
- sblkctl = inb(p->base + SBLKCTL);
- outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+ aic_outb(temp_p,
+ (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
+ SBLKCTL);
/*
* We don't know where this is set in the SEEPROM or by the
* BIOS, so we default to 100%.
*/
- outb(DFTHRSH_100, p->base + DSPCISTATUS);
+ aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
- if (p->flags & USE_DEFAULTS)
+ if (temp_p->flags & AHC_USEDEFAULTS)
{
int j;
+ unsigned char k;
/*
* Default setup; should only be used if the adapter does
* not have a SEEPROM.
*/
for (j = TARG_SCRATCH; j < 0x60; j++)
{
- if (inb(p->base + j) != 0x00) /* Check for all zeroes. */
+ k = aic_inb(temp_p, j);
+ /* Check for all zeros and ones. Break out if we pass */
+ if( (k != 0x00) && (k != 0xff) )
break;
}
- if (j == TARG_SCRATCH)
- {
- for (j = TARG_SCRATCH; j < 0x60; j++)
- {
- if (inb(p->base + 1) != 0xFF) /* Check for all ones. */
- break;
- }
- }
- if ((j != 0x60) && (p->scsi_id != 0))
+ /* If j makes it to 0x60, then all entries are either 0x00 or
+ * 0xff. We would then assume we have *not* been initialized
+ * and drop through here. OTOH, if even one entry is inited,
+ * then as long as we appear to have a valid SCSI ID, we'll use
+ * the leftover BIOS values.
+ */
+ if ((j != 0x60) && (temp_p->scsi_id != 0))
{
- p->flags &= ~USE_DEFAULTS;
- if (aic7xxx_verbose)
+ temp_p->flags &= ~AHC_USEDEFAULTS;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
{
printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
}
}
else
{
- if (aic7xxx_verbose)
- {
- printk(KERN_INFO "aic7xxx: No BIOS found; using default "
- "settings.\n");
- }
/*
* Assume only one connector and always turn on
* termination.
*/
+ temp_p->flags &= ~AHC_BIOS_ENABLED;
+ temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
sxfrctl1 = STPWEN;
- p->scsi_id = 7;
+ temp_p->scsi_id = 7;
}
- outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
- p->base + SCSICONF);
+ aic_outb(temp_p, (temp_p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+ SCSICONF);
/* In case we are a wide card. */
- outb(p->scsi_id, p->base + SCSICONF + 1);
- if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
- {
- /*
- * If there wasn't a BIOS or the board wasn't in this mode
- * to begin with, turn off Ultra.
- */
- p->flags &= ~ULTRA_ENABLED;
- }
+ aic_outb(temp_p, temp_p->scsi_id, SCSICONF + 1);
}
-
- /*
- * Print some additional information about the adapter.
- */
- printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
- "IO Mem 0x%x, IRQ %d",
- (p->flags & USE_DEFAULTS) ? "dis" : "en",
- p->base, p->mbase, p->irq);
- if ((class_revid & DEVREVID) < 3)
+ else /* not using defaults */
{
- printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+ if (sxfrctl1 & STPWEN)
+ temp_p->flags |= AHC_TERM_ENB_A;
}
- printk("\n");
-
- /*
- * I don't think we need to bother with allowing
- * spurious interrupts for the 787x/785x, but what
- * the hey.
- */
- aic7xxx_spurious_count = 1;
if (aic7xxx_extended)
- p->flags |= EXTENDED_TRANSLATION;
-
- if (aic7xxx_verbose)
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+ temp_p->flags |= AHC_EXTEND_TRANS_A;
/*
* Put our termination setting into sxfrctl1 now that the
* generic initialization is complete.
*/
- sxfrctl1 |= inb(p->base + SXFRCTL1);
- outb(sxfrctl1, p->base + SXFRCTL1);
-
- if (aic7xxx_register(template, p) == 0)
+ sxfrctl1 |= aic_inb(temp_p, SXFRCTL1);
+ aic_outb(temp_p, sxfrctl1, SXFRCTL1);
+ if ( list_p == NULL )
{
- aic7xxx_free(p);
+ list_p = current_p = temp_p;
}
else
{
- found = found + 1;
+ current_p = list_p;
+ while(current_p->next != NULL)
+ current_p = current_p->next;
+ current_p->next = temp_p;
+ }
+ temp_p->next = NULL;
+ found++;
+ } /* Found an Adaptec PCI device. */
+ else /* Well, we found one, but we couldn't get any memory */
+ {
+ printk("aic7xxx: Found <%s>\n",
+ board_names[aic7xxx_pci_devices[i].board_name_index]);
+ printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
+ "skipping.\n");
+ }
+ } /* while(pdev=....) */
+ } /* for PCI_DEVICES */
+ } /* PCI BIOS present */
+#endif CONFIG_PCI
+ /*
+ * Now, we re-order the probed devices by BIOS address and BUS class.
+ * In general, we follow this algorithm to make the adapters show up
+ * in the same order under linux that the computer finds them.
+ * 1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
+ * address, going from lowest to highest.
+ * 2: All PCI controllers with BIOS_ENABLED next, according to BIOS
+ * address, going from lowest to highest.
+ * 3: Remaining VLB/EISA controllers going in slot order.
+ * 4: Remaining PCI controllers, going in PCI device order (reversable)
+ */
+
+ {
+ struct aic7xxx_host *vlb_enab, *vlb_disab, *pci;
+ struct aic7xxx_host *prev_p;
+ struct aic7xxx_host *p;
+ unsigned char left;
-#ifdef AIC7XXX_USE_EXT_SCBRAM
+ prev_p = vlb_enab = vlb_disab = pci = NULL;
+
+ temp_p = list_p;
+ while (temp_p != NULL)
+ {
+ switch(temp_p->type)
+ {
+ case AHC_AIC7770:
+ case AHC_274:
+ case AHC_284:
+ if (temp_p->flags & AHC_BIOS_ENABLED)
+ {
+ if (vlb_enab == NULL)
+ {
+ vlb_enab = temp_p;
+ temp_p = temp_p->next;
+ vlb_enab->next = NULL;
+ }
+ else
+ {
+ current_p = vlb_enab;
+ prev_p = NULL;
+ while ( (current_p != NULL) &&
+ (current_p->bios_address < temp_p->bios_address))
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ vlb_enab = temp_p;
+ temp_p = temp_p->next;
+ vlb_enab->next = current_p;
+ }
+ }
+ }
+ else
+ {
+ if (vlb_disab == NULL)
+ {
+ vlb_disab = temp_p;
+ temp_p = temp_p->next;
+ vlb_disab->next = NULL;
+ }
+ else
+ {
+ current_p = vlb_disab;
+ prev_p = NULL;
+ while ( (current_p != NULL) &&
+ (current_p->base < temp_p->base))
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ vlb_disab = temp_p;
+ temp_p = temp_p->next;
+ vlb_disab->next = current_p;
+ }
+ }
+ }
+ break;
+ default: /* All PCI controllers fall through to default */
+ if (pci == NULL)
+ {
+ pci = temp_p;
+ temp_p = temp_p->next;
+ pci->next = NULL;
+ }
+ else
+ {
+ current_p = pci;
+ prev_p = NULL;
+ if (!aic7xxx_reverse_scan)
+ {
+ while ( (current_p != NULL) &&
+ ( (PCI_SLOT(current_p->pci_device_fn) |
+ (current_p->pci_bus << 8)) <
+ (PCI_SLOT(temp_p->pci_device_fn) |
+ (temp_p->pci_bus << 8)) ) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ else
+ {
+ while ( (current_p != NULL) &&
+ ( (PCI_SLOT(current_p->pci_device_fn) |
+ (current_p->pci_bus << 8)) >
+ (PCI_SLOT(temp_p->pci_device_fn) |
+ (temp_p->pci_bus << 8)) ) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
/*
- * Set the shared SCB data once we've successfully probed a
- * 398x adapter.
- *
- * Note that we can only do this if the use of external
- * SCB RAM is enabled.
+ * Are we dealing with a 7985 where we need to sort the
+ * channels as well, if so, the bios_address values should
+ * be the same
*/
- if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+ if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
+ (temp_p->pci_bus == current_p->pci_bus) &&
+ (PCI_SLOT(temp_p->pci_device_fn) ==
+ PCI_SLOT(current_p->pci_device_fn)) )
{
- if (shared_scb_data == NULL)
+ if (temp_p->flags & AHC_CHNLB)
+ {
+ if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ else
{
- shared_scb_data = p->scb_data;
+ if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
}
}
-#endif
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ pci = temp_p;
+ temp_p = temp_p->next;
+ pci->next = current_p;
+ }
}
-
- index++;
- /*
- * Disable spurious interrupts.
- */
- aic7xxx_spurious_count = 0;
- } /* Found an Adaptec PCI device. */
+ break;
+ } /* End of switch(temp_p->type) */
+ } /* End of while (temp_p != NULL) */
+ /*
+ * At this point, the cards have been broken into 4 sorted lists, now
+ * we run through the lists in order and register each controller
+ */
+ left = found;
+ temp_p = vlb_enab;
+ while(temp_p != NULL)
+ {
+ template->name = board_names[temp_p->board_name_index];
+ p = aic7xxx_alloc(template, temp_p);
+ if (p != NULL)
+ {
+ p->instance = found - left;
+ if (aic7xxx_register(template, p, (--left)) == 0)
+ {
+ found--;
+ aic7xxx_free(p);
+ }
+ }
+ current_p = temp_p;
+ temp_p = (struct aic7xxx_host *)temp_p->next;
+ kfree(current_p);
+ }
+ temp_p = pci;
+ while(temp_p != NULL)
+ {
+ template->name = board_names[temp_p->board_name_index];
+ p = aic7xxx_alloc(template, temp_p);
+ if (p != NULL)
+ {
+ p->instance = found - left;
+ if (aic7xxx_register(template, p, (--left)) == 0)
+ {
+ found--;
+ aic7xxx_free(p);
+ }
+ }
+ current_p = temp_p;
+ temp_p = (struct aic7xxx_host *)temp_p->next;
+ kfree(current_p);
+ }
+ temp_p = vlb_disab;
+ while(temp_p != NULL)
+ {
+ template->name = board_names[temp_p->board_name_index];
+ p = aic7xxx_alloc(template, temp_p);
+ if (p != NULL)
+ {
+ p->instance = found - left;
+ if (aic7xxx_register(template, p, (--left)) == 0)
+ {
+ found--;
+ aic7xxx_free(p);
+ }
}
+ current_p = temp_p;
+ temp_p = (struct aic7xxx_host *)temp_p->next;
+ kfree(current_p);
}
}
-#endif CONFIG_PCI
return (found);
}
-static void
-aic7xxx_fake_scsi_done(Scsi_Cmnd *cmd)
-{
- memset(&cmd->sense_buffer[0], '\0', sizeof(cmd->sense_buffer));
-}
-
-static void
-aic7xxx_make_fake_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *cmd, int which)
-{
- Scsi_Cmnd *cmd2;
-
- if (which == 0)
- p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 = cmd2 =
- kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
- else
- p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 = cmd2 =
- kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
- if (cmd2 != NULL)
- {
- memcpy(cmd2, cmd, sizeof(Scsi_Cmnd));
- memset(&cmd2->cmnd[0], '\0', sizeof(cmd2->cmnd));
- cmd2->cmnd[0] = TEST_UNIT_READY;
- cmd2->cmd_len = 6;
- cmd2->bufflen = 0;
- cmd2->request_bufflen = 0;
- cmd2->buffer = NULL;
- cmd2->request_buffer = NULL;
- cmd2->use_sg = 0;
- cmd2->underflow = 0;
- }
-}
-
/*+F*************************************************************************
* Function:
* aic7xxx_buildscb
* Setup the control byte if we need negotiation and have not
* already requested it.
*/
+ hscb->control = 0;
+ scb->tag_action = 0;
if (p->discenable & mask)
{
hscb->control |= DISCENB;
-#ifdef AIC7XXX_TAGGED_QUEUEING
- if (cmd->device->tagged_queue)
+ if (p->tagenable & mask)
{
cmd->tag = hscb->tag;
- p->device_status[TARGET_INDEX(cmd)].commands_sent++;
- if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 200)
+ p->dev_commands_sent[TARGET_INDEX(cmd)]++;
+ if (p->dev_commands_sent[TARGET_INDEX(cmd)] < 200)
{
hscb->control |= MSG_SIMPLE_Q_TAG;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
}
else
{
- hscb->control |= MSG_ORDERED_Q_TAG;
- p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+ if (p->orderedtag & mask)
+ {
+ hscb->control |= MSG_ORDERED_Q_TAG;
+ scb->tag_action = MSG_ORDERED_Q_TAG;
+ }
+ else
+ {
+ hscb->control |= MSG_SIMPLE_Q_TAG;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ }
+ p->dev_commands_sent[TARGET_INDEX(cmd)] = 0;
}
}
-#endif /* Tagged queueing */
}
- if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
+ if ( (p->needwdtr & mask) &&
+ !(p->wdtr_pending & mask) &&
+ !(scb->tag_action))
{
- if ( cmd->cmnd[0] == TEST_UNIT_READY )
- {
- p->wdtr_pending |= mask;
- hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_WDTR;
- }
+ p->wdtr_pending |= mask;
+ hscb->control |= MK_MESSAGE;
+ if (p->needwdtr_copy & mask)
+ scb->flags |= SCB_MSGOUT_WDTR_16BIT;
else
- {
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
- aic7xxx_make_fake_cmnd(p, cmd, 0);
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
- aic7xxx_make_fake_cmnd(p, cmd, 1);
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 != NULL )
- aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0,
- aic7xxx_fake_scsi_done);
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
- aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
- aic7xxx_fake_scsi_done);
- }
-#if 0
- printk("scsi%d: Sending WDTR request to target %d.\n",
- p->host_no, cmd->target);
-#endif
+ scb->flags |= SCB_MSGOUT_WDTR_8BIT;
}
else
{
- if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
+ if ( (p->needsdtr & mask) &&
+ !(p->sdtr_pending & mask) &&
+ !(p->wdtr_pending & mask) &&
+ !(scb->tag_action) )
{
- if ( cmd->cmnd[0] == TEST_UNIT_READY )
- {
- p->sdtr_pending |= mask;
- hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_SDTR;
- }
- else
- {
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd0 == NULL )
- aic7xxx_make_fake_cmnd(p, cmd, 0);
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 == NULL )
- aic7xxx_make_fake_cmnd(p, cmd, 1);
- if ( p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1 != NULL )
- aic7xxx_queue(p->device_status[TARGET_INDEX(cmd)].scsi_cmnd1,
- aic7xxx_fake_scsi_done);
- }
-#if 0
- printk("scsi%d: Sending SDTR request to target %d.\n",
- p->host_no, cmd->target);
-#endif
+ p->sdtr_pending |= mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
}
}
-#if 0
- printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
- "mask(0x%x).\n",
- cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
-#endif
hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
- ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+ ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
/*
* The interpretation of request_buffer and request_bufflen
* little-endian format.
*/
hscb->SCSI_cmd_length = cmd->cmd_len;
- hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
+ hscb->SCSI_cmd_pointer = cpu_to_le32(VIRT_TO_BUS(cmd->cmnd));
if (cmd->use_sg)
{
int i;
sg = (struct scatterlist *)cmd->request_buffer;
+ scb->sg_length = 0;
for (i = 0; i < cmd->use_sg; i++)
{
- scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
- scb->sg_list[i].length = (unsigned int) sg[i].length;
+ scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
+ scb->sg_list[i].length = cpu_to_le32(sg[i].length);
+ scb->sg_length += sg[i].length;
}
- hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+ hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(scb->sg_list));
hscb->SG_segment_count = cmd->use_sg;
- scb->sg_count = hscb->SG_segment_count;
+ scb->sg_count = cmd->use_sg;
/* Copy the first SG into the data pointer area. */
hscb->data_pointer = scb->sg_list[0].address;
- hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
-#if 0
- printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
- cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
-#endif
+ hscb->data_count = scb->sg_list[0].length;
}
else
{
-#if 0
- printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
- (unsigned long) cmd->request_buffer, cmd->request_bufflen);
-#endif
if (cmd->request_bufflen)
{
- hscb->SG_segment_count = 1;
scb->sg_count = 1;
- scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
- scb->sg_list[0].length = cmd->request_bufflen;
- hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
- hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
- hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
+ scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(cmd->request_buffer));
+ scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen);
+ scb->sg_length = cmd->request_bufflen;
+ hscb->SG_segment_count = 1;
+ hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0]));
+ hscb->data_count = scb->sg_list[0].length;
+ hscb->data_pointer = scb->sg_list[0].address;
}
else
{
- hscb->SG_segment_count = 0;
scb->sg_count = 0;
+ scb->sg_length = 0;
+ hscb->SG_segment_count = 0;
hscb->SG_list_pointer = 0;
+ hscb->data_count = 0;
hscb->data_pointer = 0;
- hscb->data_count = SCB_LIST_NULL << 24;
}
}
}
int
aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
{
- long processor_flags;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb;
int tindex = TARGET_INDEX(cmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
p = (struct aic7xxx_host *) cmd->host->hostdata;
-
/*
* Check to see if channel was scanned.
*/
- if (!(p->flags & A_SCANNED) && (cmd->channel == 0))
+ if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
{
- printk(KERN_INFO "scsi%d: Scanning channel A for devices.\n", p->host_no);
- p->flags |= A_SCANNED;
+ printk(INFO_LEAD "Scanning channel for devices.\n",
+ p->host_no, 0, -1, -1);
+ p->flags |= AHC_A_SCANNED;
}
else
{
- if (!(p->flags & B_SCANNED) && (cmd->channel == 1))
+ if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
{
- printk(KERN_INFO "scsi%d: Scanning channel B for devices.\n", p->host_no);
- p->flags |= B_SCANNED;
+ printk(INFO_LEAD "Scanning channel for devices.\n",
+ p->host_no, 1, -1, -1);
+ p->flags |= AHC_B_SCANNED;
}
}
-#if 0
- printk("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n",
- cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel,
- cmd->lun & 0x07);
-#endif
-
- if ( (p->device_status[tindex].active_cmds > cmd->device->queue_depth) &&
- !(p->wdtr_pending & (0x1 << tindex)) &&
- !(p->sdtr_pending & (0x1 << tindex)) )
+ if (p->dev_active_cmds[tindex] > cmd->device->queue_depth)
{
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Commands queued exceeds queue "
- "depth, active=%d\n",
+ printk(WARN_LEAD "Commands queued exceeds queue "
+ "depth, active=%d\n",
p->host_no, CTL_OF_CMD(cmd),
- p->device_status[tindex].active_cmds);
+ p->dev_active_cmds[tindex]);
+ if ( p->dev_active_cmds[tindex] > 220 )
+ p->dev_active_cmds[tindex] = 0;
}
- scb = aic7xxx_allocate_scb(p);
+ DRIVER_LOCK
+ scb = aic7xxx_allocate_scb(p, FALSE);
+ DRIVER_UNLOCK
if (scb == NULL)
{
- panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
+ panic("(scsi%d) aic7xxx_queue:Couldn't get a free SCB.\n", p->host_no);
}
else
{
scb->cmd = cmd;
aic7xxx_position(cmd) = scb->hscb->tag;
-#if 0
- debug_scb(scb);
-#endif;
/*
* Construct the SCB beforehand, so the sequencer is
*/
aic7xxx_buildscb(p, cmd, scb);
-#if 0
- if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
- {
- printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
- "address.\n");
- }
- printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
- scb->hscb->tag, (unsigned int) scb->cmd,
- scb->flags, (unsigned int) p->free_scb);
-#endif
-
/*
* Make sure the Scsi_Cmnd pointer is saved, the struct it points to
* is set up properly, and the parity error flag is reset, then send
scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
- save_flags(processor_flags);
- cli();
- if (p->device_status[tindex].delayed_scbs.head != NULL)
+ DRIVER_LOCK
+ if (p->delayed_scbs[tindex].head != NULL)
{
- scbq_insert_tail(&p->device_status[tindex].delayed_scbs, scb);
+ scbq_insert_tail(&p->delayed_scbs[tindex], scb);
}
else
{
scbq_insert_tail(&p->waiting_scbs, scb);
}
- if ((p->flags & (IN_ISR | IN_ABORT | RESET_PENDING)) == 0)
+ if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
{
aic7xxx_run_waiting_queues(p);
}
- restore_flags(processor_flags);
-
-#if 0
- printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
- (long) cmd, (long) scb->cmd, scb->hscb->tag);
-#endif;
+ DRIVER_UNLOCK
}
return (0);
}
struct aic7xxx_scb *scb;
struct aic7xxx_hwscb *hscb;
int result = -1;
- char channel;
+ int channel;
unsigned char saved_scbptr, lastphase;
- unsigned char hscb_index, linked_next;
+ unsigned char hscb_index;
int disconnected;
scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
hscb = scb->hscb;
- lastphase = inb(p->base + LASTPHASE);
- if (aic7xxx_verbose > 1)
+ lastphase = aic_inb(p, LASTPHASE);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
{
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus Device reset, scb flags 0x%x, ",
+ printk(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
p->host_no, CTL_OF_SCB(scb), scb->flags);
switch (lastphase)
{
case P_DATAOUT:
- printk("Data-Out phase, ");
+ printk("Data-Out phase\n");
break;
case P_DATAIN:
- printk("Data-In phase, ");
+ printk("Data-In phase\n");
break;
case P_COMMAND:
- printk("Command phase, ");
+ printk("Command phase\n");
break;
case P_MESGOUT:
- printk("Message-Out phase, ");
+ printk("Message-Out phase\n");
break;
case P_STATUS:
- printk("Status phase, ");
+ printk("Status phase\n");
break;
case P_MESGIN:
- printk("Message-In phase, ");
+ printk("Message-In phase\n");
break;
default:
/*
* We're not in a valid phase, so assume we're idle.
*/
- printk("while idle, LASTPHASE = 0x%x, ", lastphase);
+ printk("while idle, LASTPHASE = 0x%x\n", lastphase);
break;
}
- printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
- inb(p->base + SCSISIGI),
- inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
- inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+ printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+ "0x%x\n", p->host_no, CTL_OF_SCB(scb),
+ aic_inb(p, SCSISIGI),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
}
- channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+ channel = cmd->channel;
/*
* Send a Device Reset Message:
* fails, we'll get another timeout a few seconds later which will
* attempt a bus reset.
*/
- saved_scbptr = inb(p->base + SCBPTR);
+ saved_scbptr = aic_inb(p, SCBPTR);
disconnected = FALSE;
if (lastphase != P_BUSFREE)
{
- if (inb(p->base + SCB_TAG) >= p->scb_data->numscbs)
+ if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
{
- /*
- * Perform a bus reset.
- *
- * XXX - We want to queue an abort for the timedout SCB
- * instead.
- */
- printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
- "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
+ printk(WARN_LEAD "Invalid SCB ID %d is active, "
+ "SCB flags = 0x%x.\n", p->host_no,
+ CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
return(SCSI_RESET_ERROR);
}
- if (scb->hscb->tag == inb(p->base + SCB_TAG))
+ if (scb->hscb->tag == aic_inb(p, SCB_TAG))
{
if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
{
/* Send the abort message to the active SCB. */
- outb(1, p->base + MSG_LEN);
- outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
- outb(lastphase | ATNO, p->base + SCSISIGO);
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset message in "
- "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+ aic_outb(p, MSG_BUS_DEV_RESET, MSG_OUT);
+ aic_outb(p, lastphase | ATNO, SCSISIGO);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Device reset message in "
+ "message buffer\n", p->host_no, CTL_OF_SCB(scb));
scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
aic7xxx_error(scb->cmd) = DID_RESET;
- p->device_status[TARGET_INDEX(scb->cmd)].flags &=
- ~DEVICE_SUCCESS;
- p->device_status[TARGET_INDEX(scb->cmd)].flags |=
- BUS_DEVICE_RESET_PENDING;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] &=
+ ~DEVICE_SUCCESS;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] |=
+ BUS_DEVICE_RESET_PENDING;
return(SCSI_RESET_PENDING);
}
else
{
- /* We want to send out the message, but it could screw an already */
- /* in place and being used message. Instead, we return an error */
- /* to try and start the bus reset phase since this command is */
+ /* We want to send out the message, but it could screw an already */
+ /* in place and being used message. Instead, we return an error */
+ /* to try and start the bus reset phase since this command is */
/* probably hung (aborts failed, and now reset is failing). We */
/* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
/* any more on this device, but instead will escalate to a bus or */
/* host reset (additionally, we won't try to abort any more). */
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Device reset, Message buffer "
- "in use\n", p->host_no, CTL_OF_SCB(scb));
- scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ printk(WARN_LEAD "Device reset, Message buffer "
+ "in use\n", p->host_no, CTL_OF_SCB(scb));
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
aic7xxx_error(scb->cmd) = DID_RESET;
- p->device_status[TARGET_INDEX(scb->cmd)].flags &=
- ~DEVICE_SUCCESS;
- p->device_status[TARGET_INDEX(scb->cmd)].flags |=
- BUS_DEVICE_RESET_PENDING;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] &=
+ ~DEVICE_SUCCESS;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] |=
+ BUS_DEVICE_RESET_PENDING;
return(SCSI_RESET_ERROR);
}
}
- }
+ } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
hscb_index = aic7xxx_find_scb(p, scb);
if (hscb_index == SCB_LIST_NULL)
{
- disconnected = TRUE;
- linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+ disconnected = (aic7xxx_scb_on_qoutfifo(p, scb)) ? FALSE : TRUE;
}
else
{
- outb(hscb_index, p->base + SCBPTR);
- if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+ aic_outb(p, hscb_index, SCBPTR);
+ if (aic_inb(p, SCB_CONTROL) & DISCONNECTED)
{
disconnected = TRUE;
}
- linked_next = inb(p->base + SCB_LINKED_NEXT);
}
if (disconnected)
{
/*
- * Simply set the ABORT_SCB control bit and preserve the
- * linked next pointer.
+ * Simply set the MK_MESSAGE flag and the SEQINT handler will do
+ * the rest on a reconnect.
*/
- scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
- scb->hscb->data_count &= ~0xFF000000;
- scb->hscb->data_count |= linked_next << 24;
- if ((p->flags & PAGE_ENABLED) == 0)
- {
- scb->hscb->control &= ~DISCONNECTED;
- }
- scb->flags |= SCB_RESET | SCB_DEVICE_RESET | SCB_QUEUED_ABORT;
- p->device_status[TARGET_INDEX(scb->cmd)].flags &= ~DEVICE_SUCCESS;
- p->device_status[TARGET_INDEX(scb->cmd)].flags |=
- BUS_DEVICE_RESET_PENDING;
+ scb->hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] &= ~DEVICE_SUCCESS;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] |=
+ BUS_DEVICE_RESET_PENDING;
if (hscb_index != SCB_LIST_NULL)
{
unsigned char scb_control;
- scb_control = inb(p->base + SCB_CONTROL);
- outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+ aic_outb(p, hscb_index, SCBPTR);
+ scb_control = aic_inb(p, SCB_CONTROL);
+ aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
}
/*
* Actually requeue this SCB in case we can select the
* device before it reconnects. If the transaction we
- * want to abort is not tagged, unbusy it first so that
- * we don't get held back from sending the command.
+ * want to abort is not tagged, then this will be the only
+ * outstanding command and we can simply shove it on the
+ * qoutfifo and be done. If it is tagged, then it goes right
+ * in with all the others, no problem :) We need to add it
+ * to the qinfifo and let the sequencer know it is there.
+ * Now, the only problem left to deal with is, *IF* this
+ * command completes, in spite of the MK_MESSAGE bit in the
+ * control byte, then we need to pick that up in the interrupt
+ * routine and clean things up. This *shouldn't* ever happen.
*/
- if ((scb->hscb->control & TAG_ENB) == 0)
- {
- aic7xxx_search_qinfifo(p, cmd->target, channel, cmd->lun,
- SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
- }
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Queueing device reset command.\n",
- p->host_no, CTL_OF_SCB(scb));
- if ( !(scb->flags & SCB_WAITINGQ) ) /* Make sure we don't already have */
- { /* an abort scb queued, or else we */
- /* corrupt the waiting queue and */
- /* active_cmds counter by queueing */
- /* again. */
- scbq_insert_head(&p->waiting_scbs, scb);
- scb->flags |= SCB_WAITINGQ;
- p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
- }
- else
- {
- scb->flags &= ~SCB_ABORT;
- }
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Queueing device reset "
+ "command.\n", p->host_no, CTL_OF_SCB(scb));
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ scb->flags |= SCB_QUEUED_ABORT;
result = SCSI_RESET_PENDING;
}
else if (result == -1)
{
result = SCSI_RESET_ERROR;
}
- outb(saved_scbptr, p->base + SCBPTR);
+ aic_outb(p, saved_scbptr, SCBPTR);
return (result);
}
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_panic_abort
+ *
+ * Description:
+ * Abort the current SCSI command(s).
+ *-F*************************************************************************/
+void
+aic7xxx_panic_abort(struct aic7xxx_host *p)
+{
+ int i;
+
+ printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION);
+ printk("Controller type:\n %s\n", board_names[p->board_name_index]);
+ for(i=0; i<MAX_TARGETS; i++)
+ {
+ if(p->dev_flags[i] & DEVICE_PRESENT)
+ {
+ printk(INFO_LEAD "dev_flags=0x%x, WDTR:%s, SDTR:%s, q_depth=%d:%d\n",
+ p->host_no, 0, i, 0, p->dev_flags[i],
+ (p->needwdtr_copy & (1 << i)) ? "Yes" : "No",
+ (p->needsdtr_copy & (1 << i)) ? "Yes" : "No",
+ p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]);
+ }
+ }
+ printk("SIMODE0=0x%x, SIMODE1=0x%x, SSTAT0=0x%x, SSTAT1=0x%x, INTSTAT=0x%x\n",
+ aic_inb(p, SIMODE0), aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+ aic_inb(p, SSTAT1), aic_inb(p, INTSTAT) );
+ printk("p->flags=0x%x, p->type=0x%x, sequencer %s paused\n",
+ p->flags, p->type,
+ (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+ panic("Stopping to debug\n");
+}
+
/*+F*************************************************************************
* Function:
* aic7xxx_abort
struct aic7xxx_host *p;
int result, found=0;
unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
- unsigned long processor_flags;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
Scsi_Cmnd *cmd_next, *cmd_prev;
p = (struct aic7xxx_host *) cmd->host->hostdata;
scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- save_flags(processor_flags);
- pause_sequencer(p);
- cli();
+ /*
+ * I added a new config option to the driver: "panic_on_abort" that will
+ * cause the driver to panic and the machine to stop on the first abort
+ * or reset call into the driver. At that point, it prints out a lot of
+ * usefull information for me which I can then use to try and debug the
+ * problem. Simply enable the boot time prompt in order to activate this
+ * code.
+ */
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p);
+
+ DRIVER_LOCK
/*
* Run the isr to grab any command in the QOUTFIFO and any other misc.
* code.
*/
- while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
+ pause_sequencer(p);
+ while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
{
- aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL);
+ aic7xxx_isr(p->irq, p, (void *)NULL);
pause_sequencer(p);
}
- if (scb == NULL) /* Totally bogus cmd since it points beyond our */
- { /* valid SCB range. The suspect scb hasn't been */
- /* allocated yet. */
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called with bogus Scsi_Cmnd->"
- "SCB mapping.\n", p->host_no, CTL_OF_CMD(cmd));
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
+ if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout))
+ /* Totally bogus cmd since it points beyond our */
+ { /* valid SCB range or doesn't even match it's own*/
+ /* timeout serial number. */
+ if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+ printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd "
+ "pointer.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_ABORT_NOT_RUNNING);
}
if (scb->cmd != cmd) /* Hmmm...either this SCB is currently free with a */
{
if (cmd_next == cmd)
{
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for command "
- "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
- if ( cmd_prev == NULL )
- p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
- else
- cmd_prev->host_scribble = cmd_next->host_scribble;
- cmd_next->done(cmd_next);
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
- return(SCSI_ABORT_SUCCESS);
- }
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Abort called for command "
+ "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
+ if ( cmd_prev == NULL )
+ p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+ else
+ cmd_prev->host_scribble = cmd_next->host_scribble;
+ cmd_next->done(cmd_next);
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
+ * completion */
+ }
cmd_prev = cmd_next;
cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
}
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Abort called for already completed"
- " command.\n", p->host_no, CTL_OF_CMD(cmd));
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
+ if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+ printk(INFO_LEAD "Abort called for already completed"
+ " command.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_ABORT_NOT_RUNNING);
}
if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
{
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB aborted once already, "
- "escalating.\n", p->host_no, CTL_OF_SCB(scb));
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB aborted once already, "
+ "escalating.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_ABORT_SNOOZE);
}
- if ( (p->flags & (RESET_PENDING | ABORT_PENDING)) ||
- (p->device_status[TARGET_INDEX(scb->cmd)].flags &
+ if ( (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) ||
+ (p->dev_flags[TARGET_INDEX(scb->cmd)] &
BUS_DEVICE_RESET_PENDING) )
{
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset/Abort pending for this "
- "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Reset/Abort pending for this "
+ "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_ABORT_PENDING);
}
found = 0;
- p->flags |= IN_ABORT;
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+ p->flags |= AHC_IN_ABORT;
+ if (aic7xxx_verbose & VERBOSE_ABORT)
+ printk(INFO_LEAD "Aborting scb %d, flags 0x%x\n",
p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
/*
* level code to reset the timeout.
*/
- if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
+ if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
{
/*
* Check to see if the sequencer is just sitting on this command, or
* if it's actively being run.
*/
- result = inb(p->base + LASTPHASE);
+ result = aic_inb(p, LASTPHASE);
switch (result)
{
case P_DATAOUT: /* For any of these cases, we can assume we are */
case P_STATUS: /* The SCSI_ABORT_SNOOZE will give us two abort */
case P_MESGOUT: /* chances to finish and then escalate to a */
case P_MESGIN: /* reset call */
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB is currently active. "
- "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
- unpause_sequencer(p, TRUE);
- p->flags &= ~IN_ABORT;
- scb->flags |= SCB_RECOVERY_SCB; /* Note the fact that we've been */
- p->flags |= ABORT_PENDING; /* here so we will know not to */
- restore_flags(processor_flags); /* muck with other SCBs if this */
- return(SCSI_ABORT_PENDING); /* one doesn't complete and clear */
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB is currently active. "
+ "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ p->flags &= ~AHC_IN_ABORT;
+ scb->flags |= SCB_RECOVERY_SCB; /* Note the fact that we've been */
+ p->flags |= AHC_ABORT_PENDING; /* here so we will know not to */
+ DRIVER_UNLOCK /* muck with other SCBs if this */
+ return(SCSI_ABORT_PENDING); /* one doesn't complete and clear */
break; /* out. */
default:
break;
{
int tindex = TARGET_INDEX(cmd);
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on waiting list and "
- "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB found on waiting list and "
+ "aborted.\n", p->host_no, CTL_OF_SCB(scb));
scbq_remove(&p->waiting_scbs, scb);
- scbq_remove(&p->device_status[tindex].delayed_scbs, scb);
- p->device_status[tindex].active_cmds++;
+ scbq_remove(&p->delayed_scbs[tindex], scb);
+ p->dev_active_cmds[tindex]++;
+ p->activescbs++;
scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
found = 1;
if ( found == 0 )
{
if ( ((found = aic7xxx_search_qinfifo(p, cmd->target,
- INT_TO_CHAN(cmd->channel),
- cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
- FALSE, NULL)) != 0) && (aic7xxx_verbose > 1))
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found in QINFIFO and "
+ cmd->channel,
+ cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+ FALSE, NULL)) != 0) &&
+ (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
+ printk(INFO_LEAD "SCB found in QINFIFO and "
"aborted.\n", p->host_no, CTL_OF_SCB(scb));
}
{
unsigned char scb_next_ptr;
prev_hscbptr = SCB_LIST_NULL;
- saved_hscbptr = inb(p->base + SCBPTR);
- next_hscbptr = inb(p->base + WAITING_SCBH);
+ saved_hscbptr = aic_inb(p, SCBPTR);
+ next_hscbptr = aic_inb(p, WAITING_SCBH);
while ( next_hscbptr != SCB_LIST_NULL )
{
- outb( next_hscbptr, p->base + SCBPTR );
- if ( scb->hscb->tag == inb(p->base + SCB_TAG) )
+ aic_outb(p, next_hscbptr, SCBPTR );
+ if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
{
found = 1;
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB found on hardware waiting"
- " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
- if ( prev_hscbptr == SCB_LIST_NULL )
- outb(inb(p->base + SCB_NEXT), p->base + WAITING_SCBH);
- else
- {
- scb_next_ptr = inb(p->base + SCB_NEXT);
- outb(prev_hscbptr, p->base + SCBPTR);
- outb(scb_next_ptr, p->base + SCB_NEXT);
- outb(next_hscbptr, p->base + SCBPTR);
- }
- outb(SCB_LIST_NULL, p->base + SCB_TAG);
- aic7xxx_add_curscb_to_free_list(p);
- scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB found on hardware waiting"
+ " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ if ( prev_hscbptr == SCB_LIST_NULL )
+ {
+ aic_outb(p, aic_inb(p, SCB_NEXT), WAITING_SCBH);
+ aic_outb(p, 0, SCSISEQ); /* stop the selection since we just
+ * grabbed the scb out from under the
+ * card */
+ }
+ else
+ {
+ scb_next_ptr = aic_inb(p, SCB_NEXT);
+ aic_outb(p, prev_hscbptr, SCBPTR);
+ aic_outb(p, scb_next_ptr, SCB_NEXT);
+ aic_outb(p, next_hscbptr, SCBPTR);
+ }
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic7xxx_add_curscb_to_free_list(p);
+ scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
break;
}
prev_hscbptr = next_hscbptr;
- next_hscbptr = inb(p->base + SCB_NEXT);
+ next_hscbptr = aic_inb(p, SCB_NEXT);
}
- outb( saved_hscbptr, p->base + SCBPTR );
+ aic_outb(p, saved_hscbptr, SCBPTR );
}
/*
if ( found == 0 )
{
- p->flags |= ABORT_PENDING;
+ p->flags |= AHC_ABORT_PENDING;
scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
- scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+ scb->hscb->control |= MK_MESSAGE;
result=aic7xxx_find_scb(p, scb);
if ( result != SCB_LIST_NULL )
{
- saved_hscbptr = inb(p->base + SCBPTR);
- outb(result, p->base + SCBPTR);
- tmp_char = inb(p->base + SCB_CONTROL);
- outb( tmp_char | MK_MESSAGE | ABORT_SCB, p->base + SCB_CONTROL);
- outb(saved_hscbptr, p->base + SCBPTR);
+ saved_hscbptr = aic_inb(p, SCBPTR);
+ aic_outb(p, result, SCBPTR);
+ tmp_char = aic_inb(p, SCB_CONTROL);
+ aic_outb(p, tmp_char | MK_MESSAGE, SCB_CONTROL);
+ aic_outb(p, saved_hscbptr, SCBPTR);
}
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) SCB disconnected. Queueing Abort"
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB disconnected. Queueing Abort"
" SCB.\n", p->host_no, CTL_OF_SCB(scb));
- if ( (scb->hscb->control & TAG_ENB) == 0 )
- {
- aic7xxx_search_qinfifo(p, cmd->target, INT_TO_CHAN(cmd->channel),
- cmd->lun, SCB_LIST_NULL, 0, TRUE, &p->waiting_scbs);
- }
- if ( !(scb->flags & SCB_WAITINGQ) )
- {
- scbq_insert_head(&p->waiting_scbs, scb);
- scb->flags |= SCB_WAITINGQ;
- p->device_status[TARGET_INDEX(scb->cmd)].active_cmds--;
- }
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
}
- else
- {
- scb->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
- scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ if (found)
+ {
+ aic7xxx_run_done_queue(p, TRUE);
+ aic7xxx_run_waiting_queues(p);
}
- aic7xxx_run_done_queue(p, TRUE);
- aic7xxx_run_waiting_queues(p);
- p->flags &= ~IN_ABORT;
- restore_flags(processor_flags);
+ p->flags &= ~AHC_IN_ABORT;
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
/*
* On the return value. If we found the command and aborted it, then we know
struct aic7xxx_host *p;
int tindex;
int result = -1;
- char channel = 'A';
- unsigned long processor_flags;
-#define DEVICE_RESET 0x01
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
+#define DEVICE_RESET 0x01
#define BUS_RESET 0x02
#define HOST_RESET 0x04
#define FAIL 0x08
-#define RESET_DELAY 0x10
- int action;
+#define RESET_DELAY 0x10
+ int action;
Scsi_Cmnd *cmd_prev, *cmd_next;
if ( cmd == NULL )
{
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(aic7xxx) Reset called with NULL Scsi_Cmnd "
- "pointer, failing.\n");
+ printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL Scsi_Cmnd "
+ "pointer, failing.\n");
return(SCSI_RESET_SNOOZE);
}
p = (struct aic7xxx_host *) cmd->host->hostdata;
scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- channel = INT_TO_CHAN(cmd->channel);
tindex = TARGET_INDEX(cmd);
- save_flags(processor_flags);
+ /*
+ * I added a new config option to the driver: "panic_on_abort" that will
+ * cause the driver to panic and the machine to stop on the first abort
+ * or reset call into the driver. At that point, it prints out a lot of
+ * usefull information for me which I can then use to try and debug the
+ * problem. Simply enable the boot time prompt in order to activate this
+ * code.
+ */
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p);
+
+ DRIVER_LOCK
+
pause_sequencer(p);
- cli();
- while ( (inb(p->base + INTSTAT) & INT_PEND) && !(p->flags & IN_ISR) )
+ while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
{
- aic7xxx_isr(p->irq, (void *)NULL, (void *)NULL );
+ aic7xxx_isr(p->irq, p, (void *)NULL );
pause_sequencer(p);
}
if (scb == NULL)
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with bogus Scsi_Cmnd"
- "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd"
+ "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
{
action = HOST_RESET;
}
else if (scb->cmd != cmd)
{
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called with recycled SCB "
- "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called with recycled SCB "
+ "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
cmd_prev = NULL;
cmd_next = p->completeq.head;
while ( cmd_next != NULL )
{
if (cmd_next == cmd)
{
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, found cmd on completeq"
- ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
- if ( cmd_prev == NULL )
- p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
- else
- cmd_prev->host_scribble = cmd_next->host_scribble;
- cmd_next->done(cmd_next);
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
- return(SCSI_RESET_SUCCESS);
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Reset, found cmd on completeq"
+ ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_NOT_RUNNING);
}
cmd_prev = cmd_next;
cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
}
if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset, cmd not found,"
- " failing.\n", p->host_no, CTL_OF_CMD(cmd));
- unpause_sequencer(p, TRUE);
- restore_flags(processor_flags);
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Reset, cmd not found,"
+ " failing.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_RESET_NOT_RUNNING);
}
else
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, no scb, "
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called, no scb, "
"flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
scb = NULL;
action = HOST_RESET;
}
else
{
- if (aic7xxx_verbose)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called, scb %d, flags "
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called, scb %d, flags "
"0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ if ( aic7xxx_scb_on_qoutfifo(p, scb) )
+ {
+ if(aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_NOT_RUNNING);
+ }
if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
{
action = HOST_RESET;
action = DEVICE_RESET;
}
}
- if ( ((jiffies - p->last_reset) < (HZ * AIC7XXX_RESET_DELAY)) &&
- (action & (HOST_RESET | BUS_RESET | DEVICE_RESET)) )
+ if ( (action & DEVICE_RESET) &&
+ (p->dev_flags[tindex] & BUS_DEVICE_RESET_PENDING) )
{
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after "
- "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
- action = RESET_DELAY;
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset already sent to "
+ "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
}
- if ( ((jiffies - p->device_status[tindex].last_reset) <
- (HZ * AIC7XXX_RESET_DELAY)) && !(action & (HOST_RESET | BUS_RESET)))
+ if ( (action & DEVICE_RESET) &&
+ (scb->flags & SCB_QUEUED_ABORT) )
{
- if (aic7xxx_verbose > 1)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Reset called too soon after last "
- "reset without requesting\n"
- "(scsi%d:%d:%d:%d) bus or host reset, escalating.\n", p->host_no,
- CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ {
+ printk(INFO_LEAD "Have already attempted to reach "
+ "device with queued\n", p->host_no, CTL_OF_CMD(cmd));
+ printk(INFO_LEAD "message, will escalate to bus "
+ "reset.\n", p->host_no, CTL_OF_CMD(cmd));
+ }
action = BUS_RESET;
}
if ( (action & DEVICE_RESET) &&
- (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING) )
+ (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) )
{
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset already sent to "
- "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset stupid when "
+ "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
action = BUS_RESET;
}
- if ( (action & DEVICE_RESET) &&
- (scb->flags & SCB_QUEUED_ABORT) )
+ if ( (action & BUS_RESET) && !(p->type & AHC_TWIN) )
{
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Have already attempted to reach "
- "device with queued\n(scsi%d:%d:%d:%d) message, will escalate to bus "
- "reset.\n", p->host_no, CTL_OF_CMD(cmd), p->host_no, CTL_OF_CMD(cmd));
- action = BUS_RESET;
+ action = HOST_RESET;
}
- if ( (action & DEVICE_RESET) && (p->flags & (RESET_PENDING | ABORT_PENDING)) )
+ if ( ((jiffies - p->dev_last_reset[tindex]) < (HZ * 3)) &&
+ !(action & (HOST_RESET | BUS_RESET)))
{
- if (aic7xxx_verbose > 2)
- printk(KERN_WARNING "(scsi%d:%d:%d:%d) Bus device reset stupid when "
- "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ {
+ printk(INFO_LEAD "Reset called too soon after last "
+ "reset without requesting\n", p->host_no, CTL_OF_CMD(cmd));
+ printk(INFO_LEAD "bus or host reset, escalating.\n", p->host_no,
+ CTL_OF_CMD(cmd));
+ }
action = BUS_RESET;
}
- if ( (action & BUS_RESET) && (p->bus_type != AIC_TWIN) )
+ if ( ((jiffies - p->last_reset) < (HZ * 3)) &&
+ (action & (HOST_RESET | BUS_RESET)) )
{
- action = HOST_RESET;
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Reset called too soon after "
+ "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = RESET_DELAY;
}
- if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & RESET_PENDING)
- && ((jiffies - p->reset_start) > (2 * HZ * AIC7XXX_RESET_DELAY)) )
+ if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & AHC_IN_RESET)
+ && ((jiffies - p->reset_start) > (2 * HZ * 3)) )
{
printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!! Card must have left to go "
- "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
- restore_flags(processor_flags);
+ "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_RESET_SNOOZE);
}
/*
switch (action)
{
case RESET_DELAY:
- restore_flags(processor_flags);
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_RESET_PENDING);
break;
case FAIL:
- restore_flags(processor_flags);
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(SCSI_RESET_ERROR);
break;
case DEVICE_RESET:
- p->flags |= RESET_PENDING;
+ p->flags |= AHC_IN_RESET;
result = aic7xxx_bus_device_reset(p, cmd);
aic7xxx_run_done_queue(p, TRUE);
+ /* We can't rely on run_waiting_queues to unpause the sequencer for
+ * PCI based controllers since we use AAP */
aic7xxx_run_waiting_queues(p);
- p->flags &= ~RESET_PENDING;
- restore_flags(processor_flags);
+ unpause_sequencer(p, FALSE);
+ p->flags &= ~AHC_IN_RESET;
+ DRIVER_UNLOCK
return(result);
break;
case BUS_RESET:
case HOST_RESET:
default:
p->reset_start = jiffies;
- p->flags |= RESET_PENDING;
- aic7xxx_reset_channel(p, channel, TRUE);
- if ( (p->bus_type == AIC_TWIN) && (action & HOST_RESET) )
+ p->flags |= AHC_IN_RESET;
+ aic7xxx_reset_channel(p, cmd->channel, TRUE);
+ if ( (p->type & AHC_TWIN) && (action & HOST_RESET) )
{
- aic7xxx_reset_channel(p, (channel == 'A') ? 'B' : 'A', TRUE);
+ aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
restart_sequencer(p);
- pause_sequencer(p);
}
if (scb == NULL)
{
- cmd->result = DID_RESET << 16;
+ cmd->result = DID_RESET << 16;
cmd->done(cmd);
}
p->last_reset = jiffies;
else
{
result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
- while (inb(p->base + QOUTCNT)) inb(p->base + QOUTFIFO);
- if (p->flags & PAGE_ENABLED) outb(0, p->base + CMDOUTCNT);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE),
+ SIMODE1);
aic7xxx_clear_intstat(p);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_index = 0;
+ p->msg_len = 0;
}
- p->flags &= ~RESET_PENDING;
+ p->flags &= ~AHC_IN_RESET;
+ /* We can't rely on run_waiting_queues to unpause the sequencer for
+ * PCI based controllers since we use AAP */
aic7xxx_run_waiting_queues(p);
- restore_flags(processor_flags);
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
return(result);
break;
}
sectors = 32;
cylinders = disk->capacity / (heads * sectors);
- if ((p->flags & EXTENDED_TRANSLATION) && (cylinders > 1024))
+ if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
{
heads = 255;
sectors = 63;
#ifndef _aic7xxx_h
#define _aic7xxx_h
-#define AIC7XXX_H_VERSION "$Revision: 3.2.1 $"
+#define AIC7XXX_H_VERSION "3.2.4"
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+#if defined(__i386__)
+# define AIC7XXX_BIOSPARAM aic7xxx_biosparam
+#else
+# define AIC7XXX_BIOSPARAM NULL
+#endif
/*
* Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
* to do with card config are filled in after the card is detected.
*/
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,65)
+#define AIC7XXX { \
+ next: NULL, \
+ module: NULL, \
+ proc_dir: NULL, \
+ proc_info: aic7xxx_proc_info, \
+ name: NULL, \
+ detect: aic7xxx_detect, \
+ release: NULL, \
+ info: aic7xxx_info, \
+ command: NULL, \
+ queuecommand: aic7xxx_queue, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: NULL, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: NULL, \
+ eh_host_reset_handler: NULL, \
+ abort: aic7xxx_abort, \
+ reset: aic7xxx_reset, \
+ slave_attach: NULL, \
+ bios_param: AIC7XXX_BIOSPARAM, \
+ can_queue: 255, /* max simultaneous cmds */\
+ this_id: -1, /* scsi id of host adapter */\
+ sg_tablesize: 0, /* max scatter-gather cmds */\
+ cmd_per_lun: 3, /* cmds per lun (linked cmds) */\
+ present: 0, /* number of 7xxx's present */\
+ unchecked_isa_dma: 0, /* no memory DMA restrictions */\
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+#else
#define AIC7XXX { \
- NULL, \
- NULL, \
- NULL, \
- aic7xxx_proc_info, \
- NULL, \
- aic7xxx_detect, \
- NULL, \
- aic7xxx_info, \
- NULL, \
- aic7xxx_queue, \
- aic7xxx_abort, \
- aic7xxx_reset, \
- NULL, \
- aic7xxx_biosparam, \
- -1, /* max simultaneous cmds */\
- -1, /* scsi id of host adapter */\
- 0, /* max scatter-gather cmds */\
- 2, /* cmds per lun (linked cmds) */\
- 0, /* number of 7xxx's present */\
- 0, /* no memory DMA restrictions */\
- ENABLE_CLUSTERING \
+ next: NULL, \
+ usage_count: NULL, \
+ proc_dir: NULL, \
+ proc_info: aic7xxx_proc_info, \
+ name: NULL, \
+ detect: aic7xxx_detect, \
+ release: NULL, \
+ info: aic7xxx_info, \
+ command: NULL, \
+ queuecommand: aic7xxx_queue, \
+ abort: aic7xxx_abort, \
+ reset: aic7xxx_reset, \
+ slave_attach: NULL, \
+ bios_param: AIC7XXX_BIOSPARAM, \
+ can_queue: 255, /* max simultaneous cmds */\
+ this_id: -1, /* scsi id of host adapter */\
+ sg_tablesize: 0, /* max scatter-gather cmds */\
+ cmd_per_lun: 3, /* cmds per lun (linked cmds) */\
+ present: 0, /* number of 7xxx's present */\
+ unchecked_isa_dma: 0, /* no memory DMA restrictions */\
+ use_clustering: ENABLE_CLUSTERING \
}
+#endif
extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
+ * 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Where this Software is combined with software released under the terms of
bit ONEBIT 0x08
}
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection. On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+ address 0x01b
+ access_mode RW
+ bit SOFT1 0x80
+ bit SOFT0 0x40
+ bit SOFTCMDEN 0x20
+ bit HAS_BRDCTL 0x10 /* External Board control */
+ bit SEEPROM 0x08 /* External serial eeprom logic */
+ bit EEPROM 0x04 /* Writable external BIOS ROM */
+ bit ROM 0x02 /* Logic for accessing external ROM */
+ bit SSPIOCPS 0x01 /* Termination and cable detection */
+}
+
/*
* SCSI Block Control (p. 3-32)
* Controls Bus type and channel selection. In a twin channel configuration
mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/
mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */
- mask NO_MATCH_BUSY 0x50|SEQINT /* Couldn't find BUSY SCB */
+ mask ABORT_REQUESTED 0x50|SEQINT /* Reconect of aborted SCB */
mask REJECT_MSG 0x60|SEQINT /* Reject message received */
mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */
- mask ABORT_CMDCMPLT 0x91 /*
- * Command tagged for abort
- * completed successfully.
- */
mask AWAITING_MSG 0xa0|SEQINT /*
* Kernel requested to specify
- * a message to this target
- * (command was null), so tell
- * it that it can fill the
- * message buffer.
- */
- mask MSG_BUFFER_BUSY 0xc0|SEQINT /*
- * Sequencer wants to use the
- * message buffer, but it
- * already contains a message
+ * a message to this target
+ * (command was null), so tell
+ * it that it can fill the
+ * message buffer.
*/
+ mask TRACEPOINT 0xb0|SEQINT
+ mask TRACEPOINT2 0xc0|SEQINT
mask MSGIN_PHASEMIS 0xd0|SEQINT /*
* Target changed phase on us
* when we were expecting
register ERROR {
address 0x092
access_mode RO
- bit PARERR 0x08
+ bit PCIERRSTAT 0x40 /* PCI only */
+ bit MPARERR 0x20 /* PCI only */
+ bit DPARERR 0x10 /* PCI only */
+ bit SQPARERR 0x08
bit ILLOPCODE 0x04
bit ILLSADDR 0x02
bit ILLHADDR 0x01
register CLRINT {
address 0x092
access_mode WO
+ bit CLRPARERR 0x10 /* PCI only */
bit CLRBRKADRINT 0x08
bit CLRSCSIINT 0x04
bit CLRCMDINT 0x02
bit MK_MESSAGE 0x80
bit DISCENB 0x40
bit TAG_ENB 0x20
- bit MUST_DMAUP_SCB 0x10
- bit ABORT_SCB 0x08
bit DISCONNECTED 0x04
mask SCB_TAG_TYPE 0x03
}
size 4
}
SCB_DATACNT {
- size 3
- }
- SCB_LINKED_NEXT {
- size 1
+ /*
+ * Really only 3 bytes, but padded to make
+ * the kernel's job easier.
+ */
+ size 4
}
SCB_CMDPTR {
size 4
TARG_SCRATCH {
size 16
}
+ /*
+ * Bit vector of targets that have ULTRA enabled.
+ */
ULTRA_ENB {
size 2
}
size 2
}
/*
- * Length of pending message
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
*/
- MSG_LEN {
- size 1
- }
- /* We reserve 8bytes to store outgoing messages */
MSG_OUT {
- size 8
+ size 1
}
/* Parameters for DMA Logic */
DMAPARAMS {
bit FIFOFLUSH 0x02
bit FIFORESET 0x01
}
- /*
- * Number of SCBs supported by
- * this card.
- */
- SCBCOUNT {
- size 1
- }
- /*
- * Two's complement of SCBCOUNT
- */
- COMP_SCBCOUNT {
- size 1
- }
- /*
- * Mask of bits to test against
- * when looking at the Queue Count
- * registers. Works around a bug
- * on aic7850 chips.
- */
- QCNTMASK {
- size 1
- }
SEQ_FLAGS {
size 1
- bit RESELECTED 0x80
- bit IDENTIFY_SEEN 0x40
- bit TAGGED_SCB 0x20
+ bit IDENTIFY_SEEN 0x80
+ bit SCBPTR_VALID 0x20
bit DPHASE 0x10
- bit PAGESCBS 0x04
+ bit AMTARGET 0x08
bit WIDE_BUS 0x02
bit TWIN_BUS 0x01
}
SAVED_TCL {
size 1
}
+ /* Working value of the number of SG segments left */
SG_COUNT {
size 1
}
- /* working value of SG pointer */
+ /* Working value of SG pointer */
SG_NEXT {
size 4
}
- /*
- * head of list of SCBs awaiting
- * selection
- */
- WAITING_SCBH {
- size 1
- }
- SAVED_LINKPTR {
- size 1
- }
- SAVED_SCBPTR {
- size 1
- }
- /*
- * The sequencer will stick the frist byte of any rejected message here
- * so we can see what is getting thrown away.
- */
- REJBYTE {
- size 1
- }
/*
* The last bus phase as seen by the sequencer.
*/
mask P_MESGIN CDI|IOI|MSGI
mask P_BUSFREE 0x01
}
- MSGIN_EXT_LEN {
- size 1
- }
- MSGIN_EXT_OPCODE {
- size 1
- }
/*
- * location 3, stores the last
- * byte of an extended message if
- * it passes the two bytes of space
- * we allow now. This byte isn't
- * used for anything, it just makes
- * the code shorter for tossing
- * extra bytes.
+ * head of list of SCBs awaiting
+ * selection
*/
- MSGIN_EXT_BYTES {
- size 3
+ WAITING_SCBH {
+ size 1
}
/*
* head of list of SCBs that are
FREE_SCBH {
size 1
}
+ /*
+ * Address of the hardware scb array in the host.
+ */
HSCB_ADDR {
size 4
}
- CUR_SCBID {
+ /*
+ * Address of the 256 byte array storing the SCBID of outstanding
+ * untagged SCBs indexed by TCL.
+ */
+ SCBID_ADDR {
+ size 4
+ }
+ /*
+ * Address of the array of command descriptors used to store
+ * information about incoming selections.
+ */
+ TMODE_CMDADDR {
+ size 4
+ }
+ KERNEL_QINPOS {
+ size 1
+ }
+ QINPOS {
+ size 1
+ }
+ QOUTPOS {
+ size 1
+ }
+ /*
+ * Offset into the command descriptor array for the next
+ * available desciptor to use.
+ */
+ TMODE_CMDADDR_NEXT {
size 1
}
ARG_1 {
mask SEND_MSG 0x80
mask SEND_SENSE 0x40
mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
alias RETURN_1
}
/*
- * Running count of commands placed in
- * the QOUTFIFO. This is cleared by the
- * kernel driver every FIFODEPTH commands.
- *
- * NOTE: These scratch RAM registers are overlaying SCSICONF
- * and SCSICONF2 and are only used on cards that are
- * capable of SCB paging. Currently, only the PCI
- * controllers can do this, which is good because the
- * AIC-7770 based controllers use the SCSICONF register
- * to control termination. In other words, do not
- * destroy the contents of SCSICONF and SCSICONF2 for
- * AIC-7770 based controllers.
+ * Snapshot of MSG_OUT taken after each message is sent.
*/
- CMDOUTCNT {
- size 1
- }
- /*
- * Maximum number of entries allowed in
- * the QOUT/INFIFO.
- */
- FIFODEPTH {
+ LAST_MSG {
size 1
}
/*
SCSICONF {
address 0x05a
size 1
+ bit TERM_ENB 0x80
bit RESET_SCSI 0x40
- }
- SCSICONF2 {
- address 0x05b
- size 1
- bit RESET_SCSI 0x40
+ mask HSCSIID 0x07 /* our SCSI ID */
+ mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
}
HOSTCONF {
address 0x05d
const SCB_LIST_NULL 0xff
+/* Offsets into the SCBID array where different data is stored */
+const UNTAGGEDSCB_OFFSET 0
+const QOUTFIFO_OFFSET 1
+const QINFIFO_OFFSET 2
/* WDTR Message values */
const BUS_8_BIT 0x00
const BUS_32_BIT 0x02
const MAX_OFFSET_8BIT 0x0f
const MAX_OFFSET_16BIT 0x08
+const HOST_MSG 0xFF
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+const CMD_GROUP0_BYTE_DELTA -4
+const CMD_GROUP2_BYTE_DELTA -6
+const CMD_GROUP4_BYTE_DELTA 4
+const CMD_GROUP5_BYTE_DELTA 11
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+/*
+ * Mask of bits to test against when looking at the Queue Count
+ * registers. Works around a bug on aic7850 chips.
+ */
+const QCNTMASK download
+/*
+ * Number of command descriptors in the command descriptor array.
+ */
+const TMODE_NUMCMDS download
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
+ * 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Where this Software is combined with software released under the terms of
* $Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $
*/
-#include <aic7xxx.reg>
-#include <scsi_message.h>
+#include "aic7xxx.reg"
+#include "scsi_message.h"
/*
* A few words on the waiting SCB list:
* automatically consume the entries.
*/
-/*
- * We assume that the kernel driver may reset us at any time, even in the
- * middle of a DMA, so clear DFCNTRL too.
- */
reset:
clr SCSISIGO; /* De-assert BSY */
/* Always allow reselection */
+.if ( TARGET_MODE )
+ mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
+.else
mvi SCSISEQ, ENRSELI|ENAUTOATNP;
+.endif
call clear_target_state;
+ and SXFRCTL0, ~SPIOEN;
poll_for_work:
- test SSTAT0,SELDO jnz select;
- test SSTAT0,SELDI jnz reselect;
+ mov A, QINPOS;
+poll_for_work_loop:
+ and SEQCTL, ~PAUSEDIS;
+ test SSTAT0, SELDO|SELDI jnz selection;
test SCSISEQ, ENSELO jnz poll_for_work;
.if ( TWIN_CHANNEL )
/*
* either a selection or reselection occurs.
*/
xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
- test SSTAT0,SELDO jnz select;
- test SSTAT0,SELDI jnz reselect;
+ test SSTAT0, SELDO|SELDI jnz selection;
test SCSISEQ, ENSELO jnz poll_for_work;
xor SBLKCTL,SELBUSB; /* Toggle back */
.endif
cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
test_queue:
/* Has the driver posted any work for us? */
- mov A, QCNTMASK;
- test QINCNT,A jz poll_for_work;
+ or SEQCTL, PAUSEDIS;
+ cmp KERNEL_QINPOS, A je poll_for_work_loop;
+ inc QINPOS;
+ and SEQCTL, ~PAUSEDIS;
/*
* We have at least one queued SCB now and we don't have any
*/
.if ( SCB_PAGING )
mov ALLZEROS call get_free_or_disc_scb;
- cmp SINDEX, SCB_LIST_NULL je poll_for_work;
.endif
dequeue_scb:
- mov CUR_SCBID,QINFIFO;
+ add A, -1, QINPOS;
+ mvi QINFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
+ mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+ call dma_finish;
+ mov SINDEX, DFDAT;
.if !( SCB_PAGING )
/* In the non-paging case, the SCBID == hardware SCB index */
- mov SCBPTR, CUR_SCBID;
+ mov SCBPTR, SINDEX;
.endif
dma_queued_scb:
/*
* DMA the SCB from host ram into the current SCB location.
*/
mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov CUR_SCBID call dma_scb;
-
-/*
- * See if there is not already an active SCB for this target. This code
- * locks out on a per target basis instead of target/lun. Although this
- * is not ideal for devices that have multiple luns active at the same
- * time, it is faster than looping through all SCB's looking for active
- * commands. We also don't have enough spare SCB space for us to store the
- * SCBID of the currently busy transaction for each target/lun making it
- * impossible to link up the SCBs.
- */
-test_busy:
- test SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb;
- mvi SEQCTL, PAUSEDIS|FASTMODE;
- mov SAVED_SCBPTR, SCBPTR;
- mov SCB_TCL call index_untagged_scb;
- mov ARG_1, SINDIR; /*
- * ARG_1 should
- * now have the SCB ID of
- * any active, non-tagged,
- * command for this target.
- */
- cmp ARG_1, SCB_LIST_NULL je make_busy;
-.if ( SCB_PAGING )
- /*
- * Put this SCB back onto the free list. It
- * may be necessary to satisfy the search for
- * the active SCB.
- */
- mov SCBPTR, SAVED_SCBPTR;
- call add_scb_to_free_list;
- /* Find the active SCB */
- mov ALLZEROS call findSCB;
- /*
- * If we couldn't find it, tell the kernel. This should
- * never happen.
- */
- cmp SINDEX, SCB_LIST_NULL jne paged_busy_link;
- mvi INTSTAT, NO_MATCH_BUSY;
-paged_busy_link:
- /* Link us in */
- mov SCB_LINKED_NEXT, CUR_SCBID;
- /* Put it back on the disconnected list */
- call add_scb_to_disc_list;
- mvi SEQCTL, FASTMODE;
- jmp poll_for_work;
-.else
-simple_busy_link:
- mov SCBPTR, ARG_1;
- mov SCB_LINKED_NEXT, CUR_SCBID;
- mvi SEQCTL, FASTMODE;
- jmp poll_for_work;
-.endif
-make_busy:
- mov DINDIR, CUR_SCBID;
- mov SCBPTR, SAVED_SCBPTR;
- mvi SEQCTL, FASTMODE;
+ call dma_scb;
start_scb:
/*
mov WAITING_SCBH, SCBPTR;
start_waiting:
/*
- * Pull the first entry off of the waiting SCB list
- * We don't have to "test_busy" because only transactions that
- * have passed that test can be in the WAITING_SCB list.
+ * Pull the first entry off of the waiting SCB list.
*/
mov SCBPTR, WAITING_SCBH;
call start_selection;
and SCSIID, OID; /* Clear old target */
or SCSIID, A;
mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+
+/*
+ * Initialize Ultra mode setting and clear the SCSI channel.
+ * SINDEX should contain any additional bit's the client wants
+ * set in SXFRCTL0.
+ */
+initialize_channel:
+ or A, CLRSTCNT|CLRCHN, SINDEX;
+ or SXFRCTL0, A;
+.if ( ULTRA )
+ultra:
+ mvi SINDEX, ULTRA_ENB+1;
+ test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */
+ dec SINDEX;
+ultra_2:
+ mov FUNCTION1,SAVED_TCL;
+ mov A,FUNCTION1;
+ test SINDIR, A jz ndx_dtr;
+ or SXFRCTL0, FAST20;
+.endif
+
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ * The SCSIRATE settings for each target are stored in an array
+ * based at TARG_SCRATCH.
+ */
+ndx_dtr:
+ shr A,4,SAVED_TCL;
+ test SBLKCTL,SELBUSB jz ndx_dtr_2;
+ or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
+ or A,0x08; /* Channel B entries add 8 */
+ndx_dtr_2:
+ add SINDEX,TARG_SCRATCH,A;
+ mov SCSIRATE,SINDIR ret;
+
+
+selection:
+ test SSTAT0,SELDO jnz select_out;
+select_in:
+.if ( TARGET_MODE )
+ test SSTAT0, TARGET jz initiator_reselect;
+ /*
+ * We've just been selected. Assert BSY and
+ * setup the phase for receiving the messages
+ * from the target.
+ */
+ mvi SCSISIGO, P_MESGOUT|BSYO;
+ mvi CLRSINT0, CLRSELDO;
+
+ /*
+ * If ATN isn't asserted, go directly to bus free.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+
+ /*
+ * Setup the DMA for sending the identify and
+ * command information.
+ */
+ mov A, TMODE_CMDADDR_NEXT;
+ mvi TMODE_CMDADDR call set_32byte_haddr_and_clrcnt;
+ mvi DFCNTRL, FIFORESET;
+
+ clr SINDEX;
+ /* Watch ATN closely now */
+message_loop:
+ or SXFRCTL0, SPIOEN;
+ test SSTAT0, SPIORDY jz .;
+ and SXFRCTL0, ~SPIOEN;
+ mov DINDEX, SCSIDATL;
+ mov DFDAT, DINDEX;
+ inc SINDEX;
+
+ /* Message Testing... */
+ test DINDEX, MSG_IDENTIFYFLAG jz . + 2;
+ mov ARG_1, DINDEX;
+
+ test SCSISIGI, ATNI jnz message_loop;
+ add A, -4, SINDEX;
+ jc target_cmdphase;
+ mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */
+
+target_cmdphase:
+ add HCNT[0], 1, A;
+ mvi SCSISIGO, P_COMMAND|BSYO;
+ or SXFRCTL0, SPIOEN;
+ test SSTAT0, SPIORDY jz .;
+ mov A, SCSIDATL;
+ mov DFDAT, A; /* Store for host */
+
+ /*
+ * Determine the number of bytes to read
+ * based on the command group code. Count is
+ * one less than the total since we've already
+ * fetched the first byte.
+ */
+ clr SINDEX;
+ shr A, CMD_GROUP_CODE_SHIFT;
+ add SEQADDR0, A;
+
+ add SINDEX, CMD_GROUP0_BYTE_DELTA;
+ nop; /* Group 1 and 2 are the same */
+ add SINDEX, CMD_GROUP2_BYTE_DELTA;
+ nop; /* Group 3 is reserved */
+ add SINDEX, CMD_GROUP4_BYTE_DELTA;
+ add SINDEX, CMD_GROUP5_BYTE_DELTA;
+ /* Group 6 and 7 are not handled yet */
+
+ mov A, SINDEX;
+ add HCNT[0], A;
+
+command_loop:
+ test SSTAT0, SPIORDY jz .;
+ cmp SINDEX, 1 jne . + 2;
+ and SXFRCTL0, ~SPIOEN; /* Last Byte */
+ mov DFDAT, SCSIDATL;
+ dec SINDEX;
+ test SINDEX, 0xFF jnz command_loop;
+
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+
+ call dma_finish;
+
+ test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
+
+ mvi SCSISIGO, P_MESGIN|BSYO;
+
+ or SXFRCTL0, SPIOEN;
+
+ mvi MSG_DISCONNECT call target_outb;
+
+selectin_post:
+ inc TMODE_CMDADDR_NEXT;
+ cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
+ clr TMODE_CMDADDR_NEXT;
+ mvi QOUTFIFO, SCB_LIST_NULL;
+ mvi INTSTAT,CMDCMPLT;
+
+ test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
+
+ /* Busy loop on something then go to data or status phase */
+
+target_busfree:
+ clr SCSISIGO;
+ jmp poll_for_work;
+
+.endif /* TARGET_MODE */
/*
* Reselection has been initiated by a target. Make a note that we've been
* reselected, but haven't seen an IDENTIFY message from the target yet.
*/
-reselect:
- clr MSG_LEN; /* Don't have anything in the mesg buffer */
+initiator_reselect:
mvi CLRSINT0, CLRSELDI;
/* XXX test for and handle ONE BIT condition */
and SAVED_TCL, SELID_MASK, SELID;
- or SEQ_FLAGS,RESELECTED;
- jmp select2;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE; /*
+ * We aren't expecting a
+ * bus free, so interrupt
+ * the kernel driver if it
+ * happens.
+ */
+ mvi SPIOEN call initialize_channel;
+ mvi MSG_OUT, MSG_NOOP; /* No message to send */
+ jmp ITloop;
/*
* After the selection, remove this SCB from the "waiting SCB"
* WAITING_SCBH. Our next pointer will be set to null the next time this
* SCB is used, so don't bother with it now.
*/
-select:
+select_out:
/* Turn off the selection hardware */
mvi SCSISEQ, ENRSELI|ENAUTOATNP; /*
* ATN on parity errors
mov SCBPTR, WAITING_SCBH;
mov WAITING_SCBH,SCB_NEXT;
mov SAVED_TCL, SCB_TCL;
-/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted. Prepare
- * the message to send.
- *
- * Messages are stored in scratch RAM starting with a length byte
- * followed by the message itself.
- */
-
-mk_identify:
- and MSG_OUT,0x7,SCB_TCL; /* lun */
- and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */
- or MSG_OUT,A; /* or in disconnect privledge */
- or MSG_OUT,MSG_IDENTIFYFLAG;
- mvi MSG_LEN, 1;
-
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-mk_tag:
- test SCB_CONTROL,TAG_ENB jz mk_message;
- and MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
- mov MSG_OUT[2],SCB_TAG;
- add MSG_LEN,2; /* update message length */
-
-/*
- * Interrupt the driver, and allow it to tweak the message buffer
- * if it asks.
- */
-mk_message:
- test SCB_CONTROL,MK_MESSAGE jz select2;
- mvi INTSTAT,AWAITING_MSG;
-
-select2:
mvi CLRSINT1,CLRBUSFREE;
or SIMODE1, ENBUSFREE; /*
* We aren't expecting a
* the kernel driver if it
* happens.
*/
+ mvi SPIOEN call initialize_channel;
/*
- * Initialize Ultra mode setting and clear the SCSI channel.
- */
- or SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN;
-.if ( ULTRA )
-ultra:
- mvi SINDEX, ULTRA_ENB+1;
- test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */
- dec SINDEX;
-ultra_2:
- mov FUNCTION1,SAVED_TCL;
- mov A,FUNCTION1;
- test SINDIR, A jz ndx_dtr;
- or SXFRCTL0, FAST20;
-.endif
-
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCRATCH.
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted.
*/
-ndx_dtr:
- shr A,4,SAVED_TCL;
- test SBLKCTL,SELBUSB jz ndx_dtr_2;
- or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
- or A,0x08; /* Channel B entries add 8 */
-ndx_dtr_2:
- add SINDEX,TARG_SCRATCH,A;
- mov SCSIRATE,SINDIR;
-
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+ or SEQ_FLAGS, IDENTIFY_SEEN;
/*
- * Main loop for information transfer phases. If BSY is false, then
- * we have a bus free condition, expected or not. Otherwise, wait
- * for the target to assert REQ before checking MSG, C/D and I/O
- * for the bus phase.
- *
+ * Main loop for information transfer phases. Wait for the target
+ * to assert REQ before checking MSG, C/D and I/O for the bus phase.
*/
ITloop:
- test SSTAT1,REQINIT jz ITloop;
- test SSTAT1, SCSIPERR jnz ITloop;
+ call phase_lock;
- and A,PHASE_MASK,SCSISIGI;
- mov LASTPHASE,A;
- mov SCSISIGO,A;
+ mov A, LASTPHASE;
- cmp ALLZEROS,A je p_dataout;
- cmp A,P_DATAIN je p_datain;
+ test A, ~P_DATAIN jz p_data;
cmp A,P_COMMAND je p_command;
cmp A,P_MESGOUT je p_mesgout;
cmp A,P_STATUS je p_status;
and SIMODE1, ~ENBUSFREE;
call clear_target_state;
mov NONE, SCSIDATL; /* Ack the last byte */
+ and SXFRCTL0, ~SPIOEN;
test SSTAT1,REQINIT|BUSFREE jz .;
test SSTAT1, BUSFREE jnz poll_for_work;
mvi INTSTAT, BAD_PHASE;
clear_target_state:
- clr DFCNTRL;
+ clr DFCNTRL; /*
+ * We assume that the kernel driver
+ * may reset us at any time, even
+ * in the middle of a DMA, so clear
+ * DFCNTRL too.
+ */
clr SCSIRATE; /*
* We don't know the target we will
* connect to, so default to narrow
* transfers to avoid parity problems.
*/
- and SXFRCTL0, ~FAST20;
+ and SXFRCTL0, ~(FAST20);
mvi LASTPHASE, P_BUSFREE;
/* clear target specific flags */
- and SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret;
-
-p_dataout:
- mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET;
- jmp data_phase_init;
+ and SEQ_FLAGS, (WIDE_BUS|TWIN_BUS) ret;
/*
* If we re-enter the data phase after going through another phase, the
mvi SCB_RESID_DCNT call bcopy_3;
jmp data_phase_loop;
-p_datain:
+p_data:
mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
-data_phase_init:
+ test LASTPHASE, IOI jnz . + 2;
+ or DMAPARAMS, DIRECTION;
call assert; /*
* Ensure entering a data
* phase is okay - seen identify, etc.
mvi HCNT[1], 0xff;
mvi HCNT[2], 0xff;
call set_stcnt_from_hcnt;
+ and DMAPARAMS, ~(HDMAEN|SDMAEN);
data_phase_inbounds:
/* If we are the last SG block, ensure wideodd is off. */
jmp ITloop;
/*
- * Message out phase. If there is not an active message, but the target
- * took us into this phase anyway, build a no-op message and send it.
+ * Message out phase. If MSG_OUT is 0x80, build I full indentify message
+ * sequence and send it to the target. In addition, if the MK_MESSAGE bit
+ * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
+ * it's own message.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the hsot to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
*/
p_mesgout:
- test MSG_LEN, 0xff jnz p_mesgout_start;
- mvi MSG_NOOP call mk_mesg; /* build NOP message */
-p_mesgout_start:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+p_mesgout_identify:
+.if ( WIDE )
+ and SINDEX,0xf,SCB_TCL; /* lun */
+.else
+ and SINDEX,0x7,SCB_TCL; /* lun */
+.endif
+ and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */
+ or SINDEX,A; /* or in disconnect privledge */
+ or SINDEX,MSG_IDENTIFYFLAG;
+p_mesgout_mk_message:
+ test SCB_CONTROL,MK_MESSAGE jz p_mesgout_tag;
+ mov SCSIDATL, SINDEX; /* Send the last byte */
+ jmp p_mesgout_from_host + 1;/* Skip HOST_MSG test */
/*
- * Set up automatic PIO transfer from MSG_OUT. Bit 3 in
- * SXFRCTL0 (SPIOEN) is already on.
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
*/
- mvi SINDEX,MSG_OUT;
- mov DINDEX,MSG_LEN;
-
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDATL, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCB_TAG jmp p_mesgout_onebyte;
/*
- * When target asks for a byte, drop ATN if it's the last one in
- * the message. Otherwise, keep going until the message is exhausted.
- * ATN must be dropped *at least* 90ns before we ack the last byte, so
- * the code is aranged to execute two instructions before the byte is
- * transferred to give a good margin of safety
- *
- * Keep an eye out for a phase change, in case the target issues
- * a MESSAGE REJECT.
+ * Interrupt the driver, and allow it to send a message
+ * if it asks.
*/
-p_mesgout_loop:
- test SSTAT1, REQINIT jz p_mesgout_loop;
- test SSTAT1, SCSIPERR jnz p_mesgout_loop;
- and LASTPHASE, PHASE_MASK, SCSISIGI;
- cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
-p_mesgout_testretry:
- test DINDEX,0xff jnz p_mesgout_dropatn;
- or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
- jmp p_mesgout_start;
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ mvi INTSTAT,AWAITING_MSG;
+ /*
+ * Did the host detect a phase change?
+ */
+ cmp RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDATL, SINDEX;
+
/*
* If the next bus phase after ATN drops is a message out, it means
* that the target is requesting that the last message(s) be resent.
*/
-p_mesgout_dropatn:
- cmp DINDEX,1 jne p_mesgout_outb; /* last byte? */
- mvi CLRSINT1,CLRATNO; /* drop ATN */
-p_mesgout_outb:
- dec DINDEX;
- mov SCSIDATL,SINDIR;
- jmp p_mesgout_loop;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+ jmp p_mesgout;
p_mesgout_done:
mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
- clr MSG_LEN; /* no active msg */
+ mov LAST_MSG, MSG_OUT;
+ cmp MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
+ and SCB_CONTROL, ~MK_MESSAGE;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
jmp ITloop;
/*
*/
p_mesgin:
mvi ACCUM call inb_first; /* read the 1st message byte */
- mov REJBYTE,A; /* save it for the driver */
test A,MSG_IDENTIFYFLAG jnz mesgin_identify;
cmp A,MSG_DISCONNECT je mesgin_disconnect;
/*
* We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
* and trigger a completion interrupt. Before doing so, check to see if there
- * is a residual or the status byte is something other than NO_ERROR (0). In
- * either of these conditions, we upload the SCB back to the host so it can
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
* process this information. In the case of a non zero status byte, we
* additionally interrupt the kernel driver synchronously, allowing it to
* decide if sense should be retrieved. If the kernel driver wishes to request
* First check for residuals
*/
test SCB_RESID_SGCNT,0xff jnz upload_scb;
- test SCB_TARGET_STATUS,0xff jz status_ok; /* Good Status? */
+ test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */
upload_scb:
mvi DMAPARAMS, FIFORESET;
mov SCB_TAG call dma_scb;
check_status:
- test SCB_TARGET_STATUS,0xff jz status_ok; /* Just a residual? */
+ test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */
mvi INTSTAT,BAD_STATUS; /* let driver know */
- cmp RETURN_1, SEND_SENSE jne status_ok;
+ cmp RETURN_1, SEND_SENSE jne complete;
/* This SCB becomes the next to execute as it will retrieve sense */
- mov SCB_LINKED_NEXT, SCB_TAG;
- jmp dma_next_scb;
-
-status_ok:
-/* First, mark this target as free. */
- test SCB_CONTROL,TAG_ENB jnz complete; /*
- * Tagged commands
- * don't busy the
- * target.
- */
- mov SAVED_SCBPTR, SCBPTR;
- mov SAVED_LINKPTR, SCB_LINKED_NEXT;
- mov SCB_TCL call index_untagged_scb;
- mov DINDIR, SAVED_LINKPTR;
- mov SCBPTR, SAVED_SCBPTR;
-
-complete:
- /* Post the SCB and issue an interrupt */
-.if ( SCB_PAGING )
- /*
- * Spin loop until there is space
- * in the QOUTFIFO.
- */
- mov A, FIFODEPTH;
- cmp CMDOUTCNT, A je .;
- inc CMDOUTCNT;
-.endif
- mov QOUTFIFO,SCB_TAG;
- mvi INTSTAT,CMDCMPLT;
- test SCB_CONTROL, ABORT_SCB jz dma_next_scb;
- mvi INTSTAT, ABORT_CMDCMPLT;
-
-dma_next_scb:
- cmp SCB_LINKED_NEXT, SCB_LIST_NULL je add_to_free_list;
-.if !( SCB_PAGING )
- /* Only DMA on top of ourselves if we are the SCB to download */
- mov A, SCB_LINKED_NEXT;
- cmp SCB_TAG, A je dma_next_scb2;
- call add_scb_to_free_list;
- mov SCBPTR, A;
- jmp add_to_waiting_list;
-.endif
-dma_next_scb2:
mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov SCB_LINKED_NEXT call dma_scb;
+ mov SCB_TAG call dma_scb;
add_to_waiting_list:
mov SCB_NEXT,WAITING_SCBH;
mov WAITING_SCBH, SCBPTR;
*/
call start_selection;
jmp await_busfree;
+
+complete:
+ /* If we are untagged, clear our address up in host ram */
+ test SCB_CONTROL, TAG_ENB jnz complete_post;
+ mov A, SAVED_TCL;
+ mvi UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
+ mvi DFCNTRL, FIFORESET;
+ mvi DFDAT, SCB_LIST_NULL;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ call dma_finish;
+
+complete_post:
+ /* Post the SCB and issue an interrupt */
+ mov A, QOUTPOS;
+ mvi QOUTFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
+ mvi DFCNTRL, FIFORESET;
+ mov DFDAT, SCB_TAG;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ call dma_finish;
+ inc QOUTPOS;
+ mvi INTSTAT,CMDCMPLT;
+
add_to_free_list:
call add_scb_to_free_list;
jmp await_busfree;
* or simply to do nothing.
*/
mesgin_extended:
- mvi MSGIN_EXT_LEN call inb_next;
- mov A, MSGIN_EXT_LEN;
-mesgin_extended_loop:
- mov DINDEX call inb_next;
- dec A;
- cmp DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test;
- dec DINDEX; /* dump by repeatedly filling the last byte */
-mesgin_extended_loop_test:
- test A, 0xFF jnz mesgin_extended_loop;
-mesgin_extended_intr:
mvi INTSTAT,EXTENDED_MSG; /* let driver know */
- cmp RETURN_1,SEND_REJ je rej_mesgin;
- cmp RETURN_1,SEND_MSG jne mesgin_done;
-/* The kernel has setup a message to be sent */
- or SCSISIGO,ATNO,LASTPHASE; /* turn on ATNO */
- jmp mesgin_done;
+ jmp ITloop;
/*
* Is it a disconnect message? Set a flag in the SCB to remind us
*/
mesgin_disconnect:
or SCB_CONTROL,DISCONNECTED;
-.if ( SCB_PAGING )
call add_scb_to_disc_list;
-.endif
jmp await_busfree;
/*
* clearing the "disconnected" bit so we don't "find" it by accident later.
*/
mesgin_identify:
- test A,0x78 jnz rej_mesgin; /*!DiscPriv|!LUNTAR|!Reserved*/
+.if ( WIDE )
+ and A,0x0f; /* lun in lower four bits */
+.else
and A,0x07; /* lun in lower three bits */
+.endif
or SAVED_TCL,A; /* SAVED_TCL should be complete now */
- mov SAVED_TCL call index_untagged_scb;
- mov ARG_1, SINDIR;
+
+ call get_untagged_SCBID;
+ cmp ARG_1, SCB_LIST_NULL je snoop_tag;
.if ( SCB_PAGING )
- cmp ARG_1,SCB_LIST_NULL jne use_findSCB;
-.else
- cmp ARG_1,SCB_LIST_NULL je snoop_tag;
- /* Directly index the SCB */
- mov SCBPTR,ARG_1;
- test SCB_CONTROL,DISCONNECTED jz not_found;
- jmp setup_SCB;
+ test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB;
.endif
+ /*
+ * If the SCB was found in the disconnected list (as is
+ * always the case in non-paging scenarios), SCBPTR is already
+ * set to the correct SCB. So, simply setup the SCB and get
+ * on with things.
+ */
+ mov SCBPTR call rem_scb_from_disc_list;
+ jmp setup_SCB;
/*
* Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
* If we get one, we use the tag returned to find the proper
- * SCB. With SCB paging, this requires using findSCB for both tagged
+ * SCB. With SCB paging, this requires using search for both tagged
* and non-tagged transactions since the SCB may exist in any slot.
* If we're not using SCB paging, we can use the tag as the direct
* index to the SCB.
snoop_tag:
mov NONE,SCSIDATL; /* ACK Identify MSG */
snoop_tag_loop:
- test SSTAT1,REQINIT jz snoop_tag_loop;
- test SSTAT1, SCSIPERR jnz snoop_tag_loop;
- and LASTPHASE, PHASE_MASK, SCSISIGI;
+ call phase_lock;
cmp LASTPHASE, P_MESGIN jne not_found;
cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
get_tag:
- or SEQ_FLAGS, TAGGED_SCB;
mvi ARG_1 call inb_next; /* tag value */
-/*
- * See if the tag is in range. The tag is < SCBCOUNT if we add
- * the complement of SCBCOUNT to the incomming tag and there is
- * no carry.
- */
- mov A,COMP_SCBCOUNT;
- add SINDEX,A,ARG_1;
- jc not_found;
.if ! ( SCB_PAGING )
index_by_tag:
mov SCBPTR,ARG_1;
- mov A, SAVED_TCL;
- cmp SCB_TCL,A jne not_found;
test SCB_CONTROL,TAG_ENB jz not_found;
- test SCB_CONTROL,DISCONNECTED jz not_found;
+ mov SCBPTR call rem_scb_from_disc_list;
.else
/*
* Ensure that the SCB the tag points to is for an SCB transaction
* to the reconnecting target.
*/
-use_findSCB:
- mov ALLZEROS call findSCB; /* Have to search */
- cmp SINDEX, SCB_LIST_NULL je not_found;
+use_retrieveSCB:
+ call retrieveSCB;
.endif
setup_SCB:
+ mov A, SAVED_TCL;
+ cmp SCB_TCL, A jne not_found_cleanup_scb;
+ test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
and SCB_CONTROL,~DISCONNECTED;
or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ and SCB_CONTROL, ~MK_MESSAGE;
+ mvi HOST_MSG call mk_mesg;
jmp mesgin_done;
+not_found_cleanup_scb:
+ test SCB_CONTROL, DISCONNECTED jz . + 3;
+ call add_scb_to_disc_list;
+ jmp not_found;
+ call add_scb_to_free_list;
not_found:
mvi INTSTAT, NO_MATCH;
mvi MSG_BUS_DEV_RESET call mk_mesg;
* if there is no active message already. SINDEX is returned intact.
*/
mk_mesg:
- mvi SEQCTL, PAUSEDIS|FASTMODE;
- test MSG_LEN,0xff jz mk_mesg1; /* Should always succeed */
-
- /*
- * Hmmm. For some reason the mesg buffer is in use.
- * Tell the driver. It should look at SINDEX to find
- * out what we wanted to use the buffer for and resolve
- * the conflict.
- */
- mvi SEQCTL,FASTMODE;
- mvi INTSTAT,MSG_BUFFER_BUSY;
-
-mk_mesg1:
or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
- mvi MSG_LEN,1; /* length = 1 */
- mov MSG_OUT,SINDEX; /* 1-byte message */
- mvi SEQCTL,FASTMODE ret;
+ mov MSG_OUT,SINDEX ret;
/*
* Functions to read data in Automatic PIO mode.
inb_last:
mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
+.if ( TARGET_MODE )
+/*
+ * Send a byte to an initiator in Automatic PIO mode.
+ * SPIOEN must be on prior to calling this routine.
+ */
+target_outb:
+ mov SCSIDATL, SINDEX;
+ test SSTAT0, SPIORDY jz .;
+ ret;
+.endif
+
mesgin_phasemis:
/*
* We expected to receive another byte, but the target changed phase
* message.
*/
assert:
- test SEQ_FLAGS,RESELECTED jz return; /* reselected? */
test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */
mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */
-.if ( SCB_PAGING )
/*
* Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
- * or by the SCBIDn ARG_1. The search begins at the SCB index passed in
- * via SINDEX. If the SCB cannot be found, SINDEX will be SCB_LIST_NULL,
- * otherwise, SCBPTR is set to the proper SCB.
+ * or by the SCBID ARG_1. The search begins at the SCB index passed in
+ * via SINDEX which is an SCB that must be on the disconnected list. If
+ * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
+ * is set to the proper SCB.
*/
findSCB:
- mov SCBPTR,SINDEX; /* switch to next SCB */
+ mov SCBPTR,SINDEX; /* Initialize SCBPTR */
+ cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID;
+ mov A, SAVED_TCL;
+ mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */
+findSCB_by_SCBID:
mov A, ARG_1; /* Tag passed in ARG_1 */
- cmp SCB_TAG,A jne findSCB_loop;
- test SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/
+ mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */
+findSCB_next:
+ cmp SCB_NEXT, SCB_LIST_NULL je notFound;
+ mov SCBPTR,SCB_NEXT;
+ dec SINDEX; /* Last comparison moved us too far */
findSCB_loop:
- inc SINDEX;
- mov A,SCBCOUNT;
- cmp SINDEX,A jne findSCB;
+ cmp SINDIR, A jne findSCB_next;
+ mov SINDEX, SCBPTR ret;
+notFound:
+ mvi SINDEX, SCB_LIST_NULL ret;
+
/*
- * We didn't find it. If we're paging, pull an SCB and DMA down the
- * one we want. If we aren't paging or the SCB we dma down has the
- * abort flag set, return not found.
+ * Retrieve an SCB by SCBID first searching the disconnected list falling
+ * back to DMA'ing the SCB down from the host. This routine assumes that
+ * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
+ * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL,
+ * we go directly to the host for the SCB.
+ */
+retrieveSCB:
+ test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host;
+ mov SCBPTR call findSCB; /* Continue the search */
+ cmp SINDEX, SCB_LIST_NULL je retrieve_from_host;
+
+/*
+ * This routine expects SINDEX to contain the index of the SCB to be
+ * removed and SCBPTR to be pointing to that SCB.
*/
- mov ALLZEROS call get_free_or_disc_scb;
- mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov ARG_1 call dma_scb;
- test SCB_RESID_SGCNT, 0xff jz . + 2;
- or SCB_CONTROL, MUST_DMAUP_SCB;
- test SCB_CONTROL, ABORT_SCB jz return;
-find_error:
- mvi SINDEX, SCB_LIST_NULL ret;
-foundSCB:
- test SCB_CONTROL, ABORT_SCB jnz find_error;
rem_scb_from_disc_list:
/* Remove this SCB from the disconnection list */
cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev;
- mov SAVED_LINKPTR, SCB_PREV;
+ mov DINDEX, SCB_PREV;
mov SCBPTR, SCB_NEXT;
- mov SCB_PREV, SAVED_LINKPTR;
+ mov SCB_PREV, DINDEX;
mov SCBPTR, SINDEX;
unlink_prev:
cmp SCB_PREV,SCB_LIST_NULL je rHead;/* At the head of the list */
- mov SAVED_LINKPTR, SCB_NEXT;
+ mov DINDEX, SCB_NEXT;
mov SCBPTR, SCB_PREV;
- mov SCB_NEXT, SAVED_LINKPTR;
+ mov SCB_NEXT, DINDEX;
mov SCBPTR, SINDEX ret;
rHead:
mov DISCONNECTED_SCBH,SCB_NEXT ret;
-.else
- ret;
-.endif
+
+retrieve_from_host:
+/*
+ * We didn't find it. Pull an SCB and DMA down the one we want.
+ * We should never get here in the non-paging case.
+ */
+ mov ALLZEROS call get_free_or_disc_scb;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ /* Jump instead of call as we want to return anyway */
+ mov ARG_1 jmp dma_scb;
+
+/*
+ * Determine whether a target is using tagged or non-tagged transactions
+ * by first looking for a matching transaction based on the TCL and if
+ * that fails, looking up this device in the host's untagged SCB array.
+ * The TCL to search for is assumed to be in SAVED_TCL. The value is
+ * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
+ * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
+ * in an SCB instead of having to go to the host.
+ */
+get_untagged_SCBID:
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
+ mvi ARG_1, SCB_LIST_NULL;
+ mov DISCONNECTED_SCBH call findSCB;
+ cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host;
+ or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
+ test SCB_CONTROL, TAG_ENB jnz . + 2;
+ mov ARG_1, SCB_TAG ret;
+ mvi ARG_1, SCB_LIST_NULL ret;
+
+set_SCBID_host_addr_and_cnt:
+ mov DINDEX, SINDEX;
+ mvi SCBID_ADDR call set_1byte_haddr_and_clrcnt;
+ mvi HCNT[0], 1 ret;
+
+get_SCBID_from_host:
+ mov A, SAVED_TCL;
+ mvi UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
+ mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+ call dma_finish;
+ mov ARG_1, DFDAT ret;
+
+phase_lock:
+ test SSTAT1, REQINIT jz phase_lock;
+ test SSTAT1, SCSIPERR jnz phase_lock;
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ mov SCSISIGO, LASTPHASE ret;
set_stcnt_from_hcnt:
mov STCNT[0], HCNT[0];
mov DINDIR, SINDIR;
mov DINDIR, SINDIR ret;
+/*
+ * Setup haddr and count assuming that A is an
+ * index into an array of 32byte objects.
+ */
+set_32byte_haddr_and_clrcnt:
+ shr DINDEX, 3, A;
+ shl A, 5;
+set_1byte_haddr_and_clrcnt: /* DINDEX must be 0 upon call */
+ add HADDR[0], A, SINDIR;
+ mov A, DINDEX;
+ adc HADDR[1], A, SINDIR;
+ clr A;
+ adc HADDR[2], A, SINDIR;
+ adc HADDR[3], A, SINDIR;
+ /* Clear Count */
+ clr HCNT[1];
+ clr HCNT[2] ret;
+
dma_scb:
/*
* SCB index is in SINDEX. Determine the physical address in
* the host where this SCB is located and load HADDR with it.
*/
- shr DINDEX, 3, SINDEX;
- shl A, 5, SINDEX;
- add HADDR[0], A, HSCB_ADDR[0];
- mov A, DINDEX;
- adc HADDR[1], A, HSCB_ADDR[1];
- clr A;
- adc HADDR[2], A, HSCB_ADDR[2];
- adc HADDR[3], A, HSCB_ADDR[3];
- /* Setup Count */
+ mov A, SINDEX;
+ mvi HSCB_ADDR call set_32byte_haddr_and_clrcnt;
mvi HCNT[0], 28;
- clr HCNT[1];
- clr HCNT[2];
mov DFCNTRL, DMAPARAMS;
test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
/* Fill it with the SCB data */
test DFCNTRL, HDMAEN jnz .;
ret;
-index_untagged_scb:
- mov DINDEX, SINDEX;
- shr DINDEX, 4;
- and DINDEX, 0x03; /* Bottom two bits of tid */
- add DINDEX, SCB_BUSYTARGETS;
- shr A, 6, SINDEX; /* Target ID divided by 4 */
- test SINDEX, SELBUSB jz index_untagged_scb2;
- add A, 2; /* Add 2 positions */
-index_untagged_scb2:
- mov SCBPTR, A; /*
- * Select the SCB with this
- * target's information.
- */
- mov SINDEX, DINDEX ret;
-
add_scb_to_free_list:
+.if ( SCB_PAGING )
mov SCB_NEXT, FREE_SCBH;
- mvi SCB_TAG, SCB_LIST_NULL;
- mov FREE_SCBH, SCBPTR ret;
+ mov FREE_SCBH, SCBPTR;
+.endif
+ mvi SCB_TAG, SCB_LIST_NULL ret;
.if ( SCB_PAGING )
get_free_or_disc_scb:
mvi SINDEX, SCB_LIST_NULL ret;
dequeue_disc_scb:
mov SCBPTR, DISCONNECTED_SCBH;
-/*
- * Whenever we are going to dequeue a disconnected SCB and wipe it out
- * with a new SCB, we always send the SCB back to the kernel to make sure
- * any residual info and any save/restore pointers are saved for later use..
- */
dma_up_scb:
mvi DMAPARAMS, FIFORESET;
mov SCB_TAG call dma_scb;
dequeue_free_scb:
mov SCBPTR, FREE_SCBH;
mov FREE_SCBH, SCB_NEXT ret;
+.endif
add_scb_to_disc_list:
/*
mov SCBPTR,SCB_NEXT;
mov SCB_PREV,DISCONNECTED_SCBH;
mov SCBPTR,DISCONNECTED_SCBH ret;
-.endif
-/*
- * SCSI messages definitions.
- */
-
/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
#define MSG_CMDCOMPLETE 0x00 /* M/M */
#define MSG_EXTENDED 0x01 /* O/O */
/* Identify message */ /* M/M */
#define MSG_IDENTIFYFLAG 0x80
+#define MSG_IDENTIFY_DISCFLAG 0x40
#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG)
#define MSG_EXT_WDTR 0x03
#define MSG_EXT_WDTR_LEN 0x02
+#define MSG_EXT_WDTR_BUS_8_BIT 0x00
+#define MSG_EXT_WDTR_BUS_16_BIT 0x01
+#define MSG_EXT_WDTR_BUS_32_BIT 0x02
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
+ * 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Where this Software is combined with software released under the terms of
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $
+ * $Id: sequencer.h,v 1.3 1997/09/27 19:37:31 gibbs Exp $
*/
-#if defined(__KERNEL__)
-typedef unsigned char u_int8_t;
-#endif
-
struct ins_format1 {
- u_int8_t immediate;
- u_int8_t source;
- u_int8_t destination;
- u_int8_t opcode_ret;
+ unsigned char immediate;
+ unsigned char source;
+ unsigned char destination;
+ unsigned char opcode_ret;
+#define DOWNLOAD_CONST_IMMEDIATE 0x80
};
struct ins_format2 {
- u_int8_t shift_control;
- u_int8_t source;
- u_int8_t destination;
- u_int8_t opcode_ret;
+ unsigned char shift_control;
+ unsigned char source;
+ unsigned char destination;
+ unsigned char opcode_ret;
#define RETURN_BIT 0x01
};
struct ins_format3 {
- u_int8_t immediate;
- u_int8_t source;
- u_int8_t address;
- u_int8_t opcode_addr;
+ unsigned char immediate;
+ unsigned char source;
+ unsigned char address;
+ unsigned char opcode_addr;
#define ADDR_HIGH_BIT 0x01
};
+#ifndef __KERNEL__
struct instruction {
union {
struct ins_format1 format1;
struct ins_format2 format2;
struct ins_format3 format3;
- u_int8_t bytes[4];
+ unsigned char bytes[4];
} format;
u_int srcline;
struct symbol *patch_label;
- struct {
- struct instruction *stqe_next; /* next element */
- } links;
+ STAILQ_ENTRY(instruction) links;
};
+#endif
#define AIC_OP_OR 0x0
#define AIC_OP_AND 0x1
static int aic7xxx_buffer_size = 0;
static char *aic7xxx_buffer = NULL;
-static const char *bus_names[] = { "Single", "Twin", "Wide" };
-static const char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
- "AIC-787x", "AIC-788x" };
/*+F*************************************************************************
{
struct Scsi_Host *HBAptr;
struct aic7xxx_host *p;
- int found = FALSE;
int size = 0;
unsigned char i;
#ifdef AIC7XXX_PROC_STATS
#endif
HBAptr = NULL;
- for (i=0; i < NUMBER(aic7xxx_boards); i++)
- {
- if ((HBAptr = aic7xxx_boards[i]) != NULL)
- {
- if (HBAptr->host_no == hostno)
- {
- break;
- }
- while ((HBAptr->hostdata != NULL) && !found &&
- ((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
- {
- if (HBAptr->host_no == hostno)
- {
- found = TRUE;
- }
- }
-
- if (!found)
- {
- HBAptr = NULL;
- }
- else
- {
- break;
- }
- }
- }
+ for(p=first_aic7xxx; p->host->host_no != hostno; p=p->next)
+ ;
- if (HBAptr == NULL)
+ if (!p)
{
size += sprintf(buffer, "Can't find adapter for host number %d\n", hostno);
if (size > length)
}
}
+ HBAptr = p->host;
+
if (inout == TRUE) /* Has data been written to the file? */
{
return (aic7xxx_set_info(buffer, length, HBAptr));
* if proc_stats is defined, then we sweep the stats structure to see
* how many drives we will be printing out for and add 384 bytes per
* device with active stats.
+ *
+ * Hmmmm...that 1.5k seems to keep growing as items get added so they
+ * can be easily viewed for debugging purposes. So, we bumped that
+ * 1.5k to 4k so we can quit having to bump it all the time.
*/
- size = 1536;
+ size = 4096;
#ifdef AIC7XXX_PROC_STATS
for (target = 0; target < MAX_TARGETS; target++)
{
for (lun = 0; lun < MAX_LUNS; lun++)
{
if (p->stats[target][lun].xfers != 0)
- size += 384;
+ size += 512;
}
}
#endif
size = 0;
size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
- size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
- size += sprintf(BLS, "%s", rcs_version(AIC7XXX_H_VERSION));
-#if 0
- size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
-#endif
+ size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
+ size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Compile Options:\n");
#ifdef AIC7XXX_RESET_DELAY
size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY);
#endif
-#ifdef AIC7XXX_CMDS_PER_LUN
- size += sprintf(BLS, " AIC7XXX_CMDS_PER_LUN : %d\n", AIC7XXX_CMDS_PER_LUN);
-#endif
-#ifdef AIC7XXX_TAGGED_QUEUEING
- size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Enabled\n");
-#else
- size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Disabled\n");
-#endif
-#ifdef AIC7XXX_PAGE_ENABLE
- size += sprintf(BLS, " AIC7XXX_PAGE_ENABLE : Enabled\n");
-#else
- size += sprintf(BLS, " AIC7XXX_PAGE_ENABLE : Disabled\n");
-#endif
+ size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n");
+ size += sprintf(BLS, " Check below to see "
+ "which\n"
+ " devices use tagged "
+ "queueing\n");
+ size += sprintf(BLS, " AIC7XXX_PAGE_ENABLE : Enabled (This is no longer "
+ "an option)\n");
#ifdef AIC7XXX_PROC_STATS
size += sprintf(BLS, " AIC7XXX_PROC_STATS : Enabled\n");
#else
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Adapter Configuration:\n");
size += sprintf(BLS, " SCSI Adapter: %s\n",
- board_names[p->chip_type]);
- size += sprintf(BLS, " (%s chipset)\n",
- chip_names[p->chip_class]);
- size += sprintf(BLS, " Host Bus: %s\n", bus_names[p->bus_type]);
- size += sprintf(BLS, " Base IO: %#.4x\n", p->base);
- size += sprintf(BLS, " Base IO Memory: 0x%x\n", p->mbase);
+ board_names[p->board_name_index]);
+ if (p->flags & AHC_TWIN)
+ size += sprintf(BLS, " Twin Channel\n");
+ else
+ {
+ char *channel = "";
+ char *ultra = "";
+ char *wide = "Narrow ";
+ if (p->flags & AHC_MULTI_CHANNEL)
+ {
+ channel = " Channel A";
+ if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+ channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
+ }
+ if (p->type & AHC_WIDE)
+ wide = "Wide ";
+ if (p->type & AHC_ULTRA)
+ ultra = "Ultra ";
+ size += sprintf(BLS, " %s%sController%s\n",
+ ultra, wide, channel);
+ }
+ if( !(p->maddr) )
+ {
+ size += sprintf(BLS, " Programmed I/O Base: %lx\n", p->base);
+ }
+ else
+ {
+ size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
+ }
+ if( !(p->type & AHC_AIC78x0) )
+ {
+ size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address);
+ size += sprintf(BLS, " %s\n",
+ (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
+ }
+ else
+ {
+ size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n",
+ (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
+ }
size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
- size += sprintf(BLS, " SCBs: Used %d, HW %d, Page %d\n",
- p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs);
- size += sprintf(BLS, " Interrupts: %d", p->isr_count);
- if (p->chip_class == AIC_777x)
+ size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n",
+ p->activescbs, p->max_activescbs);
+ size += sprintf(BLS, " Allocated %d, HW %d, "
+ "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
+ p->scb_data->maxscbs);
+ size += sprintf(BLS, " Interrupts: %ld", p->isr_count);
+ if (p->type & AHC_AIC7770)
{
size += sprintf(BLS, " %s\n",
(p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
{
size += sprintf(BLS, "\n");
}
- size += sprintf(BLS, " Serial EEPROM: %s\n",
- (p->flags & HAVE_SEEPROM) ? "True" : "False");
+ size += sprintf(BLS, " BIOS Control Word: 0x%04x\n",
+ p->bios_control);
+ size += sprintf(BLS, " Adapter Control Word: 0x%04x\n",
+ p->adapter_control);
size += sprintf(BLS, " Extended Translation: %sabled\n",
- (p->flags & EXTENDED_TRANSLATION) ? "En" : "Dis");
+ (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
size += sprintf(BLS, " SCSI Bus Reset: %sabled\n",
aic7xxx_no_reset ? "Dis" : "En");
- size += sprintf(BLS, " Ultra SCSI: %sabled\n",
- (p->flags & ULTRA_ENABLED) ? "En" : "Dis");
- size += sprintf(BLS, "Disconnect Enable Flags: 0x%x\n", p->discenable);
-
+ size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
+ if (p->type & AHC_ULTRA)
+ {
+ size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb);
+ }
+ size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
+ size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
+#ifdef AIC7XXX_CMDS_PER_LUN
+ size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN);
+#else
+ size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8);
+#endif
+ size += sprintf(BLS, " Tagged Queue By Device array for aic7xxx host "
+ "instance %d:\n", p->instance);
+ size += sprintf(BLS, " {");
+ for(i=0; i < (MAX_TARGETS - 1); i++)
+ size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
+ size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
+ size += sprintf(BLS, " Actual queue depth per device for aic7xxx host "
+ "instance %d:\n", p->instance);
+ size += sprintf(BLS, " {");
+ for(i=0; i < (MAX_TARGETS - 1); i++)
+ size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
+ size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
+
#ifdef AIC7XXX_PROC_STATS
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Statistics:\n");
{
continue;
}
- if (p->bus_type == AIC_TWIN)
+ if (p->type & AHC_TWIN)
{
- size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
- 'A' + (target >> 3), (target & 0x7), lun);
+ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+ p->host_no, (target >> 3), (target & 0x7), lun);
}
else
{
- size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
- 'A', target, lun);
+ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+ p->host_no, 0, target, lun);
}
size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
sp->xfers, sp->r_total, sp->w_total);
#define SELID_MASK 0xf0
#define ONEBIT 0x08
+#define SPIOCAP 0x1b
+#define SOFT1 0x80
+#define SOFT0 0x40
+#define SOFTCMDEN 0x20
+#define HAS_BRDCTL 0x10
+#define SEEPROM 0x08
+#define EEPROM 0x04
+#define ROM 0x02
+#define SSPIOCPS 0x01
+
#define BRDCTL 0x1d
#define BRDDAT7 0x80
#define BRDDAT6 0x40
#define DISC_DSB 0x32
-#define MSG_LEN 0x34
-
-#define MSG_OUT 0x35
+#define MSG_OUT 0x34
-#define DMAPARAMS 0x3d
+#define DMAPARAMS 0x35
#define WIDEODD 0x40
#define SCSIEN 0x20
#define SDMAENACK 0x10
#define FIFOFLUSH 0x02
#define FIFORESET 0x01
-#define SCBCOUNT 0x3e
-
-#define COMP_SCBCOUNT 0x3f
-
-#define QCNTMASK 0x40
-
-#define SEQ_FLAGS 0x41
-#define RESELECTED 0x80
-#define IDENTIFY_SEEN 0x40
-#define TAGGED_SCB 0x20
+#define SEQ_FLAGS 0x36
+#define IDENTIFY_SEEN 0x80
+#define SCBPTR_VALID 0x20
#define DPHASE 0x10
-#define PAGESCBS 0x04
+#define AMTARGET 0x08
#define WIDE_BUS 0x02
#define TWIN_BUS 0x01
-#define SAVED_TCL 0x42
+#define SAVED_TCL 0x37
-#define SG_COUNT 0x43
+#define SG_COUNT 0x38
-#define SG_NEXT 0x44
+#define SG_NEXT 0x39
-#define WAITING_SCBH 0x48
-
-#define SAVED_LINKPTR 0x49
-
-#define SAVED_SCBPTR 0x4a
-
-#define REJBYTE 0x4b
-
-#define LASTPHASE 0x4c
+#define LASTPHASE 0x3d
#define P_MESGIN 0xe0
#define PHASE_MASK 0xe0
#define P_STATUS 0xc0
#define P_BUSFREE 0x01
#define P_DATAOUT 0x00
-#define MSGIN_EXT_LEN 0x4d
+#define WAITING_SCBH 0x3e
+
+#define DISCONNECTED_SCBH 0x3f
-#define MSGIN_EXT_OPCODE 0x4e
+#define FREE_SCBH 0x40
-#define MSGIN_EXT_BYTES 0x4f
+#define HSCB_ADDR 0x41
-#define DISCONNECTED_SCBH 0x52
+#define SCBID_ADDR 0x45
-#define FREE_SCBH 0x53
+#define TMODE_CMDADDR 0x49
-#define HSCB_ADDR 0x54
+#define KERNEL_QINPOS 0x4d
-#define CUR_SCBID 0x58
+#define QINPOS 0x4e
-#define ARG_1 0x59
-#define RETURN_1 0x59
+#define QOUTPOS 0x4f
+
+#define TMODE_CMDADDR_NEXT 0x50
+
+#define ARG_1 0x51
+#define RETURN_1 0x51
#define SEND_MSG 0x80
#define SEND_SENSE 0x40
#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
-#define SCSICONF 0x5a
-
-#define CMDOUTCNT 0x5a
-
-#define FIFODEPTH 0x5b
+#define LAST_MSG 0x52
-#define SCSICONF2 0x5b
+#define SCSICONF 0x5a
+#define TERM_ENB 0x80
#define RESET_SCSI 0x40
+#define HWSCSIID 0x0f
+#define HSCSIID 0x07
#define HOSTCONF 0x5d
#define SEQINT_MASK 0xf1
#define DATA_OVERRUN 0xe1
#define MSGIN_PHASEMIS 0xd1
-#define MSG_BUFFER_BUSY 0xc1
+#define TRACEPOINT2 0xc1
+#define TRACEPOINT 0xb1
#define AWAITING_MSG 0xa1
-#define ABORT_CMDCMPLT 0x91
#define RESIDUAL 0x81
#define BAD_STATUS 0x71
#define REJECT_MSG 0x61
-#define NO_MATCH_BUSY 0x51
+#define ABORT_REQUESTED 0x51
#define EXTENDED_MSG 0x41
#define NO_MATCH 0x31
#define NO_IDENT 0x21
#define SEQINT 0x01
#define CLRINT 0x92
+#define CLRPARERR 0x10
#define CLRBRKADRINT 0x08
#define CLRSCSIINT 0x04
#define CLRCMDINT 0x02
#define CLRSEQINT 0x01
#define ERROR 0x92
-#define PARERR 0x08
+#define PCIERRSTAT 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
#define ILLOPCODE 0x04
#define ILLSADDR 0x02
#define ILLHADDR 0x01
#define MK_MESSAGE 0x80
#define DISCENB 0x40
#define TAG_ENB 0x20
-#define MUST_DMAUP_SCB 0x10
-#define ABORT_SCB 0x08
#define DISCONNECTED 0x04
#define SCB_TAG_TYPE 0x03
#define SCB_DATACNT 0xb0
-#define SCB_LINKED_NEXT 0xb3
-
#define SCB_CMDPTR 0xb4
#define SCB_CMDLEN 0xb8
#define DI_2840 0x01
+#define CMD_GROUP_CODE_SHIFT 0x05
#define BUS_8_BIT 0x00
+#define QOUTFIFO_OFFSET 0x01
+#define CMD_GROUP2_BYTE_DELTA 0xfa
#define MAX_OFFSET_8BIT 0x0f
#define BUS_16_BIT 0x01
+#define QINFIFO_OFFSET 0x02
+#define CMD_GROUP5_BYTE_DELTA 0x0b
#define MAX_OFFSET_16BIT 0x08
+#define UNTAGGEDSCB_OFFSET 0x00
#define SCB_LIST_NULL 0xff
#define SG_SIZEOF 0x08
+#define CMD_GROUP4_BYTE_DELTA 0x04
+#define CMD_GROUP0_BYTE_DELTA 0xfc
+#define HOST_MSG 0xff
#define BUS_32_BIT 0x02
+
+
+/* Downloaded Constant Definitions */
+#define TMODE_NUMCMDS 0x01
+#define QCNTMASK 0x00
/*
* DO NOT EDIT - This file is automatically generated.
*/
-static u_int8_t seqprog[] = {
+static unsigned char seqprog[] = {
0xff, 0x6a, 0x03, 0x02,
+ 0x32, 0x6a, 0x00, 0x00,
0x12, 0x6a, 0x00, 0x00,
- 0x00, 0x65, 0x6f, 0x16,
- 0x40, 0x0b, 0x3c, 0x1a,
- 0x20, 0x0b, 0x37, 0x1a,
- 0x40, 0x00, 0x03, 0x1a,
+ 0x00, 0x65, 0x92, 0x16,
+ 0xf7, 0x01, 0x01, 0x02,
+ 0xff, 0x4e, 0x64, 0x02,
+ 0xbf, 0x60, 0x60, 0x02,
+ 0x60, 0x0b, 0x37, 0x1a,
+ 0x40, 0x00, 0x05, 0x1a,
0x08, 0x1f, 0x1f, 0x04,
- 0x40, 0x0b, 0x3c, 0x1a,
- 0x20, 0x0b, 0x37, 0x1a,
- 0x40, 0x00, 0x03, 0x1a,
+ 0x60, 0x0b, 0x37, 0x1a,
+ 0x40, 0x00, 0x05, 0x1a,
0x08, 0x1f, 0x1f, 0x04,
- 0xff, 0x48, 0x2c, 0x18,
- 0xff, 0x40, 0x64, 0x02,
- 0x00, 0x9c, 0x03, 0x1e,
- 0x00, 0x6a, 0xad, 0x17,
- 0xff, 0x65, 0x03, 0x1c,
- 0xff, 0x9b, 0x58, 0x02,
- 0xff, 0x58, 0x90, 0x02,
- 0x0d, 0x6a, 0x3d, 0x00,
- 0x00, 0x58, 0x77, 0x17,
- 0x28, 0xa0, 0x2a, 0x1a,
- 0x50, 0x6a, 0x60, 0x00,
- 0xff, 0x90, 0x4a, 0x02,
- 0x00, 0xa1, 0xa1, 0x17,
- 0xff, 0x6c, 0x59, 0x02,
- 0xff, 0x59, 0x27, 0x1c,
- 0xff, 0x4a, 0x90, 0x02,
- 0x00, 0x65, 0xaa, 0x17,
- 0x00, 0x6a, 0x52, 0x17,
- 0xff, 0x65, 0x1f, 0x18,
- 0x51, 0x6a, 0x91, 0x00,
- 0xff, 0x58, 0xb3, 0x02,
- 0x00, 0x65, 0xb6, 0x17,
- 0x10, 0x6a, 0x60, 0x00,
- 0x00, 0x65, 0x03, 0x10,
- 0xff, 0x59, 0x90, 0x02,
- 0xff, 0x58, 0xb3, 0x02,
- 0x10, 0x6a, 0x60, 0x00,
- 0x00, 0x65, 0x03, 0x10,
- 0xff, 0x58, 0x6d, 0x02,
- 0xff, 0x4a, 0x90, 0x02,
- 0x10, 0x6a, 0x60, 0x00,
- 0xff, 0x48, 0xba, 0x02,
- 0xff, 0x90, 0x48, 0x02,
- 0xff, 0x48, 0x90, 0x02,
- 0x00, 0x65, 0x2f, 0x16,
- 0x00, 0x65, 0x03, 0x10,
+ 0xff, 0x3e, 0x1d, 0x18,
+ 0x40, 0x60, 0x60, 0x00,
+ 0x00, 0x4d, 0x06, 0x1c,
+ 0x01, 0x4e, 0x4e, 0x06,
+ 0xbf, 0x60, 0x60, 0x02,
+ 0x00, 0x6a, 0xd8, 0x17,
+ 0xff, 0x4e, 0x64, 0x06,
+ 0x02, 0x6a, 0x93, 0x17,
+ 0x0d, 0x6a, 0x93, 0x00,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0xff, 0x99, 0x65, 0x02,
+ 0xff, 0x65, 0x90, 0x02,
+ 0x0d, 0x6a, 0x35, 0x00,
+ 0x00, 0x65, 0xb3, 0x17,
+ 0xff, 0x3e, 0xba, 0x02,
+ 0xff, 0x90, 0x3e, 0x02,
+ 0xff, 0x3e, 0x90, 0x02,
+ 0x00, 0x65, 0x20, 0x16,
+ 0x00, 0x65, 0x05, 0x10,
0xf7, 0x1f, 0x65, 0x02,
0x08, 0xa1, 0x64, 0x02,
0x00, 0x65, 0x65, 0x00,
0x0f, 0x05, 0x05, 0x02,
0x00, 0x05, 0x05, 0x00,
0x5a, 0x6a, 0x00, 0x01,
- 0xff, 0x6a, 0x34, 0x02,
- 0x20, 0x6a, 0x0b, 0x00,
- 0xf0, 0x19, 0x42, 0x02,
- 0x80, 0x41, 0x41, 0x00,
- 0x00, 0x65, 0x4c, 0x10,
- 0x12, 0x6a, 0x00, 0x00,
- 0x40, 0x6a, 0x0b, 0x00,
- 0xff, 0x48, 0x90, 0x02,
- 0xff, 0xba, 0x48, 0x02,
- 0xff, 0xa1, 0x42, 0x02,
- 0x07, 0xa1, 0x35, 0x02,
- 0x40, 0xa0, 0x64, 0x02,
- 0x00, 0x35, 0x35, 0x00,
- 0x80, 0x35, 0x35, 0x00,
- 0x01, 0x6a, 0x34, 0x00,
- 0x20, 0xa0, 0x4a, 0x1e,
- 0x23, 0xa0, 0x36, 0x02,
- 0xff, 0xb9, 0x37, 0x02,
- 0x02, 0x34, 0x34, 0x06,
- 0x80, 0xa0, 0x4c, 0x1e,
- 0xa1, 0x6a, 0x91, 0x00,
- 0x08, 0x6a, 0x0c, 0x00,
- 0x08, 0x11, 0x11, 0x00,
- 0x1a, 0x01, 0x01, 0x00,
+ 0x12, 0x65, 0x64, 0x00,
+ 0x00, 0x01, 0x01, 0x00,
0x31, 0x6a, 0x65, 0x00,
- 0x80, 0x42, 0x52, 0x1a,
+ 0x80, 0x37, 0x2d, 0x1a,
0xff, 0x65, 0x65, 0x06,
- 0xff, 0x42, 0x6e, 0x02,
+ 0xff, 0x37, 0x6e, 0x02,
0xff, 0x6e, 0x64, 0x02,
- 0x00, 0x6c, 0x56, 0x1e,
+ 0x00, 0x6c, 0x31, 0x1e,
0x20, 0x01, 0x01, 0x00,
- 0x4c, 0x42, 0x64, 0x0a,
- 0x08, 0x1f, 0x5a, 0x1e,
- 0x08, 0x42, 0x42, 0x00,
+ 0x4c, 0x37, 0x64, 0x0a,
+ 0x08, 0x1f, 0x35, 0x1e,
+ 0x08, 0x37, 0x37, 0x00,
0x08, 0x64, 0x64, 0x00,
0x20, 0x64, 0x65, 0x06,
- 0xff, 0x6c, 0x04, 0x02,
- 0x01, 0x0c, 0x5c, 0x1e,
- 0x04, 0x0c, 0x5c, 0x1a,
- 0xe0, 0x03, 0x64, 0x02,
- 0xff, 0x64, 0x4c, 0x02,
- 0xff, 0x64, 0x03, 0x02,
- 0x00, 0x6a, 0x74, 0x1c,
- 0x40, 0x64, 0x79, 0x1c,
- 0x80, 0x64, 0xa5, 0x1c,
- 0xa0, 0x64, 0xb0, 0x1c,
- 0xc0, 0x64, 0xad, 0x1c,
- 0xe0, 0x64, 0xc3, 0x1c,
+ 0xff, 0x6c, 0x04, 0x03,
+ 0x40, 0x0b, 0x78, 0x1a,
+ 0x80, 0x0b, 0x71, 0x1e,
+ 0xa4, 0x6a, 0x03, 0x00,
+ 0x40, 0x6a, 0x0b, 0x00,
+ 0x10, 0x03, 0x6f, 0x1e,
+ 0xff, 0x50, 0x64, 0x02,
+ 0x49, 0x6a, 0xa9, 0x17,
+ 0x01, 0x6a, 0x93, 0x00,
+ 0xff, 0x6a, 0x65, 0x02,
+ 0x08, 0x01, 0x01, 0x00,
+ 0x02, 0x0b, 0x41, 0x1e,
+ 0xf7, 0x01, 0x01, 0x02,
+ 0xff, 0x06, 0x66, 0x02,
+ 0xff, 0x66, 0x99, 0x02,
+ 0x01, 0x65, 0x65, 0x06,
+ 0x80, 0x66, 0x48, 0x1e,
+ 0xff, 0x66, 0x51, 0x02,
+ 0x10, 0x03, 0x40, 0x1a,
+ 0xfc, 0x65, 0x64, 0x06,
+ 0x00, 0x65, 0x4c, 0x12,
+ 0xff, 0x6a, 0x99, 0x00,
+ 0x01, 0x64, 0x8c, 0x06,
+ 0x84, 0x6a, 0x03, 0x00,
+ 0x08, 0x01, 0x01, 0x00,
+ 0x02, 0x0b, 0x4f, 0x1e,
+ 0xff, 0x06, 0x64, 0x02,
+ 0xff, 0x64, 0x99, 0x02,
+ 0xff, 0x6a, 0x65, 0x02,
+ 0x5b, 0x64, 0x64, 0x0a,
+ 0x00, 0x62, 0x62, 0x06,
+ 0xfc, 0x65, 0x65, 0x06,
+ 0xff, 0x6a, 0x6a, 0x02,
+ 0xfa, 0x65, 0x65, 0x06,
+ 0xff, 0x6a, 0x6a, 0x02,
+ 0x04, 0x65, 0x65, 0x06,
+ 0x0b, 0x65, 0x65, 0x06,
+ 0xff, 0x65, 0x64, 0x02,
+ 0x00, 0x8c, 0x8c, 0x06,
+ 0x02, 0x0b, 0x5d, 0x1e,
+ 0x01, 0x65, 0x60, 0x18,
+ 0xf7, 0x01, 0x01, 0x02,
+ 0xff, 0x06, 0x99, 0x02,
+ 0xff, 0x65, 0x65, 0x06,
+ 0xff, 0x65, 0x5d, 0x1a,
+ 0x0a, 0x93, 0x93, 0x00,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0x40, 0x51, 0x69, 0x1e,
+ 0xe4, 0x6a, 0x03, 0x00,
+ 0x08, 0x01, 0x01, 0x00,
+ 0x04, 0x6a, 0x5c, 0x17,
+ 0x01, 0x50, 0x50, 0x06,
+ 0x01, 0x50, 0x6c, 0x98,
+ 0xff, 0x6a, 0x50, 0x02,
+ 0xff, 0x6a, 0x9d, 0x00,
+ 0x02, 0x6a, 0x91, 0x00,
+ 0x40, 0x51, 0x6f, 0x1a,
+ 0xff, 0x6a, 0x03, 0x02,
+ 0x00, 0x65, 0x05, 0x10,
+ 0x20, 0x6a, 0x0b, 0x00,
+ 0xf0, 0x19, 0x37, 0x02,
+ 0x08, 0x6a, 0x0c, 0x00,
+ 0x08, 0x11, 0x11, 0x00,
+ 0x08, 0x6a, 0x28, 0x16,
+ 0x08, 0x6a, 0x34, 0x00,
+ 0x00, 0x65, 0x82, 0x10,
+ 0x12, 0x6a, 0x00, 0x00,
+ 0x40, 0x6a, 0x0b, 0x00,
+ 0xff, 0x3e, 0x90, 0x02,
+ 0xff, 0xba, 0x3e, 0x02,
+ 0xff, 0xa1, 0x37, 0x02,
+ 0x08, 0x6a, 0x0c, 0x00,
+ 0x08, 0x11, 0x11, 0x00,
+ 0x08, 0x6a, 0x28, 0x16,
+ 0x80, 0x6a, 0x34, 0x00,
+ 0x80, 0x36, 0x36, 0x00,
+ 0x00, 0x65, 0x9b, 0x17,
+ 0xff, 0x3d, 0x64, 0x02,
+ 0xbf, 0x64, 0x9a, 0x1e,
+ 0x80, 0x64, 0xc9, 0x1c,
+ 0xa0, 0x64, 0xd4, 0x1c,
+ 0xc0, 0x64, 0xd1, 0x1c,
+ 0xe0, 0x64, 0xf5, 0x1c,
0x01, 0x6a, 0x91, 0x00,
- 0x00, 0x65, 0x5c, 0x10,
+ 0x00, 0x65, 0x82, 0x10,
0xf7, 0x11, 0x11, 0x02,
- 0x00, 0x65, 0x6f, 0x16,
+ 0x00, 0x65, 0x92, 0x16,
0xff, 0x06, 0x6a, 0x02,
- 0x09, 0x0c, 0x6c, 0x1e,
- 0x08, 0x0c, 0x03, 0x1a,
+ 0xf7, 0x01, 0x01, 0x02,
+ 0x09, 0x0c, 0x8f, 0x1e,
+ 0x08, 0x0c, 0x05, 0x1a,
0x01, 0x6a, 0x91, 0x00,
0xff, 0x6a, 0x93, 0x02,
0xff, 0x6a, 0x04, 0x02,
0xdf, 0x01, 0x01, 0x02,
- 0x01, 0x6a, 0x4c, 0x00,
- 0x0f, 0x41, 0x41, 0x03,
- 0x7d, 0x6a, 0x3d, 0x00,
- 0x00, 0x65, 0x7a, 0x10,
+ 0x01, 0x6a, 0x3d, 0x00,
+ 0x03, 0x36, 0x36, 0x03,
0x08, 0x6a, 0x66, 0x00,
- 0xa9, 0x6a, 0x74, 0x17,
- 0x00, 0x65, 0x82, 0x10,
- 0x79, 0x6a, 0x3d, 0x00,
- 0x00, 0x65, 0x4f, 0x17,
- 0x10, 0x41, 0x76, 0x1a,
+ 0xa9, 0x6a, 0xa6, 0x17,
+ 0x00, 0x65, 0xa5, 0x10,
+ 0x79, 0x6a, 0x35, 0x00,
+ 0x40, 0x3d, 0x9d, 0x1a,
+ 0x04, 0x35, 0x35, 0x00,
+ 0x00, 0x65, 0x6c, 0x17,
+ 0x10, 0x36, 0x97, 0x1a,
0x88, 0x6a, 0x66, 0x00,
- 0xac, 0x6a, 0x70, 0x17,
- 0x00, 0x65, 0x6d, 0x17,
- 0xff, 0xa3, 0x43, 0x02,
- 0x44, 0x6a, 0x66, 0x00,
- 0xa4, 0x6a, 0x73, 0x17,
- 0xff, 0x43, 0x88, 0x1a,
+ 0xac, 0x6a, 0xa2, 0x17,
+ 0x00, 0x65, 0x9f, 0x17,
+ 0xff, 0xa3, 0x38, 0x02,
+ 0x39, 0x6a, 0x66, 0x00,
+ 0xa4, 0x6a, 0xa5, 0x17,
+ 0xff, 0x38, 0xac, 0x1a,
0x80, 0x02, 0x02, 0x00,
0xff, 0x6a, 0x8c, 0x00,
0xff, 0x6a, 0x8d, 0x00,
0xff, 0x6a, 0x8e, 0x00,
- 0x00, 0x65, 0x6d, 0x17,
- 0x01, 0x43, 0x8a, 0x18,
- 0xbf, 0x3d, 0x3d, 0x02,
- 0x00, 0x3d, 0x44, 0x17,
- 0x80, 0x02, 0xa2, 0x1a,
- 0xff, 0x65, 0x9c, 0x1e,
- 0xff, 0x43, 0x43, 0x06,
- 0xff, 0x43, 0x9c, 0x1e,
+ 0x00, 0x65, 0x9f, 0x17,
+ 0xe7, 0x35, 0x35, 0x02,
+ 0x01, 0x38, 0xae, 0x18,
+ 0xbf, 0x35, 0x35, 0x02,
+ 0x00, 0x35, 0x61, 0x17,
+ 0x80, 0x02, 0xc6, 0x1a,
+ 0xff, 0x65, 0xc0, 0x1e,
+ 0xff, 0x38, 0x38, 0x06,
+ 0xff, 0x38, 0xc0, 0x1e,
0xff, 0x6a, 0x64, 0x02,
- 0x08, 0x44, 0x44, 0x06,
- 0x00, 0x45, 0x45, 0x08,
+ 0x08, 0x39, 0x39, 0x06,
+ 0x00, 0x3a, 0x3a, 0x08,
0x88, 0x6a, 0x66, 0x00,
- 0x44, 0x6a, 0x73, 0x17,
+ 0x39, 0x6a, 0xa5, 0x17,
0x08, 0x6a, 0x8c, 0x00,
0xff, 0x6a, 0x8d, 0x02,
0xff, 0x6a, 0x8e, 0x02,
0x0d, 0x93, 0x93, 0x00,
- 0x00, 0x65, 0x9d, 0x17,
- 0x88, 0x6a, 0x95, 0x17,
- 0x00, 0x65, 0x6d, 0x17,
- 0x10, 0x0c, 0x82, 0x1e,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0x88, 0x6a, 0xc9, 0x17,
+ 0x00, 0x65, 0x9f, 0x17,
+ 0x10, 0x0c, 0xa5, 0x1e,
0xff, 0x08, 0xa9, 0x02,
0xff, 0x09, 0xaa, 0x02,
0xff, 0x0a, 0xab, 0x02,
- 0xff, 0x43, 0xa8, 0x02,
- 0x10, 0x41, 0x41, 0x00,
- 0x00, 0x65, 0x5c, 0x10,
+ 0xff, 0x38, 0xa8, 0x02,
+ 0x10, 0x36, 0x36, 0x00,
+ 0x00, 0x65, 0x82, 0x10,
0x7f, 0x02, 0x02, 0x02,
0xe1, 0x6a, 0x91, 0x00,
- 0x00, 0x65, 0x5c, 0x10,
- 0x00, 0x65, 0x4f, 0x17,
+ 0x00, 0x65, 0x82, 0x10,
+ 0x00, 0x65, 0x6c, 0x17,
0x88, 0x6a, 0x66, 0x00,
- 0xb4, 0x6a, 0x72, 0x17,
+ 0xb4, 0x6a, 0xa4, 0x17,
0xff, 0x6a, 0x8d, 0x02,
0xff, 0x6a, 0x8e, 0x02,
- 0x00, 0x65, 0x6d, 0x17,
- 0x3d, 0x6a, 0x44, 0x17,
- 0x00, 0x65, 0x5c, 0x10,
- 0x00, 0x65, 0x4f, 0x17,
+ 0x00, 0x65, 0x9f, 0x17,
+ 0x3d, 0x6a, 0x61, 0x17,
+ 0x00, 0x65, 0x82, 0x10,
+ 0x00, 0x65, 0x6c, 0x17,
0xff, 0x06, 0xa2, 0x02,
- 0x00, 0x65, 0x5c, 0x10,
- 0xff, 0x34, 0xb2, 0x1a,
- 0x08, 0x6a, 0x32, 0x17,
- 0x35, 0x6a, 0x65, 0x00,
- 0xff, 0x34, 0x66, 0x02,
- 0x01, 0x0c, 0xb4, 0x1e,
- 0x04, 0x0c, 0xb4, 0x1a,
- 0xe0, 0x03, 0x4c, 0x02,
- 0xa0, 0x4c, 0xc0, 0x18,
- 0xff, 0x66, 0xbb, 0x1a,
- 0x10, 0x4c, 0x03, 0x00,
- 0x00, 0x65, 0xb2, 0x10,
- 0x01, 0x66, 0xbd, 0x18,
+ 0x00, 0x65, 0x82, 0x10,
+ 0xff, 0x34, 0x65, 0x02,
+ 0x80, 0x65, 0xe6, 0x18,
+ 0x0f, 0xa1, 0x65, 0x02,
+ 0x07, 0xa1, 0x65, 0x02,
+ 0x40, 0xa0, 0x64, 0x02,
+ 0x00, 0x65, 0x65, 0x00,
+ 0x80, 0x65, 0x65, 0x00,
+ 0x80, 0xa0, 0xde, 0x1e,
+ 0xff, 0x65, 0x06, 0x02,
+ 0x00, 0x65, 0xe7, 0x10,
+ 0x20, 0xa0, 0xe9, 0x1e,
+ 0xff, 0x65, 0x06, 0x02,
+ 0x00, 0x65, 0x9b, 0x17,
+ 0xa0, 0x3d, 0xef, 0x18,
+ 0x23, 0xa0, 0x06, 0x02,
+ 0x00, 0x65, 0x9b, 0x17,
+ 0xa0, 0x3d, 0xef, 0x18,
+ 0x00, 0xb9, 0xe9, 0x10,
+ 0xff, 0x65, 0xe9, 0x18,
+ 0xa1, 0x6a, 0x91, 0x00,
+ 0x10, 0x51, 0xef, 0x1c,
0x40, 0x6a, 0x0c, 0x00,
- 0xff, 0x66, 0x66, 0x06,
- 0xff, 0x6c, 0x06, 0x02,
- 0x00, 0x65, 0xb4, 0x10,
+ 0xff, 0x65, 0x06, 0x02,
+ 0x00, 0x65, 0x9b, 0x17,
+ 0xa0, 0x3d, 0xef, 0x18,
+ 0x10, 0x3d, 0x03, 0x00,
+ 0x00, 0x65, 0xd4, 0x10,
0x40, 0x6a, 0x0c, 0x00,
- 0xff, 0x6a, 0x34, 0x02,
- 0x00, 0x65, 0x5c, 0x10,
- 0x64, 0x6a, 0x3f, 0x17,
- 0xff, 0x64, 0x4b, 0x02,
- 0x80, 0x64, 0x0e, 0x1b,
- 0x04, 0x64, 0x01, 0x1d,
- 0x02, 0x64, 0x04, 0x1d,
- 0x00, 0x6a, 0xd1, 0x1c,
- 0x03, 0x64, 0x0c, 0x1d,
- 0x01, 0x64, 0xf5, 0x1c,
- 0x07, 0x64, 0x30, 0x1d,
- 0x08, 0x64, 0xcf, 0x1c,
+ 0xff, 0x34, 0x52, 0x02,
+ 0x80, 0x34, 0xf3, 0x18,
+ 0x7f, 0xa0, 0xa0, 0x02,
+ 0x08, 0x6a, 0x34, 0x00,
+ 0x00, 0x65, 0x82, 0x10,
+ 0x64, 0x6a, 0x59, 0x17,
+ 0x80, 0x64, 0x2f, 0x1b,
+ 0x04, 0x64, 0x22, 0x1d,
+ 0x02, 0x64, 0x25, 0x1d,
+ 0x00, 0x6a, 0x02, 0x1d,
+ 0x03, 0x64, 0x2d, 0x1d,
+ 0x01, 0x64, 0x20, 0x1d,
+ 0x07, 0x64, 0x50, 0x1d,
+ 0x08, 0x64, 0x00, 0x1d,
0x11, 0x6a, 0x91, 0x00,
- 0x07, 0x6a, 0x32, 0x17,
+ 0x07, 0x6a, 0x52, 0x17,
0xff, 0x06, 0x6a, 0x02,
- 0x00, 0x65, 0x5c, 0x10,
- 0xff, 0xa8, 0xd3, 0x1a,
- 0xff, 0xa2, 0xda, 0x1e,
- 0x01, 0x6a, 0x3d, 0x00,
- 0x00, 0xb9, 0x77, 0x17,
- 0xff, 0xa2, 0xda, 0x1e,
+ 0x00, 0x65, 0x82, 0x10,
+ 0xff, 0xa8, 0x04, 0x1b,
+ 0xff, 0xa2, 0x0f, 0x1f,
+ 0x01, 0x6a, 0x35, 0x00,
+ 0x00, 0xb9, 0xb3, 0x17,
+ 0xff, 0xa2, 0x0f, 0x1f,
0x71, 0x6a, 0x91, 0x00,
- 0x40, 0x59, 0xda, 0x18,
- 0xff, 0xb9, 0xb3, 0x02,
- 0x00, 0x65, 0xe7, 0x10,
- 0x20, 0xa0, 0xe0, 0x1a,
- 0xff, 0x90, 0x4a, 0x02,
- 0xff, 0xb3, 0x49, 0x02,
- 0x00, 0xa1, 0xa1, 0x17,
- 0xff, 0x49, 0x6d, 0x02,
- 0xff, 0x4a, 0x90, 0x02,
- 0xff, 0x5b, 0x64, 0x02,
- 0x00, 0x5a, 0xe1, 0x1c,
- 0x01, 0x5a, 0x5a, 0x06,
- 0xff, 0xb9, 0x9d, 0x02,
+ 0x40, 0x51, 0x0f, 0x19,
+ 0x0d, 0x6a, 0x35, 0x00,
+ 0x00, 0xb9, 0xb3, 0x17,
+ 0xff, 0x3e, 0xba, 0x02,
+ 0xff, 0x90, 0x3e, 0x02,
+ 0x00, 0x65, 0x20, 0x16,
+ 0x00, 0x65, 0x8b, 0x10,
+ 0x20, 0xa0, 0x16, 0x1b,
+ 0xff, 0x37, 0x64, 0x02,
+ 0x00, 0x6a, 0x93, 0x17,
+ 0x01, 0x6a, 0x93, 0x00,
+ 0xff, 0x6a, 0x99, 0x00,
+ 0x0a, 0x93, 0x93, 0x00,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0xff, 0x4f, 0x64, 0x02,
+ 0x01, 0x6a, 0x93, 0x17,
+ 0x01, 0x6a, 0x93, 0x00,
+ 0xff, 0xb9, 0x99, 0x02,
+ 0x0a, 0x93, 0x93, 0x00,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0x01, 0x4f, 0x4f, 0x06,
0x02, 0x6a, 0x91, 0x00,
- 0x08, 0xa0, 0xe7, 0x1e,
- 0x91, 0x6a, 0x91, 0x00,
- 0xff, 0xb3, 0xf3, 0x1c,
- 0xff, 0xb3, 0x64, 0x02,
- 0x00, 0xb9, 0xed, 0x1c,
- 0x00, 0x65, 0xaa, 0x17,
- 0xff, 0x64, 0x90, 0x02,
- 0x00, 0x65, 0xef, 0x10,
- 0x0d, 0x6a, 0x3d, 0x00,
- 0x00, 0xb3, 0x77, 0x17,
- 0xff, 0x48, 0xba, 0x02,
- 0xff, 0x90, 0x48, 0x02,
- 0x00, 0x65, 0x2f, 0x16,
- 0x00, 0x65, 0x69, 0x10,
- 0x00, 0x65, 0xaa, 0x17,
- 0x00, 0x65, 0x69, 0x10,
- 0x4d, 0x6a, 0x3a, 0x17,
- 0xff, 0x4d, 0x64, 0x02,
- 0x00, 0x66, 0x3a, 0x17,
- 0xff, 0x64, 0x64, 0x06,
- 0x52, 0x66, 0xfb, 0x18,
- 0xff, 0x66, 0x66, 0x06,
- 0xff, 0x64, 0xf7, 0x1a,
+ 0x00, 0x65, 0xd5, 0x17,
+ 0x00, 0x65, 0x8b, 0x10,
0x41, 0x6a, 0x91, 0x00,
- 0x20, 0x59, 0xcd, 0x1c,
- 0x80, 0x59, 0xcf, 0x18,
- 0x10, 0x4c, 0x03, 0x00,
- 0x00, 0x65, 0xcf, 0x10,
+ 0x00, 0x65, 0x82, 0x10,
0x04, 0xa0, 0xa0, 0x00,
- 0x00, 0x65, 0xb6, 0x17,
- 0x00, 0x65, 0x69, 0x10,
- 0x10, 0x41, 0xcf, 0x1e,
- 0xff, 0x43, 0xa3, 0x02,
+ 0x00, 0x65, 0xe1, 0x17,
+ 0x00, 0x65, 0x8b, 0x10,
+ 0x10, 0x36, 0x00, 0x1f,
+ 0xff, 0x38, 0xa3, 0x02,
0xa4, 0x6a, 0x66, 0x00,
- 0x44, 0x6a, 0x73, 0x17,
+ 0x39, 0x6a, 0xa5, 0x17,
0xac, 0x6a, 0x66, 0x00,
- 0x14, 0x6a, 0x73, 0x17,
- 0xa9, 0x6a, 0x74, 0x17,
- 0x00, 0x65, 0xcf, 0x10,
- 0xef, 0x41, 0x41, 0x02,
- 0x00, 0x65, 0xcf, 0x10,
- 0x78, 0x64, 0xcd, 0x1a,
+ 0x14, 0x6a, 0xa5, 0x17,
+ 0xa9, 0x6a, 0xa6, 0x17,
+ 0x00, 0x65, 0x00, 0x11,
+ 0xef, 0x36, 0x36, 0x02,
+ 0x00, 0x65, 0x00, 0x11,
+ 0x0f, 0x64, 0x64, 0x02,
0x07, 0x64, 0x64, 0x02,
- 0x00, 0x42, 0x42, 0x00,
- 0x00, 0x42, 0xa1, 0x17,
- 0xff, 0x6c, 0x59, 0x02,
- 0xff, 0x59, 0x28, 0x19,
- 0xff, 0x59, 0x18, 0x1d,
- 0xff, 0x59, 0x90, 0x02,
- 0x04, 0xa0, 0x2d, 0x1f,
- 0x00, 0x65, 0x2a, 0x11,
+ 0x00, 0x37, 0x37, 0x00,
+ 0x00, 0x65, 0x8b, 0x17,
+ 0xff, 0x51, 0x37, 0x1d,
+ 0x20, 0x36, 0x3f, 0x1f,
+ 0x00, 0x90, 0x7d, 0x17,
+ 0x00, 0x65, 0x40, 0x11,
0xff, 0x06, 0x6a, 0x02,
- 0x01, 0x0c, 0x19, 0x1f,
- 0x04, 0x0c, 0x19, 0x1b,
- 0xe0, 0x03, 0x4c, 0x02,
- 0xe0, 0x4c, 0x2d, 0x19,
- 0x20, 0x12, 0x2d, 0x19,
- 0x20, 0x41, 0x41, 0x00,
- 0x59, 0x6a, 0x3a, 0x17,
- 0xff, 0x3f, 0x64, 0x02,
- 0x00, 0x59, 0x65, 0x06,
- 0x00, 0x65, 0x2d, 0x13,
- 0xff, 0x59, 0x90, 0x02,
- 0xff, 0x42, 0x64, 0x02,
- 0x00, 0xa1, 0x2d, 0x19,
- 0x20, 0xa0, 0x2d, 0x1f,
- 0x04, 0xa0, 0x2d, 0x1f,
- 0x00, 0x6a, 0x52, 0x17,
- 0xff, 0x65, 0x2d, 0x1d,
+ 0x00, 0x65, 0x9b, 0x17,
+ 0xe0, 0x3d, 0x4d, 0x19,
+ 0x20, 0x12, 0x4d, 0x19,
+ 0x51, 0x6a, 0x54, 0x17,
+ 0xff, 0x51, 0x90, 0x02,
+ 0x20, 0xa0, 0x4d, 0x1f,
+ 0x00, 0x90, 0x7d, 0x17,
+ 0x00, 0x65, 0x7a, 0x17,
+ 0xff, 0x37, 0x64, 0x02,
+ 0x00, 0xa1, 0x49, 0x19,
+ 0x04, 0xa0, 0x49, 0x1f,
0xfb, 0xa0, 0xa0, 0x02,
- 0x40, 0x41, 0x41, 0x00,
- 0x00, 0x65, 0xcf, 0x10,
+ 0x80, 0x36, 0x36, 0x00,
+ 0x80, 0xa0, 0x00, 0x1f,
+ 0x7f, 0xa0, 0xa0, 0x02,
+ 0xff, 0x6a, 0x52, 0x17,
+ 0x00, 0x65, 0x00, 0x11,
+ 0x04, 0xa0, 0x4c, 0x1f,
+ 0x00, 0x65, 0xe1, 0x17,
+ 0x00, 0x65, 0x4d, 0x11,
+ 0x00, 0x65, 0xd5, 0x17,
0x31, 0x6a, 0x91, 0x00,
- 0x0c, 0x6a, 0x32, 0x17,
- 0x00, 0x65, 0xcf, 0x10,
+ 0x0c, 0x6a, 0x52, 0x17,
+ 0x00, 0x65, 0x00, 0x11,
0x61, 0x6a, 0x91, 0x00,
- 0x00, 0x65, 0xcf, 0x10,
- 0x50, 0x6a, 0x60, 0x00,
- 0xff, 0x34, 0x36, 0x1f,
- 0x10, 0x6a, 0x60, 0x00,
- 0xc1, 0x6a, 0x91, 0x00,
- 0x10, 0x4c, 0x03, 0x00,
- 0x01, 0x6a, 0x34, 0x00,
- 0xff, 0x65, 0x35, 0x02,
- 0x10, 0x6a, 0x60, 0x01,
+ 0x00, 0x65, 0x00, 0x11,
+ 0x10, 0x3d, 0x03, 0x00,
+ 0xff, 0x65, 0x34, 0x03,
0xff, 0x06, 0x6a, 0x02,
- 0x01, 0x0c, 0x3b, 0x1f,
- 0x04, 0x0c, 0x3b, 0x1b,
- 0xe0, 0x03, 0x4c, 0x02,
- 0xe0, 0x4c, 0x42, 0x19,
+ 0x01, 0x0c, 0x55, 0x1f,
+ 0x04, 0x0c, 0x55, 0x1b,
+ 0xe0, 0x03, 0x3d, 0x02,
+ 0xe0, 0x3d, 0x5f, 0x19,
0xff, 0x65, 0x66, 0x02,
0xff, 0x12, 0x6d, 0x03,
0xff, 0x06, 0x6a, 0x03,
+ 0xff, 0x65, 0x06, 0x02,
+ 0x02, 0x0b, 0x5d, 0x1f,
+ 0xff, 0x6a, 0x6a, 0x03,
0xd1, 0x6a, 0x91, 0x00,
- 0x00, 0x65, 0x5c, 0x10,
+ 0x00, 0x65, 0x82, 0x10,
0xff, 0x65, 0x93, 0x02,
- 0x01, 0x0b, 0x4c, 0x1b,
- 0x10, 0x0c, 0x45, 0x1f,
- 0x04, 0x0b, 0x49, 0x1b,
+ 0x01, 0x0b, 0x69, 0x1b,
+ 0x10, 0x0c, 0x62, 0x1f,
+ 0x04, 0x0b, 0x66, 0x1b,
0xff, 0x6a, 0x65, 0x02,
- 0x04, 0x93, 0x4b, 0x1b,
- 0x01, 0x94, 0x4a, 0x1f,
- 0x10, 0x94, 0x4b, 0x1b,
+ 0x04, 0x93, 0x68, 0x1b,
+ 0x01, 0x94, 0x67, 0x1f,
+ 0x10, 0x94, 0x68, 0x1b,
0xc7, 0x93, 0x93, 0x02,
- 0x38, 0x93, 0x4d, 0x1b,
+ 0x38, 0x93, 0x6a, 0x1b,
0xff, 0x6a, 0x6a, 0x03,
- 0x80, 0x41, 0x4e, 0x1f,
- 0x40, 0x41, 0x4e, 0x1b,
+ 0x80, 0x36, 0x6b, 0x1b,
0x21, 0x6a, 0x91, 0x01,
0xff, 0x65, 0x90, 0x02,
- 0xff, 0x59, 0x64, 0x02,
- 0x00, 0xb9, 0x56, 0x19,
- 0x04, 0xa0, 0x60, 0x1b,
- 0x01, 0x65, 0x65, 0x06,
- 0xff, 0x3e, 0x64, 0x02,
- 0x00, 0x65, 0x52, 0x19,
- 0x00, 0x6a, 0xad, 0x17,
- 0x0d, 0x6a, 0x3d, 0x00,
- 0x00, 0x59, 0x77, 0x17,
- 0xff, 0xa8, 0x5e, 0x1f,
- 0x10, 0xa0, 0xa0, 0x00,
- 0x08, 0xa0, 0x4e, 0x1f,
+ 0xff, 0x51, 0x72, 0x19,
+ 0xff, 0x37, 0x64, 0x02,
+ 0xa1, 0x6a, 0x77, 0x11,
+ 0xff, 0x51, 0x64, 0x02,
+ 0xb9, 0x6a, 0x77, 0x11,
+ 0xff, 0xba, 0x79, 0x1d,
+ 0xff, 0xba, 0x90, 0x02,
+ 0xff, 0x65, 0x65, 0x06,
+ 0x00, 0x6c, 0x74, 0x19,
+ 0xff, 0x90, 0x65, 0x03,
0xff, 0x6a, 0x65, 0x01,
- 0x08, 0xa0, 0x5f, 0x1b,
- 0xff, 0xba, 0x66, 0x1d,
- 0xff, 0xbb, 0x49, 0x02,
+ 0x20, 0x36, 0x88, 0x1f,
+ 0x00, 0x90, 0x6e, 0x17,
+ 0xff, 0x65, 0x88, 0x1d,
+ 0xff, 0xba, 0x82, 0x1d,
+ 0xff, 0xbb, 0x66, 0x02,
0xff, 0xba, 0x90, 0x02,
- 0xff, 0x49, 0xbb, 0x02,
+ 0xff, 0x66, 0xbb, 0x02,
0xff, 0x65, 0x90, 0x02,
- 0xff, 0xbb, 0x6b, 0x1d,
- 0xff, 0xba, 0x49, 0x02,
+ 0xff, 0xbb, 0x87, 0x1d,
+ 0xff, 0xba, 0x66, 0x02,
0xff, 0xbb, 0x90, 0x02,
- 0xff, 0x49, 0xba, 0x02,
+ 0xff, 0x66, 0xba, 0x02,
0xff, 0x65, 0x90, 0x03,
- 0xff, 0xba, 0x52, 0x03,
- 0xff, 0x6a, 0x6a, 0x03,
+ 0xff, 0xba, 0x3f, 0x03,
+ 0x00, 0x6a, 0xd8, 0x17,
+ 0x0d, 0x6a, 0x35, 0x00,
+ 0x00, 0x51, 0xb3, 0x11,
+ 0xff, 0x3f, 0x96, 0x1d,
+ 0xff, 0x6a, 0x51, 0x00,
+ 0x00, 0x3f, 0x6e, 0x17,
+ 0xff, 0x65, 0x96, 0x1d,
+ 0x20, 0x36, 0x36, 0x00,
+ 0x20, 0xa0, 0x92, 0x1b,
+ 0xff, 0xb9, 0x51, 0x03,
+ 0xff, 0x6a, 0x51, 0x01,
+ 0xff, 0x65, 0x66, 0x02,
+ 0x45, 0x6a, 0xab, 0x17,
+ 0x01, 0x6a, 0x8c, 0x01,
+ 0xff, 0x37, 0x64, 0x02,
+ 0x00, 0x6a, 0x93, 0x17,
+ 0x0d, 0x6a, 0x93, 0x00,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0xff, 0x99, 0x51, 0x03,
+ 0x01, 0x0c, 0x9b, 0x1f,
+ 0x04, 0x0c, 0x9b, 0x1b,
+ 0xe0, 0x03, 0x3d, 0x02,
+ 0xff, 0x3d, 0x03, 0x03,
0xff, 0x8c, 0x08, 0x02,
0xff, 0x8d, 0x09, 0x02,
0xff, 0x8e, 0x0a, 0x03,
0xff, 0x6c, 0x6d, 0x02,
0xff, 0x6c, 0x6d, 0x02,
0xff, 0x6c, 0x6d, 0x03,
- 0x3d, 0x65, 0x66, 0x0a,
- 0x55, 0x65, 0x64, 0x0a,
- 0x00, 0x54, 0x88, 0x06,
+ 0x3d, 0x64, 0x66, 0x0a,
+ 0x55, 0x64, 0x64, 0x0a,
+ 0x00, 0x6c, 0x88, 0x06,
0xff, 0x66, 0x64, 0x02,
- 0x00, 0x55, 0x89, 0x08,
+ 0x00, 0x6c, 0x89, 0x08,
0xff, 0x6a, 0x64, 0x02,
- 0x00, 0x56, 0x8a, 0x08,
- 0x00, 0x57, 0x8b, 0x08,
- 0x1c, 0x6a, 0x8c, 0x00,
+ 0x00, 0x6c, 0x8a, 0x08,
+ 0x00, 0x6c, 0x8b, 0x08,
0xff, 0x6a, 0x8d, 0x02,
- 0xff, 0x6a, 0x8e, 0x02,
- 0xff, 0x3d, 0x93, 0x02,
- 0x04, 0x3d, 0x8f, 0x1b,
+ 0xff, 0x6a, 0x8e, 0x03,
+ 0xff, 0x65, 0x64, 0x02,
+ 0x41, 0x6a, 0xa9, 0x17,
+ 0x1c, 0x6a, 0x8c, 0x00,
+ 0xff, 0x35, 0x93, 0x02,
+ 0x04, 0x35, 0xc3, 0x1b,
0xa0, 0x6a, 0x65, 0x00,
0x1c, 0x65, 0x64, 0x06,
0xff, 0x6c, 0x99, 0x02,
0xff, 0x6c, 0x99, 0x02,
0xff, 0x6c, 0x99, 0x02,
0xff, 0x6c, 0x99, 0x02,
- 0x00, 0x65, 0x86, 0x19,
+ 0x00, 0x65, 0xba, 0x19,
0x0a, 0x93, 0x93, 0x00,
- 0x00, 0x65, 0x9d, 0x17,
- 0x04, 0x3d, 0x4e, 0x1f,
- 0xa0, 0x6a, 0x95, 0x17,
- 0x00, 0x65, 0x96, 0x17,
- 0x00, 0x65, 0x96, 0x17,
- 0x00, 0x65, 0x96, 0x11,
+ 0x00, 0x65, 0xd1, 0x17,
+ 0x04, 0x35, 0x6b, 0x1f,
+ 0xa0, 0x6a, 0xc9, 0x17,
+ 0x00, 0x65, 0xca, 0x17,
+ 0x00, 0x65, 0xca, 0x17,
+ 0x00, 0x65, 0xca, 0x11,
0xff, 0x65, 0x66, 0x02,
0xff, 0x99, 0x6d, 0x02,
0xff, 0x99, 0x6d, 0x02,
0xff, 0x99, 0x6d, 0x02,
0xff, 0x99, 0x6d, 0x02,
0xff, 0x99, 0x6d, 0x03,
- 0x08, 0x94, 0x9d, 0x1f,
+ 0x08, 0x94, 0xd1, 0x1f,
0xf7, 0x93, 0x93, 0x02,
- 0x08, 0x93, 0x9f, 0x1b,
+ 0x08, 0x93, 0xd3, 0x1b,
0xff, 0x6a, 0x6a, 0x03,
- 0xff, 0x65, 0x66, 0x02,
- 0x4c, 0x66, 0x66, 0x0a,
- 0x03, 0x66, 0x66, 0x02,
- 0xbc, 0x66, 0x66, 0x06,
- 0x6a, 0x65, 0x64, 0x0a,
- 0x08, 0x65, 0xa8, 0x1f,
- 0x02, 0x64, 0x64, 0x06,
- 0xff, 0x64, 0x90, 0x02,
- 0xff, 0x66, 0x65, 0x03,
- 0xff, 0x53, 0xba, 0x02,
- 0xff, 0x6a, 0xb9, 0x00,
- 0xff, 0x90, 0x53, 0x03,
- 0xff, 0x53, 0xb4, 0x19,
- 0xff, 0x52, 0xb0, 0x19,
+ 0xff, 0x40, 0xba, 0x02,
+ 0xff, 0x90, 0x40, 0x02,
+ 0xff, 0x6a, 0xb9, 0x01,
+ 0xff, 0x40, 0xdf, 0x19,
+ 0xff, 0x3f, 0xdb, 0x19,
0xff, 0x6a, 0x65, 0x01,
- 0xff, 0x52, 0x90, 0x02,
- 0x01, 0x6a, 0x3d, 0x00,
- 0x00, 0xb9, 0x77, 0x17,
- 0x00, 0x90, 0x61, 0x11,
- 0xff, 0x53, 0x90, 0x02,
- 0xff, 0xba, 0x53, 0x03,
+ 0xff, 0x3f, 0x90, 0x02,
+ 0x01, 0x6a, 0x35, 0x00,
+ 0x00, 0xb9, 0xb3, 0x17,
+ 0x00, 0x90, 0x7d, 0x11,
+ 0xff, 0x40, 0x90, 0x02,
+ 0xff, 0xba, 0x40, 0x03,
0xff, 0x6a, 0xbb, 0x00,
- 0xff, 0x52, 0xba, 0x02,
- 0xff, 0x90, 0x52, 0x02,
- 0xff, 0xba, 0x4e, 0x1d,
+ 0xff, 0x3f, 0xba, 0x02,
+ 0xff, 0x90, 0x3f, 0x02,
+ 0xff, 0xba, 0x6b, 0x1d,
0xff, 0xba, 0x90, 0x02,
- 0xff, 0x52, 0xbb, 0x02,
- 0xff, 0x52, 0x90, 0x03,
+ 0xff, 0x3f, 0xbb, 0x02,
+ 0xff, 0x3f, 0x90, 0x03,
};
-#define ULTRA 0x8
-#define SCB_PAGING 0x4
-#define TWIN_CHANNEL 0x2
+#define WIDE 0x20
+#define ULTRA 0x10
+#define SCB_PAGING 0x8
+#define TWIN_CHANNEL 0x4
+#define TARGET_MODE 0x2
struct patch {
int options;
int negative;
int begin;
int end;
} patches[] = {
- { 0x00000002, 0, 0x006, 0x00b },
- { 0x00000004, 0, 0x00e, 0x010 },
- { 0x00000004, 1, 0x011, 0x012 },
- { 0x00000004, 0, 0x01a, 0x023 },
- { 0x00000004, 1, 0x023, 0x027 },
- { 0x00000002, 0, 0x02f, 0x033 },
- { 0x00000008, 0, 0x04f, 0x056 },
- { 0x00000004, 0, 0x0e0, 0x0e3 },
- { 0x00000004, 1, 0x0e8, 0x0ed },
- { 0x00000004, 0, 0x102, 0x103 },
- { 0x00000004, 0, 0x113, 0x114 },
- { 0x00000004, 1, 0x114, 0x118 },
- { 0x00000004, 1, 0x123, 0x128 },
- { 0x00000004, 0, 0x128, 0x12a },
- { 0x00000004, 0, 0x152, 0x16c },
- { 0x00000004, 1, 0x16c, 0x16d },
- { 0x00000004, 0, 0x1ad, 0x1bd },
+ { 0x00000002, 0, 0x001, 0x002 },
+ { 0x00000002, 1, 0x002, 0x003 },
+ { 0x00000004, 0, 0x009, 0x00d },
+ { 0x00000008, 0, 0x012, 0x013 },
+ { 0x00000008, 1, 0x018, 0x019 },
+ { 0x00000004, 0, 0x020, 0x024 },
+ { 0x00000010, 0, 0x02a, 0x031 },
+ { 0x00000002, 0, 0x038, 0x071 },
+ { 0x00000020, 0, 0x0d6, 0x0d7 },
+ { 0x00000020, 1, 0x0d7, 0x0d8 },
+ { 0x00000020, 0, 0x12f, 0x130 },
+ { 0x00000020, 1, 0x130, 0x131 },
+ { 0x00000008, 0, 0x134, 0x135 },
+ { 0x00000008, 1, 0x13c, 0x13f },
+ { 0x00000008, 0, 0x13f, 0x140 },
+ { 0x00000002, 0, 0x15c, 0x15f },
+ { 0x00000008, 0, 0x1d5, 0x1d7 },
+ { 0x00000008, 0, 0x1d8, 0x1e1 },
{ 0x00000000, 0, 0x000, 0x000 }
};
case SCSI_IOCTL_PROBE_HOST:
return ioctl_probe(dev->host, arg);
case SCSI_IOCTL_SEND_COMMAND:
- if(!suser()) return -EACCES;
+ if(!suser() || securelevel > 0) return -EACCES;
return ioctl_command((Scsi_Device *) dev, arg);
case SCSI_IOCTL_DOORLOCK:
if (!dev->removable || !dev->lockable) return 0;
int blkdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENODEV;
+
+ if (securelevel > 0 && (filp->f_mode & FMODE_WRITE))
+ return -EACCES; /* cevans */
+
filp->f_op = get_blkfops(MAJOR(inode->i_rdev));
if (filp->f_op != NULL){
ret = 0;
#define ID_ERR 0x10 /* ID field not found */
#define MC_ERR 0x20 /* media changed */
#define ECC_ERR 0x40 /* Uncorrectable ECC error */
-#define BBD_ERR 0x80 /* block marked bad */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
struct hd_geometry {
unsigned char heads;
unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */
unsigned short eide_pio; /* min cycle time (ns), no IORDY */
unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */
- unsigned short reserved69; /* reserved (word 69) */
- unsigned short reserved70; /* reserved (word 70) */
- /* unsigned short reservedxx[57];*/ /* reserved (words 71-127) */
- /* unsigned short vendor7 [32];*/ /* vendor unique (words 128-159) */
- /* unsigned short reservedyy[96];*/ /* reserved (words 160-255) */
+ unsigned short word69;
+ unsigned short word70;
+ /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */
+ unsigned short word71;
+ unsigned short word72;
+ unsigned short word73;
+ unsigned short word74;
+ unsigned short word75;
+ unsigned short word76;
+ unsigned short word77;
+ unsigned short word78;
+ unsigned short word79;
+ unsigned short word80;
+ unsigned short word81;
+ unsigned short word82;
+ unsigned short word83;
+ unsigned short word84;
+ unsigned short word85;
+ unsigned short word86;
+ unsigned short word87;
+ unsigned short dma_ultra;
+ unsigned short reserved[167];
};
#ifdef __KERNEL__
#ifndef _FRAD_H_
#define _FRAD_H_
+#include <linux/config.h>
#include <linux/if.h>
/* Structures and constants associated with the DLCI device driver */
--- /dev/null
+#ifdef MODVERSIONS
+#undef CONFIG_MODVERSIONS
+#define CONFIG_MODVERSIONS
+#ifndef _set_ver
+#define _set_ver(sym,vers) sym ## _R ## vers
+#endif
+#include <linux/modules/b1capi.ver>
+#include <linux/modules/b1pci.ver>
+#include <linux/modules/capidrv.ver>
+#include <linux/modules/capiutil.ver>
+#include <linux/modules/fatfs_syms.ver>
+#include <linux/modules/firewall.ver>
+#include <linux/modules/isdn_syms.ver>
+#include <linux/modules/ksyms.ver>
+#include <linux/modules/md.ver>
+#include <linux/modules/misc.ver>
+#include <linux/modules/msdosfs_syms.ver>
+#include <linux/modules/netsyms.ver>
+#include <linux/modules/nls.ver>
+#include <linux/modules/p8022.ver>
+#include <linux/modules/p8022tr.ver>
+#include <linux/modules/ppp.ver>
+#include <linux/modules/procfs_syms.ver>
+#include <linux/modules/psnap.ver>
+#include <linux/modules/scsi_syms.ver>
+#include <linux/modules/serial.ver>
+#include <linux/modules/slhc.ver>
+#include <linux/modules/vfatfs_syms.ver>
+#undef CONFIG_MODVERSIONS
+#endif
#define PCI_DEVICE_ID_KTI_ET32P2 0x3000
#define PCI_VENDOR_ID_ADAPTEC 0x9004
+#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078
#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078
#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578
#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078
#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278
#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378
#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478
+#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895
#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078
#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178
#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278
X(__brelse),
X(__bforget),
X(ll_rw_block),
+ X(brw_page),
X(__wait_on_buffer),
X(mark_buffer_uptodate),
X(unlock_buffer),
*/
__u32 i=length>>1;
+ char hops = packet->ipx_tctrl;
+
+ packet->ipx_tctrl = 0; /* hop count excluded from checksum calc */
/*
* Loop through all complete words except the checksum field
if(packet->ipx_pktsize&htons(1))
sum+=ntohs(0xff00)&*p;
+ packet->ipx_tctrl = hops;
/*
* Do final fixup
*/
#
# Please send comments / questions / bug fixes to roadcapw@titus.org
#
+# 070498 Stepan Kasal <kasal@math.cas.cz> - one change borrowed
+# from 2.1.x version:
+# 131197 Michael Chastain (mec@shout.net) - output all lines for a
+# choice list, not just the selected one. This makes the output
+# the same as Configure output, which is important for smart config
+# dependencies.
+# It also fixes the bug when menuconfig sets CONFIG_M386=y every time
+# it is ran.
#----------------------------------------------------------------------------
#
: ${current:=$default}
#
- # Then extract the actual option from the list of choices.
+ # Output all choices (to be compatible with other configs).
#
- current=${choices#*$current} ; set $current
-
- define_bool "$1" "y"
+ set -- $choices
+ while [ -n "$2" ]
+ do
+ if eval [ "$1" = "$current" ]
+ then
+ define_bool "$2" "y"
+ else
+ define_bool "$2" "n"
+ fi
+ shift ; shift
+ done
}
function mainmenu_name () {