## Automatically generated incremental diff ## From: linux-2.0.31-pre5 ## To: linux-2.0.31-pre6 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.0.31-pre5/arch/alpha/kernel/traps.c linux-2.0.31-pre6/arch/alpha/kernel/traps.c --- linux-2.0.31-pre5/arch/alpha/kernel/traps.c 1997-02-02 05:00:20.000000000 -0800 +++ linux-2.0.31-pre6/arch/alpha/kernel/traps.c 2003-08-15 15:04:01.000000000 -0700 @@ -420,7 +420,7 @@ unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { - if (regs.r0 != 112) + if (regs.r0 != 112 && regs.r0 < 300) printk("", regs.r0, a0, a1, a2); return -1; } diff -urN linux-2.0.31-pre5/CREDITS linux-2.0.31-pre6/CREDITS --- linux-2.0.31-pre5/CREDITS 2003-08-15 15:04:00.000000000 -0700 +++ linux-2.0.31-pre6/CREDITS 2003-08-15 15:04:01.000000000 -0700 @@ -413,7 +413,7 @@ S: Australia N: Ralf Flaxa -E: rfflaxa@immd4.informatik.uni-erlangen.de +E: rf@lst.de D: The Linux Support Team Erlangen D: Creator of LST distribution D: Author of installation tool LISA diff -urN linux-2.0.31-pre5/Documentation/Configure.help linux-2.0.31-pre6/Documentation/Configure.help --- linux-2.0.31-pre5/Documentation/Configure.help 2003-08-15 15:04:00.000000000 -0700 +++ linux-2.0.31-pre6/Documentation/Configure.help 2003-08-15 15:04:01.000000000 -0700 @@ -472,7 +472,10 @@ the same end. SYN cookies use less space than RST cookies, but have a small probability of introducing an non timed-out failure to connect in the remote TCP. You can use both options - simultatenously. + simultatenously. If you are SYN flooded, the source address + reported by the kernel is likely to have been forged by the attacker. + The source address is reported as an aid in tracing the packets to + their actual source. SYN flood protection CONFIG_RST_COOKIES @@ -486,7 +489,10 @@ The SYN_COOKIES option provides an alternative method to accomplish the same end. RST cookies use more space than SYN cookies on your machine, but never increase the probability of a frozen connection - in a remote TCP. You can use both options simultatenously. + in a remote TCP. You can use both options simultatenously. If you + are SYN flooded, the source address reported by the kernel is likely + to have been forged by the attacker. The source address is reported + as an aid in tracing the packets to their actual source. Sun floppy controller support CONFIG_BLK_DEV_SUNFD @@ -1554,24 +1560,41 @@ of PCI-SCSI controllers. This driver supports parity checking, tagged command queuing, fast scsi II transfer up to 10 MB/s with narrow scsi devices and 20 MB/s with wide scsi devices. - This driver has been tested OK with linux/i386 and is currently - untested under linux/Alpha. If you intend to use this driver under - linux/Alpha, just try it first with read-only or mounted read-only - devices. Memory mapped io is currently not supported under - linux/Alpha. Please read drivers/scsi/README.ncr53c8xx for more - information. + Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 + controllers has been recently added to the driver. + Please read drivers/scsi/README.ncr53c8xx for more information. + Linux/i386 and Linux/Alpha are supported by this driver. + +synchronous data transfers frequency +CONFIG_SCSI_NCR53C8XX_SYNC + SCSI-2 specifications allow scsi devices to negotiate a synchronous + transfer period of 25 nano-seconds or more. + The transfer period value is 4 times the agreed transfer period. + So, data can be transferred at a 10 MHz frequency, allowing 10 + MB/second throughput with 8 bits scsi-2 devices and 20 MB/second + with wide16 devices. This frequency can be used safely with + differential devices but may cause problems with singled-ended + devices. + Specify 0 if you want to only use asynchronous data transfers. + Otherwise, specify a value between 5 and 10. Commercial O/Ses + generally use 5 Mhz frequency for synchronous transfers. It is a + reasonable default value. + However, a flawless singled-ended scsi bus supports 10 MHz data + transfers. Regardless the value chosen in the Linux configuration, + the synchronous period can be changed after boot-up through the + /proc/scsi file system. The generic command is: + echo "setsync #target period" >/proc/scsi/ncr53c8xx/0 + Use a 25 ns period for 10 Mhz synchronous data transfers. + If you don't know what to do now, go with the default. -force normal IO +use normal IO CONFIG_SCSI_NCR53C8XX_IOMAPPED - Under linux/Alpha only normal io is currently supported. - Under linux/i386, this option allows you to force the driver to use - normal IO. Memory mapped IO has less latency than normal IO. - During the initialization phase, the driver first tries to use - memory mapped io. If nothing seems wrong, it will use memory mapped - io. If a flaw is detected, it will use normal io. However, it's - possible that memory mapped does not work properly for you and the - driver has not detected the problem; then you would want to say Y - here. The normal answer therefore is N. + This option allows you to force the driver to use normal IO. + Memory mapped IO has less latency than normal IO and works for most + Intel-based hardware. + Under Linux/Alpha only normal IO is currently supported by the driver + and so, this option has no effect. + The normal answer therefore is N. not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT @@ -1596,36 +1619,51 @@ The safe answer therefore is N. The normal answer therefore is Y. -force asynchronous transfer mode -CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS - This option allows you to force asynchronous transfer mode for all - devices at linux startup. You can enable synchronous negotiation - with the "setsync" control command after boot-up, for example: - echo "setsync 2 25" >/proc/scsi/ncr53c8xx/0 - asks the driver to set the period to 25 ns (10MB/sec) for target 2 - of controller 0 (please read drivers/scsi/README.ncr53c8xx for more - information). The safe answer therefore is Y. The normal answer - therefore is N. - -force synchronous negotiation -CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO - Some scsi-2 devices support synchronous negotiations but do not - report this feature in byte 7 of inquiry data. - Answer Y only if you suspect some device to be so humble. - The normal answer therefore is N. - -disable master parity checking -CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK - Some hardware may have problems with parity during master cycles on - PCI bus. Only seen once. Answer Y if you suspect such problem. The - normal answer therefore is N. - -disable scsi parity checking -CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK - Parity on scsi bus is a system option. If one device checks parity, - then all devices on the scsi bus must generate parity. However, the - parity can be ignored by the scsi devices. Answer Y only if you - know what you are doing. The normal answer therefore is N. +maximum number of queued commands +CONFIG_SCSI_NCR53C8XX_MAX_TAGS + This option allows you to specify the maximum number of commands + that can be queued to a device, when tagged command queuing is + possible. The default value is 4. Minimum is 2, maximum is 12. The + normal answer therefore is the default one. + +detect and read serial NVRAM +CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT + Enable support for reading the serial NVRAM data on Symbios and + some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for + systems with more than one Symbios compatible controller where at least + one has a serial NVRAM, or for a system with a mixture of Symbios and + Tekram cards. Enables setting the boot order of host adaptors + to something other than the default order or "reverse probe" order. + Also enables Symbios and Tekram cards to be distinguished so + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a + mixture of Symbios and Tekram cards so the Symbios cards can make use of + the full range of Symbios features, differential, led pin, without + causing problems for the Tekram card(s). + (added by Richard Waltham: dormouse@farsrobt.demon.co.uk) + Also enables setting host and targets SCSI features as defined in the + user setup for each host using a serial NVRAM (added by the maintainer). + The default answer is N, the normal answer should be Y. + Read drivers/scsi/README.ncr53c8xx for more information. + +assume boards are SYMBIOS compatible +CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + This option allows you to enable some features depending on GPIO + wiring. These General Purpose Input/Output pins can be used for + vendor specific features or implementation of the standard SYMBIOS + features. Genuine SYMBIOS boards use GPIO0 in output for controller + LED and GPIO3 bit as a flag indicating singled-ended/differential + interface. + If all the boards of your system are genuine SYMBIOS boards or use + BIOS and drivers from SYMBIOS, you would want to enable this option. + The driver behaves correctly on my system with this option enabled. + (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev + 0x12). This option must be set to N if your system has at least one + 53C8XX based scsi board with a vendor-specific BIOS (example: Tekram + DC-390/U/W/F). If unsure, say N. + However, if all your non Symbios compatible boards have NvRAM, setting + option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver to + distinguish Symbios compatible boards from other ones. + So, you can answer Y if all non Symbios compatible boards have NVRAM. Always IN2000 SCSI support CONFIG_SCSI_IN2000 @@ -1742,6 +1780,13 @@ and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. +Tekram DC390(T) (AMD PCscsi) SCSI support +CONFIG_SCSI_DC390T + This driver supports the Tekram DC390(T) PCI SCSI Hostadapter with + the Am53C974A chip, and perhaps other cards using the same chip. + This driver does _not_ support the DC390W/U/F adaptor with the + NCR/Symbios chips. + AM53/79C974 PCI SCSI support CONFIG_SCSI_AM53C974 This is support for the AM53/79C974 SCSI host adapters. Please read @@ -2397,6 +2442,12 @@ FMV-184 and it is not working, you may need to disable Plug & Play mode of the card. +Intel EtherExpress/Pro 100B support' +CONFIG_EEXPRESS_PRO100B + If you have an Intel EtherExpress Pro 100 10/100Mbps PCI Ethernet + card, answer yes. As of kernel release 2.0.31 this driver was + still experimental. + EtherExpressPro support CONFIG_EEXPRESS_PRO If you have a network (ethernet) card of this type, say Y and read diff -urN linux-2.0.31-pre5/drivers/char/random.c linux-2.0.31-pre6/drivers/char/random.c --- linux-2.0.31-pre5/drivers/char/random.c 2003-08-15 15:04:00.000000000 -0700 +++ linux-2.0.31-pre6/drivers/char/random.c 2003-08-15 15:04:01.000000000 -0700 @@ -1337,8 +1337,13 @@ do_gettimeofday(&tv); seq = tmp[1] + tv.tv_usec+tv.tv_sec*1000000; #if 0 - printk("init_seq(%lx, %lx, %d, %d) = %d\n", - saddr, daddr, sport, dport, seq); + /* + ugh...we can only use in_ntoa once per printk, splitting + a single line of info into multiple printk's confuses klogd, + and Linus says in_ntoa sucks anyway :) + */ + printk("init_seq(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d) = %d\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, seq); #endif return (seq); } @@ -1361,7 +1366,7 @@ /* * Pick a random secret the first time we open a TCP - * connection, and expire secretes older than 5 minutes. + * connection, and expire secrets older than 5 minutes. */ if (is_init == 0 || jiffies-secret_timestamp[offset] > 600*HZ) { if (is_init == 0) valid_secret[0] = valid_secret[1] = 0; @@ -1386,14 +1391,14 @@ if (!validate) { if (seq == sseq) seq++; #if 0 - printk("init_seq(%lx, %lx, %d, %d, %d) = %d\n", - saddr, daddr, sport, dport, sseq, seq); + printk("init_seq(%d.%d.%d.%d:%d %d.%d.%d.%d:%d, %d) = %d\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq, seq); #endif return (seq); } else { if (seq == sseq || (seq+1) == sseq) { - printk("validated probe(%lx, %lx, %d, %d, %d)\n", - saddr, daddr, sport, dport, sseq); + printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq); return 1; } if (jiffies-secret_timestamp[(offset+1)%2] <= 1200*HZ) { @@ -1405,15 +1410,15 @@ seq = tmp[1]; if (seq == sseq || (seq+1) == sseq) { #ifdef 0 - printk("validated probe(%lx, %lx, %d, %d, %d)\n", - saddr, daddr, sport, dport, sseq); + printk("validated probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq); #endif return 1; } } #ifdef 0 - printk("failed validation on probe(%lx, %lx, %d, %d, %d)\n", - saddr, daddr, sport, dport, sseq); + printk("failed validation on probe(%d.%d.%d.%d:%d, %d.%d.%d.%d:%d, %d)\n", + NIPQUAD(saddr), sport, NIPQUAD(daddr), dport, sseq); #endif return 0; } diff -urN linux-2.0.31-pre5/drivers/char/tty_io.c linux-2.0.31-pre6/drivers/char/tty_io.c --- linux-2.0.31-pre5/drivers/char/tty_io.c 2003-08-15 15:04:00.000000000 -0700 +++ linux-2.0.31-pre6/drivers/char/tty_io.c 2003-08-15 15:04:01.000000000 -0700 @@ -11,7 +11,7 @@ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. * * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the - * tty_struct and tty_queue structures. Previously there was a array + * tty_struct and tty_queue structures. Previously there was an array * of 256 tty_struct's which was statically allocated, and the * tty_queue structures were allocated at boot time. Both are now * dynamically allocated only when the tty is open. @@ -44,6 +44,9 @@ * * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 + * + * Rewrote init_dev and release_dev to eliminate races. + * -- Bill Hawes , June 97 */ #include @@ -830,14 +833,27 @@ (unsigned int)count); } +/* Semaphore to protect creating and releasing a tty */ +static struct semaphore tty_sem = MUTEX; +static void down_tty_sem(int index) +{ + down(&tty_sem); +} +static void up_tty_sem(int index) +{ + up(&tty_sem); +} +static void release_mem(struct tty_struct *tty, int idx); + /* - * This is so ripe with races that you should *really* not touch this - * unless you know exactly what you are doing. All the changes have to be - * made atomically, or there may be incorrect pointers all over the place. + * Rewritten to remove races and properly clean up after a failed open. + * The new code protects the open with a semaphore, so it's really + * quite straightforward. The semaphore locking can probably be + * relaxed for the (most common) case of reopening a tty. */ static int init_dev(kdev_t device, struct tty_struct **ret_tty) { - struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc; + struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; struct tty_driver *driver; @@ -849,163 +865,221 @@ return -ENODEV; idx = MINOR(device) - driver->minor_start; - tty = o_tty = NULL; + + /* + * Check whether we need to acquire the tty semaphore to avoid + * race conditions. For now, play it safe. + */ + down_tty_sem(idx); + + /* check whether we're reopening an existing tty */ + tty = driver->table[idx]; + if(tty) goto fast_track; + + /* + * First time open is complex, especially for PTY devices. + * This code guarantees that either everything succeeds and the + * TTY is ready for operation, or else the table slots are vacated + * and the allocated memory released. (Except that the termios + * and locked termios may be retained.) + */ + + o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; - o_tty_loc = NULL; - o_tp_loc = o_ltp_loc = NULL; - tty_loc = &driver->table[idx]; - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; + tty = (struct tty_struct*) get_free_page(GFP_KERNEL); + if(!tty) + goto fail_no_mem; + initialize_tty_struct(tty); + tty->device = device; + tty->driver = *driver; -repeat: - retval = -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER && - *tty_loc && (*tty_loc)->count) - goto end_init; - retval = -ENOMEM; - if (!*tty_loc && !tty) { - if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) - goto end_init; - initialize_tty_struct(tty); - tty->device = device; - tty->driver = *driver; - goto repeat; - } - if (!*tp_loc && !tp) { + tp_loc = &driver->termios[idx]; + if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) - goto end_init; + goto free_mem_out; *tp = driver->init_termios; - goto repeat; } - if (!*ltp_loc && !ltp) { + + ltp_loc = &driver->termios_locked[idx]; + if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) - goto end_init; + goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); - goto repeat; } + if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty_loc = &driver->other->table[idx]; - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; + o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + if (!o_tty) + goto free_mem_out; + initialize_tty_struct(o_tty); + o_tty->device = (kdev_t) MKDEV(driver->other->major, + driver->other->minor_start + idx); + o_tty->driver = *driver->other; - if (!*o_tty_loc && !o_tty) { - kdev_t o_device; - - o_tty = (struct tty_struct *) - get_free_page(GFP_KERNEL); - if (!o_tty) - goto end_init; - o_device = MKDEV(driver->other->major, - driver->other->minor_start + idx); - initialize_tty_struct(o_tty); - o_tty->device = o_device; - o_tty->driver = *driver->other; - goto repeat; - } - if (!*o_tp_loc && !o_tp) { + o_tp_loc = &driver->other->termios[idx]; + if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) - goto end_init; + goto free_mem_out; *o_tp = driver->other->init_termios; - goto repeat; } - if (!*o_ltp_loc && !o_ltp) { + + o_ltp_loc = &driver->other->termios_locked[idx]; + if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) - goto end_init; + goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); - goto repeat; } - + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->table[idx] = o_tty; + if (!*o_tp_loc) + *o_tp_loc = o_tp; + if (!*o_ltp_loc) + *o_ltp_loc = o_ltp; + o_tty->termios = *o_tp_loc; + o_tty->termios_locked = *o_ltp_loc; + (*driver->other->refcount)++; + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; } - /* Now we have allocated all the structures: update all the pointers.. */ - if (!*tp_loc) { + + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_mem to clean up, so + * there's no need to null out the local pointers. + */ + driver->table[idx] = tty; + if (!*tp_loc) *tp_loc = tp; - tp = NULL; - } - if (!*ltp_loc) { + if (!*ltp_loc) *ltp_loc = ltp; - ltp = NULL; + tty->termios = *tp_loc; + tty->termios_locked = *ltp_loc; + (*driver->refcount)++; + tty->count++; + + /* + * Structures all installed ... call the ldisc open routines. + * If we fail here just call release_mem to clean up. No need + * to decrement the use counts, as release_mem doesn't care. + */ + if (tty->ldisc.open) { + retval = (tty->ldisc.open)(tty); + if (retval) + goto release_mem_out; } - if (!*tty_loc) { - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - *tty_loc = tty; - (*driver->refcount)++; - (*tty_loc)->count++; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); - if (retval < 0) { - (*tty_loc)->count--; - tty = NULL; - goto end_init; - } + if (o_tty && o_tty->ldisc.open) { + retval = (o_tty->ldisc.open)(o_tty); + if (retval) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + goto release_mem_out; } - tty = NULL; - } else { - if ((*tty_loc)->flags & (1 << TTY_CLOSING)) { - printk("Attempt to open closing tty %s.\n", - tty_name(*tty_loc)); - printk("Ack!!!! This should never happen!!\n"); - return -EINVAL; - } - (*tty_loc)->count++; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - if (!*o_tp_loc) { - *o_tp_loc = o_tp; - o_tp = NULL; - } - if (!*o_ltp_loc) { - *o_ltp_loc = o_ltp; - o_ltp = NULL; - } - if (!*o_tty_loc) { - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - *o_tty_loc = o_tty; - (*driver->other->refcount)++; - if (o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); - if (retval < 0) { - (*tty_loc)->count--; - o_tty = NULL; - goto end_init; - } - } - o_tty = NULL; - } - (*tty_loc)->link = *o_tty_loc; - (*o_tty_loc)->link = *tty_loc; - if (driver->subtype == PTY_TYPE_MASTER) - (*o_tty_loc)->count++; + goto success; + + /* + * This fast open can be used if the tty is already open. + * No memory is allocated, and the only failures are from + * attempting to open a closing tty or attempting multiple + * opens on a pty master. + */ +fast_track: + retval = -EIO; + if (test_bit(TTY_CLOSING, &tty->flags)) + goto end_init; + + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) + goto end_init; + tty->link->count++; } - (*tty_loc)->driver = *driver; - *ret_tty = *tty_loc; + tty->count++; + tty->driver = *driver; /* N.B. why do this every time?? */ + +success: retval = 0; + *ret_tty = tty; + + /* All paths come through here to release the semaphore */ end_init: - if (tty) - free_page((unsigned long) tty); - if (o_tty) - free_page((unsigned long) o_tty); - if (tp) - kfree_s(tp, sizeof(struct termios)); + up_tty_sem(idx); + return retval; + + /* Release locally allocated memory ... nothing placed in slots */ +free_mem_out: if (o_tp) kfree_s(o_tp, sizeof(struct termios)); + if (o_tty) + free_page((unsigned long) o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); - if (o_ltp) - kfree_s(o_ltp, sizeof(struct termios)); - return retval; + if (tp) + kfree_s(tp, sizeof(struct termios)); + free_page((unsigned long) tty); + +fail_no_mem: + retval = -ENOMEM; + goto end_init; + + /* call the tty release_mem routine to clean out this slot */ +release_mem_out: + printk("init_dev: ldisc open failed, clearing slot %d\n", idx); + release_mem(tty, idx); + goto end_init; +} + +/* + * Releases memory associated with a tty structure, and clears out the + * driver table slots. + */ +static void release_mem(struct tty_struct *tty, int idx) +{ + struct tty_struct *o_tty; + struct termios *tp; + + if ((o_tty = tty->link) != NULL) { + o_tty->driver.table[idx] = NULL; + if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = o_tty->driver.termios[idx]; + o_tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + o_tty->magic = 0; + (*o_tty->driver.refcount)--; + free_page((unsigned long) o_tty); + } + + tty->driver.table[idx] = NULL; + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = tty->driver.termios[idx]; + tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + tty->magic = 0; + (*tty->driver.refcount)--; + free_page((unsigned long) tty); } /* @@ -1016,8 +1090,7 @@ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; - struct termios *tp, *o_tp, *ltp, *o_ltp; - struct task_struct **p; + int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; @@ -1028,10 +1101,11 @@ tty_fasync(filp->f_inode, filp, 0); - tp = tty->termios; - ltp = tty->termios_locked; - idx = MINOR(tty->device) - tty->driver.minor_start; + pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && + tty->driver.subtype == PTY_TYPE_MASTER); + o_tty = tty->link; + #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver.num) { printk("release_dev: bad idx when trying to free (%s)\n", @@ -1043,15 +1117,15 @@ idx, kdevname(tty->device)); return; } - if (tp != tty->driver.termios[idx]) { - printk("release_dev: driver.termios[%d] not termios for (" - "%s)\n", + if (tty->termios != tty->driver.termios[idx]) { + printk("release_dev: driver.termios[%d] not termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (ltp != tty->driver.termios_locked[idx]) { - printk("release_dev: driver.termios_locked[%d] not termios_locked for (" - "%s)\n", + if (tty->termios_locked != tty->driver.termios_locked[idx]) { + printk("release_dev: driver.termios_locked[%d] not " + "termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } @@ -1062,10 +1136,6 @@ tty->count); #endif - o_tty = tty->link; - o_tp = (o_tty) ? o_tty->termios : NULL; - o_ltp = (o_tty) ? o_tty->termios_locked : NULL; - #ifdef TTY_PARANOIA_CHECK if (tty->driver.other) { if (o_tty != tty->driver.other->table[idx]) { @@ -1074,34 +1144,90 @@ idx, kdevname(tty->device)); return; } - if (o_tp != tty->driver.other->termios[idx]) { - printk("release_dev: other->termios[%d] not o_termios for (" - "%s)\n", + if (o_tty->termios != tty->driver.other->termios[idx]) { + printk("release_dev: other->termios[%d] not o_termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_ltp != tty->driver.other->termios_locked[idx]) { - printk("release_dev: other->termios_locked[%d] not o_termios_locked for (" - "%s)\n", + if (o_tty->termios_locked != + tty->driver.other->termios_locked[idx]) { + printk("release_dev: other->termios_locked[%d] not " + "o_termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_tty->link != tty) { printk("release_dev: bad pty pointers\n"); return; } } #endif - + /* + * Sanity check: if tty->count is going to zero, there shouldn't be + * any waiters on tty->read_wait or tty->write_wait. We test the + * wait queues and kick everyone out _before_ actually starting to + * close. This ensures that we won't block while releasing the tty + * structure. + * + * The test for the o_tty closing is necessary, since the master and + * slave sides may close in any order. If the slave side closes out + * first, its count will be one, since the master side holds an open. + * Thus this test wouldn't be triggered at the time the slave closes, + * so we do it now. + * + * Note that it's possible for the tty to be opened again while we're + * flushing out waiters. By recalculating the closing flags before + * each iteration we avoid any problems. + */ + while (1) { + tty_closing = tty->count <= 1; + o_tty_closing = o_tty && + (o_tty->count <= (pty_master ? 1 : 0)); + do_sleep = 0; + + if (tty_closing) { + if (waitqueue_active(&tty->read_wait)) { + wake_up(&tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&tty->write_wait)) { + wake_up(&tty->write_wait); + do_sleep++; + } + } + if (o_tty_closing) { + if (waitqueue_active(&o_tty->read_wait)) { + wake_up(&o_tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&o_tty->write_wait)) { + wake_up(&o_tty->write_wait); + do_sleep++; + } + } + if (!do_sleep) + break; + + printk("release_dev: %s: read/write wait queue active!\n", + tty_name(tty)); + schedule(); + } + + /* + * The closing flags are now consistent with the open counts on + * both sides, and we've completed the last operation that could + * block, so it's safe to proceed with closing. + */ + if (tty->driver.close) tty->driver.close(tty, filp); - if (tty->driver.type == TTY_DRIVER_TYPE_PTY && - tty->driver.subtype == PTY_TYPE_MASTER) { - if (--tty->link->count < 0) { + + if (pty_master) { + if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", - tty->count, tty_name(tty)); - tty->link->count = 0; + o_tty->count, tty_name(o_tty)); + o_tty->count = 0; } } if (--tty->count < 0) { @@ -1109,41 +1235,48 @@ tty->count, tty_name(tty)); tty->count = 0; } - if (tty->count) - return; /* - * We're committed; at this point, we must not block! + * Perform some housekeeping before deciding whether to return. + * + * Set the TTY_CLOSING flag if this was the last open. In the + * case of a pty we may have to wait around for the other side + * to close, and TTY_CLOSING makes sure we can't be reopened. */ - if (o_tty) { - if (o_tty->count) - return; - tty->driver.other->table[idx] = NULL; - tty->driver.other->termios[idx] = NULL; - kfree_s(o_tp, sizeof(struct termios)); + if(tty_closing) + set_bit(TTY_CLOSING, &tty->flags); + if(o_tty_closing) + set_bit(TTY_CLOSING, &o_tty->flags); + + /* + * If _either_ side is closing, make sure there aren't any + * processes that still think tty or o_tty is their controlling + * tty. Also, clear redirect if it points to either tty. + */ + if (tty_closing || o_tty_closing) { + struct task_struct *p; + + for_each_task(p) { + if (p->tty == tty || (o_tty && p->tty == o_tty)) + p->tty = NULL; + } + + if (redirect == tty || (o_tty && redirect == o_tty)) + redirect = NULL; } + + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) + return; + filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure..."); #endif - tty->flags |= (1 << TTY_CLOSING); - - /* - * Make sure there aren't any processes that still think this - * tty is their controlling tty. - */ - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (*p == 0) - continue; - if ((*p)->tty == tty) - (*p)->tty = NULL; - if (o_tty && (*p)->tty == o_tty) - (*p)->tty = NULL; - } /* - * Shutdown the current line discipline, and reset it to - * N_TTY. + * Shutdown the current line discipline, and reset it to N_TTY. + * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -1155,41 +1288,34 @@ o_tty->ldisc = ldiscs[N_TTY]; } - tty->driver.table[idx] = NULL; - if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { - tty->driver.termios[idx] = NULL; - kfree_s(tp, sizeof(struct termios)); - } - if (tty == redirect || o_tty == redirect) - redirect = NULL; /* * Make sure that the tty's task queue isn't activated. If it - * is, take it out of the linked list. + * is, take it out of the linked list. The tqueue isn't used by + * pty's, so skip the test for them. */ - cli(); - if (tty->flip.tqueue.sync) { - struct tq_struct *tq, *prev; + if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { + cli(); + if (tty->flip.tqueue.sync) { + struct tq_struct *tq, *prev; - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if (tq == &tty->flip.tqueue) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if (tq == &tty->flip.tqueue) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } } } + sti(); } - sti(); - tty->magic = 0; - (*tty->driver.refcount)--; - free_page((unsigned long) tty); - filp->private_data = 0; - if (o_tty) { - o_tty->magic = 0; - (*o_tty->driver.refcount)--; - free_page((unsigned long) o_tty); - } + + /* + * The release_mem function takes care of the details of clearing + * the slots and preserving the termios structure. + */ + release_mem(tty, idx); } /* @@ -1274,11 +1400,6 @@ return 0; } -/* - * Note that releasing a pty master also releases the child, so - * we have to make the redirection checks after that and on both - * sides of a pty. - */ static void tty_release(struct inode * inode, struct file * filp) { release_dev(filp); diff -urN linux-2.0.31-pre5/drivers/char/tty_ioctl.c linux-2.0.31-pre6/drivers/char/tty_ioctl.c --- linux-2.0.31-pre5/drivers/char/tty_ioctl.c 1995-09-17 22:54:08.000000000 -0700 +++ linux-2.0.31-pre6/drivers/char/tty_ioctl.c 2003-08-15 15:04:01.000000000 -0700 @@ -376,7 +376,6 @@ { struct tty_struct * real_tty; int retval; - int opt = 0; if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER) @@ -414,19 +413,19 @@ sizeof (struct termios)); return 0; case TCSETSF: - opt |= TERMIOS_FLUSH; + return set_termios(real_tty, arg, TERMIOS_FLUSH); case TCSETSW: - opt |= TERMIOS_WAIT; + return set_termios(real_tty, arg, TERMIOS_WAIT); case TCSETS: - return set_termios(real_tty, arg, opt); + return set_termios(real_tty, arg, 0); case TCGETA: return get_termio(real_tty,(struct termio *) arg); case TCSETAF: - opt |= TERMIOS_FLUSH; + return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_TERMIO); case TCSETAW: - opt |= TERMIOS_WAIT; + return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO); case TCSETA: - return set_termios(real_tty, arg, opt|TERMIOS_TERMIO); + return set_termios(real_tty, arg, TERMIOS_TERMIO); case TCXONC: retval = tty_check_change(tty); if (retval) diff -urN linux-2.0.31-pre5/drivers/isdn/isdn_tty.c linux-2.0.31-pre6/drivers/isdn/isdn_tty.c --- linux-2.0.31-pre5/drivers/isdn/isdn_tty.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/isdn/isdn_tty.c 2003-08-15 15:04:01.000000000 -0700 @@ -2445,13 +2445,16 @@ * Get phone-number from modem-commandbuffer */ static void -isdn_tty_getdial(char *p, char *q) +isdn_tty_getdial(char *p, char *q, int max) { int first = 1; - while (strchr("0123456789,#.*WPTS-", *p) && *p) { - if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) + max--; + while (strchr("0123456789,#.*WPTS-", *p) && *p && (max > 0)) { + if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) { *q++ = *p; + max--; + } p++; first = 0; } @@ -3092,7 +3095,7 @@ break; case 'D': /* D - Dial */ - isdn_tty_getdial(++p, ds); + isdn_tty_getdial(++p, ds, sizeof(ds)); p += strlen(p); if (!strlen(m->msn)) isdn_tty_modem_result(10, info); diff -urN linux-2.0.31-pre5/drivers/net/3c509.c linux-2.0.31-pre6/drivers/net/3c509.c --- linux-2.0.31-pre5/drivers/net/3c509.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/3c509.c 2003-08-15 15:04:01.000000000 -0700 @@ -279,8 +279,10 @@ memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr)); dev->base_addr = ioaddr; dev->irq = irq; - dev->if_port = if_port; - + if (dev->mem_start) + dev->if_port = dev->mem_start & 3; + else + dev->if_port = if_port; request_region(dev->base_addr, EL3_IO_EXTENT, "3c509"); { @@ -692,8 +694,9 @@ printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } - lp->stats.rx_dropped++; outw(RxDiscard, ioaddr + EL3_CMD); + lp->stats.rx_dropped++; + SLOW_DOWN_IO; while (inw(ioaddr + EL3_STATUS) & 0x1000) printk(" Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); @@ -724,7 +727,7 @@ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); } else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); } static int diff -urN linux-2.0.31-pre5/drivers/net/3c59x.c linux-2.0.31-pre6/drivers/net/3c59x.c --- linux-2.0.31-pre5/drivers/net/3c59x.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/3c59x.c 2003-08-15 15:04:01.000000000 -0700 @@ -40,14 +40,8 @@ Setting to > 1512 effectively disables this feature. */ static const rx_copybreak = 200; -#include -#ifdef MODULE #include #include -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif #include #include diff -urN linux-2.0.31-pre5/drivers/net/Config.in linux-2.0.31-pre6/drivers/net/Config.in --- linux-2.0.31-pre5/drivers/net/Config.in 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/Config.in 2003-08-15 15:04:01.000000000 -0700 @@ -50,7 +50,7 @@ tristate '3c507 support' CONFIG_EL16 fi tristate '3c509/3c579 support' CONFIG_EL3 - tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX + tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX fi bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE if [ "$CONFIG_LANCE" = "y" ]; then @@ -98,6 +98,7 @@ tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT + tristate 'Intel EtherExpress/Pro 100B 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 diff -urN linux-2.0.31-pre5/drivers/net/dgrs.c linux-2.0.31-pre6/drivers/net/dgrs.c --- linux-2.0.31-pre5/drivers/net/dgrs.c 1996-12-21 07:22:33.000000000 -0800 +++ linux-2.0.31-pre6/drivers/net/dgrs.c 2003-08-15 15:04:01.000000000 -0700 @@ -97,7 +97,6 @@ #include #include -#include /* for CONFIG_PCI */ /* * API changed at linux version 2.1.0 diff -urN linux-2.0.31-pre5/drivers/net/eepro100.c linux-2.0.31-pre6/drivers/net/eepro100.c --- linux-2.0.31-pre5/drivers/net/eepro100.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.31-pre6/drivers/net/eepro100.c 2003-08-15 15:04:01.000000000 -0700 @@ -0,0 +1,1731 @@ +/* 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 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + This driver is for the Intel EtherExpress Pro 100B boards. + It should work with other i82557 boards (if any others exist). + To use a built-in driver, install as drivers/net/eepro100.c. + To use as a module, use the compile-command at the end of the file. + + The author may be reached as becker@CESDIS.usra.edu, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771 + For updates see + +*/ + +static const char *version = +"eepro100.c:v0.32 4/8/97 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. */ + +static int congenb = 0; /* Enable congestion control in the DP83840. */ +static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ +static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */ +static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */ +static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */ + +/* If defined use the copy-only-tiny-buffer scheme for higher performance. + The value sets the copy breakpoint. Lower uses more memory, but is + faster. */ +#define SKBUFF_RX_COPYBREAK 256 + +#include +#include +#ifdef MODULE +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include +#include + +#include +#include +#include + +/* 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 +#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 +#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 + +/* The total I/O port extent of the board. Nominally 0x18, but rounded up + for PCI allocation. */ +#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 + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's +single-chip fast ethernet controller for PCI, as used on the Intel +EtherExpress Pro 100 adapter. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board. The system BIOS should be set to assign the +PCI INTA signal to an otherwise unused system IRQ line. While it's +possible to share PCI interrupt lines, it negatively impacts performance and +only recent kernels support it. + +III. Driver operation + +IIIA. General +The Speedo3 is very similar to other Intel network chips, that is to say +"apparently designed on a different planet". This chips retains the complex +Rx and Tx descriptors and multiple buffers pointers as previous chips, but +also has simplified Tx and Rx buffer modes. This driver uses the "flexible" +Tx mode, but in a simplified lower-overhead manner: it associates only a +single buffer descriptor with each frame descriptor. + +Despite the extra space overhead in each recieve skbuff, the driver must use +the simplified Rx buffer mode to assure that only a single data buffer is +associated with each RxFD. The driver implements this by reserving space +for the Rx descriptor at the head of each Rx skbuff + +The Speedo-3 has receive and command unit base addresses that are added to +almost all descriptor pointers. The driver sets these to zero, so that all +pointer fields are absolute addresses. + +The System Control Block (SCB) of some previous Intel chips exists on the +chip in both PCI I/O and memory space. This driver uses the I/O space +registers, but might switch to memory mapped mode to better support non-x86 +processors. + +IIIB. Transmit structure + +The driver must use the complex Tx command+descriptor mode in order to +have a indirect pointer to the skbuff data section. Each Tx command block +(TxCB) is associated with a single, immediately appended Tx buffer descriptor +(TxBD). A fixed ring of these TxCB+TxBD pairs are kept as part of the +speedo_private data structure for each adapter instance. + +This ring structure is used for all normal transmit packets, but the +transmit packet descriptors aren't long enough for most non-Tx commands such +as CmdConfigure. This is complicated by the possibility that the chip has +already loaded the link address in the previous descriptor. So for these +commands we convert the next free descriptor on the ring to a NoOp, and point +that descriptor's link to the complex command. + +An additional complexity of these non-transmit commands are that they may be +added asynchronous to the normal transmit queue, so we disable interrupts +whenever the Tx descriptor ring is manipulated. + +A notable aspect of the these special configure commands is that they do +work with the normal Tx ring entry scavenge method. The Tx ring scavenge +is done at interrupt time using the 'dirty_tx' index, and checking for the +command-complete bit. While the setup frames may have the NoOp command on the +Tx ring marked as complete, but not have completed the setup command, this +is not a problem. The tx_ring entry can be still safely reused, as the +tx_skbuff[] entry is always empty for config_cmd and mc_setup frames. + +Commands may have bits set e.g. CmdSuspend in the command word to either +suspend or stop the transmit/command unit. This driver always flags the last +command with CmdSuspend, erases the CmdSuspend in the previous command, and +then issues a CU_RESUME. +Note: Watch out for the potential race condition here: imagine + erasing the previous suspend + the chip processes the previous command + the chip processes the final command, and suspends + doing the CU_RESUME + the chip processes the next-yet-valid post-final-command. +So blindly sending a CU_RESUME is only safe if we do it immediately after +after erasing the previous CmdSuspend, without the possibility of an +intervening delay. Thus the resume command is always within the +interrupts-disabled region. This is a timing dependence, but handling this +condition in a timing-independent way would considerably complicate the code. + +Note: In previous generation Intel chips, restarting the command unit was a +notoriously slow process. This is presumably no longer true. + +IIIC. Receive structure + +Because of the bus-master support on the Speedo3 this driver uses the new +SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer. +This scheme allocates full-sized skbuffs as receive buffers. The value +SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to +trade-off the memory wasted by passing the full-sized skbuff to the queue +layer for all frames vs. the copying cost of copying a frame to a +correctly-sized skbuff. + +For small frames the copying cost is negligible (esp. considering that we +are pre-loading the cache with immediately useful header information), so we +allocate a new, minimally-sized skbuff. For large frames the copying cost +is non-trivial, and the larger copy might flush the cache of useful data, so +we pass up the skbuff the packet was received into. + +IIID. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'sp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.) After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero. Iff the 'sp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +Thanks to Steve Williams of Intel for arranging the non-disclosure agreement +that stated that I could disclose the information. But I still resent +having to sign an Intel NDA when I'm helping Intel sell their own product! + +*/ + +/* A few values that may be tweaked. */ +/* The ring sizes should be a power of two for efficiency. */ +#define TX_RING_SIZE 16 /* Effectively 2 entries fewer. */ +#define RX_RING_SIZE 16 +/* Size of an pre-allocated Rx buffer: + slack.*/ +#define PKT_BUF_SZ 1536 + +/* 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. */ +#define INTR_WORK 16 + +/* How to wait for the command unit to accept a command. + Typically this takes 0 ticks. */ +static inline void wait_for_cmd_done(int cmd_ioaddr) +{ + short wait = 100; + do ; + while(inb(cmd_ioaddr) && --wait >= 0); +} + +/* 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. + All accesses need not be longword aligned. */ +enum speedo_offsets { + SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */ + SCBPointer = 4, /* General purpose pointer. */ + SCBPort = 8, /* Misc. commands and operands. */ + SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */ + SCBCtrlMDI = 16, /* MDI interface control. */ + SCBEarlyRx = 20, /* Early receive byte count. */ +}; +/* Commands that can be put in a command list entry. */ +enum commands { + CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7, + CmdSuspend = 0x4000, /* Suspend after completion. */ + CmdIntr = 0x2000, /* Interrupt after completion. */ + CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ +}; + +/* The SCB accepts the following controls for the Tx and Rx units: */ +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +#define RX_START 0x0001 +#define RX_RESUME 0x0002 +#define RX_ABORT 0x0004 +#define RX_ADDR_LOAD 0x0006 +#define RX_RESUMENR 0x0007 +#define INT_MASK 0x0100 +#define DRVR_INT 0x0200 /* Driver generated interrupt. */ + +/* The Speedo3 Rx and Tx frame/buffer descriptors. */ +struct descriptor { /* A generic descriptor. */ + s16 status; /* Offset 0. */ + s16 command; /* Offset 2. */ + u32 link; /* struct descriptor * */ + unsigned char params[0]; +}; + +/* The Speedo3 Rx and Tx buffer descriptors. */ +struct RxFD { /* Receive frame descriptor. */ + s32 status; + u32 link; /* struct RxFD * */ + u32 rx_buf_addr; /* void * */ + u16 count; + u16 size; +}; + +/* Elements of the RxFD.status word. */ +#define RX_COMPLETE 0x8000 + +struct TxFD { /* Transmit frame descriptor set. */ + s32 status; + u32 link; /* void * */ + u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */ + s32 count; /* # of TBD (=1), Tx start thresh., etc. */ + /* This constitutes a single "TBD" entry -- we only use one. */ + u32 tx_buf_addr; /* void *, frame to be transmitted. */ + s32 tx_buf_size; /* Length of Tx frame. */ +}; + +/* Elements of the dump_statistics block. This block must be lword aligned. */ +struct speedo_stats { + u32 tx_good_frames; + u32 tx_coll16_errs; + u32 tx_late_colls; + u32 tx_underruns; + u32 tx_lost_carrier; + u32 tx_deferred; + u32 tx_one_colls; + u32 tx_multi_colls; + u32 tx_total_colls; + u32 rx_good_frames; + u32 rx_crc_errs; + u32 rx_align_errs; + u32 rx_resource_errs; + u32 rx_overrun_errs; + u32 rx_colls_errs; + u32 rx_runt_errs; + u32 done_marker; +}; + +struct speedo_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; + struct TxFD tx_ring[TX_RING_SIZE]; /* Commands (usually CmdTxPacket). */ + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct descriptor *last_cmd; /* Last command sent. */ + /* 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; + struct timer_list timer; /* Media selection timer. */ + long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + struct descriptor config_cmd; /* A configure command, with header... */ + 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. */ + 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. */ + unsigned int default_port:1; /* Last dev->if_port value. */ + unsigned int rx_bug:1; /* Work around receiver hang errata. */ + unsigned int rx_bug10:1; /* Receiver might hang at 10mbps. */ + unsigned int rx_bug100:1; /* Receiver might hang at 100mbps. */ + unsigned short phy[2]; /* PHY media interfaces available. */ +}; + +/* The parameters for a CmdConfigure operation. + There are so many options that it would be difficult to document each bit. + We mostly use the default or recommended settings. */ +const char basic_config_cmd[22] = { + 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0, + 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ + 0x3f, 0x05, }; + +/* PHY media interface chips. */ +static const char *phys[] = { + "None", "i82553-A/B", "i82553-C", "i82503", + "DP83840", "80c240", "80c24", "unknown-7", + "unknown-8", "unknown-9", "DP83840A", "unknown-11", + "unknown-12", "unknown-13", "unknown-14", "unknown-15", }; +enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, + S80C24, PhyUndefined, DP83840A=10, }; +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); + +static int read_eeprom(int ioaddr, int location); +static int mdio_read(int ioaddr, int phy_id, int location); +static int mdio_write(int ioaddr, int phy_id, int location, int value); +static int speedo_open(struct device *dev); +static void speedo_timer(unsigned long data); +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); +static void set_rx_mode(struct device *dev); + + + +#ifdef MODULE +/* The parameters that may be passed in... */ +/* '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 options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int debug = -1; /* The debug level */ + +/* A list of all installed Speedo devices, for removing the driver module. */ +static struct device *root_speedo_dev = NULL; +#endif + +int eepro100_init(struct device *dev) +{ + int cards_found = 0; + + if (pcibios_present()) { + int pci_index; + for (pci_index = 0; pci_index < 8; pci_index++) { + unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency; +#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) + int pci_ioaddr; +#else + long pci_ioaddr; +#endif + unsigned short pci_command; + + if (pcibios_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82557, + pci_index, &pci_bus, + &pci_device_fn)) + break; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + if (speedo_debug > 2) + printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n", + (int)pci_ioaddr, pci_irq_line); + + /* 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; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(" PCI latency timer (CFLT) is unreasonably low at %d." + " Setting to 255 clocks.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 255); + } 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]); +#else + speedo_found1(dev, pci_ioaddr, pci_irq_line, + dev ? dev->mem_start : 0); +#endif + cards_found++; + } + } + + return cards_found; +} + +static void speedo_found1(struct device *dev, int ioaddr, int irq, int options) +{ + static int did_version = 0; /* Already printed version info. */ + struct speedo_private *sp; + int i; + 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 + + /* Read the station address EEPROM before doing the reset. + Perhaps this should even be done before accepting the device, + then we wouldn't have a device name with which to report the error. */ + { + u16 sum = 0; + int j; + for (j = 0, i = 0; i < 0x40; i++) { + unsigned short value = read_eeprom(ioaddr, i); + eeprom[i] = value; + sum += value; + if (i < 3) { + dev->dev_addr[j++] = value; + dev->dev_addr[j++] = value >> 8; + } + } + if (sum != 0xBABA) + printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, " + "check settings before activating this device!\n", + dev->name, sum); + /* Don't unregister_netdev(dev); as the EEPro may actually be + usable, especially if the MAC address is set later. */ + } + + /* Reset the chip: stop Tx and Rx processes and clear counters. + This takes less than 10usec and will easily finish before the next + action. */ + outl(0, ioaddr + SCBPort); + + printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ", + dev->name, ioaddr); + for (i = 0; i < 5; i++) + printk("%2.2X:", dev->dev_addr[i]); + printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq); + +#ifndef kernel_bloat + /* OK, this is pure kernel bloat. I don't like it when other drivers + waste non-pageable kernel space to emit similar messages, but I need + them for bug reports. */ + { + const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"}; + /* The self-test results must be paragraph aligned. */ + int str[6], *volatile self_test_results; + int boguscnt = 16000; /* Timeout for set-test. */ + if (eeprom[3] & 0x03) + printk(KERN_INFO " Receiver lock-up bug exists -- enabling" + " work-around.\n"); + printk(KERN_INFO " Board assembly %4.4x%2.2x-%3.3d, Physical" + " connectors present:", + eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff); + for (i = 0; i < 4; i++) + if (eeprom[5] & (1<>8)&15], eeprom[6] & 0x1f); + 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(" MDIO register %d is %4.4x.\n", + i, mdio_read(ioaddr, eeprom[6] & 0x1f, i)); + for (i = 5; i < 7; i++) + printk(" MDIO register %d is %4.4x.\n", + i, mdio_read(ioaddr, eeprom[6] & 0x1f, i)); + printk(" 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; + if (congenb) + mdi_reg23 |= 0x0100; + printk(" DP83840 specific setup, setting register 23 to %4.4x.\n", + mdi_reg23); + mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23); + } + if ((options >= 0) && (options & 0x60)) { + printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n", + (options & 0x20 ? 100 : 10), + (options & 0x10 ? "full" : "half")); + mdio_write(ioaddr, eeprom[6] & 0x1f, 0, + ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */ + ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + } + + /* Perform a system self-test. */ + self_test_results = (int*) ((((int) str) + 15) & ~0xf); + self_test_results[0] = 0; + 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. */ + printk(KERN_ERR "Self test failed, status %8.8x:\n" + KERN_ERR " Failure to initialize the i82557.\n" + KERN_ERR " Verify that the card is a bus-master" + " capable slot.\n", + self_test_results[1]); + } else + printk(KERN_INFO " General self-test: %s.\n" + KERN_INFO " Serial sub-system self-test: %s.\n" + KERN_INFO " Internal registers self-test: %s.\n" + KERN_INFO " ROM checksum self-test: %s (%#8.8x).\n", + self_test_results[1] & 0x1000 ? "failed" : "passed", + self_test_results[1] & 0x0020 ? "failed" : "passed", + self_test_results[1] & 0x0008 ? "failed" : "passed", + self_test_results[1] & 0x0004 ? "failed" : "passed", + self_test_results[0]); + } +#endif /* kernel_bloat */ + + /* We do a request_region() only to register /proc/ioports info. */ + request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet"); + + dev->base_addr = ioaddr; + dev->irq = irq; + + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL); + sp = dev->priv; + memset(sp, 0, sizeof(*sp)); +#ifdef MODULE + sp->next_module = root_speedo_dev; + root_speedo_dev = dev; +#endif + + sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0; + sp->default_port = options >= 0 ? (options & 0x0f) : 0; + + sp->phy[0] = eeprom[6]; + sp->phy[1] = eeprom[7]; + sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1; + + printk(KERN_INFO " Operating in %s duplex mode.\n", + sp->full_duplex ? "full" : "half"); + if (sp->rx_bug) + printk(KERN_INFO " Reciever lock-up workaround activated.\n"); + + /* The Speedo-specific entries in the device structure. */ + dev->open = &speedo_open; + 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 + + return; +} + +/* Serial EEPROM section. + A "bit" grungy, but we work our way through bit-by-bit :->. */ +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#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. */ +#define eeprom_delay(nanosec) do { int _i = 3; 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) + +static int read_eeprom(int ioaddr, int location) +{ + int i; + unsigned short retval = 0; + int ee_addr = ioaddr + SCBeeprom; + int read_cmd = location | EE_READ_CMD; + + outw(EE_ENB & ~EE_CS, ee_addr); + outw(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outw(EE_ENB | dataval, ee_addr); + 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); + + for (i = 15; i >= 0; i--) { + outw(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); + outw(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outw(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +static int mdio_read(int ioaddr, int phy_id, int location) +{ + int val, boguscnt = 64*4; /* <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); + } + } while (! (val & 0x10000000)); + return val & 0xffff; +} + +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 */ + 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); + } + } while (! (val & 0x10000000)); + return val & 0xffff; +} + + +static int +speedo_open(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + +#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 */ +#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); + + MOD_INC_USE_COUNT; + + /* Load the statistics block address. */ + outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); + outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd); + sp->lstats.done_marker = 0; + + speedo_init_rx_ring(dev); + outl(0, ioaddr + SCBPointer); + outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd); + + /* Todo: verify that we must wait for previous command completion. */ + wait_for_cmd_done(ioaddr + SCBCmd); + outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer); + outw(INT_MASK | RX_START, ioaddr + SCBCmd); + + /* Fill the first command with our physical address. */ + { + unsigned short *eaddrs = (unsigned short *)dev->dev_addr; + unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr); + + /* Avoid a bug(?!) here by marking the command already completed. */ + sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000; + sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1])); + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; + } + sp->last_cmd = (struct descriptor *)&sp->tx_ring[0]; + sp->cur_tx = 1; + sp->dirty_tx = 0; + sp->tx_full = 0; + + outl(0, ioaddr + SCBPointer); + outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd); + + dev->if_port = sp->default_port; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* Start the chip's Tx process and unmask interrupts. */ + /* Todo: verify that we must wait for previous command completion. */ + wait_for_cmd_done(ioaddr + SCBCmd); + outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer); + outw(CU_START, ioaddr + SCBCmd); + + /* Setup the chip and configure the multicast list. */ + sp->mc_setup_frm = NULL; + sp->mc_setup_frm_len = 0; + sp->rx_mode = -1; /* Invalid -> always reset the mode. */ + set_rx_mode(dev); + + if (speedo_debug > 2) { + printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n", + dev->name, inw(ioaddr + SCBStatus)); + } + /* Set the timer. The timer serves a dual purpose: + 1) to monitor the media interface (e.g. link beat) and perhaps switch + to an alternate media type + 2) to monitor Rx activity, and restart the Rx process if the receiver + hangs. */ + init_timer(&sp->timer); + sp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + sp->timer.data = (unsigned long)dev; + sp->timer.function = &speedo_timer; /* timer handler */ + add_timer(&sp->timer); + + outw(CU_DUMPSTATS, ioaddr + SCBCmd); + return 0; +} + +/* Media monitoring and control. */ +static void speedo_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int tickssofar = jiffies - sp->last_rx_time; + + if (speedo_debug > 3) { + int ioaddr = dev->base_addr; + printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n", + dev->name, inw(ioaddr + SCBStatus)); + } + if (sp->rx_bug) { + if (tickssofar > 2*HZ || sp->rx_mode < 0) { + /* We haven't received a packet in a Long Time. We might have been + bitten by the receiver hang bug. This can be cleared by sending + a set multicast list command. */ + set_rx_mode(dev); + } + /* We must continue to monitor the media. */ + sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */ + add_timer(&sp->timer); + } +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +speedo_init_rx_ring(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + struct RxFD *rxf, *last_rxf = NULL; + int i; + + sp->cur_rx = 0; + sp->dirty_rx = RX_RING_SIZE - 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; + } + /* Mark the last entry as end-of-list. */ + last_rxf->status = 0xC0000002; /* '2' is flag value only. */ + sp->last_rxf = last_rxf; +} + +static void speedo_tx_timeout(struct device *dev) +{ + 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("%s: Tx timeout fill index %d scavenge index %d.\n", + dev->name, sp->cur_tx, sp->dirty_tx); + printk(" Tx queue "); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (int)sp->tx_ring[i].status); + printk(".\n 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(" (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("%s: Trying to restart the transmitter...\n", dev->name); + outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), + ioaddr + SCBPointer); + outw(CU_START, ioaddr + SCBCmd); + } else { + outw(DRVR_INT, ioaddr + SCBCmd); + } + sp->stats.tx_errors++; + dev->trans_start = jiffies; + return; +} + +static int +speedo_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + 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 (set_bit(0, (void*)&dev->tbusy) != 0) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < TX_TIMEOUT - 2) + return 1; + if (tickssofar < TX_TIMEOUT) { + /* Reap sent packets from the full Tx queue. */ + outw(DRVR_INT, ioaddr + SCBCmd); + return 1; + } + speedo_tx_timeout(dev); + return 0; + } + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + { /* Prevent interrupts from changing the Tx ring from underneath us. */ + unsigned long flags; + + save_flags(flags); + cli(); + /* Calculate the Tx descriptor entry. */ + entry = sp->cur_tx++ % TX_RING_SIZE; + + sp->tx_skbuff[entry] = skb; + /* Todo: be a little more clever about setting the interrupt bit. */ + sp->tx_ring[entry].status = + (CmdSuspend | CmdTx | CmdTxFlex) << 16; + sp->tx_ring[entry].link = + virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + sp->tx_ring[entry].tx_desc_addr = + virt_to_bus(&sp->tx_ring[entry].tx_buf_addr); + /* The data region is always in one buffer descriptor, Tx FIFO + threshold of 256. */ + sp->tx_ring[entry].count = 0x01208000; + sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data); + sp->tx_ring[entry].tx_buf_size = skb->len; + /* Todo: perhaps leave the interrupt bit set if the Tx queue is more + than half full. Argument against: we should be receiving packets + and scavenging the queue. Argument for: if so, it shouldn't + matter. */ + sp->last_cmd->command &= ~(CmdSuspend | CmdIntr); + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + /* Trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + restore_flags(flags); + } + + /* Leave room for set_rx_mode() to fill two entries. */ + if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3) + sp->tx_full = 1; + else + dev->tbusy = 0; + + dev->trans_start = jiffies; + + return 0; +} + +/* 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 = INTR_WORK; + unsigned short status; + +#ifndef final_version + if (dev == NULL) { + printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq); + return; + } +#endif + + 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); + return; + } + dev->interrupt = 1; +#endif + + do { + status = inw(ioaddr + SCBStatus); + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(status & 0xfc00, ioaddr + SCBStatus); + + if (speedo_debug > 4) + printk(KERN_DEBUG "%s: interrupt status=%#4.4x.\n", + dev->name, status); + + if ((status & 0xfc00) == 0) + break; + + if (status & 0x4000) /* Packet received. */ + 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(" 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?!) */ + /* No idea of what went wrong. Restart the receiver. */ + outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), + ioaddr + SCBPointer); + outw(RX_START, ioaddr + SCBCmd); + } + sp->stats.rx_errors++; + } + + /* User interrupt, Command/Tx unit interrupt or CU not active. */ + if (status & 0xA400) { + unsigned int dirty_tx = sp->dirty_tx; + + while (sp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + int status = sp->tx_ring[entry].status; + + if (speedo_debug > 5) + printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n", + entry, status); + if ((status & 0x8000) == 0) + break; /* It still hasn't been processed. */ + /* Free the original skb. */ + if (sp->tx_skbuff[entry]) { + sp->stats.tx_packets++; /* Count only user packets. */ + dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE); + sp->tx_skbuff[entry] = 0; + } + dirty_tx++; + } + +#ifndef final_version + if (sp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dirty_tx, sp->cur_tx, sp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (sp->tx_full && dev->tbusy + && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) { + /* The ring is no longer full, clear tbusy. */ + sp->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + sp->dirty_tx = dirty_tx; + } + + if (--boguscnt < 0) { + printk("%s: Too much work at interrupt, status=0x%4.4x.\n", + dev->name, status); + /* Clear all interrupt sources. */ + outl(0xfc00, ioaddr + SCBStatus); + break; + } + } while (1); + + if (speedo_debug > 3) + 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; + return; +} + +static int +speedo_rx(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int entry = sp->cur_rx % RX_RING_SIZE; + int status; + + if (speedo_debug > 4) + printk(KERN_DEBUG " In speedo_rx().\n"); + /* If we own the next entry, it's a new packet. Send it up. */ + while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) { + + if (speedo_debug > 4) + printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status, + sp->rx_ringp[entry]->count & 0x3fff); + if (status & 0x0200) { + printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n", + dev->name, status); + } else if ( ! (status & 0x2000)) { + /* There was a fatal error. This *should* be impossible. */ + sp->stats.rx_errors++; + printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n", + dev->name, status); + } else { + /* Malloc up new buffer, compatible with net-2e. */ + short pkt_len = sp->rx_ringp[entry]->count & 0x3fff; + struct sk_buff *skb; + int rx_in_place = 0; + + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len > SKBUFF_RX_COPYBREAK) { + struct sk_buff *newskb; + char *temp; + + /* 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("%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("%s: Warning -- the skbuff addresses do not match" + " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name, + 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("%s: Memory squeeze, deferring packet.\n", dev->name); + /* Check that at least two ring entries are free. + If not, free one and mark stats->rx_dropped++. */ + /* ToDo: This is not correct!!!! We should count the number + of linked-in Rx buffer to very that we have at least two + remaining. */ + for (i = 0; i < RX_RING_SIZE; i++) + if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status) + & RX_COMPLETE)) + break; + + if (i > RX_RING_SIZE -2) { + sp->stats.rx_dropped++; + sp->rx_ringp[entry]->status = 0; + sp->cur_rx++; + } + 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 */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len); + } + 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++; + } + + /* ToDo: This is better than before, but should be checked. */ + { + struct RxFD *rxf = sp->rx_ringp[entry]; + rxf->status = 0xC0000003; /* '3' for verification only */ + rxf->link = 0; /* None yet. */ + rxf->count = 0; + rxf->size = PKT_BUF_SZ; + sp->last_rxf->link = virt_to_bus(rxf); + sp->last_rxf->status &= ~0xC0000000; + sp->last_rxf = rxf; + entry = (++sp->cur_rx) % RX_RING_SIZE; + } + } + + sp->last_rx_time = jiffies; + return 0; +} + +static int +speedo_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (speedo_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, inw(ioaddr + SCBStatus)); + + /* Shut off the media monitoring timer. */ + del_timer(&sp->timer); + + /* Disable interrupts, and stop the chip's Rx process. */ + 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++) { + struct sk_buff *skb = sp->rx_skbuff[i]; + sp->rx_skbuff[i] = 0; + /* Clear the Rx descriptors. */ + if (skb) + dev_kfree_skb(skb, FREE_WRITE); + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct sk_buff *skb = sp->tx_skbuff[i]; + sp->tx_skbuff[i] = 0; + /* Clear the Tx descriptors. */ + if (skb) + dev_kfree_skb(skb, FREE_WRITE); + } + if (sp->mc_setup_frm) { + kfree(sp->mc_setup_frm); + sp->mc_setup_frm_len = 0; + } + + /* Print a few items for debugging. */ + if (speedo_debug > 3) { + printk("%s:Printing Rx ring (next to receive into %d).\n", + dev->name, sp->cur_rx); + + for (i = 0; i < RX_RING_SIZE; i++) + printk(" Rx ring entry %d %8.8x.\n", + i, (int)sp->rx_ringp[i]->status); + + for (i = 0; i < 5; i++) + printk(" PHY index %d register %d is %4.4x.\n", + 1, i, mdio_read(ioaddr, 1, i)); + for (i = 21; i < 26; i++) + printk(" PHY index %d register %d is %4.4x.\n", + 1, i, mdio_read(ioaddr, 1, i)); + } + MOD_DEC_USE_COUNT; + + return 0; +} + +/* The Speedo-3 has an especially awkward and unusable method of getting + statistics out of the chip. It takes an unpredictable length of time + for the dump-stats command to complete. To avoid a busy-wait loop we + update the stats with the previous dump results, and then trigger a + new dump. + + These problems are mitigated by the current /proc implementation, which + calls this routine first to judge the output length, and then to emit the + output. + + Oh, and incoming frames are dropped while executing dump-stats! + */ +static struct enet_statistics * +speedo_get_stats(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */ + sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs; + sp->stats.tx_window_errors += sp->lstats.tx_late_colls; + sp->stats.tx_fifo_errors += sp->lstats.tx_underruns; + sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier; + /*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/ + sp->stats.collisions += sp->lstats.tx_total_colls; + sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs; + sp->stats.rx_frame_errors += sp->lstats.rx_align_errs; + sp->stats.rx_over_errors += sp->lstats.rx_resource_errs; + sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs; + sp->stats.rx_length_errors += sp->lstats.rx_runt_errs; + sp->lstats.done_marker = 0x0000; + if (dev->start) + outw(CU_DUMPSTATS, ioaddr + SCBCmd); + } + return &sp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + This is very ugly with Intel chips -- we usually have to execute an + entire configuration command, plus process a multicast command. + This is complicated. We must put a large configuration command and + an arbitrarily-sized multicast command in the transmit list. + To minimize the disruption -- the previous command might have already + loaded the link -- we convert the current command block, normally a Tx + command, into a no-op and link it to the new command. +*/ +static void +set_rx_mode(struct device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + int ioaddr = dev->base_addr; + char new_rx_mode; + unsigned long flags; + int entry, i; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + new_rx_mode = 3; + } else if (dev->flags & IFF_ALLMULTI) { + new_rx_mode = 1; + } else + new_rx_mode = 0; + + if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) { + /* The Tx ring is full -- don't add anything! Presumably the new mode + is in config_cmd_data and will be added anyway. */ + sp->rx_mode = -1; + return; + } + + if (new_rx_mode != sp->rx_mode) { + /* We must change the configuration. Construct a CmdConfig frame. */ + memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd)); + sp->config_cmd_data[1] = (txfifo << 4) | rxfifo; + sp->config_cmd_data[4] = rxdmacount; + sp->config_cmd_data[5] = txdmacount + 0x80; + sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48; + sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80; + sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05; + if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */ + sp->config_cmd_data[15] |= 0x80; + sp->config_cmd_data[8] = 0; + } + save_flags(flags); + cli(); + /* Fill the "real" tx_ring frame with a no-op and point it to us. */ + entry = sp->cur_tx++ % TX_RING_SIZE; + sp->tx_skbuff[entry] = 0; /* Nothing to free. */ + sp->tx_ring[entry].status = CmdNOp << 16; + sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd); + sp->config_cmd.status = 0; + sp->config_cmd.command = CmdSuspend | CmdConfigure; + sp->config_cmd.link = + virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); + sp->last_cmd->command &= ~CmdSuspend; + /* Immediately trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + sp->last_cmd = &sp->config_cmd; + restore_flags(flags); + if (speedo_debug > 5) { + int i; + printk(" CmdConfig frame in entry %d.\n", entry); + for(i = 0; i < 32; i++) + printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]); + printk(".\n"); + } + } + + if (new_rx_mode == 0 && dev->mc_count < 3) { + /* The simple case of 0-2 multicast list entries occurs often, and + fits within one tx_ring[] entry. */ + u16 *setup_params; + unsigned short *eaddrs; + struct dev_mc_list *mclist; + + save_flags(flags); + cli(); + entry = sp->cur_tx++ % TX_RING_SIZE; + sp->tx_skbuff[entry] = 0; + sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16; + sp->tx_ring[entry].link = + virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */ + setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr; + *setup_params++ = dev->mc_count*6; + /* Fill in the multicast addresses. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (unsigned short *)mclist->dmi_addr; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + } + + sp->last_cmd->command &= ~CmdSuspend; + /* Immediately trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + restore_flags(flags); + } else if (new_rx_mode == 0) { + /* This does not work correctly, but why not? */ + struct dev_mc_list *mclist; + unsigned short *eaddrs; + struct descriptor *mc_setup_frm = sp->mc_setup_frm; + u16 *setup_params = (short *)mc_setup_frm->params; + int i; + + if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 + || sp->mc_setup_frm == NULL) { + /* Allocate a new frame, 10bytes + addrs, with a few + extra entries for growth. */ + if (sp->mc_setup_frm) + kfree(sp->mc_setup_frm); + sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24; + printk("%s: Allocating a setup frame of size %d.\n", + dev->name, sp->mc_setup_frm_len); + sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, + intr_count ? GFP_ATOMIC : GFP_KERNEL); + if (sp->mc_setup_frm == NULL) { + printk("%s: Failed to allocate a setup frame.\n", dev->name); + sp->rx_mode = -1; /* We failed, try again. */ + return; + } + } + mc_setup_frm = sp->mc_setup_frm; + /* Construct the new setup frame. */ + printk("%s: Constructing a setup frame at %p, %d bytes.\n", + dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len); + mc_setup_frm->status = 0; + mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList; + /* Link set below. */ + setup_params = (short *)mc_setup_frm->params; + *setup_params++ = dev->mc_count*6; + /* Fill in the multicast addresses. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (unsigned short *)mclist->dmi_addr; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + *setup_params++ = *eaddrs++; + } + + /* Disable interrupts while playing with the Tx Cmd list. */ + save_flags(flags); + cli(); + entry = sp->cur_tx++ % TX_RING_SIZE; + + if (speedo_debug > 5) + printk(" CmdMCSetup frame length %d in entry %d.\n", + dev->mc_count, entry); + + /* Change the command to a NoOp, pointing to the CmdMulti command. */ + sp->tx_skbuff[entry] = 0; + sp->tx_ring[entry].status = CmdNOp << 16; + sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm); + + /* Set the link in the setup frame. */ + mc_setup_frm->link = + virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); + + sp->last_cmd->command &= ~CmdSuspend; + /* Immediately trigger the command unit resume. */ + outw(CU_RESUME, ioaddr + SCBCmd); + sp->last_cmd = mc_setup_frm; + restore_flags(flags); + printk("%s: Last command at %p is %4.4x.\n", + dev->name, sp->last_cmd, sp->last_cmd->command); + } + + sp->rx_mode = new_rx_mode; +} + +#ifdef MODULE +#if (LINUX_VERSION_CODE < VERSION(1,3,38)) /* 1.3.38 and later */ +char kernel_version[] = UTS_RELEASE; +#endif + +int +init_module(void) +{ + int cards_found; + + if (debug >= 0) + speedo_debug = debug; + if (speedo_debug) + printk(KERN_INFO "%s", version); + + root_speedo_dev = NULL; + cards_found = eepro100_init(NULL); + return cards_found < 0 ? cards_found : 0; +} + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_speedo_dev) { + next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module; + unregister_netdev(root_speedo_dev); + release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE); + kfree(root_speedo_dev); + root_speedo_dev = next_dev; + } +} +#else /* not MODULE */ +int eepro100_probe(struct device *dev) +{ + int cards_found = 0; + + cards_found = eepro100_init(dev); + + if (speedo_debug > 0 && cards_found) + printk(version); + + return cards_found ? 0 : -ENODEV; +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c" + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff -urN linux-2.0.31-pre5/drivers/net/Makefile linux-2.0.31-pre6/drivers/net/Makefile --- linux-2.0.31-pre5/drivers/net/Makefile 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/Makefile 2003-08-15 15:04:01.000000000 -0700 @@ -320,6 +320,15 @@ endif endif +ifeq ($(CONFIG_EEXPRESS_PRO100B),y) +L_OBJS += eepro100.o +else + ifeq ($(CONFIG_EEXPRESS_PRO100B),m) + M_OBJS += eepro100.o + endif +endif + + ifeq ($(CONFIG_WAVELAN),y) L_OBJS += wavelan.o else diff -urN linux-2.0.31-pre5/drivers/net/ne.c linux-2.0.31-pre6/drivers/net/ne.c --- linux-2.0.31-pre5/drivers/net/ne.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/ne.c 2003-08-15 15:04:01.000000000 -0700 @@ -62,9 +62,11 @@ /* Do we have a non std. amount of memory? (in units of 256 byte pages) */ /* #define PACKETBUF_MEMSIZE 0x40 */ +#if defined(HAVE_DEVLIST) || !defined(MODULE) /* A zero-terminated list of I/O addresses to be probed. */ static unsigned int netcard_portlist[] = { 0x300, 0x280, 0x320, 0x340, 0x360, 0}; +#endif /* defined(HAVE_DEVLIST) || !defined(MODULE) */ #ifdef CONFIG_PCI /* Ack! People are making PCI ne2000 clones! Oh the horror, the horror... */ @@ -160,7 +162,9 @@ int ne_probe(struct device *dev) { +#ifndef MODULE int i; +#endif /* MODULE */ int base_addr = dev ? dev->base_addr : 0; /* First check any supplied i/o locations. User knows best. */ diff -urN linux-2.0.31-pre5/drivers/net/smc-ultra32.c linux-2.0.31-pre6/drivers/net/smc-ultra32.c --- linux-2.0.31-pre5/drivers/net/smc-ultra32.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/smc-ultra32.c 2003-08-15 15:04:01.000000000 -0700 @@ -47,7 +47,6 @@ #include -#include #include #include #include diff -urN linux-2.0.31-pre5/drivers/net/Space.c linux-2.0.31-pre6/drivers/net/Space.c --- linux-2.0.31-pre5/drivers/net/Space.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/net/Space.c 2003-08-15 15:04:01.000000000 -0700 @@ -50,6 +50,7 @@ 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 *); @@ -163,6 +164,9 @@ #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 diff -urN linux-2.0.31-pre5/drivers/scsi/aic7xxx/aic7xxx.reg linux-2.0.31-pre6/drivers/scsi/aic7xxx/aic7xxx.reg --- linux-2.0.31-pre5/drivers/scsi/aic7xxx/aic7xxx.reg 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/aic7xxx/aic7xxx.reg 2003-08-15 15:04:01.000000000 -0700 @@ -1079,6 +1079,21 @@ CUR_SCBID { size 1 } + /* + * Running count of commands placed in + * the QOUTFIFO. This is cleared by the + * kernel driver every FIFODEPTH commands. + */ + CMDOUTCNT { + size 1 + } + /* + * Maximum number of entries allowed in + * the QOUT/INFIFO. + */ + FIFODEPTH { + size 1 + } ARG_1 { size 1 mask SEND_MSG 0x80 diff -urN linux-2.0.31-pre5/drivers/scsi/aic7xxx/aic7xxx.seq linux-2.0.31-pre6/drivers/scsi/aic7xxx/aic7xxx.seq --- linux-2.0.31-pre5/drivers/scsi/aic7xxx/aic7xxx.seq 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/aic7xxx/aic7xxx.seq 2003-08-15 15:04:01.000000000 -0700 @@ -643,6 +643,15 @@ 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; diff -urN linux-2.0.31-pre5/drivers/scsi/aic7xxx.c linux-2.0.31-pre6/drivers/scsi/aic7xxx.c --- linux-2.0.31-pre5/drivers/scsi/aic7xxx.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/aic7xxx.c 2003-08-15 15:04:01.000000000 -0700 @@ -829,6 +829,7 @@ 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 */ @@ -3880,6 +3881,19 @@ #endif while (qoutcnt > 0) { + if ((p->flags & PAGE_ENABLED) != 0) + { + p->cmdoutcnt += qoutcnt; + if (p->cmdoutcnt >= p->qfullcount) + { + /* + * Since paging only occurs on aic78x0 chips, we can use + * Auto Access Pause to clear the command count. + */ + outb(0, p->base + CMDOUTCNT); + p->cmdoutcnt = 0; + } + } for (i = 0; i < qoutcnt; i++) { scb_index = inb(p->base + QOUTFIFO); @@ -4638,9 +4652,9 @@ { old_verbose = aic7xxx_verbose; printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please " - "verify driver"); + "verify driver\n"); printk(KERN_INFO " detected settings and use manual termination " - "if necessary."); + "if necessary.\n"); /* Configure auto termination. */ outb(SEECS | SEEMS, p->base + SEECTL); @@ -5291,6 +5305,9 @@ */ outb(p->qcntmask, p->base + QCNTMASK); + outb(p->qfullcount, p->base + FIFODEPTH); + outb(0, p->base + CMDOUTCNT); + /* * We don't have any waiting selections or disconnected SCBs. */ diff -urN linux-2.0.31-pre5/drivers/scsi/aic7xxx_reg.h linux-2.0.31-pre6/drivers/scsi/aic7xxx_reg.h --- linux-2.0.31-pre5/drivers/scsi/aic7xxx_reg.h 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/aic7xxx_reg.h 2003-08-15 15:04:01.000000000 -0700 @@ -252,15 +252,19 @@ #define CUR_SCBID 0x58 -#define ARG_1 0x59 -#define RETURN_1 0x59 -#define SEND_MSG 0x80 -#define SEND_SENSE 0x40 -#define SEND_REJ 0x20 +#define CMDOUTCNT 0x59 #define SCSICONF 0x5a #define RESET_SCSI 0x40 +#define FIFODEPTH 0x5a + +#define ARG_1 0x5b +#define RETURN_1 0x5b +#define SEND_MSG 0x80 +#define SEND_SENSE 0x40 +#define SEND_REJ 0x20 + #define HOSTCONF 0x5d #define HA_274_BIOSCTRL 0x5f diff -urN linux-2.0.31-pre5/drivers/scsi/aic7xxx_seq.h linux-2.0.31-pre6/drivers/scsi/aic7xxx_seq.h --- linux-2.0.31-pre5/drivers/scsi/aic7xxx_seq.h 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/aic7xxx_seq.h 2003-08-15 15:04:01.000000000 -0700 @@ -16,28 +16,28 @@ 0xff, 0x48, 0x2c, 0x18, 0xff, 0x40, 0x64, 0x02, 0x00, 0x9c, 0x03, 0x1e, - 0x00, 0x6a, 0xaa, 0x17, + 0x00, 0x6a, 0xad, 0x17, 0xff, 0x65, 0x03, 0x1c, 0xff, 0x9b, 0x58, 0x02, 0xff, 0x58, 0x90, 0x02, 0x0d, 0x6a, 0x3d, 0x00, - 0x00, 0x58, 0x74, 0x17, + 0x00, 0x58, 0x77, 0x17, 0x28, 0xa0, 0x2a, 0x1a, 0x50, 0x6a, 0x60, 0x00, 0xff, 0x90, 0x4a, 0x02, - 0x00, 0xa1, 0x9e, 0x17, - 0xff, 0x6c, 0x59, 0x02, - 0xff, 0x59, 0x27, 0x1c, + 0x00, 0xa1, 0xa1, 0x17, + 0xff, 0x6c, 0x5b, 0x02, + 0xff, 0x5b, 0x27, 0x1c, 0xff, 0x4a, 0x90, 0x02, - 0x00, 0x65, 0xa7, 0x17, - 0x00, 0x6a, 0x4f, 0x17, + 0x00, 0x65, 0xaa, 0x17, + 0x00, 0x6a, 0x52, 0x17, 0xff, 0x65, 0x1f, 0x18, 0x51, 0x6a, 0x91, 0x00, 0xff, 0x58, 0xb3, 0x02, - 0x00, 0x65, 0xb8, 0x17, + 0x00, 0x65, 0xbb, 0x17, 0x10, 0x6a, 0x60, 0x00, 0x00, 0x65, 0x03, 0x10, - 0xff, 0x59, 0x90, 0x02, + 0xff, 0x5b, 0x90, 0x02, 0xff, 0x58, 0xb3, 0x02, 0x10, 0x6a, 0x60, 0x00, 0x00, 0x65, 0x03, 0x10, @@ -121,26 +121,26 @@ 0x7d, 0x6a, 0x3d, 0x00, 0x00, 0x65, 0x7a, 0x10, 0x08, 0x6a, 0x66, 0x00, - 0xa9, 0x6a, 0x71, 0x17, + 0xa9, 0x6a, 0x74, 0x17, 0x00, 0x65, 0x82, 0x10, 0x79, 0x6a, 0x3d, 0x00, - 0x00, 0x65, 0x4c, 0x17, + 0x00, 0x65, 0x4f, 0x17, 0x10, 0x41, 0x76, 0x1a, 0x88, 0x6a, 0x66, 0x00, - 0xac, 0x6a, 0x6d, 0x17, - 0x00, 0x65, 0x6a, 0x17, + 0xac, 0x6a, 0x70, 0x17, + 0x00, 0x65, 0x6d, 0x17, 0xff, 0xa3, 0x43, 0x02, 0x44, 0x6a, 0x66, 0x00, - 0xa4, 0x6a, 0x70, 0x17, + 0xa4, 0x6a, 0x73, 0x17, 0xff, 0x43, 0x88, 0x1a, 0x80, 0x02, 0x02, 0x00, 0xff, 0x6a, 0x8c, 0x00, 0xff, 0x6a, 0x8d, 0x00, 0xff, 0x6a, 0x8e, 0x00, - 0x00, 0x65, 0x6a, 0x17, + 0x00, 0x65, 0x6d, 0x17, 0x01, 0x43, 0x8a, 0x18, 0xbf, 0x3d, 0x3d, 0x02, - 0x00, 0x3d, 0x41, 0x17, + 0x00, 0x3d, 0x44, 0x17, 0x80, 0x02, 0xa2, 0x1a, 0xff, 0x65, 0x9c, 0x1e, 0xff, 0x43, 0x43, 0x06, @@ -149,14 +149,14 @@ 0x08, 0x44, 0x44, 0x06, 0x00, 0x45, 0x45, 0x08, 0x88, 0x6a, 0x66, 0x00, - 0x44, 0x6a, 0x70, 0x17, + 0x44, 0x6a, 0x73, 0x17, 0x08, 0x6a, 0x8c, 0x00, 0xff, 0x6a, 0x8d, 0x02, 0xff, 0x6a, 0x8e, 0x02, 0x0d, 0x93, 0x93, 0x00, - 0x00, 0x65, 0x9a, 0x17, - 0x88, 0x6a, 0x92, 0x17, - 0x00, 0x65, 0x6a, 0x17, + 0x00, 0x65, 0x9d, 0x17, + 0x88, 0x6a, 0x95, 0x17, + 0x00, 0x65, 0x6d, 0x17, 0x10, 0x0c, 0x82, 0x1e, 0xff, 0x08, 0xa9, 0x02, 0xff, 0x09, 0xaa, 0x02, @@ -167,19 +167,19 @@ 0x7f, 0x02, 0x02, 0x02, 0xe1, 0x6a, 0x91, 0x00, 0x00, 0x65, 0x5c, 0x10, - 0x00, 0x65, 0x4c, 0x17, + 0x00, 0x65, 0x4f, 0x17, 0x88, 0x6a, 0x66, 0x00, - 0xb4, 0x6a, 0x6f, 0x17, + 0xb4, 0x6a, 0x72, 0x17, 0xff, 0x6a, 0x8d, 0x02, 0xff, 0x6a, 0x8e, 0x02, - 0x00, 0x65, 0x6a, 0x17, - 0x3d, 0x6a, 0x41, 0x17, + 0x00, 0x65, 0x6d, 0x17, + 0x3d, 0x6a, 0x44, 0x17, 0x00, 0x65, 0x5c, 0x10, - 0x00, 0x65, 0x4c, 0x17, + 0x00, 0x65, 0x4f, 0x17, 0xff, 0x06, 0xa2, 0x02, 0x00, 0x65, 0x5c, 0x10, 0xff, 0x34, 0xb2, 0x1a, - 0x08, 0x6a, 0x2f, 0x17, + 0x08, 0x6a, 0x32, 0x17, 0x35, 0x6a, 0x65, 0x00, 0xff, 0x34, 0x66, 0x02, 0x01, 0x0c, 0xb4, 0x1e, @@ -197,116 +197,119 @@ 0x40, 0x6a, 0x0c, 0x00, 0xff, 0x6a, 0x34, 0x02, 0x00, 0x65, 0x5c, 0x10, - 0x64, 0x6a, 0x3c, 0x17, + 0x64, 0x6a, 0x3f, 0x17, 0xff, 0x64, 0x4b, 0x02, - 0x80, 0x64, 0x0b, 0x1b, - 0x04, 0x64, 0xfe, 0x1c, - 0x02, 0x64, 0x01, 0x1d, + 0x80, 0x64, 0x0e, 0x1b, + 0x04, 0x64, 0x01, 0x1d, + 0x02, 0x64, 0x04, 0x1d, 0x00, 0x6a, 0xd1, 0x1c, - 0x03, 0x64, 0x09, 0x1d, - 0x01, 0x64, 0xf2, 0x1c, - 0x07, 0x64, 0x2d, 0x1d, + 0x03, 0x64, 0x0c, 0x1d, + 0x01, 0x64, 0xf5, 0x1c, + 0x07, 0x64, 0x30, 0x1d, 0x08, 0x64, 0xcf, 0x1c, 0x11, 0x6a, 0x91, 0x00, - 0x07, 0x6a, 0x2f, 0x17, + 0x07, 0x6a, 0x32, 0x17, 0xff, 0x06, 0x6a, 0x02, 0x00, 0x65, 0x5c, 0x10, 0xff, 0xa8, 0xd3, 0x1a, 0xff, 0xa2, 0xda, 0x1e, 0x01, 0x6a, 0x3d, 0x00, - 0x00, 0xb9, 0x74, 0x17, + 0x00, 0xb9, 0x77, 0x17, 0xff, 0xa2, 0xda, 0x1e, 0x71, 0x6a, 0x91, 0x00, - 0x40, 0x59, 0xda, 0x18, + 0x40, 0x5b, 0xda, 0x18, 0xff, 0xb9, 0xb3, 0x02, - 0x00, 0x65, 0xe4, 0x10, + 0x00, 0x65, 0xe7, 0x10, 0x20, 0xa0, 0xe0, 0x1a, 0xff, 0x90, 0x4a, 0x02, 0xff, 0xb3, 0x49, 0x02, - 0x00, 0xa1, 0x9e, 0x17, + 0x00, 0xa1, 0xa1, 0x17, 0xff, 0x49, 0x6d, 0x02, 0xff, 0x4a, 0x90, 0x02, + 0xff, 0x5a, 0x64, 0x02, + 0x00, 0x59, 0xe1, 0x1c, + 0x01, 0x59, 0x59, 0x06, 0xff, 0xb9, 0x9d, 0x02, 0x02, 0x6a, 0x91, 0x00, - 0x08, 0xa0, 0xe4, 0x1e, + 0x08, 0xa0, 0xe7, 0x1e, 0x91, 0x6a, 0x91, 0x00, - 0xff, 0xb3, 0xf0, 0x1c, + 0xff, 0xb3, 0xf3, 0x1c, 0xff, 0xb3, 0x64, 0x02, - 0x00, 0xb9, 0xea, 0x1c, - 0x00, 0x65, 0xa7, 0x17, + 0x00, 0xb9, 0xed, 0x1c, + 0x00, 0x65, 0xaa, 0x17, 0xff, 0x64, 0x90, 0x02, - 0x00, 0x65, 0xec, 0x10, + 0x00, 0x65, 0xef, 0x10, 0x0d, 0x6a, 0x3d, 0x00, - 0x00, 0xb3, 0x74, 0x17, + 0x00, 0xb3, 0x77, 0x17, 0xff, 0x48, 0xba, 0x02, 0xff, 0x90, 0x48, 0x02, 0x00, 0x65, 0x2f, 0x16, 0x00, 0x65, 0x69, 0x10, - 0x00, 0x65, 0xa7, 0x17, + 0x00, 0x65, 0xaa, 0x17, 0x00, 0x65, 0x69, 0x10, - 0x4d, 0x6a, 0x37, 0x17, + 0x4d, 0x6a, 0x3a, 0x17, 0xff, 0x4d, 0x64, 0x02, - 0x00, 0x66, 0x37, 0x17, + 0x00, 0x66, 0x3a, 0x17, 0xff, 0x64, 0x64, 0x06, - 0x52, 0x66, 0xf8, 0x18, + 0x52, 0x66, 0xfb, 0x18, 0xff, 0x66, 0x66, 0x06, - 0xff, 0x64, 0xf4, 0x1a, + 0xff, 0x64, 0xf7, 0x1a, 0x41, 0x6a, 0x91, 0x00, - 0x20, 0x59, 0xcd, 0x1c, - 0x80, 0x59, 0xcf, 0x18, + 0x20, 0x5b, 0xcd, 0x1c, + 0x80, 0x5b, 0xcf, 0x18, 0x10, 0x4c, 0x03, 0x00, 0x00, 0x65, 0xcf, 0x10, 0x04, 0xa0, 0xa0, 0x00, - 0x00, 0x65, 0xb8, 0x17, + 0x00, 0x65, 0xbb, 0x17, 0x00, 0x65, 0x69, 0x10, 0x10, 0x41, 0xcf, 0x1e, 0xff, 0x43, 0xa3, 0x02, 0xa4, 0x6a, 0x66, 0x00, - 0x44, 0x6a, 0x70, 0x17, + 0x44, 0x6a, 0x73, 0x17, 0xac, 0x6a, 0x66, 0x00, - 0x14, 0x6a, 0x70, 0x17, - 0xa9, 0x6a, 0x71, 0x17, + 0x14, 0x6a, 0x73, 0x17, + 0xa9, 0x6a, 0x74, 0x17, 0x00, 0x65, 0xcf, 0x10, 0xef, 0x41, 0x41, 0x02, 0x00, 0x65, 0xcf, 0x10, 0x78, 0x64, 0xcd, 0x1a, 0x07, 0x64, 0x64, 0x02, 0x00, 0x42, 0x42, 0x00, - 0x00, 0x42, 0x9e, 0x17, - 0xff, 0x6c, 0x59, 0x02, - 0xff, 0x59, 0x25, 0x19, - 0xff, 0x59, 0x15, 0x1d, - 0xff, 0x59, 0x90, 0x02, - 0x04, 0xa0, 0x2a, 0x1f, - 0x00, 0x65, 0x27, 0x11, + 0x00, 0x42, 0xa1, 0x17, + 0xff, 0x6c, 0x5b, 0x02, + 0xff, 0x5b, 0x28, 0x19, + 0xff, 0x5b, 0x18, 0x1d, + 0xff, 0x5b, 0x90, 0x02, + 0x04, 0xa0, 0x2d, 0x1f, + 0x00, 0x65, 0x2a, 0x11, 0xff, 0x06, 0x6a, 0x02, - 0x01, 0x0c, 0x16, 0x1f, - 0x04, 0x0c, 0x16, 0x1b, + 0x01, 0x0c, 0x19, 0x1f, + 0x04, 0x0c, 0x19, 0x1b, 0xe0, 0x03, 0x4c, 0x02, - 0xe0, 0x4c, 0x2a, 0x19, - 0x20, 0x12, 0x2a, 0x19, + 0xe0, 0x4c, 0x2d, 0x19, + 0x20, 0x12, 0x2d, 0x19, 0x20, 0x41, 0x41, 0x00, - 0x59, 0x6a, 0x37, 0x17, + 0x5b, 0x6a, 0x3a, 0x17, 0xff, 0x3f, 0x64, 0x02, - 0x00, 0x59, 0x65, 0x06, - 0x00, 0x65, 0x2a, 0x13, - 0xff, 0x59, 0x90, 0x02, + 0x00, 0x5b, 0x65, 0x06, + 0x00, 0x65, 0x2d, 0x13, + 0xff, 0x5b, 0x90, 0x02, 0xff, 0x42, 0x64, 0x02, - 0x00, 0xa1, 0x2a, 0x19, - 0x20, 0xa0, 0x2a, 0x1f, - 0x04, 0xa0, 0x2a, 0x1f, - 0x00, 0x6a, 0x4f, 0x17, - 0xff, 0x65, 0x2a, 0x1d, + 0x00, 0xa1, 0x2d, 0x19, + 0x20, 0xa0, 0x2d, 0x1f, + 0x04, 0xa0, 0x2d, 0x1f, + 0x00, 0x6a, 0x52, 0x17, + 0xff, 0x65, 0x2d, 0x1d, 0xfb, 0xa0, 0xa0, 0x02, 0x40, 0x41, 0x41, 0x00, 0x00, 0x65, 0xcf, 0x10, 0x31, 0x6a, 0x91, 0x00, - 0x0c, 0x6a, 0x2f, 0x17, + 0x0c, 0x6a, 0x32, 0x17, 0x00, 0x65, 0xcf, 0x10, 0x61, 0x6a, 0x91, 0x00, 0x00, 0x65, 0xcf, 0x10, 0x50, 0x6a, 0x60, 0x00, - 0xff, 0x34, 0x33, 0x1f, + 0xff, 0x34, 0x36, 0x1f, 0x10, 0x6a, 0x60, 0x00, 0xc1, 0x6a, 0x91, 0x00, 0x10, 0x4c, 0x03, 0x00, @@ -314,50 +317,50 @@ 0xff, 0x65, 0x35, 0x02, 0x10, 0x6a, 0x60, 0x01, 0xff, 0x06, 0x6a, 0x02, - 0x01, 0x0c, 0x38, 0x1f, - 0x04, 0x0c, 0x38, 0x1b, + 0x01, 0x0c, 0x3b, 0x1f, + 0x04, 0x0c, 0x3b, 0x1b, 0xe0, 0x03, 0x4c, 0x02, - 0xe0, 0x4c, 0x3f, 0x19, + 0xe0, 0x4c, 0x42, 0x19, 0xff, 0x65, 0x66, 0x02, 0xff, 0x12, 0x6d, 0x03, 0xff, 0x06, 0x6a, 0x03, 0xd1, 0x6a, 0x91, 0x00, 0x00, 0x65, 0x5c, 0x10, 0xff, 0x65, 0x93, 0x02, - 0x01, 0x0b, 0x49, 0x1b, - 0x10, 0x0c, 0x42, 0x1f, - 0x04, 0x0b, 0x46, 0x1b, + 0x01, 0x0b, 0x4c, 0x1b, + 0x10, 0x0c, 0x45, 0x1f, + 0x04, 0x0b, 0x49, 0x1b, 0xff, 0x6a, 0x65, 0x02, - 0x04, 0x93, 0x48, 0x1b, - 0x01, 0x94, 0x47, 0x1f, - 0x10, 0x94, 0x48, 0x1b, + 0x04, 0x93, 0x4b, 0x1b, + 0x01, 0x94, 0x4a, 0x1f, + 0x10, 0x94, 0x4b, 0x1b, 0xc7, 0x93, 0x93, 0x02, - 0x38, 0x93, 0x4a, 0x1b, + 0x38, 0x93, 0x4d, 0x1b, 0xff, 0x6a, 0x6a, 0x03, - 0x80, 0x41, 0x4b, 0x1f, - 0x40, 0x41, 0x4b, 0x1b, + 0x80, 0x41, 0x4e, 0x1f, + 0x40, 0x41, 0x4e, 0x1b, 0x21, 0x6a, 0x91, 0x01, 0xff, 0x65, 0x90, 0x02, - 0xff, 0x59, 0x64, 0x02, - 0x00, 0xb9, 0x53, 0x19, - 0x04, 0xa0, 0x5d, 0x1b, + 0xff, 0x5b, 0x64, 0x02, + 0x00, 0xb9, 0x56, 0x19, + 0x04, 0xa0, 0x60, 0x1b, 0x01, 0x65, 0x65, 0x06, 0xff, 0x3e, 0x64, 0x02, - 0x00, 0x65, 0x4f, 0x19, - 0x00, 0x6a, 0xaa, 0x17, + 0x00, 0x65, 0x52, 0x19, + 0x00, 0x6a, 0xad, 0x17, 0x0d, 0x6a, 0x3d, 0x00, - 0x00, 0x59, 0x74, 0x17, - 0xff, 0xa8, 0x5b, 0x1f, + 0x00, 0x5b, 0x77, 0x17, + 0xff, 0xa8, 0x5e, 0x1f, 0x10, 0xa0, 0xa0, 0x00, - 0x08, 0xa0, 0x4b, 0x1f, + 0x08, 0xa0, 0x4e, 0x1f, 0xff, 0x6a, 0x65, 0x01, - 0x08, 0xa0, 0x5c, 0x1b, - 0xff, 0xba, 0x63, 0x1d, + 0x08, 0xa0, 0x5f, 0x1b, + 0xff, 0xba, 0x66, 0x1d, 0xff, 0xbb, 0x49, 0x02, 0xff, 0xba, 0x90, 0x02, 0xff, 0x49, 0xbb, 0x02, 0xff, 0x65, 0x90, 0x02, - 0xff, 0xbb, 0x68, 0x1d, + 0xff, 0xbb, 0x6b, 0x1d, 0xff, 0xba, 0x49, 0x02, 0xff, 0xbb, 0x90, 0x02, 0xff, 0x49, 0xba, 0x02, @@ -386,7 +389,7 @@ 0xff, 0x6a, 0x8d, 0x02, 0xff, 0x6a, 0x8e, 0x02, 0xff, 0x3d, 0x93, 0x02, - 0x04, 0x3d, 0x8c, 0x1b, + 0x04, 0x3d, 0x8f, 0x1b, 0xa0, 0x6a, 0x65, 0x00, 0x1c, 0x65, 0x64, 0x06, 0xff, 0x6c, 0x99, 0x02, @@ -396,14 +399,14 @@ 0xff, 0x6c, 0x99, 0x02, 0xff, 0x6c, 0x99, 0x02, 0xff, 0x6c, 0x99, 0x02, - 0x00, 0x65, 0x83, 0x19, + 0x00, 0x65, 0x86, 0x19, 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0x9a, 0x17, - 0x04, 0x3d, 0x4b, 0x1f, - 0xa0, 0x6a, 0x92, 0x17, - 0x00, 0x65, 0x93, 0x17, - 0x00, 0x65, 0x93, 0x17, - 0x00, 0x65, 0x93, 0x11, + 0x00, 0x65, 0x9d, 0x17, + 0x04, 0x3d, 0x4e, 0x1f, + 0xa0, 0x6a, 0x95, 0x17, + 0x00, 0x65, 0x96, 0x17, + 0x00, 0x65, 0x96, 0x17, + 0x00, 0x65, 0x96, 0x11, 0xff, 0x65, 0x66, 0x02, 0xff, 0x99, 0x6d, 0x02, 0xff, 0x99, 0x6d, 0x02, @@ -412,40 +415,40 @@ 0xff, 0x99, 0x6d, 0x02, 0xff, 0x99, 0x6d, 0x02, 0xff, 0x99, 0x6d, 0x03, - 0x08, 0x94, 0x9a, 0x1f, + 0x08, 0x94, 0x9d, 0x1f, 0xf7, 0x93, 0x93, 0x02, - 0x08, 0x93, 0x9c, 0x1b, + 0x08, 0x93, 0x9f, 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, 0xa5, 0x1f, + 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, 0xb6, 0x19, - 0xff, 0x52, 0xad, 0x19, + 0xff, 0x53, 0xb9, 0x19, + 0xff, 0x52, 0xb0, 0x19, 0xff, 0x6a, 0x65, 0x01, 0xff, 0x52, 0x90, 0x02, - 0x10, 0xa0, 0xb1, 0x1f, + 0x10, 0xa0, 0xb4, 0x1f, 0xef, 0xa0, 0xa0, 0x02, - 0x00, 0x65, 0xb3, 0x11, - 0xff, 0xa8, 0xb3, 0x1b, - 0xff, 0xb3, 0xb5, 0x1d, + 0x00, 0x65, 0xb6, 0x11, + 0xff, 0xa8, 0xb6, 0x1b, + 0xff, 0xb3, 0xb8, 0x1d, 0x01, 0x6a, 0x3d, 0x00, - 0x00, 0xb9, 0x74, 0x17, - 0x00, 0x90, 0x5e, 0x11, + 0x00, 0xb9, 0x77, 0x17, + 0x00, 0x90, 0x61, 0x11, 0xff, 0x53, 0x90, 0x02, 0xff, 0xba, 0x53, 0x03, 0xff, 0x6a, 0xbb, 0x00, 0xff, 0x52, 0xba, 0x02, 0xff, 0x90, 0x52, 0x02, - 0xff, 0xba, 0x4b, 0x1d, + 0xff, 0xba, 0x4e, 0x1d, 0xff, 0xba, 0x90, 0x02, 0xff, 0x52, 0xbb, 0x02, 0xff, 0x52, 0x90, 0x03, @@ -466,14 +469,15 @@ { 0x00000004, 1, 0x023, 0x027 }, { 0x00000002, 0, 0x02f, 0x033 }, { 0x00000008, 0, 0x04f, 0x056 }, - { 0x00000004, 1, 0x0e5, 0x0ea }, - { 0x00000004, 0, 0x0ff, 0x100 }, - { 0x00000004, 0, 0x110, 0x111 }, - { 0x00000004, 1, 0x111, 0x115 }, - { 0x00000004, 1, 0x120, 0x125 }, - { 0x00000004, 0, 0x125, 0x127 }, - { 0x00000004, 0, 0x14f, 0x169 }, - { 0x00000004, 1, 0x169, 0x16a }, - { 0x00000004, 0, 0x1aa, 0x1bf }, + { 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, 0x1c2 }, { 0x00000000, 0, 0x000, 0x000 } }; diff -urN linux-2.0.31-pre5/drivers/scsi/ChangeLog.ncr53c8xx linux-2.0.31-pre6/drivers/scsi/ChangeLog.ncr53c8xx --- linux-2.0.31-pre5/drivers/scsi/ChangeLog.ncr53c8xx 1996-08-31 23:15:32.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/ChangeLog.ncr53c8xx 2003-08-15 15:04:01.000000000 -0700 @@ -1,3 +1,421 @@ +Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.4 + Several clean-ups: + - Asynchronous pre-scaler calculation. + Synchronous divisor calculation. + - Use FE_ as feature identifier prefix instead of _F_. + - Change 'ns_sync' identifier to "minsync". + - Some others. + Apply some SPI2-R12 recommendations. + - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2, + FAST SCSI-2, ULTRA, ULTRA-2. + - Reset the SCSI on bus mode change. + +Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3c + - Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation + of the corresponding pci fix-up code when a small driver is needed. + - Use "ncr53c8xx" as driver name for both request_irq() and + request_region(). Using different names confused 'lsdev'. + (Suggestion sent by Henrik Storner). + +Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3b + - Print an error message on unexpected boot command line option. + - Switch to asynchronous data transfer mode after SCSI wide + negotiation. + +Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3b + - Print an error message on unexpected boot command line option. + - Switch to asynchronous data transfer mode after SCSI wide + negotiation. + +Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3a + - Add PCI LATENCY TIMER fixup code. + Increase it if necessary according to burst size. + Boot option bit : 'pcifix:4' + - On phase mismatch, calculate residual data size for all OUTPUT + phases. That's only required for interrupted DATA OUT phase, but + this information is usefull for problem solving. + - Add KERN_INFO to some messages printed to the log. + (Patch sent by Wolfram Kleff). + +Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.3 + - NvRAM support code slightly improved (I think): + Use IO or MMIO according to driver setup for reading the NvRAM. + Use structures for NvRAM data instead of raw data. + - Prevent from queuing more than 1 command to the scsi SCRIPT with + negotiation attached when tagged command queueing is enabled. + - Fix-up for old 53C8XX chips that support PCI READ LINE but not + CACHE LINE SIZE. If the cache line size is unknown, set burst + to 8 dwords and disable READ LINE, otherwise set burst max to + the cache line size value. + +Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.2c (for linux-2.1.40) + - Remove reference to 'x86' symbol when MODULE is defined, since this + symbol is not exported for module loading. + The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE + configuration register. + - Bytes/words read one bit at a time from the serial NVRAM were'nt + initialized with zero. + - Some comments added. Minor cosmetic changes. + +Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.2b + - Patch for NVRAM support by Richard Waltham applied. + The code detects Symbios NVRAM format and Tekram NVRAM format. + This enhancement allows to get hosts and devices user set up + from the NVRAM. + - Use the NVRAM contents when present to initialize user definable + target parameters. + - Update the README file. + +Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.1b + - Cosmetic changes. + - Some heavy testings under pre-linux-2.1.37-6 + +Sun May 4 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.1a + - PFEN wrongly used for PREFETCH feature bit testing. + Changed to _F_PFEN. + - 2 SCR_COPY that need NO FLUSH bit to be removed had been missed + in tp->getscr[] script (loads SXFER and SCNTL3 on reselection). + +Sat May 3 22:30 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.1 + - Use the NO FLUSH option for MOVE MEMORY (COPY) each time it is + possible. More than 100 COPY with NO FLUSH and 6 with FLUSH for + my configuration (max queued command / device = 8). + This option bit is removed from the script instance for chips + that donnot support prefetching. + - Rewrite the ncr_exception() routine more simple (I think) and + remove useless code. + - Change the data_in and data_out script management. + Use the bottom part of these scripts instead of the beginning. + That avoids to zero the scatter/gather array when a command is + queued (1k) and to deal with some weird IID on MOVE 0 bytes when + a target wants to transfer more bytes than expected. + - Misc. improvements in the init code. + - Remove IOMAPPED/MMIO automatic switching option. + Was useless and reported not reliable. + - Fix a double read of DSTAT and remove DFE testing in the + Phase mismatch service routine. + - Etc... + +Fri Apr 26 20:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.0a + - Add support if the Diamond FirePort 40 (SYM53C875J chip) + +Mon Apr 22 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.0 + - incorporate __initdata and __initfunc directives in order to + allow 'init' to free unused memory after driver initialisations. + Patch sent by Roberto Fichera. + - rewrite the init code of the driver. Now a feature descriptor + is used for each real chip types. The code is a lot more clean, + since the driver uses device and revision ids only in the + detection procedure. + - add 'pcifix' boot command line. This command allows to fix up PCI + config space for new chips which support features based on the + cache line size and 'write and invalidate'. + - incorporate in the driver, the code used for error recovery + testing. This code is normally not compiled; have to define + SCSI_NCR_DEBUG_ERROR_RECOVERY in order to compile it. + - take into account actual SCSI bus mode for 53C895 LVD/SE controller. + In single ended mode only fast20 is supported. + (Just to not be late since such controllers are not yet available) + + +Sat Apr 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18f + - fix an old bug included in the initial port (version 0.0). + The driver allocated 10 bytes of static data and uses 12 bytes. + No danger, since data are generally aligned on 4 bytes boundary + and so byte 10 and 11 are free (I hope ...) + +Wed Apr 16 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18e + - reset all when an unexpected data cycle is detected while + disconnecting. + - make changes to abort() ans reset() functions according to + Leonard's documentation. + - small fix in some message for hard errors. + +Sat Apr 5 13:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18d + - Probe NCR pci device ids in reverse order if asked by user from + the boot command line. Suggested by Richard Waltham. + - Make a separate function that prints out verbose information on + severe error (assumed from hardware). + - Add the transfer period factor and the max commands per lun value + to the proc info data. If debug flags are set or verbosity is + greater than 1, debug flags and verbosity are returned in proc + info data. + - Update the documentation. + +Thu Mar 20 23:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 1.18c + - Add special features support for NCR53C885 and NCR53C896 chip. + Quite obvious, but untested, and based on the fact that: + The 885 supports same features as the 875. + The 896 is a 64 bits PCI version of the 895. + - Improve recovery from SCSI GROSS ERRORS. + I can get such errors by making the driver negotiate offset 8 with + a disk and setting the ncr chip to a lower offset value. + I got bunches of errors that have been gracefully recovered by + the driver. + The driver now uses its timer handler in order to wait 2 sec. for + devices to settle after SCSI reset and so does not uselessly freeze + the system with interrupt masked for seconds. + - Enable 'burst op code fetch' and 'read line' for 815 chips. + - Use a 2 commands queue depth instead of 1 for devices that does + not support tagged command queuing. + - The ULTRA timing flag setting was based on the output resulting + period factor of the ncr and not on the negotiated one. + This flag setting was wrong only for 24 ns negotiated period factor. + - Some other minor changes and cleanups. + +Thu Feb 27 23:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h revision 1.18b + - 'On paper' support of the NCR53C895 Ultra-2 chip. + (Clock quadrupler + 7 clock divisors) + - Load the main part of the script into the on-board RAM. + - 810A rev. 0x11 PCI problem fixed. + This chip is now supported with all PCI features enabled and + 16 dwords burst transfers. + - Align on 32 boundary some internal structures. + That fixes the 810A problem and allows cache line bursting when + moving the global header (64 bytes) from/to CCBs to/from NCB. + - Synchronous parameters calculation rewritten. The driver + now uses all available clock divisors and will be able to support + clock frequencies that are not multiple of 40 Mhz if necessary. + +Sat Feb 8 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.17a + - IRQ mode set up from boot setup command. + irqm:0 open drain (default) + irqm:1 preserve initial setting (assumed from BIOS) + irqm:2 totem pole + - DIFF mode set up from boot setup command. + Suggested by Richard Waltham. + diff:0 never set up diff mode (default) + diff:1 set up diff mode according to initial setting (BIOS?) + diff:2 always set up diff mode + diff:3 set up diff mode if GPIO3 is zero (SYMBIOS boards) + - Change CONFIG option for LED support. + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT allows LED support and + DIFF support for SYMBIOS boards and compatibles (clones?). + - Set 16 DWORD bursts for 810A rev. >= 0x12 since my SC200 with + such a chip have no problem with it (MB with Triton 2 HX). + 810A rev. 0x11 are set to 8 DWORD bursts since they may give + problems with PCI read multiple and Triton 2 HX. + Thanks to Stefan for this information. + +Sat Jan 25 22:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.17 + - Controller LED support. + Only works with LED pin wired to GPIO_FETCHN, so probably with + all boards using SMDS BIOS. + This option can be enabled only if CONFIG_EXPERIMENTAL is set. + - Assume clock doubler for 875 chip when clock frequency measurement + result is 40 MHz. May help when some old stuff as SDMS BIOS 3.0 + or some old driver has broken the normal BIOS settings. + - Add wide negotiation control from boot setup command. + May be usefull with systems using a 875 based board connected to + a wide device through a 50 pins to 68 pins converter. + - Add a "boot fail safe option" to the boot setup command line. + - Rewrite the "reset_command" routine. + Low-level driver are responsible to keep the involved command + alive. The new code seems to behave correctly. + - Change some variables used by the script from u_long to u_int32. + - Remove some useless code. + +Sun Jan 12 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.16e + - Add support of PCI burst length control from boot setup command. + burst:0 disable burst + burst:255 get burst from initial settings (BIOS settings?) + burst:#x set burst transfers to 1<<#x + - Only check xfer direction for common op-codes. + For all device specific / vendor specific opcodes the driver + now uses the xfer direction decided by the target. + +Sun Jan 05 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.16d + - The driver is now able to process scsi commands without + knowledge of xfer data direction. + Stefan agreed with this change for Linux. This change is + not needed under FreeBSD since low-level drivers receive + the expected data direction for each scsi request. + - Save ctest5 features bits at start-up and restore them at + module release step. + Avoid side effects when a ncr driver which trusts bios + settings is reloaded (could be the ncr53c8xx itself). + + +Wed Jan 01 23:30 1997 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - revision 1.16c + - Bad decision about 20MHz for 13 ns period factor. + Was wrong, so I restore the previous algorithm. + - Burst length 128 not correctly set in dmode. + +Thu Dec 26 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16b + - Remove useless code. + - Try to improve error recovery in case of abort and reset. + - Remove DEBUG_NEGO by default. + - Add boot setup command support. + Now, all experimental config options can be removed. + - Update README file. + + +Mon Dec 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.16a + New display for speed ##.# MB/s (From Stefan) + - I add "WIDE" qualifier after ULTRA and FAST + - I get "FAST WIDE SCSI-2 20 MB/s" with my Atlas. That's nice. + + Richard Waltham reports SYMBIOS set the 875 to 20 MB/s for 13 ns + period factor. I decide to trust SYMBIOS. 20 MB/s output speed + instead of 19.2 MB/s should not cause problem. The ncr is only able + to use 16.67 MB/s when 20 MB/s is not possible. + + Fix from Markus Kossman: "Ultra SCSI enabled" wrongly printed + when not enabled. + + Set DEBUG_NEGO by default in order to get reports about sync nego. + Will remove it in the next patch. + +Thu Dec 19 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16 + Incorporate new definitions in ncr53c8xx.h (From Stefan). + Check changes against Stefan's current version of the driver. + All seems ok. + +Sat Nov 30 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h + Make changes in order to support: + - Clock doubler and so 80 Mhz scsi clock for 875 chips. + - Sync transfers below 7.5 MB/sec. + Use Clock/2 between 5 and 10 Mega-transfers/s and Clock/4 below 5. + - Ultra SCSI data transfers. + - Offset 16. + + Works with my configuration. However I cannot test Ultra transfers, + since my disks are only fast scsi-2. + +Tue Nov 28 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + I received yesterday my Promise SCSI Ultra board. + NCR53C875 rev. 3 with clock doubler. + Add the code to support some bus features, the large 536 dma fifo and + burst 128. Works. + +Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.14c + Severall control command improvements: + + - Allow to specify "all" to commands that apply to #target. + For example: "setsync all 255" sets asynchronous data + transfers for all targets on a bus. + + - Allow to control disconnection privilege per device, as follow: + "setflag #target no_sync" disables disconnection for #target. + "setflag #target" with no flag specified reenables it. + + Obviously #target may be specified as "all" in order to control + disconnection for all targets with a single control command. + + - README file updated and some hints about SCSI problems solving added. + +Sun Oct 27 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.14b + Add the following config parameters: + + - CONFIG_SCSI_NCR53C8XX_MAX_TAGS + Max number of queued tagged commands. + Allow from 2 to 12, default 4. + + - CONFIG_SCSI_NCR53C8XX_SYNC + Synchronous transfers frequency in MHz. + Allow from 5 to 10, default 5, 0 means asynchronous. + (And so remove CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS) + +Sun Oct 20 16:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + ncr_scatter() rewritten. + remove "ncr dead" detection. + +Sun Oct 13 19:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.14a + Enabling some special features makes problems with some hardware. + So, disable them by default. + Add SCSI_NCR_SPECIAL_FEATURES define to play with. + +Sun Oct 13 14:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h + Incorporate Stefan's patch for clock frequency detection. + (Committed in FreeBSD/ncr.c rev. 1.81). + The driver then does about the following: + Assume 40 MHz clock for all ncr chips except: + - NCR53C860 chips: + Assume 80 Mhz clock. + - NCR53C875 chips: + If clock doubler enabled, disable it and assume 40 Mhz clock. + Else if (scntl3&7)=0 measure scsi clock frequency. + Else trust bios setting of scntl3&7 (3=40 Mhz, 5=80Mhz). + +Wed Oct 9 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c - release 1.14 + For now, just change the clock detection as follow: + - If clock doubler selected by BIOS, assume 40 MHz clock since + clock doubler will be disabled by chip reset. + - Else if NCR53C860 assume 80 MHz clock. + - Else trust BIOS setting if (scntl3&7 >= 3) + - Else assume 40 MHz clock. + +Sat Oct 05 17:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + Stefan sent me a patch that improves the clock frequency detection + of the driver. Stefan uses the general timer register stime1 in + order to measure as accurately as possible the scsi clock. + Works ok with my 825, but needs still testing. So will be + released later. + +Sun Sep 29 17:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + Preserve dcntl/dmode/ctest3/ctest4 features bits at start-up. + Add the define option SCSI_NCR_TRUST_BIOS_SETTING. + - If this option is defined, the driver will preserve the + corresponding bits of io registers. + - Else, the driver will set features bits according to chip + and revision ids. + +Sun Sep 22 17:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + Remove useless fields and code and so spare cpu: + - profile data are accumulated in jiffies ticks and converted + to milli-seconds when read through proc fs. + - when IOMAPPED is not defined, try only MMIO. + (avoid testing a value in order to choose between IO and MMIO) + +Sun Sep 01 20:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.h, ncr53c8xx.c - Version 1.13 + Adaptation of the tagged command queuing depth control of the + FreeBSD driver to Linux. Now, tagged command queueing can be + disabled at run time by a "settags N 0" control command. + Add the following heuristic in order to manage intelligently (perhaps) + QUEUE_FULL status: + - Each time a QUEUE FULL status is returned by a device, disable tagged + command queuing for that device. + - Every 100 successfully complete commands, increment the maximum + queuable commands (up to the allowed limit). + Fri Aug 30 10:00 1996 Gerard Roudier (groudier@club-internet.fr) * ncr53c8xx.c - Version 1.12c Incorporate the changes of FreeBSD/ncr.c revision 1.76. diff -urN linux-2.0.31-pre5/drivers/scsi/Config.in linux-2.0.31-pre6/drivers/scsi/Config.in --- linux-2.0.31-pre5/drivers/scsi/Config.in 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/Config.in 2003-08-15 15:04:01.000000000 -0700 @@ -21,10 +21,13 @@ 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 - int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + dep_tristate ' 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 + 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 15 fi dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI @@ -62,15 +65,17 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then + bool ' detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT bool ' enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE - bool ' force normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED - bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT - bool ' force asynchronous transfer mode' CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS - bool ' force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO - fi - if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' disable master parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK - bool ' disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK + bool ' use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED + int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4 + int ' synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 5 + if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then + bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + fi fi fi dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI @@ -80,6 +85,9 @@ dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI fi dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI +if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_AM53C974" != "y" ]; then + dep_tristate 'Tekram DC-390(T) SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI +fi dep_tristate 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 $CONFIG_SCSI dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI if [ "$CONFIG_SCSI_U14_34F" != "n" ]; then diff -urN linux-2.0.31-pre5/drivers/scsi/dc390.h linux-2.0.31-pre6/drivers/scsi/dc390.h --- linux-2.0.31-pre5/drivers/scsi/dc390.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.31-pre6/drivers/scsi/dc390.h 2003-08-15 15:04:01.000000000 -0700 @@ -0,0 +1,147 @@ +/*********************************************************************** + * FILE NAME : DC390.H * + * BY : C.L. Huang * + * Description: Device Driver for Tekram DC-390(T) PCI SCSI * + * Bus Master Host Adapter * + ***********************************************************************/ + +/* Kernel version autodetection */ + +#include +/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */ +#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) + +#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50) +#define VERSION_ELF_1_2_13 +#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95) +#define VERSION_1_3_85 +#else +#define VERSION_2_0_0 +#endif + +/* + * AMD 53C974 driver, header file + */ + +#ifndef DC390_H +#define DC390_H + +#if defined(HOSTS_C) || defined(MODULE) + +#ifdef VERSION_2_0_0 +#include +#else +#include +#endif + +extern int DC390_detect(Scsi_Host_Template *psht); +extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); +extern int DC390_abort(Scsi_Cmnd *cmd); + +#ifdef VERSION_2_0_0 +extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags); +#else +extern int DC390_reset(Scsi_Cmnd *cmd); +#endif + +#ifdef VERSION_ELF_1_2_13 +extern int DC390_bios_param(Disk *disk, int devno, int geom[]); +#else +extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]); +#endif + +#ifdef MODULE +static int DC390_release(struct Scsi_Host *); +#else +#define DC390_release NULL +#endif + +#ifndef VERSION_ELF_1_2_13 +extern struct proc_dir_entry proc_scsi_tmscsim; +extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); +#endif + +#ifdef VERSION_2_0_0 + +#define DC390_T { \ + NULL, /* *next */ \ + NULL, /* *usage_count */ \ + &proc_scsi_tmscsim, /* *proc_dir */ \ + tmscsim_proc_info, /* (*proc_info)() */ \ + "Tekram DC390(T) V1.10 Dec-05-1996", /* *name */ \ + DC390_detect, \ + DC390_release, /* (*release)() */ \ + NULL, /* *(*info)() */ \ + NULL, /* (*command)() */ \ + DC390_queue_command, \ + DC390_abort, \ + DC390_reset, \ + NULL, /* slave attach */\ + DC390_bios_param, \ + 10,/* can queue(-1) */ \ + 7, /* id(-1) */ \ + SG_ALL, \ + 2, /* cmd per lun(2) */ \ + 0, /* present */ \ + 0, /* unchecked isa dma */ \ + DISABLE_CLUSTERING \ + } +#endif + + +#ifdef VERSION_1_3_85 + +#define DC390_T { \ + NULL, /* *next */ \ + NULL, /* *usage_count */ \ + &proc_scsi_tmscsim, /* *proc_dir */ \ + tmscsim_proc_info, /* (*proc_info)() */ \ + "Tekram DC390(T) V1.10 Dec-05-1996", /* *name */ \ + DC390_detect, \ + DC390_release, /* (*release)() */ \ + NULL, /* *(*info)() */ \ + NULL, /* (*command)() */ \ + DC390_queue_command, \ + DC390_abort, \ + DC390_reset, \ + NULL, /* slave attach */\ + DC390_bios_param, \ + 10,/* can queue(-1) */ \ + 7, /* id(-1) */ \ + SG_ALL, \ + 2, /* cmd per lun(2) */ \ + 0, /* present */ \ + 0, /* unchecked isa dma */ \ + DISABLE_CLUSTERING \ + } +#endif + + +#ifdef VERSION_ELF_1_2_13 + +#define DC390_T { \ + NULL, \ + NULL, \ + "Tekram DC390(T) V1.10 Dec-05-1996",\ + DC390_detect, \ + DC390_release, \ + NULL, /* info */ \ + NULL, /* command, deprecated */ \ + DC390_queue_command, \ + DC390_abort, \ + DC390_reset, \ + NULL, /* slave attach */\ + DC390_bios_param, \ + 10,/* can queue(-1) */ \ + 7, /* id(-1) */ \ + 16,/* old (SG_ALL) */ \ + 2, /* cmd per lun(2) */ \ + 0, /* present */ \ + 0, /* unchecked isa dma */ \ + DISABLE_CLUSTERING \ + } +#endif + +#endif /* defined(HOSTS_C) || defined(MODULE) */ + +#endif /* DC390_H */ diff -urN linux-2.0.31-pre5/drivers/scsi/hosts.c linux-2.0.31-pre6/drivers/scsi/hosts.c --- linux-2.0.31-pre5/drivers/scsi/hosts.c 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/hosts.c 2003-08-15 15:04:01.000000000 -0700 @@ -153,6 +153,10 @@ #include "NCR53c406a.h" #endif +#ifdef CONFIG_SCSI_DC390T +#include "dc390.h" +#endif + #ifdef CONFIG_SCSI_AM53C974 #include "AM53C974.h" #endif @@ -284,6 +288,9 @@ #ifdef CONFIG_SCSI_DTC3280 DTC3x80, #endif +#ifdef CONFIG_SCSI_DC390T + DC390_T, +#endif #ifdef CONFIG_SCSI_NCR53C7xx NCR53c7xx, #endif diff -urN linux-2.0.31-pre5/drivers/scsi/Makefile linux-2.0.31-pre6/drivers/scsi/Makefile --- linux-2.0.31-pre5/drivers/scsi/Makefile 2003-08-15 15:04:01.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/Makefile 2003-08-15 15:04:01.000000000 -0700 @@ -178,6 +178,14 @@ endif endif +ifeq ($(CONFIG_SCSI_DC390T),y) +L_OBJS += tmscsim.o +else + ifeq ($(CONFIG_SCSI_DC390T),m) + M_OBJS += tmscsim.o + endif +endif + ifeq ($(CONFIG_SCSI_AM53C974),y) L_OBJS += AM53C974.o else @@ -380,6 +388,9 @@ seagate.o: seagate.c $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c +tmscsim.o : tmscsim.c + $(CC) $(CFLAGS) -c tmscsim.c + 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake.c $(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl diff -urN linux-2.0.31-pre5/drivers/scsi/ncr53c8xx.c linux-2.0.31-pre6/drivers/scsi/ncr53c8xx.c --- linux-2.0.31-pre5/drivers/scsi/ncr53c8xx.c 1996-12-11 07:02:43.000000000 -0800 +++ linux-2.0.31-pre6/drivers/scsi/ncr53c8xx.c 2003-08-15 15:04:01.000000000 -0700 @@ -36,11 +36,35 @@ ** And has been ported to NetBSD by ** Charles M. Hannum ** +**----------------------------------------------------------------------------- +** +** Brief history +** +** December 10 1995 by Gerard Roudier: +** Initial port to Linux. +** +** June 23 1996 by Gerard Roudier: +** Support for 64 bits architectures (Alpha). +** +** November 30 1996 by Gerard Roudier: +** Support for Fast-20 scsi. +** Support for large DMA fifo and 128 dwords bursting. +** +** February 27 1997 by Gerard Roudier: +** Support for Fast-40 scsi. +** Support for on-Board RAM. +** +** May 3 1997 by Gerard Roudier: +** Full support for scsi scripts instructions pre-fetching. +** +** May 19 1997 by Richard Waltham : +** Support for NvRAM detection and reading. +** ******************************************************************************* */ /* -** 30 August 1996, version 1.12c +** 26 July 1997, version 2.4 ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -51,26 +75,22 @@ ** Etc... ** ** Supported NCR chips: -** 53C810 (NCR BIOS in flash-bios required) -** 53C815 (~53C810 with on board rom BIOS) -** 53C820 (Wide, NCR BIOS in flash bios required) -** 53C825 (Wide, ~53C820 with on board rom BIOS) -** 53C860 (not yet tested) -** 53C875 (not yet tested) +** 53C810 (8 bits, Fast SCSI-2, no rom BIOS) +** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS) +** 53C820 (Wide, Fast SCSI-2, no rom BIOS) +** 53C825 (Wide, Fast SCSI-2, on board rom BIOS) +** 53C860 (8 bits, Fast 20, no rom BIOS) +** 53C875 (Wide, Fast 20, on board rom BIOS) +** 53C895 (Wide, Fast 40, on board rom BIOS) ** ** Other features: -** Memory mapped IO (linux-1.3.X only) +** Memory mapped IO (linux-1.3.X and above only) ** Module ** Shared IRQ (since linux-1.3.72) */ -#define SCSI_NCR_DEBUG #define SCSI_NCR_DEBUG_FLAGS (0) -#define NCR_DATE "pl23 95/09/07" - -#define NCR_VERSION (2) - #define NCR_GETCC_WITHMSG /*========================================================== @@ -105,11 +125,22 @@ #include #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) -#include "linux/blk.h" +#include #else #include "../block/blk.h" #endif +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35) +#include +#else +#ifndef __initdata +#define __initdata +#endif +#ifndef __initfunc +#define __initfunc(__arginit) __arginit +#endif +#endif + #include "scsi.h" #include "hosts.h" #include "constants.h" @@ -132,15 +163,6 @@ */ /* -** Proc info and user command support -*/ - -#ifdef SCSI_NCR_PROC_INFO_SUPPORT -#define SCSI_NCR_PROFILE -#define SCSI_NCR_USER_COMMAND -#endif - -/* ** SCSI address of this device. ** The boot routines should have set it. ** If not, use this. @@ -151,24 +173,6 @@ #endif /* -** The maximal synchronous frequency in kHz. -** (0=asynchronous) -*/ - -#ifndef SCSI_NCR_MAX_SYNC -#define SCSI_NCR_MAX_SYNC (10000) -#endif - -/* -** The maximal bus with (in log2 byte) -** (0=8 bit, 1=16 bit) -*/ - -#ifndef SCSI_NCR_MAX_WIDE -#define SCSI_NCR_MAX_WIDE (1) -#endif - -/* ** The maximum number of tags per logic unit. ** Used only for disk devices that support tags. */ @@ -177,13 +181,6 @@ #define SCSI_NCR_MAX_TAGS (4) #endif -/*========================================================== -** -** Configuration and Debugging -** -**========================================================== -*/ - /* ** Number of targets supported by the driver. ** n permits target numbers 0..n-1. @@ -209,7 +206,14 @@ #else #define MAX_LUN (1) #endif + +/* +** Asynchronous pre-scaler (ns). Shall be 40 +*/ +#ifndef SCSI_NCR_MIN_ASYNC +#define SCSI_NCR_MIN_ASYNC (40) +#endif /* ** The maximum number of jobs scheduled for starting. @@ -231,12 +235,11 @@ #define MAX_SCATTER (SCSI_NCR_MAX_SCATTER) /* -** The maximum transfer length (should be >= 64k). -** MUST NOT be greater than (MAX_SCATTER-1) * NBPG. +** Io mapped or memory mapped. */ -#if 0 -#define MAX_SIZE ((MAX_SCATTER-1) * (long) NBPG) +#if defined(SCSI_NCR_IOMAPPED) +#define NCR_IOMAPPED #endif /* @@ -245,10 +248,6 @@ #define NCR_SNOOP_TIMEOUT (1000000) -#if defined(SCSI_NCR_IOMAPPED) || defined(__alpha__) -#define NCR_IOMAPPED -#endif - /*========================================================== ** ** Defines for Linux. @@ -287,7 +286,7 @@ ** virtual memory addresses of the kernel data segment into ** IO bus adresses. ** On i386 architecture, IO bus addresses match the physical -** addresses. But on Alpha architecture they are different. +** addresses. But on other architectures they can be different. ** In the original Bsd driver, vtophys() is called to translate ** data addresses to IO bus addresses. In order to minimize ** change, I decide to define vtophys() as virt_to_bus(). @@ -299,27 +298,42 @@ /* ** Memory mapped IO ** -** Linux 1.3.X allow to remap physical pages addresses greater than -** the highest physical memory address to kernel virtual pages. -** We must use vremap() to map the page and vfree() to unmap it. -** The memory base of ncr chips is set by the bios at a high physical -** address. Also we can map it, and MMIO is possible. +** Since linux-2.1, we must use ioremap() to map the io memory space. +** iounmap() to unmap it. That allows portability. +** Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater +** than the highest physical memory address to kernel virtual pages with +** vremap() / vfree(). That was not portable but worked with i386 +** architecture. */ -static inline vm_offset_t remap_pci_mem(u_long base, u_long size) +__initfunc( +static vm_offset_t remap_pci_mem(u_long base, u_long size) +) { u_long page_base = ((u_long) base) & PAGE_MASK; u_long page_offs = ((u_long) base) - page_base; +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) + u_long page_remapped = (u_long) ioremap(page_base, page_offs+size); +#else u_long page_remapped = (u_long) vremap(page_base, page_offs+size); +#endif return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL); } -static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size) -{ - if (vaddr) vfree((void *) (vaddr & PAGE_MASK)); -} +__initfunc( +static void unmap_pci_mem(vm_offset_t vaddr, u_long size) +) +{ + if (vaddr) +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) + iounmap((void *) (vaddr & PAGE_MASK)); #else + vfree((void *) (vaddr & PAGE_MASK)); +#endif +} + +#else /* linux-1.2.13 */ /* ** Linux 1.2.X assumes that addresses (virtual, physical, bus) @@ -332,6 +346,10 @@ #define vtophys(p) ((u_long) (p)) #endif +/* +** Insert a delay in micro-seconds. +*/ + static void DELAY(long us) { for (;us>1000;us-=1000) udelay(1000); @@ -348,31 +366,81 @@ ** I notice that kmalloc() returns NULL during host attach under ** Linux 1.2.13. But this ncr driver is reliable enough to ** accomodate with this joke. -**/ +** +** kmalloc() only ensure 8 bytes boundary alignment. +** The NCR need better alignment for cache line bursting. +** The global header is moved betewen the NCB and CCBs and need +** origin and destination addresses to have same lower four bits. +** +** We use 32 boundary alignment for NCB and CCBs and offset multiple +** of 32 for global header fields. That's too much but at least enough. +*/ + +#define ALIGN_SIZE(shift) (1UL << shift) +#define ALIGN_MASK(shift) (~(ALIGN_SIZE(shift)-1)) + +#define NCB_ALIGN_SHIFT 5 +#define CCB_ALIGN_SHIFT 5 +#define LCB_ALIGN_SHIFT 5 +#define SCR_ALIGN_SHIFT 5 + +#define NCB_ALIGN_SIZE ALIGN_SIZE(NCB_ALIGN_SHIFT) +#define NCB_ALIGN_MASK ALIGN_MASK(NCB_ALIGN_SHIFT) +#define CCB_ALIGN_SIZE ALIGN_SIZE(CCB_ALIGN_SHIFT) +#define CCB_ALIGN_MASK ALIGN_MASK(CCB_ALIGN_SHIFT) +#define SCR_ALIGN_SIZE ALIGN_SIZE(SCR_ALIGN_SHIFT) +#define SCR_ALIGN_MASK ALIGN_MASK(SCR_ALIGN_SHIFT) + +static void *m_alloc(int size, int a_shift) +{ + u_long addr; + void *ptr; + u_long a_size, a_mask; + + if (a_shift < 3) + a_shift = 3; + + a_size = ALIGN_SIZE(a_shift); + a_mask = ALIGN_MASK(a_shift); + + ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC); + if (ptr) { + addr = (((u_long) ptr) + a_size) & a_mask; + *((void **) (addr - sizeof(void *))) = ptr; + ptr = (void *) addr; + } -static inline void *m_alloc(int size) -{ - void *ptr = (void *) kmalloc(size, GFP_ATOMIC); - if (((unsigned long) ptr) & 3) - panic("ncr53c8xx: kmalloc returns misaligned address %lx\n", (unsigned long) ptr); return ptr; } -static inline void m_free(void *ptr, int size) - { kfree(ptr); } +#ifdef MODULE +static void m_free(void *ptr, int size) +{ + u_long addr; + + if (ptr) { + addr = (u_long) ptr; + ptr = *((void **) (addr - sizeof(void *))); + + kfree(ptr); + } +} +#endif /* ** Transfer direction ** -** The middle scsi driver of Linux does not provide the transfer -** direction in the command structure. -** FreeBsd ncr driver require this information. -** -** I spent some hours to read the scsi2 documentation to see if -** it was possible to deduce the direction of transfer from the opcode -** of the command. It seems that it's OK. -** guess_xfer_direction() seems to work. If it's wrong we will -** get a phase mismatch on some opcode. +** Low-level scsi drivers under Linux do not receive the expected +** data transfer direction from upper scsi drivers. +** The driver will only check actual data direction for common +** scsi opcodes. Other ones may cause problem, since they may +** depend on device type or be vendor specific. +** I would prefer to never trust the device for data direction, +** but that is not possible. +** +** The original driver requires the expected direction to be known. +** The Linux version of the driver has been enhanced in order to +** be able to transfer data in the direction choosen by the target. */ #define XferNone 0 @@ -385,6 +453,8 @@ ** Head of list of NCR boards ** ** Host is retrieved by its irq level. +** If interrupts are shared, the internal host control block +** address (struct ncb) is used as device id. */ static struct Scsi_Host *first_host = NULL; @@ -419,6 +489,41 @@ unsigned char and_map[MAX_TARGET]; } target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES }; +/* +** Driver setup. +** +** This structure is initialized from linux config options. +** It can be overridden at boot-up by the boot command line. +*/ +struct ncr_driver_setup { + unsigned master_parity : 1; + unsigned scsi_parity : 1; + unsigned disconnection : 1; + unsigned special_features : 1; + unsigned ultra_scsi : 2; + unsigned force_sync_nego: 1; + unsigned reverse_probe: 1; + unsigned pci_fix_up: 4; + u_char use_nvram; + u_char verbose; + u_char default_tags; + u_short default_sync; + u_short debug; + u_char burst_max; + u_char led_pin; + u_char max_wide; + u_char settle_delay; + u_char diff_support; + u_char irqm; +}; + +static struct ncr_driver_setup + driver_setup = SCSI_NCR_DRIVER_SETUP; + +#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT +static struct ncr_driver_setup + driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP; +#endif /* ** Other Linux definitions @@ -438,7 +543,149 @@ static void ncr53c8xx_timeout(unsigned long np); -#define bootverbose 1 +#define initverbose (driver_setup.verbose) +#define bootverbose (np->verbose) + +#ifdef SCSI_NCR_NVRAM_SUPPORT +/* +** Symbios NvRAM data format +*/ +#define SYMBIOS_NVRAM_SIZE 368 +#define SYMBIOS_NVRAM_ADDRESS 0x100 + +struct Symbios_nvram { +/* Header 6 bytes */ + u_short start_marker; /* 0x0000 */ + u_short byte_count; /* excluding header/trailer */ + u_short checksum; + +/* Controller set up 20 bytes */ + u_short word0; /* 0x3000 */ + u_short word2; /* 0x0000 */ + u_short word4; /* 0x0000 */ + u_short flags; +#define SYMBIOS_SCAM_ENABLE (1) +#define SYMBIOS_PARITY_ENABLE (1<<1) +#define SYMBIOS_VERBOSE_MSGS (1<<2) + u_short flags1; +#define SYMBIOS_SCAN_HI_LO (1) + u_short word10; /* 0x00 */ + u_short word12; /* 0x00 */ + u_char host_id; + u_char byte15; /* 0x04 */ + u_short word16; /* 0x0410 */ + u_short word18; /* 0x0000 */ + +/* Boot order 14 bytes * 4 */ + struct Symbios_host{ + u_char word0; /* 0x0004:ok / 0x0000:nok */ + u_short device_id; /* PCI device id */ + u_short vendor_id; /* PCI vendor id */ + u_char byte6; /* 0x00 */ + u_char device_fn; /* PCI device/function number << 3*/ + u_short word8; + u_short flags; +#define SYMBIOS_INIT_SCAN_AT_BOOT (1) + u_short io_port; /* PCI io_port address */ + } host[4]; + +/* Targets 8 bytes * 16 */ + struct Symbios_target { + u_short flags; +#define SYMBIOS_DISCONNECT_ENABLE (1) +#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1) +#define SYMBIOS_SCAN_LUNS (1<<2) +#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3) + u_char bus_width; /* 0x08/0x10 */ + u_char sync_offset; + u_char sync_period; /* 4*period factor */ + u_char byte6; /* 0x00 */ + u_short timeout; + } target[16]; + u_char spare_devices[19*8]; + u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */ +}; +typedef struct Symbios_nvram Symbios_nvram; +typedef struct Symbios_host Symbios_host; +typedef struct Symbios_target Symbios_target; + +/* +** Tekram NvRAM data format. +*/ +#define TEKRAM_NVRAM_SIZE 64 +#define TEKRAM_NVRAM_ADDRESS 0 + +struct Tekram_nvram { + struct Tekram_target { + u_char flags; +#define TEKRAM_PARITY_CHECK (1) +#define TEKRAM_SYNC_NEGO (1<<1) +#define TEKRAM_DISCONNECT_ENABLE (1<<2) +#define TEKRAM_START_CMD (1<<3) +#define TEKRAM_TAGGED_COMMANDS (1<<4) +#define TEKRAM_WIDE_NEGO (1<<5) + u_char sync_index; + u_short word2; + } target[16]; + u_char host_id; + u_char flags; +#define TEKRAM_MORE_THAN_2_DRIVES (1) +#define TEKRAM_DRIVES_SUP_1GB (1<<1) +#define TEKRAM_RESET_ON_POWER_ON (1<<2) +#define TEKRAM_ACTIVE_NEGATION (1<<3) +#define TEKRAM_IMMEDIATE_SEEK (1<<4) +#define TEKRAM_SCAN_LUNS (1<<5) +#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */ + u_char boot_delay_index; + u_char max_tags_index; + u_short flags1; +#define TEKRAM_F2_F6_ENABLED (1) + u_short spare[29]; +}; +typedef struct Tekram_nvram Tekram_nvram; +typedef struct Tekram_target Tekram_target; + +static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21}; + +#endif /* SCSI_NCR_NVRAM_SUPPORT */ + +/* +** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to +** transmit device configuration to the ncr_attach() function. +*/ +typedef struct { + int bus; + u_char device_fn; + u_int base; + u_int io_port; + int irq; +/* port and reg fields to use INB, OUTB macros */ + u_int port; + volatile struct ncr_reg *reg; +} ncr_slot; + +typedef struct { + int type; +#define SCSI_NCR_SYMBIOS_NVRAM (1) +#define SCSI_NCR_TEKRAM_NVRAM (2) +#ifdef SCSI_NCR_NVRAM_SUPPORT + union { + Symbios_nvram Symbios; + Tekram_nvram Tekram; + } data; +#endif +} ncr_nvram; + +/* +** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init +** to save data on each detected board for ncr_attach(). +*/ +typedef struct { + ncr_slot slot; + ncr_chip chip; + ncr_nvram *nvram; + int attached; +} ncr_device; /*========================================================== ** @@ -466,7 +713,7 @@ ** Can be changed at runtime too. */ -#ifdef SCSI_NCR_DEBUG +#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT #define DEBUG_FLAGS ncr_debug #else #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS @@ -499,49 +746,45 @@ ** Access to the controller chip. ** ** If NCR_IOMAPPED is defined, only IO are used by the driver. -** Else, we begins initialisations by using MMIO. -** If cache test fails, we retry using IO mapped. -** The flag "use_mmio" in the ncb structure is set to 1 if -** mmio is possible. ** **========================================================== */ /* -** IO mapped input / ouput +** IO mapped only input / ouput */ -#define IOM_INB(r) inb (np->port + offsetof(struct ncr_reg, r)) -#define IOM_INB_OFF(o) inb (np->port + (o)) -#define IOM_INW(r) inw (np->port + offsetof(struct ncr_reg, r)) -#define IOM_INL(r) inl (np->port + offsetof(struct ncr_reg, r)) -#define IOM_INL_OFF(o) inl (np->port + (o)) - -#define IOM_OUTB(r, val) outb ((val), np->port+offsetof(struct ncr_reg,r)) -#define IOM_OUTW(r, val) outw ((val), np->port+offsetof(struct ncr_reg,r)) -#define IOM_OUTL(r, val) outl ((val), np->port+offsetof(struct ncr_reg,r)) -#define IOM_OUTL_OFF(o, val) outl ((val), np->port + (o)) +#define IOM_INB(r) inb (np->port + offsetof(struct ncr_reg, r)) +#define IOM_INB_OFF(o) inb (np->port + (o)) +#define IOM_INW(r) inw (np->port + offsetof(struct ncr_reg, r)) +#define IOM_INL(r) inl (np->port + offsetof(struct ncr_reg, r)) +#define IOM_INL_OFF(o) inl (np->port + (o)) + +#define IOM_OUTB(r, val) outb ((val), np->port+offsetof(struct ncr_reg,r)) +#define IOM_OUTW(r, val) outw ((val), np->port+offsetof(struct ncr_reg,r)) +#define IOM_OUTL(r, val) outl ((val), np->port+offsetof(struct ncr_reg,r)) +#define IOM_OUTL_OFF(o, val) outl ((val), np->port + (o)) /* ** MEMORY mapped IO input / output */ -#define MMIO_INB(r) readb(&np->reg_remapped->r) -#define MMIO_INB_OFF(o) readb((char *)np->reg_remapped + (o)) -#define MMIO_INW(r) readw(&np->reg_remapped->r) -#define MMIO_INL(r) readl(&np->reg_remapped->r) -#define MMIO_INL_OFF(o) readl((char *)np->reg_remapped + (o)) - -#define MMIO_OUTB(r, val) writeb((val), &np->reg_remapped->r) -#define MMIO_OUTW(r, val) writew((val), &np->reg_remapped->r) -#define MMIO_OUTL(r, val) writel((val), &np->reg_remapped->r) -#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg_remapped + (o)) +#define MMIO_INB(r) readb(&np->reg->r) +#define MMIO_INB_OFF(o) readb((char *)np->reg + (o)) +#define MMIO_INW(r) readw(&np->reg->r) +#define MMIO_INL(r) readl(&np->reg->r) +#define MMIO_INL_OFF(o) readl((char *)np->reg + (o)) + +#define MMIO_OUTB(r, val) writeb((val), &np->reg->r) +#define MMIO_OUTW(r, val) writew((val), &np->reg->r) +#define MMIO_OUTL(r, val) writel((val), &np->reg->r) +#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg + (o)) /* -** IO mapped only input / output +** IO mapped input / output */ -#ifdef NCR_IOMAPPED +#if defined(NCR_IOMAPPED) #define INB(r) IOM_INB(r) #define INB_OFF(o) IOM_INB_OFF(o) @@ -555,24 +798,35 @@ #define OUTL_OFF(o, val) IOM_OUTL_OFF(o, val) /* -** IO mapped or MEMORY mapped depending on flag "use_mmio" +** MEMORY mapped only input / output */ #else -#define INB(r) (np->use_mmio ? MMIO_INB(r) : IOM_INB(r)) -#define INB_OFF(o) (np->use_mmio ? MMIO_INB_OFF(o) : IOM_INB_OFF(o)) -#define INW(r) (np->use_mmio ? MMIO_INW(r) : IOM_INW(r)) -#define INL(r) (np->use_mmio ? MMIO_INL(r) : IOM_INL(r)) -#define INL_OFF(o) (np->use_mmio ? MMIO_INL_OFF(o) : IOM_INL_OFF(o)) - -#define OUTB(r, val) (np->use_mmio ? MMIO_OUTB(r, val) : IOM_OUTB(r, val)) -#define OUTW(r, val) (np->use_mmio ? MMIO_OUTW(r, val) : IOM_OUTW(r, val)) -#define OUTL(r, val) (np->use_mmio ? MMIO_OUTL(r, val) : IOM_OUTL(r, val)) -#define OUTL_OFF(o, val) (np->use_mmio ? MMIO_OUTL_OFF(o, val) : IOM_OUTL_OFF(o, val)) +#define INB(r) MMIO_INB(r) +#define INB_OFF(o) MMIO_INB_OFF(o) +#define INW(r) MMIO_INW(r) +#define INL(r) MMIO_INL(r) +#define INL_OFF(o) MMIO_INL_OFF(o) + +#define OUTB(r, val) MMIO_OUTB(r, val) +#define OUTW(r, val) MMIO_OUTW(r, val) +#define OUTL(r, val) MMIO_OUTL(r, val) +#define OUTL_OFF(o, val) MMIO_OUTL_OFF(o, val) #endif +/* +** Set bit field ON, OFF +*/ + +#define OUTONB(r, m) OUTB(r, INB(r) | (m)) +#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m)) +#define OUTONW(r, m) OUTW(r, INW(r) | (m)) +#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m)) +#define OUTONL(r, m) OUTL(r, INL(r) | (m)) +#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m)) + /*========================================================== ** ** Command control block states. @@ -614,7 +868,9 @@ #define SIR_REJECT_SENT (10) #define SIR_IGN_RESIDUE (11) #define SIR_MISSING_SAVE (12) -#define SIR_MAX (12) +#define SIR_DATA_IO_IS_OUT (13) +#define SIR_DATA_IO_IS_IN (14) +#define SIR_MAX (14) /*========================================================== ** @@ -673,7 +929,6 @@ */ #define CCB_MAGIC (0xf2691ad2) -#define MAX_TAGS (16) /* hard limit */ /*========================================================== ** @@ -713,7 +968,13 @@ #define UC_SETFLAG 15 #define UC_CLEARPROF 16 +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT +#define UC_DEBUG_ERROR_RECOVERY 17 +#endif + #define UF_TRACE (0x01) +#define UF_NODISC (0x02) +#define UF_NOSCAN (0x04) /*--------------------------------------- ** @@ -727,7 +988,6 @@ u_long end; u_long select; u_long command; - u_long data; u_long status; u_long disconnect; u_long reselect; @@ -812,6 +1072,14 @@ ccb_p hold_cp; /* + ** pointer to ccb used for negotiating. + ** Avoid to start a nego for all queued commands + ** when tagged command queuing is enabled. + */ + + ccb_p nego_cp; + + /* ** statistical data */ @@ -821,13 +1089,18 @@ /* ** user settable limits for sync transfer ** and tagged commands. + ** These limits are read from the NVRAM if present. */ u_char usrsync; - u_char usrtags; u_char usrwide; + u_char usrtags; u_char usrflag; + u_char numtags; + u_char maxtags; + u_short num_good; + /* ** negotiation of wide and synch transfer. ** device quirks. @@ -911,6 +1184,15 @@ u_char usetags; u_char lasttag; + /* + ** Linux specific fields: + ** Number of active commands and current credit. + ** Should be managed by the generic scsi driver + */ + + u_char active; + u_char opennings; + /*----------------------------------------------- ** Flag to force M_ORDERED_TAG on next command ** in order to avoid spurious timeout when @@ -960,9 +1242,9 @@ ** the last transfer command. */ - u_long savep; - u_long lastp; - u_long goalp; + u_int32 savep; + u_int32 lastp; + u_int32 goalp; /* ** The virtual address of the ccb @@ -1093,6 +1375,14 @@ struct ccb { /* + ** This field forces 32 bytes alignement for phys.header, + ** in order to use cache line bursting when copying it + ** to the ncb. + */ + + struct link filler[2]; + + /* ** during reselection the ncr jumps to this point. ** If a "SIMPLE_TAG" message was received, ** then SFBR is set to the tag. @@ -1199,6 +1489,14 @@ */ u_char tag; + + /* + ** Number of segments of the scatter list. + ** Used for recalculation of savep/goalp/lastp on + ** SIR_DATA_IO_IS_OUT interrupt. + */ + + u_char segments; }; #define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl)) @@ -1211,32 +1509,69 @@ */ struct ncb { + /* + ** The global header. + ** Accessible to both the host and the + ** script-processor. + ** Is 32 bytes aligned since ncb is, in order to + ** allow cache line bursting when copying it from or + ** to ccbs. + */ + struct head header; + /*----------------------------------------------- ** Specific Linux fields **----------------------------------------------- */ int unit; /* Unit number */ - int chip; /* Chip number */ + char chip_name[8]; /* Chip name */ + char inst_name[16]; /* Instance name */ struct timer_list timer; /* Timer link header */ int ncr_cache; /* Cache test variable */ - int release_stage; /* Synchronisation stage on release */ Scsi_Cmnd *waiting_list; /* Waiting list header for commands */ /* that we can't put into the squeue */ -#ifndef NCR_IOMAPPED - volatile struct ncr_reg* - reg_remapped; /* Virtual address of the memory */ - /* base of the ncr chip */ - int use_mmio; /* Indicate mmio is OK */ + u_long settle_time; /* Reset in progess */ + u_char release_stage; /* Synchronisation stage on release */ + u_char verbose; /* Boot verbosity for this controller*/ +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + u_char debug_error_recovery; + u_char stalling; + u_char assert_atn; #endif + /*----------------------------------------------- ** Added field to support differences ** between ncr chips. + ** sv_xxx are some io register bit value at start-up and + ** so assumed to have been set by the sdms bios. + ** rv_xxx are the bit fields of io register that will keep + ** the features used by the driver. **----------------------------------------------- */ u_short device_id; u_char revision_id; -#define ChipDevice ((np)->device_id) -#define ChipVersion ((np)->revision_id & 0xf0) + + u_char sv_scntl0; + u_char sv_scntl3; + u_char sv_dmode; + u_char sv_dcntl; + u_char sv_ctest3; + u_char sv_ctest4; + u_char sv_ctest5; + u_char sv_gpcntl; + u_char sv_stest2; + u_char sv_stest4; + + u_char rv_scntl0; + u_char rv_scntl3; + u_char rv_dmode; + u_char rv_dcntl; + u_char rv_ctest3; + u_char rv_ctest4; + u_char rv_ctest5; + u_char rv_stest2; + + u_char scsi_mode; /*----------------------------------------------- ** Scripts .. @@ -1262,6 +1597,9 @@ vm_offset_t vaddr; vm_offset_t paddr; + vm_offset_t vaddr2; + vm_offset_t paddr2; + /* ** pointer to the chip's registers. */ @@ -1269,14 +1607,22 @@ struct ncr_reg* reg; /* - ** A copy of the script, relocated for this ncb. + ** A copy of the scripts, relocated for this ncb. + */ + struct script *script0; + struct scripth *scripth0; + + /* + ** Scripts instance virtual address. */ struct script *script; + struct scripth *scripth; /* - ** Physical address of this instance of ncb->script + ** Scripts instance physical address. */ u_long p_script; + u_long p_scripth; /* ** The SCSI address of the host adapter. @@ -1284,11 +1630,21 @@ u_char myaddr; /* + ** Max dwords burst supported by the adapter. + */ + u_char maxburst; /* log base 2 of dwords burst */ + + /* ** timing parameters */ - u_char ns_async; - u_char ns_sync; - u_char rv_scntl3; + u_char minsync; /* Minimum sync period factor */ + u_char maxsync; /* Maximum sync period factor */ + u_char maxoffs; /* Max scsi offset */ + u_char multiplier; /* Clock multiplier (1,2,4) */ + u_char clock_divn; /* Number of clock divisors */ + u_long clock_khz; /* SCSI clock frequency in KHz */ + u_int features; /* Chip features map */ + /*----------------------------------------------- ** Link to the generic SCSI driver @@ -1314,7 +1670,7 @@ /* ** Start queue. */ - u_long squeue [MAX_START]; + u_int32 squeue [MAX_START]; u_short squeueput; u_short actccbs; @@ -1343,19 +1699,12 @@ u_long disc_ref; /* - ** The global header. - ** Accessible to both the host and the - ** script-processor. - */ - struct head header; - - /* ** The global control block. ** It's used only during the configuration phase. ** A target control block will be created ** after the first successful transfer. */ - struct ccb ccb; + struct ccb *ccb; /* ** message buffers. @@ -1365,7 +1714,7 @@ */ u_char msgout[8]; u_char msgin [8]; - u_long lastmsg; + u_int32 lastmsg; /* ** Buffer for STATUS_IN phase. @@ -1393,7 +1742,8 @@ u_short irq; }; -#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) +#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) +#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth, lbl)) /*========================================================== ** @@ -1416,12 +1766,15 @@ **---------------------------------------------------------- */ +/* +** Script fragments which are loaded into the on-board RAM +** of 825A, 875 and 895 chips. +*/ struct script { ncrcmd start [ 7]; ncrcmd start0 [ 2]; ncrcmd start1 [ 3]; ncrcmd startpos [ 1]; - ncrcmd tryloop [MAX_START*5+2]; ncrcmd trysel [ 8]; ncrcmd skip [ 8]; ncrcmd skip2 [ 3]; @@ -1439,14 +1792,6 @@ ncrcmd status [ 27]; ncrcmd msg_in [ 26]; ncrcmd msg_bad [ 6]; - ncrcmd msg_parity [ 6]; - ncrcmd msg_reject [ 8]; - ncrcmd msg_ign_residue [ 32]; - ncrcmd msg_extended [ 18]; - ncrcmd msg_ext_2 [ 18]; - ncrcmd msg_wdtr [ 27]; - ncrcmd msg_ext_3 [ 18]; - ncrcmd msg_sdtr [ 27]; ncrcmd complete [ 13]; ncrcmd cleanup [ 12]; ncrcmd cleanup0 [ 11]; @@ -1458,6 +1803,30 @@ ncrcmd disconnect1 [ 23]; ncrcmd msg_out [ 9]; ncrcmd msg_out_done [ 7]; + ncrcmd badgetcc [ 6]; + ncrcmd reselect [ 8]; + ncrcmd reselect1 [ 8]; + ncrcmd reselect2 [ 8]; + ncrcmd resel_tmp [ 5]; + ncrcmd resel_lun [ 18]; + ncrcmd resel_tag [ 24]; + ncrcmd data_io [ 6]; + ncrcmd data_in [MAX_SCATTER * 4 + 4]; +}; + +/* +** Script fragments which stay in main memory for all chips. +*/ +struct scripth { + ncrcmd tryloop [MAX_START*5+2]; + ncrcmd msg_parity [ 6]; + ncrcmd msg_reject [ 8]; + ncrcmd msg_ign_residue [ 32]; + ncrcmd msg_extended [ 18]; + ncrcmd msg_ext_2 [ 18]; + ncrcmd msg_wdtr [ 27]; + ncrcmd msg_ext_3 [ 18]; + ncrcmd msg_sdtr [ 27]; ncrcmd msg_out_abort [ 10]; ncrcmd getcc [ 4]; ncrcmd getcc1 [ 5]; @@ -1467,14 +1836,7 @@ ncrcmd getcc2 [ 14]; #endif ncrcmd getcc3 [ 10]; - ncrcmd badgetcc [ 6]; - ncrcmd reselect [ 12]; - ncrcmd reselect2 [ 6]; - ncrcmd resel_tmp [ 5]; - ncrcmd resel_lun [ 18]; - ncrcmd resel_tag [ 24]; - ncrcmd data_in [MAX_SCATTER * 4 + 7]; - ncrcmd data_out [MAX_SCATTER * 4 + 7]; + ncrcmd data_out [MAX_SCATTER * 4 + 4]; ncrcmd aborttag [ 4]; ncrcmd abort [ 22]; ncrcmd snooptest [ 9]; @@ -1493,51 +1855,59 @@ static void ncr_alloc_ccb (ncb_p np, u_long t, u_long l); static void ncr_complete (ncb_p np, ccb_p cp); static void ncr_exception (ncb_p np); -static void ncr_free_ccb (ncb_p np, ccb_p cp); -static void ncr_getclock (ncb_p np, u_char scntl3); +static void ncr_free_ccb (ncb_p np, ccb_p cp, u_long t, u_long l); +static void ncr_getclock (ncb_p np, int mult); +static void ncr_selectclock (ncb_p np, u_char scntl3); static ccb_p ncr_get_ccb (ncb_p np, u_long t,u_long l); static void ncr_init (ncb_p np, char * msg, u_long code); -static int ncr_intr (ncb_p np); +static int ncr_int_sbmc (ncb_p np); +static int ncr_int_par (ncb_p np); static void ncr_int_ma (ncb_p np); static void ncr_int_sir (ncb_p np); static void ncr_int_sto (ncb_p np); static u_long ncr_lookup (char* id); static void ncr_negotiate (struct ncb* np, struct tcb* tp); +static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * xp); -#ifdef SCSI_NCR_PROFILE -static int ncr_delta (u_long from, u_long to); +#ifdef SCSI_NCR_PROFILE_SUPPORT static void ncb_profile (ncb_p np, ccb_p cp); #endif static void ncr_script_copy_and_bind - (struct script * script, ncb_p np); -static void ncr_script_fill (struct script * scr); + (ncb_p np, ncrcmd *src, ncrcmd *dst, int len); +static void ncr_script_fill (struct script * scr, struct scripth * scripth); static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd); -static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags); -static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer); +static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags); +static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p); +static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer); static void ncr_settags (tcb_p tp, lcb_p lp); -static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide); +static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack); static int ncr_show_msg (u_char * msg); static int ncr_snooptest (ncb_p np); static void ncr_timeout (ncb_p np); static void ncr_wakeup (ncb_p np, u_long code); +static void ncr_start_reset (ncb_p np, int settle_delay); -#ifdef SCSI_NCR_USER_COMMAND +#ifdef SCSI_NCR_USER_COMMAND_SUPPORT static void ncr_usercmd (ncb_p np); #endif -static int ncr_attach (Scsi_Host_Template *tpnt, int unit, u_short device_id, - u_char revision_id, int chip, u_int base, u_int io_port, - int irq, int bus, u_char device_fn); +static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device); static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd); -static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd); +static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd); static void process_waiting_list(ncb_p np, int sts); +#define remove_from_waiting_list(np, cmd) \ + retrieve_from_waiting_list(1, (np), (cmd)) #define requeue_waiting_list(np) process_waiting_list((np), DID_OK) -#define abort_waiting_list(np) process_waiting_list((np), DID_ABORT) #define reset_waiting_list(np) process_waiting_list((np), DID_RESET) +#ifdef SCSI_NCR_NVRAM_SUPPORT +static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram); +static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram); +#endif + /*========================================================== ** ** @@ -1557,24 +1927,13 @@ + (u_long) sizeof (struct tcb) * 2; #endif -#ifdef SCSI_NCR_DEBUG +#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT static int ncr_debug = SCSI_NCR_DEBUG_FLAGS; #endif -/*========================================================== -** -** -** Global static data: auto configure -** -** -**========================================================== -*/ - -static char *ncr_name (ncb_p np) +static inline char *ncr_name (ncb_p np) { - static char name[10]; - sprintf(name, "ncr53c%d-%d", np->chip, np->unit); - return (name); + return np->inst_name; } @@ -1601,10 +1960,12 @@ #define RELOC_LABEL 0x50000000 #define RELOC_REGISTER 0x60000000 #define RELOC_KVAR 0x70000000 +#define RELOC_LABELH 0x80000000 #define RELOC_MASK 0xf0000000 #define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label)) #define PADDR(label) (RELOC_LABEL | offsetof(struct script, label)) +#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label)) #define RADDR(label) (RELOC_REGISTER | REG(label)) #define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs))) #define KVAR(which) (RELOC_KVAR | (which)) @@ -1618,10 +1979,10 @@ * Kernel variables referenced in the scripts. * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY. */ -static void *script_kvars[] = +static void *script_kvars[] __initdata = { (void *)&jiffies }; -static struct script script0 = { +static struct script script0 __initdata = { /*--------------------------< START >-----------------------*/ { /* ** Claim to be still alive ... @@ -1661,33 +2022,7 @@ */ SCR_JUMP, }/*-------------------------< STARTPOS >--------------------*/,{ - PADDR(tryloop), -}/*-------------------------< TRYLOOP >---------------------*/,{ -/* -** Load an entry of the start queue into dsa -** and try to start it by jumping to TRYSEL. -** -** Because the size depends on the -** #define MAX_START parameter, it is filled -** in at runtime. -** -**----------------------------------------------------------- -** -** ##===========< I=0; i=========== -** || SCR_COPY (4), -** || NADDR (squeue[i]), -** || RADDR (dsa), -** || SCR_CALL, -** || PADDR (trysel), -** ##========================================== -** -** SCR_JUMP, -** PADDR(tryloop), -** -**----------------------------------------------------------- -*/ -0 - + PADDRH(tryloop), }/*-------------------------< TRYSEL >----------------------*/,{ /* ** Now: @@ -1742,7 +2077,7 @@ ** patch the launch field. ** should look like an idle process. */ - SCR_COPY (4), + SCR_COPY_F (4), RADDR (dsa), PADDR (skip2), SCR_COPY (8), @@ -1848,7 +2183,7 @@ ** We patch the address part of a ** COPY command with the DSA-register. */ - SCR_COPY (4), + SCR_COPY_F (4), RADDR (dsa), PADDR (loadpos), /* @@ -1925,7 +2260,7 @@ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)), PADDR (msg_in), SCR_JUMP ^ IFTRUE (DATA (M_REJECT)), - PADDR (msg_reject), + PADDRH (msg_reject), /* ** normal processing */ @@ -2142,7 +2477,7 @@ SCR_FROM_REG (socl), 0, SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), + PADDRH (msg_parity), SCR_FROM_REG (scratcha), 0, /* @@ -2157,13 +2492,13 @@ SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)), PADDR (disconnect), SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)), - PADDR (msg_extended), + PADDRH (msg_extended), SCR_JUMP ^ IFTRUE (DATA (M_NOOP)), PADDR (clrack), SCR_JUMP ^ IFTRUE (DATA (M_REJECT)), - PADDR (msg_reject), + PADDRH (msg_reject), SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)), - PADDR (msg_ign_residue), + PADDRH (msg_ign_residue), /* ** Rest of the messages left as ** an exercise ... @@ -2182,301 +2517,41 @@ SCR_JUMP, PADDR (setmsg), -}/*-------------------------< MSG_PARITY >---------------*/,{ +}/*-------------------------< COMPLETE >-----------------*/,{ /* - ** count it + ** Complete message. + ** + ** If it's not the get condition code, + ** copy TEMP register to LASTP in header. */ - SCR_REG_REG (PS_REG, SCR_ADD, 0x01), + SCR_FROM_REG (SS_REG), 0, - /* - ** send a "message parity error" message. +/*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)), + 12, + SCR_COPY (4), + RADDR (temp), + NADDR (header.lastp), +/*>>>*/ /* + ** When we terminate the cycle by clearing ACK, + ** the target may disconnect immediately. + ** + ** We don't want to be told of an + ** "unexpected disconnect", + ** so we disable this feature. */ - SCR_LOAD_REG (scratcha, M_PARITY), + SCR_REG_REG (scntl2, SCR_AND, 0x7f), 0, - SCR_JUMP, - PADDR (setmsg), -}/*-------------------------< MSG_REJECT >---------------*/,{ /* - ** If a negotiation was in progress, - ** negotiation failed. + ** Terminate cycle ... */ - SCR_FROM_REG (HS_REG), + SCR_CLR (SCR_ACK|SCR_ATN), 0, - SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), - SIR_NEGO_FAILED, /* - ** else make host log this message - */ - SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), - SIR_REJECT_RECEIVED, - SCR_JUMP, - PADDR (clrack), - -}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ - /* - ** Terminate cycle + ** ... and wait for the disconnect. */ - SCR_CLR (SCR_ACK), + SCR_WAIT_DISC, 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get residue size. - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[1]), - /* - ** Check for message parity error. - */ - SCR_TO_REG (scratcha), - 0, - SCR_FROM_REG (socl), - 0, - SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), - SCR_FROM_REG (scratcha), - 0, - /* - ** Size is 0 .. ignore message. - */ - SCR_JUMP ^ IFTRUE (DATA (0)), - PADDR (clrack), - /* - ** Size is not 1 .. have to interrupt. - */ -/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)), - 40, - /* - ** Check for residue byte in swide register - */ - SCR_FROM_REG (scntl2), - 0, -/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), - 16, - /* - ** There IS data in the swide register. - ** Discard it. - */ - SCR_REG_REG (scntl2, SCR_OR, WSR), - 0, - SCR_JUMP, - PADDR (clrack), - /* - ** Load again the size to the sfbr register. - */ -/*>>>*/ SCR_FROM_REG (scratcha), - 0, -/*>>>*/ SCR_INT, - SIR_IGN_RESIDUE, - SCR_JUMP, - PADDR (clrack), - -}/*-------------------------< MSG_EXTENDED >-------------*/,{ - /* - ** Terminate cycle - */ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get length. - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[1]), - /* - ** Check for message parity error. - */ - SCR_TO_REG (scratcha), - 0, - SCR_FROM_REG (socl), - 0, - SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), - SCR_FROM_REG (scratcha), - 0, - /* - */ - SCR_JUMP ^ IFTRUE (DATA (3)), - PADDR (msg_ext_3), - SCR_JUMP ^ IFFALSE (DATA (2)), - PADDR (msg_bad), -}/*-------------------------< MSG_EXT_2 >----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get extended message code. - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[2]), - /* - ** Check for message parity error. - */ - SCR_TO_REG (scratcha), - 0, - SCR_FROM_REG (socl), - 0, - SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), - SCR_FROM_REG (scratcha), - 0, - SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)), - PADDR (msg_wdtr), - /* - ** unknown extended message - */ - SCR_JUMP, - PADDR (msg_bad) -}/*-------------------------< MSG_WDTR >-----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get data bus width - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[3]), - SCR_FROM_REG (socl), - 0, - SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), - /* - ** let the host do the real work. - */ - SCR_INT, - SIR_NEGO_WIDE, - /* - ** let the target fetch our answer. - */ - SCR_SET (SCR_ATN), - 0, - SCR_CLR (SCR_ACK), - 0, - - SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), - SIR_NEGO_PROTO, - /* - ** Send the M_X_WIDE_REQ - */ - SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, - NADDR (msgout), - SCR_CLR (SCR_ATN), - 0, - SCR_COPY (1), - RADDR (sfbr), - NADDR (lastmsg), - SCR_JUMP, - PADDR (msg_out_done), - -}/*-------------------------< MSG_EXT_3 >----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get extended message code. - */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin[2]), - /* - ** Check for message parity error. - */ - SCR_TO_REG (scratcha), - 0, - SCR_FROM_REG (socl), - 0, - SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), - SCR_FROM_REG (scratcha), - 0, - SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)), - PADDR (msg_sdtr), - /* - ** unknown extended message - */ - SCR_JUMP, - PADDR (msg_bad) - -}/*-------------------------< MSG_SDTR >-----------------*/,{ - SCR_CLR (SCR_ACK), - 0, - SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), - PADDR (dispatch), - /* - ** get period and offset - */ - SCR_MOVE_ABS (2) ^ SCR_MSG_IN, - NADDR (msgin[3]), - SCR_FROM_REG (socl), - 0, - SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), - PADDR (msg_parity), - /* - ** let the host do the real work. - */ - SCR_INT, - SIR_NEGO_SYNC, - /* - ** let the target fetch our answer. - */ - SCR_SET (SCR_ATN), - 0, - SCR_CLR (SCR_ACK), - 0, - - SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), - SIR_NEGO_PROTO, - /* - ** Send the M_X_SYNC_REQ - */ - SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, - NADDR (msgout), - SCR_CLR (SCR_ATN), - 0, - SCR_COPY (1), - RADDR (sfbr), - NADDR (lastmsg), - SCR_JUMP, - PADDR (msg_out_done), - -}/*-------------------------< COMPLETE >-----------------*/,{ - /* - ** Complete message. - ** - ** If it's not the get condition code, - ** copy TEMP register to LASTP in header. - */ - SCR_FROM_REG (SS_REG), - 0, -/*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)), - 12, - SCR_COPY (4), - RADDR (temp), - NADDR (header.lastp), -/*>>>*/ /* - ** When we terminate the cycle by clearing ACK, - ** the target may disconnect immediately. - ** - ** We don't want to be told of an - ** "unexpected disconnect", - ** so we disable this feature. - */ - SCR_REG_REG (scntl2, SCR_AND, 0x7f), - 0, - /* - ** Terminate cycle ... - */ - SCR_CLR (SCR_ACK|SCR_ATN), - 0, - /* - ** ... and wait for the disconnect. - */ - SCR_WAIT_DISC, - 0, -}/*-------------------------< CLEANUP >-------------------*/,{ +}/*-------------------------< CLEANUP >-------------------*/,{ /* ** dsa: Pointer to ccb ** or xxxxxxFF (no ccb) @@ -2497,7 +2572,7 @@ /* ** and copy back the header to the ccb. */ - SCR_COPY (4), + SCR_COPY_F (4), RADDR (dsa), PADDR (cleanup0), SCR_COPY (sizeof (struct head)), @@ -2517,7 +2592,7 @@ SCR_FROM_REG (SS_REG), 0, SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)), - PADDR(getcc2), + PADDRH(getcc2), /* ** And make the DSA register invalid. */ @@ -2603,7 +2678,7 @@ ** ** CAUTION: only little endian architectures supported! XXX */ - SCR_COPY (1), + SCR_COPY_F (1), NADDR (header.savep), PADDR (disconnect0), }/*-------------------------< DISCONNECT0 >--------------*/,{ @@ -2612,7 +2687,7 @@ /* ** neither this */ - SCR_COPY (1), + SCR_COPY_F (1), NADDR (header.goalp), PADDR (disconnect1), }/*-------------------------< DISCONNECT1 >--------------*/,{ @@ -2672,7 +2747,7 @@ ** If it was no ABORT message ... */ SCR_JUMP ^ IFTRUE (DATA (M_ABORT)), - PADDR (msg_out_abort), + PADDRH (msg_out_abort), /* ** ... wait for the next phase ** if it's a message out, send it again, ... @@ -2693,180 +2768,24 @@ */ SCR_JUMP, PADDR (dispatch), -}/*-------------------------< MSG_OUT_ABORT >-------------*/,{ - /* - ** After ABORT message, - ** - ** expect an immediate disconnect, ... - */ - SCR_REG_REG (scntl2, SCR_AND, 0x7f), - 0, - SCR_CLR (SCR_ACK|SCR_ATN), - 0, - SCR_WAIT_DISC, - 0, - /* - ** ... and set the status to "ABORTED" - */ - SCR_LOAD_REG (HS_REG, HS_ABORTED), - 0, - SCR_JUMP, - PADDR (cleanup), - -}/*-------------------------< GETCC >-----------------------*/,{ - /* - ** The ncr doesn't have an indirect load - ** or store command. So we have to - ** copy part of the control block to a - ** fixed place, where we can modify it. - ** - ** We patch the address part of a COPY command - ** with the address of the dsa register ... - */ - SCR_COPY (4), - RADDR (dsa), - PADDR (getcc1), - /* - ** ... then we do the actual copy. - */ - SCR_COPY (sizeof (struct head)), -}/*-------------------------< GETCC1 >----------------------*/,{ - 0, - NADDR (header), - /* - ** Initialize the status registers - */ - SCR_COPY (4), - NADDR (header.status), - RADDR (scr0), -}/*-------------------------< GETCC2 >----------------------*/,{ - /* - ** Get the condition code from a target. - ** - ** DSA points to a data structure. - ** Set TEMP to the script location - ** that receives the condition code. - ** - ** Because there is no script command - ** to load a longword into a register, - ** we use a CALL command. - */ -/*<<<*/ SCR_CALLR, - 24, - /* - ** Get the condition code. - */ - SCR_MOVE_TBL ^ SCR_DATA_IN, - offsetof (struct dsb, sense), - /* - ** No data phase may follow! - */ - SCR_CALL, - PADDR (checkatn), - SCR_JUMP, - PADDR (no_data), -/*>>>*/ - - /* - ** The CALL jumps to this point. - ** Prepare for a RESTORE_POINTER message. - ** Save the TEMP register into the saved pointer. - */ - SCR_COPY (4), - RADDR (temp), - NADDR (header.savep), - /* - ** Load scratcha, because in case of a selection timeout, - ** the host will expect a new value for startpos in - ** the scratcha register. - */ - SCR_COPY (4), - PADDR (startpos), - RADDR (scratcha), -#ifdef NCR_GETCC_WITHMSG - /* - ** If QUIRK_NOMSG is set, select without ATN. - ** and don't send a message. - */ - SCR_FROM_REG (QU_REG), - 0, - SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)), - PADDR(getcc3), - /* - ** Then try to connect to the target. - ** If we are reselected, special treatment - ** of the current job is required before - ** accepting the reselection. - */ - SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), - PADDR(badgetcc), - /* - ** save target id. - */ - SCR_FROM_REG (sdid), - 0, - SCR_TO_REG (ctest0), - 0, - /* - ** Send the IDENTIFY message. - ** In case of short transfer, remove ATN. - */ - SCR_MOVE_TBL ^ SCR_MSG_OUT, - offsetof (struct dsb, smsg2), - SCR_CLR (SCR_ATN), - 0, - /* - ** save the first byte of the message. - */ - SCR_COPY (1), - RADDR (sfbr), - NADDR (lastmsg), - SCR_JUMP, - PADDR (prepare2), - -#endif -}/*-------------------------< GETCC3 >----------------------*/,{ - /* - ** Try to connect to the target. - ** If we are reselected, special treatment - ** of the current job is required before - ** accepting the reselection. - ** - ** Silly target won't accept a message. - ** Select without ATN. - */ - SCR_SEL_TBL ^ offsetof (struct dsb, select), - PADDR(badgetcc), - /* - ** save target id. - */ - SCR_FROM_REG (sdid), - 0, - SCR_TO_REG (ctest0), - 0, - /* - ** Force error if selection timeout - */ - SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), - 0, - /* - ** don't negotiate. - */ - SCR_JUMP, - PADDR (prepare2), - -}/*------------------------< BADGETCC >---------------------*/,{ +}/*------------------------< BADGETCC >---------------------*/,{ /* ** If SIGP was set, clear it and try again. */ SCR_FROM_REG (ctest2), 0, SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)), - PADDR (getcc2), + PADDRH (getcc2), SCR_INT, SIR_SENSE_FAILED, }/*-------------------------< RESELECT >--------------------*/,{ /* + ** This NOP will be patched with LED OFF + ** SCR_REG_REG (gpreg, SCR_OR, 0x01) + */ + SCR_NO_OP, + 0, + /* ** make the DSA invalid. */ SCR_LOAD_REG (dsa, 0xff), @@ -2881,6 +2800,13 @@ */ SCR_WAIT_RESEL, PADDR(reselect2), +}/*-------------------------< RESELECT1 >--------------------*/,{ + /* + ** This NOP will be patched with LED ON + ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) + */ + SCR_NO_OP, + 0, /* ** ... zu nichts zu gebrauchen ? ** @@ -2894,7 +2820,7 @@ ** - struct ccb ** to understand what's going on. */ - SCR_REG_SFBR (ssid, SCR_AND, 0x87), + SCR_REG_SFBR (ssid, SCR_AND, 0x8F), 0, SCR_TO_REG (ctest0), 0, @@ -2902,6 +2828,12 @@ NADDR (jump_tcb), }/*-------------------------< RESELECT2 >-------------------*/,{ /* + ** This NOP will be patched with LED ON + ** SCR_REG_REG (gpreg, SCR_AND, 0xfe) + */ + SCR_NO_OP, + 0, + /* ** If it's not connected :( ** -> interrupted by SIGP bit. ** Jump to start. @@ -2984,80 +2916,530 @@ /*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)), 48, /* - ** It WAS a SIMPLE_TAG message. - ** get it and ack it! + ** It WAS a SIMPLE_TAG message. + ** get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin), + SCR_CLR (SCR_ACK), + 0, + /* + ** Wait for the second byte (the tag) + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), + 24, + /* + ** Get it and ack it! + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin), + SCR_CLR (SCR_ACK|SCR_CARRY), + 0, + SCR_RETURN, + 0, + /* + ** No message phase or no SIMPLE_TAG message + ** or no second byte: return 0. + */ +/*>>>*/ SCR_LOAD_SFBR (0), + 0, + SCR_SET (SCR_CARRY), + 0, + SCR_RETURN, + 0, + +}/*-------------------------< DATA_IO >--------------------*/,{ +/* +** Because Linux does not provide xfer data direction +** to low-level scsi drivers, we must trust the target +** for actual data direction when we cannot guess it. +** The programmed interrupt patches savep, lastp, goalp, +** etc.., and restarts the scsi script at data_out/in. +*/ + SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)), + SIR_DATA_IO_IS_OUT, + SCR_INT ^ IFTRUE (WHEN (SCR_DATA_IN)), + SIR_DATA_IO_IS_IN, + SCR_JUMP, + PADDR (no_data), + +}/*-------------------------< DATA_IN >--------------------*/,{ +/* +** Because the size depends on the +** #define MAX_SCATTER parameter, +** it is filled in at runtime. +** +** ##===========< i=0; i========= +** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), +** || PADDR (checkatn), +** || SCR_MOVE_TBL ^ SCR_DATA_IN, +** || offsetof (struct dsb, data[ i]), +** ##========================================== +** +** SCR_CALL, +** PADDR (checkatn), +** SCR_JUMP, +** PADDR (no_data), +*/ +0 +}/*--------------------------------------------------------*/ +}; + +static struct scripth scripth0 __initdata = { +/*-------------------------< TRYLOOP >---------------------*/{ +/* +** Load an entry of the start queue into dsa +** and try to start it by jumping to TRYSEL. +** +** Because the size depends on the +** #define MAX_START parameter, it is filled +** in at runtime. +** +**----------------------------------------------------------- +** +** ##===========< I=0; i=========== +** || SCR_COPY (4), +** || NADDR (squeue[i]), +** || RADDR (dsa), +** || SCR_CALL, +** || PADDR (trysel), +** ##========================================== +** +** SCR_JUMP, +** PADDRH(tryloop), +** +**----------------------------------------------------------- +*/ +0 +},/*-------------------------< MSG_PARITY >---------------*/{ + /* + ** count it + */ + SCR_REG_REG (PS_REG, SCR_ADD, 0x01), + 0, + /* + ** send a "message parity error" message. + */ + SCR_LOAD_REG (scratcha, M_PARITY), + 0, + SCR_JUMP, + PADDR (setmsg), +}/*-------------------------< MSG_REJECT >---------------*/,{ + /* + ** If a negotiation was in progress, + ** negotiation failed. + */ + SCR_FROM_REG (HS_REG), + 0, + SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)), + SIR_NEGO_FAILED, + /* + ** else make host log this message + */ + SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)), + SIR_REJECT_RECEIVED, + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{ + /* + ** Terminate cycle + */ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get residue size. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[1]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + ** Size is 0 .. ignore message. + */ + SCR_JUMP ^ IFTRUE (DATA (0)), + PADDR (clrack), + /* + ** Size is not 1 .. have to interrupt. + */ +/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)), + 40, + /* + ** Check for residue byte in swide register + */ + SCR_FROM_REG (scntl2), + 0, +/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)), + 16, + /* + ** There IS data in the swide register. + ** Discard it. + */ + SCR_REG_REG (scntl2, SCR_OR, WSR), + 0, + SCR_JUMP, + PADDR (clrack), + /* + ** Load again the size to the sfbr register. + */ +/*>>>*/ SCR_FROM_REG (scratcha), + 0, +/*>>>*/ SCR_INT, + SIR_IGN_RESIDUE, + SCR_JUMP, + PADDR (clrack), + +}/*-------------------------< MSG_EXTENDED >-------------*/,{ + /* + ** Terminate cycle + */ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get length. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[1]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + /* + */ + SCR_JUMP ^ IFTRUE (DATA (3)), + PADDRH (msg_ext_3), + SCR_JUMP ^ IFFALSE (DATA (2)), + PADDR (msg_bad), +}/*-------------------------< MSG_EXT_2 >----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get extended message code. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[2]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)), + PADDRH (msg_wdtr), + /* + ** unknown extended message + */ + SCR_JUMP, + PADDR (msg_bad) +}/*-------------------------< MSG_WDTR >-----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get data bus width + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[3]), + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + /* + ** let the host do the real work. + */ + SCR_INT, + SIR_NEGO_WIDE, + /* + ** let the target fetch our answer. + */ + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + + SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), + SIR_NEGO_PROTO, + /* + ** Send the M_X_WIDE_REQ + */ + SCR_MOVE_ABS (4) ^ SCR_MSG_OUT, + NADDR (msgout), + SCR_CLR (SCR_ATN), + 0, + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_JUMP, + PADDR (msg_out_done), + +}/*-------------------------< MSG_EXT_3 >----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get extended message code. + */ + SCR_MOVE_ABS (1) ^ SCR_MSG_IN, + NADDR (msgin[2]), + /* + ** Check for message parity error. + */ + SCR_TO_REG (scratcha), + 0, + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + SCR_FROM_REG (scratcha), + 0, + SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)), + PADDRH (msg_sdtr), + /* + ** unknown extended message + */ + SCR_JUMP, + PADDR (msg_bad) + +}/*-------------------------< MSG_SDTR >-----------------*/,{ + SCR_CLR (SCR_ACK), + 0, + SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)), + PADDR (dispatch), + /* + ** get period and offset + */ + SCR_MOVE_ABS (2) ^ SCR_MSG_IN, + NADDR (msgin[3]), + SCR_FROM_REG (socl), + 0, + SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)), + PADDRH (msg_parity), + /* + ** let the host do the real work. + */ + SCR_INT, + SIR_NEGO_SYNC, + /* + ** let the target fetch our answer. + */ + SCR_SET (SCR_ATN), + 0, + SCR_CLR (SCR_ACK), + 0, + + SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)), + SIR_NEGO_PROTO, + /* + ** Send the M_X_SYNC_REQ + */ + SCR_MOVE_ABS (5) ^ SCR_MSG_OUT, + NADDR (msgout), + SCR_CLR (SCR_ATN), + 0, + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_JUMP, + PADDR (msg_out_done), + +}/*-------------------------< MSG_OUT_ABORT >-------------*/,{ + /* + ** After ABORT message, + ** + ** expect an immediate disconnect, ... + */ + SCR_REG_REG (scntl2, SCR_AND, 0x7f), + 0, + SCR_CLR (SCR_ACK|SCR_ATN), + 0, + SCR_WAIT_DISC, + 0, + /* + ** ... and set the status to "ABORTED" + */ + SCR_LOAD_REG (HS_REG, HS_ABORTED), + 0, + SCR_JUMP, + PADDR (cleanup), + +}/*-------------------------< GETCC >-----------------------*/,{ + /* + ** The ncr doesn't have an indirect load + ** or store command. So we have to + ** copy part of the control block to a + ** fixed place, where we can modify it. + ** + ** We patch the address part of a COPY command + ** with the address of the dsa register ... + */ + SCR_COPY_F (4), + RADDR (dsa), + PADDRH (getcc1), + /* + ** ... then we do the actual copy. + */ + SCR_COPY (sizeof (struct head)), +}/*-------------------------< GETCC1 >----------------------*/,{ + 0, + NADDR (header), + /* + ** Initialize the status registers + */ + SCR_COPY (4), + NADDR (header.status), + RADDR (scr0), +}/*-------------------------< GETCC2 >----------------------*/,{ + /* + ** Get the condition code from a target. + ** + ** DSA points to a data structure. + ** Set TEMP to the script location + ** that receives the condition code. + ** + ** Because there is no script command + ** to load a longword into a register, + ** we use a CALL command. + */ +/*<<<*/ SCR_CALLR, + 24, + /* + ** Get the condition code. + */ + SCR_MOVE_TBL ^ SCR_DATA_IN, + offsetof (struct dsb, sense), + /* + ** No data phase may follow! + */ + SCR_CALL, + PADDR (checkatn), + SCR_JUMP, + PADDR (no_data), +/*>>>*/ + + /* + ** The CALL jumps to this point. + ** Prepare for a RESTORE_POINTER message. + ** Save the TEMP register into the saved pointer. + */ + SCR_COPY (4), + RADDR (temp), + NADDR (header.savep), + /* + ** Load scratcha, because in case of a selection timeout, + ** the host will expect a new value for startpos in + ** the scratcha register. + */ + SCR_COPY (4), + PADDR (startpos), + RADDR (scratcha), +#ifdef NCR_GETCC_WITHMSG + /* + ** If QUIRK_NOMSG is set, select without ATN. + ** and don't send a message. + */ + SCR_FROM_REG (QU_REG), + 0, + SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)), + PADDRH(getcc3), + /* + ** Then try to connect to the target. + ** If we are reselected, special treatment + ** of the current job is required before + ** accepting the reselection. + */ + SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select), + PADDR(badgetcc), + /* + ** save target id. + */ + SCR_FROM_REG (sdid), + 0, + SCR_TO_REG (ctest0), + 0, + /* + ** Send the IDENTIFY message. + ** In case of short transfer, remove ATN. + */ + SCR_MOVE_TBL ^ SCR_MSG_OUT, + offsetof (struct dsb, smsg2), + SCR_CLR (SCR_ATN), + 0, + /* + ** save the first byte of the message. */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin), - SCR_CLR (SCR_ACK), - 0, + SCR_COPY (1), + RADDR (sfbr), + NADDR (lastmsg), + SCR_JUMP, + PADDR (prepare2), + +#endif +}/*-------------------------< GETCC3 >----------------------*/,{ /* - ** Wait for the second byte (the tag) + ** Try to connect to the target. + ** If we are reselected, special treatment + ** of the current job is required before + ** accepting the reselection. + ** + ** Silly target won't accept a message. + ** Select without ATN. */ -/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)), - 24, + SCR_SEL_TBL ^ offsetof (struct dsb, select), + PADDR(badgetcc), /* - ** Get it and ack it! + ** save target id. */ - SCR_MOVE_ABS (1) ^ SCR_MSG_IN, - NADDR (msgin), - SCR_CLR (SCR_ACK|SCR_CARRY), + SCR_FROM_REG (sdid), 0, - SCR_RETURN, + SCR_TO_REG (ctest0), 0, /* - ** No message phase or no SIMPLE_TAG message - ** or no second byte: return 0. + ** Force error if selection timeout */ -/*>>>*/ SCR_LOAD_SFBR (0), - 0, - SCR_SET (SCR_CARRY), - 0, - SCR_RETURN, + SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)), 0, + /* + ** don't negotiate. + */ + SCR_JUMP, + PADDR (prepare2), -}/*-------------------------< DATA_IN >--------------------*/,{ -/* -** Because the size depends on the -** #define MAX_SCATTER parameter, -** it is filled in at runtime. -** -** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), -** PADDR (no_data), -** SCR_COPY (sizeof (u_long)), -** KVAR(SCRIPT_KVAR_JIFFIES), -** NADDR (header.stamp.data), -** SCR_MOVE_TBL ^ SCR_DATA_IN, -** offsetof (struct dsb, data[ 0]), -** -** ##===========< i=1; i========= -** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)), -** || PADDR (checkatn), -** || SCR_MOVE_TBL ^ SCR_DATA_IN, -** || offsetof (struct dsb, data[ i]), -** ##========================================== -** -** SCR_CALL, -** PADDR (checkatn), -** SCR_JUMP, -** PADDR (no_data), -*/ -0 }/*-------------------------< DATA_OUT >-------------------*/,{ /* ** Because the size depends on the ** #define MAX_SCATTER parameter, ** it is filled in at runtime. ** -** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)), -** PADDR (no_data), -** SCR_COPY (sizeof (u_long)), -** KVAR(SCRIPT_KVAR_JIFFIES), -** NADDR (header.stamp.data), -** SCR_MOVE_TBL ^ SCR_DATA_OUT, -** offsetof (struct dsb, data[ 0]), -** -** ##===========< i=1; i========= +** ##===========< i=0; i========= ** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)), ** || PADDR (dispatch), ** || SCR_MOVE_TBL ^ SCR_DATA_OUT, @@ -3071,8 +3453,7 @@ ** **--------------------------------------------------------- */ -0 /* was (u_long)&ident ? */ - +0 }/*-------------------------< ABORTTAG >-------------------*/,{ /* ** Abort a bad reselection. @@ -3146,12 +3527,14 @@ **========================================================== */ -void ncr_script_fill (struct script * scr) +__initfunc( +void ncr_script_fill (struct script * scr, struct scripth * scrh) +) { int i; ncrcmd *p; - p = scr->tryloop; + p = scrh->tryloop; for (i=0; itryloop + sizeof (scr->tryloop)); + assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop)); p = scr->data_in; - *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)); - *p++ =PADDR (no_data); - *p++ =SCR_COPY (sizeof (u_long)); - *p++ =KVAR(SCRIPT_KVAR_JIFFIES); - *p++ =NADDR (header.stamp.data); - *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN; - *p++ =offsetof (struct dsb, data[ 0]); - - for (i=1; idata_in + sizeof (scr->data_in)); - p = scr->data_out; - - *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)); - *p++ =PADDR (no_data); - *p++ =SCR_COPY (sizeof (u_long)); - *p++ =KVAR(SCRIPT_KVAR_JIFFIES); - *p++ =NADDR (header.stamp.data); - *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT; - *p++ =offsetof (struct dsb, data[ 0]); + p = scrh->data_out; - for (i=1; idata_out + sizeof (scr->data_out)); + assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out)); } /*========================================================== @@ -3222,19 +3589,17 @@ **========================================================== */ -static void ncr_script_copy_and_bind (struct script *script, ncb_p np) +__initfunc( +static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len) +) { ncrcmd opcode, new, old, tmp1, tmp2; - ncrcmd *src, *dst, *start, *end; + ncrcmd *start, *end; int relocs; - - np->p_script = vtophys(np->script); - - src = script->start; - dst = np->script->start; + int opchanged = 0; start = src; - end = src + (sizeof (struct script) / 4); + end = src + len/4; while (src < end) { @@ -3278,6 +3643,14 @@ ncr_name(np), (int) (src-start-1)); DELAY (1000000); } + /* + ** If PREFETCH feature not enabled, remove + ** the NO FLUSH bit if present. + */ + if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) { + dst[-1] = (opcode & ~SCR_NO_FLUSH); + ++opchanged; + } break; case 0x0: @@ -3321,6 +3694,9 @@ case RELOC_LABEL: new = (old & ~RELOC_MASK) + np->p_script; break; + case RELOC_LABELH: + new = (old & ~RELOC_MASK) + np->p_scripth; + break; case RELOC_SOFTC: new = (old & ~RELOC_MASK) + vtophys(np); break; @@ -3351,6 +3727,9 @@ *dst++ = *src++; }; + if (bootverbose > 1 && opchanged) + printf("%s: NO FLUSH bit removed from %d script instructions\n", + ncr_name(np), opchanged); } /*========================================================== @@ -3362,10 +3741,6 @@ **========================================================== */ -#define MIN_ASYNC_PD 40 -#define MIN_SYNC_PD 20 - - /* ** Linux host data structure ** @@ -3375,25 +3750,526 @@ */ struct host_data { - struct ncb ncb_data; + struct ncb *ncb; + + char ncb_align[NCB_ALIGN_SIZE-1]; /* Filler for alignment */ + struct ncb _ncb_data; + + char ccb_align[CCB_ALIGN_SIZE-1]; /* Filler for alignment */ + struct ccb _ccb_data; + + char scr_align[SCR_ALIGN_SIZE-1]; /* Filler for alignment */ struct script script_data; + + struct scripth scripth_data; }; /* -** Print something which allow to retreive the controler type, unit, +** Print something which allow to retrieve the controler type, unit, ** target, lun concerned by a kernel message. */ #define PRINT_LUN(np, target, lun) \ -printf("%s-: ", ncr_name(np), (int) (target), (int) (lun)) +printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun)) -static inline void PRINT_ADDR(Scsi_Cmnd *cmd) +static void PRINT_ADDR(Scsi_Cmnd *cmd) { struct host_data *host_data = (struct host_data *) cmd->host->hostdata; - ncb_p np = &host_data->ncb_data; + ncb_p np = host_data->ncb; if (np) PRINT_LUN(np, cmd->target, cmd->lun); } +/*========================================================== +** +** NCR chip clock divisor table. +** Divisors are multiplied by 10,000,000 in order to make +** calculations more simple. +** +**========================================================== +*/ + +#define _5M 5000000 +static u_long div_10M[] = + {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M}; + + +/*=============================================================== +** +** Prepare io register values used by ncr_init() according +** to selected and supported features. +** +** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 +** transfers. 32,64,128 are only supported by 875 and 895 chips. +** We use log base 2 (burst length) as internal code, with +** value 0 meaning "burst disabled". +** +**=============================================================== +*/ + +/* + * Burst length from burst code. + */ +#define burst_length(bc) (!(bc))? 0 : 1 << (bc) + +/* + * Burst code from io register bits. + */ +#define burst_code(dmode, ctest4, ctest5) \ + (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1 + +/* + * Set initial io register bits from burst code. + */ +static inline void ncr_init_burst(ncb_p np, u_char bc) +{ + np->rv_ctest4 &= ~0x80; + np->rv_dmode &= ~(0x3 << 6); + np->rv_ctest5 &= ~0x4; + + if (!bc) { + np->rv_ctest4 |= 0x80; + } + else { + --bc; + np->rv_dmode |= ((bc & 0x3) << 6); + np->rv_ctest5 |= (bc & 0x4); + } +} + +#ifdef SCSI_NCR_NVRAM_SUPPORT + +/* +** Get target set-up from Symbios format NVRAM. +*/ + +__initfunc( +static void + ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram) +) +{ + tcb_p tp = &np->target[target]; + Symbios_target *tn = &nvram->target[target]; + + tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255; + tp->usrwide = tn->bus_width == 0x10 ? 1 : 0; + tp->usrtags = + (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0; + + if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE)) + tp->usrflag |= UF_NODISC; + if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)) + tp->usrflag |= UF_NOSCAN; +} + +/* +** Get target set-up from Tekram format NVRAM. +*/ + +__initfunc( +static void + ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram) +) +{ + tcb_p tp = &np->target[target]; + struct Tekram_target *tn = &nvram->target[target]; + int i; + + if (tn->flags & TEKRAM_SYNC_NEGO) { + i = tn->sync_index & 0xf; + tp->usrsync = i < 12 ? Tekram_sync[i] : 255; + } + + tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0; + + if (tn->flags & TEKRAM_TAGGED_COMMANDS) { + tp->usrtags = 2 << nvram->max_tags_index; + if (tp->usrtags > SCSI_NCR_MAX_TAGS) + tp->usrtags = SCSI_NCR_MAX_TAGS; + } + + if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE)) + tp->usrflag = UF_NODISC; + + /* If any device does not support parity, we will not use this option */ + if (!(tn->flags & TEKRAM_PARITY_CHECK)) + np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */ +} +#endif /* SCSI_NCR_NVRAM_SUPPORT */ + +__initfunc( +static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram) +) +{ + u_char burst_max; + u_long period; + int i; + + /* + ** Save assumed BIOS setting + */ + + np->sv_scntl0 = INB(nc_scntl0) & 0x0a; + np->sv_scntl3 = INB(nc_scntl3) & 0x07; + np->sv_dmode = INB(nc_dmode) & 0xce; + np->sv_dcntl = INB(nc_dcntl) & 0xa8; + np->sv_ctest3 = INB(nc_ctest3) & 0x01; + np->sv_ctest4 = INB(nc_ctest4) & 0x80; + np->sv_ctest5 = INB(nc_ctest5) & 0x24; + np->sv_gpcntl = INB(nc_gpcntl); + np->sv_stest2 = INB(nc_stest2) & 0x20; + np->sv_stest4 = INB(nc_stest4); + + /* + ** Wide ? + */ + + np->maxwide = (np->features & FE_WIDE)? 1 : 0; + + /* + ** Get the frequency of the chip's clock. + ** Find the right value for scntl3. + */ + + if (np->features & FE_QUAD) + np->multiplier = 4; + else if (np->features & FE_DBLR) + np->multiplier = 2; + else + np->multiplier = 1; + + np->clock_khz = (np->features & FE_CLK80)? 80000 : 40000; + np->clock_khz *= np->multiplier; + + if (np->clock_khz != 40000) + ncr_getclock(np, np->multiplier); + + /* + * Divisor to be used for async (timer pre-scaler). + */ + i = np->clock_divn - 1; + while (i >= 0) { + --i; + if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) { + ++i; + break; + } + } + np->rv_scntl3 = i+1; + + /* + * Minimum synchronous period factor supported by the chip. + * Btw, 'period' is in tenths of nanoseconds. + */ + + period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz; + if (period <= 250) np->minsync = 10; + else if (period <= 303) np->minsync = 11; + else if (period <= 500) np->minsync = 12; + else np->minsync = (period + 40 - 1) / 40; + + /* + * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2). + */ + + if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2))) + np->minsync = 25; + else if (np->minsync < 12 && !(np->features & FE_ULTRA2)) + np->minsync = 12; + + /* + * Maximum synchronous period factor supported by the chip. + */ + + period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz); + np->maxsync = period > 2540 ? 254 : period / 10; + + /* + ** Get on-board RAM bus address when supported + */ + if (np->features & FE_RAM) { + OUTONB(nc_ctest2, 0x8); + np->paddr2 = INL(nc_scr0); + OUTOFFB(nc_ctest2, 0x8); + } + + /* + ** Prepare initial value of other IO registers + */ +#if defined SCSI_NCR_TRUST_BIOS_SETTING + np->rv_scntl0 = np->sv_scntl0; + np->rv_dmode = np->sv_dmode; + np->rv_dcntl = np->sv_dcntl; + np->rv_ctest3 = np->sv_ctest3; + np->rv_ctest4 = np->sv_ctest4; + np->rv_ctest5 = np->sv_ctest5; + burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5); +#else + + /* + ** Select burst length (dwords) + */ + burst_max = driver_setup.burst_max; + if (burst_max == 255) + burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5); + if (burst_max > 7) + burst_max = 7; + if (burst_max > np->maxburst) + burst_max = np->maxburst; + + /* + ** Select all supported special features + */ + if (np->features & FE_ERL) + np->rv_dmode |= ERL; /* Enable Read Line */ + if (np->features & FE_BOF) + np->rv_dmode |= BOF; /* Burst Opcode Fetch */ + if (np->features & FE_ERMP) + np->rv_dmode |= ERMP; /* Enable Read Multiple */ + if (np->features & FE_PFEN) + np->rv_dcntl |= PFEN; /* Prefetch Enable */ + if (np->features & FE_CLSE) + np->rv_dcntl |= CLSE; /* Cache Line Size Enable */ + if (np->features & FE_WRIE) + np->rv_ctest3 |= WRIE; /* Write and Invalidate */ + if (np->features & FE_DFS) + np->rv_ctest5 |= DFS; /* Dma Fifo Size */ + + /* + ** Select some other + */ + if (driver_setup.master_parity) + np->rv_ctest4 |= MPEE; /* Master parity checking */ + if (driver_setup.scsi_parity) + np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */ + +#ifdef SCSI_NCR_NVRAM_SUPPORT + /* + ** Get parity checking, host ID and verbose mode from NVRAM + **/ + if (nvram) { + switch(nvram->type) { + case SCSI_NCR_TEKRAM_NVRAM: + np->myaddr = nvram->data.Tekram.host_id & 0x0f; + break; + case SCSI_NCR_SYMBIOS_NVRAM: + if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE)) + np->rv_scntl0 &= ~0x0a; + np->myaddr = nvram->data.Symbios.host_id & 0x0f; + if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS) + np->verbose += 1; + break; + } + } +#endif + /* + ** Get SCSI addr of host adapter (set by bios?). + */ + if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07; + if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR; + + +#endif /* SCSI_NCR_TRUST_BIOS_SETTING */ + + /* + * Prepare initial io register bits for burst length + */ + ncr_init_burst(np, burst_max); + + /* + ** Set differential mode and LED support. + ** Ignore these features for boards known to use a + ** specific GPIO wiring (Tekram only for now). + ** Probe initial setting of GPREG and GPCNTL for + ** other ones. + */ + if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) { + switch(driver_setup.diff_support) { + case 3: + if (INB(nc_gpreg) & 0x08) + break; + case 2: + np->rv_stest2 |= 0x20; + break; + case 1: + np->rv_stest2 |= (np->sv_stest2 & 0x20); + break; + default: + break; + } + } + if ((driver_setup.led_pin || + (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) && + !(np->sv_gpcntl & 0x01)) + np->features |= FE_LED0; + + /* + ** Set irq mode. + */ + switch(driver_setup.irqm) { + case 2: + np->rv_dcntl |= IRQM; + break; + case 1: + np->rv_dcntl |= (np->sv_dcntl & IRQM); + break; + default: + break; + } + + /* + ** Configure targets according to driver setup. + ** If NVRAM present get targets setup from NVRAM. + ** Allow to override sync, wide and NOSCAN from + ** boot command line. + */ + for (i = 0 ; i < MAX_TARGET ; i++) { + tcb_p tp = &np->target[i]; + + tp->usrsync = 255; +#ifdef SCSI_NCR_NVRAM_SUPPORT + if (nvram) { + switch(nvram->type) { + case SCSI_NCR_TEKRAM_NVRAM: + ncr_Tekram_setup_target(np, i, &nvram->data.Tekram); + break; + case SCSI_NCR_SYMBIOS_NVRAM: + ncr_Symbios_setup_target(np, i, &nvram->data.Symbios); + break; + } + if (driver_setup.use_nvram & 0x2) + tp->usrsync = driver_setup.default_sync; + if (driver_setup.use_nvram & 0x4) + tp->usrwide = driver_setup.max_wide; + if (driver_setup.use_nvram & 0x8) + tp->usrflag &= ~UF_NOSCAN; + } + else { +#else + if (1) { +#endif + tp->usrsync = driver_setup.default_sync; + tp->usrwide = driver_setup.max_wide; + tp->usrtags = driver_setup.default_tags; + if (!driver_setup.disconnection) + np->target[i].usrflag = UF_NODISC; + } + } + + /* + ** Announce all that stuff to user. + */ + + i = nvram ? nvram->type : 0; + printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np), + i == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " : + (i == SCSI_NCR_TEKRAM_NVRAM ? "Tekram format NVRAM, " : ""), + np->myaddr, + np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10), + (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity", + (np->rv_stest2 & 0x20) ? ", Differential" : ""); + + if (bootverbose > 1) { + printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = " + "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n", + ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl, + np->sv_ctest3, np->sv_ctest4, np->sv_ctest5); + + printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = " + "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n", + ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl, + np->rv_ctest3, np->rv_ctest4, np->rv_ctest5); + } + + if (bootverbose && np->paddr2) + printf (KERN_INFO "%s: on-board RAM at 0x%lx\n", + ncr_name(np), np->paddr2); + + return 0; +} + + +#ifdef SCSI_NCR_DEBUG_NVRAM + +__initfunc( +void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram) +) +{ + int i; + + /* display Symbios nvram host data */ + printf("%s: HOST ID=%d%s%s%s%s\n", + ncr_name(np), nvram->host_id & 0x0f, + (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"", + (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"", + (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERSBOSE" :"", + (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :""); + + /* display Symbios nvram drive data */ + for (i = 0 ; i < 15 ; i++) { + struct Symbios_target *tn = &nvram->target[i]; + printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n", + ncr_name(np), i, + (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "", + (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "", + (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "", + (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "", + tn->bus_width, + tn->sync_period / 4, + tn->timeout); + } +} + +static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120}; + +__initfunc( +void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram) +) +{ + int i, tags, boot_delay; + char *rem; + + /* display Tekram nvram host data */ + tags = 2 << nvram->max_tags_index; + boot_delay = 0; + if (nvram->boot_delay_index < 6) + boot_delay = Tekram_boot_delay[nvram->boot_delay_index]; + switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) { + default: + case 0: rem = ""; break; + case 1: rem = " REMOVABLE=boot device"; break; + case 2: rem = " REMOVABLE=all"; break; + } + + printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n", + ncr_name(np), nvram->host_id & 0x0f, + (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"", + (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"", + (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"", + (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"", + (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"", + (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"", + (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"", + (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"", + rem, boot_delay, tags); + + /* display Tekram nvram drive data */ + for (i = 0; i <= 15; i++) { + int sync, j; + struct Tekram_target *tn = &nvram->target[i]; + j = tn->sync_index & 0xf; + sync = j < 12 ? Tekram_sync[j] : 255; + printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n", + ncr_name(np), i, + (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "", + (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "", + (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "", + (tn->flags & TEKRAM_START_CMD) ? " START" : "", + (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "", + (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "", + sync); + } +} +#endif /* SCSI_NCR_DEBUG_NVRAM */ /* ** Host attach and initialisations. @@ -3401,24 +4277,23 @@ ** Allocate host data and ncb structure. ** Request IO region and remap MMIO region. ** Do chip initialization. -** Try with mmio. -** If mmio not possible (misconfigured cache), -** retry with io mapped. ** If all is OK, install interrupt handling and ** start the timer daemon. */ -static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ushort device_id, - u_char revision_id, int chip, u_int base, u_int io_port, - int irq, int bus, u_char device_fn) - +__initfunc( +static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device) +) { struct host_data *host_data; ncb_p np; struct Scsi_Host *instance = 0; u_long flags = 0; + ncr_nvram *nvram = device->nvram; -printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip, base, io_port, irq); +printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n", + device->chip.name, unit, device->chip.revision_id, device->slot.base, + device->slot.io_port, device->slot.irq); /* ** Allocate host_data structure @@ -3429,16 +4304,36 @@ /* ** Initialize structure. */ - instance->irq = irq; host_data = (struct host_data *) instance->hostdata; - np = &host_data->ncb_data; + /* + ** Align np and first ccb to 32 boundary for cache line + ** bursting when copying the global header. + */ + np = (ncb_p) (((u_long) &host_data->_ncb_data) & NCB_ALIGN_MASK); + host_data->ncb = np; bzero (np, sizeof (*np)); - np->unit = unit; - np->chip = chip; - np->device_id = device_id; - np->revision_id = revision_id; - np->script = &host_data->script_data; + + np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK); + bzero (np->ccb, sizeof (*np->ccb)); + + /* + ** Store input informations in the host data structure. + */ + strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1); + np->unit = unit; + np->verbose = driver_setup.verbose; + sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit); + np->device_id = device->chip.device_id; + np->revision_id = device->chip.revision_id; + np->features = device->chip.features; + np->clock_divn = device->chip.nr_divisor; + np->maxoffs = device->chip.offset_max; + np->maxburst = device->chip.burst_max; + + np->script0 = + (struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK); + np->scripth0 = &host_data->scripth_data; /* ** Initialize timer structure @@ -3453,38 +4348,74 @@ ** virtual and physical memory. */ - np->paddr = base; - np->vaddr = base; + np->paddr = device->slot.base; #ifndef NCR_IOMAPPED - np->reg_remapped = (struct ncr_reg *) remap_pci_mem((u_long) base, (u_long) 128); - if (!np->reg_remapped) { + np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128); + if (!np->vaddr) { printf("%s: can't map memory mapped IO region\n", ncr_name(np)); - np->use_mmio = 0; + goto attach_error; } - printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->reg_remapped); - np->use_mmio = 1; -#endif + else + if (bootverbose > 1) + printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr); + + /* + ** Make the controller's registers available. + ** Now the INB INW INL OUTB OUTW OUTL macros + ** can be used safely. + */ + + np->reg = (struct ncr_reg*) np->vaddr; + +#endif /* !defined NCR_IOMAPPED */ + /* ** Try to map the controller chip into iospace. */ - request_region(io_port, 128, "ncr53c8xx"); - np->port = io_port; + request_region(device->slot.io_port, 128, "ncr53c8xx"); + np->port = device->slot.io_port; + +#ifdef SCSI_NCR_NVRAM_SUPPORT + if (nvram) { + switch(nvram->type) { + case SCSI_NCR_SYMBIOS_NVRAM: +#ifdef SCSI_NCR_DEBUG_NVRAM + ncr_display_Symbios_nvram(np, &nvram->data.Symbios); +#endif + break; + case SCSI_NCR_TEKRAM_NVRAM: +#ifdef SCSI_NCR_DEBUG_NVRAM + ncr_display_Tekram_nvram(np, &nvram->data.Tekram); +#endif + break; + default: + nvram = 0; +#ifdef SCSI_NCR_DEBUG_NVRAM + printf("%s: NVRAM: None or invalid data.\n", ncr_name(np)); +#endif + } + } +#endif /* ** Do chip dependent initialization. */ + (void)ncr_prepare_setting(np, nvram); - switch (device_id) { - case PCI_DEVICE_ID_NCR_53C825: - case PCI_DEVICE_ID_NCR_53C875: - np->maxwide = 1; - break; - default: - np->maxwide = 0; - break; +#ifndef NCR_IOMAPPED + if (np->paddr2 && sizeof(struct script) <= 4096) { + np->vaddr2 = remap_pci_mem((u_long) np->paddr2, (u_long) 4096); + if (!np->vaddr2) { + printf("%s: can't map memory mapped IO region\n", ncr_name(np)); + goto attach_error; + } + else + if (bootverbose > 1) + printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2); } +#endif /* !defined NCR_IOMAPPED */ /* ** Fill Linux host instance structure @@ -3495,9 +4426,10 @@ instance->max_lun = SCSI_NCR_MAX_LUN; #endif #ifndef NCR_IOMAPPED - instance->base = (char *) np->reg_remapped; + instance->base = (char *) np->reg; #endif - instance->io_port = io_port; + instance->irq = device->slot.irq; + instance->io_port = device->slot.io_port; instance->n_io_port = 128; instance->dma_channel = 0; #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0) @@ -3507,58 +4439,39 @@ /* ** Patch script to physical addresses */ - ncr_script_fill (&script0); - ncr_script_copy_and_bind (&script0, np); - np->ccb.p_ccb = vtophys (&np->ccb); + ncr_script_fill (&script0, &scripth0); - /* - ** init data structure - */ - - np->jump_tcb.l_cmd = SCR_JUMP; - np->jump_tcb.l_paddr = NCB_SCRIPT_PHYS (np, abort); - - /* - ** Make the controller's registers available. - ** Now the INB INW INL OUTB OUTW OUTL macros - ** can be used safely. - */ + np->scripth = np->scripth0; + np->p_scripth = vtophys(np->scripth); - np->reg = (struct ncr_reg*) np->vaddr; + np->script = (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0; + np->p_script = (np->vaddr2) ? np->paddr2 : vtophys(np->script0); -#ifndef NCR_IOMAPPED -retry_chip_init: -#endif + ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script)); + ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth)); + np->ccb->p_ccb = vtophys (np->ccb); /* - ** Get SCSI addr of host adapter (set by bios?). + ** Patch the script for LED support. */ - np->myaddr = INB(nc_scid) & 0x07; - if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR; + if (np->features & FE_LED0) { + np->script0->reselect[0] = SCR_REG_REG(gpreg, SCR_OR, 0x01); + np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe); + np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe); + } /* - ** Get the value of the chip's clock. - ** Find the right value for scntl3. + ** init data structure */ - ncr_getclock (np, INB(nc_scntl3)); + np->jump_tcb.l_cmd = SCR_JUMP; + np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort); /* ** Reset chip. */ - OUTW (nc_sien , 0); /* Disable scsi interrupts */ - OUTB (nc_dien , 0); /* Disable dma interrupts */ - - OUTB (nc_istat, SRST); - DELAY (1000); - OUTB (nc_istat, 0 ); - - /* - ** Reset chip, once again. - */ - OUTB (nc_istat, SRST); DELAY (1000); OUTB (nc_istat, 0 ); @@ -3568,14 +4481,6 @@ */ if (ncr_snooptest (np)) { -#ifndef NCR_IOMAPPED - if (np->use_mmio) { -printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n", - ncr_name(np), (u_long) np->port); - np->use_mmio = 0; - goto retry_chip_init; - } -#endif printf ("CACHE INCORRECTLY CONFIGURED.\n"); goto attach_error; }; @@ -3584,55 +4489,50 @@ ** Install the interrupt handler. */ #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70) -# ifdef SCSI_NCR_SHARE_IRQ - printf("%s: requesting shared irq %d (dev_id=0x%lx)\n", - ncr_name(np), irq, (u_long) np); - if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) { -# else - if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) { -# endif +#ifdef SCSI_NCR_SHARE_IRQ + if (bootverbose > 1) + printf("%s: requesting shared irq %d (dev_id=0x%lx)\n", + ncr_name(np), device->slot.irq, (u_long) np); + if (request_irq(device->slot.irq, ncr53c8xx_intr, + SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) { +#else + if (request_irq(device->slot.irq, ncr53c8xx_intr, + SA_INTERRUPT, "ncr53c8xx", NULL)) { +#endif #else - if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) { + if (request_irq(device->slot.irq, ncr53c8xx_intr, + SA_INTERRUPT, "ncr53c8xx")) { #endif - printf("%s: request irq %d failure\n", ncr_name(np), irq); + printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq); goto attach_error; } - np->irq = irq; + np->irq = device->slot.irq; /* ** After SCSI devices have been opened, we cannot ** reset the bus safely, so we do it here. ** Interrupt handler does the real work. - */ - - OUTB (nc_scntl1, CRST); - DELAY (1000); - - /* ** Process the reset exception, ** if interrupts are not enabled yet. ** Then enable disconnects. */ save_flags(flags); cli(); + ncr_start_reset(np, driver_setup.settle_delay); ncr_exception (np); restore_flags(flags); -#ifndef SCSI_NCR_NO_DISCONNECT np->disc = 1; -#endif /* ** The middle-level SCSI driver does not ** wait devices to settle. + ** Wait synchronously if more than 2 seconds. */ -#ifdef SCSI_NCR_SETTLE_TIME -#if SCSI_NCR_SETTLE_TIME > 2 - printf("%s: waiting for scsi devices to settle...\n", ncr_name(np)); -#endif -#if SCSI_NCR_SETTLE_TIME > 0 - DELAY(SCSI_NCR_SETTLE_TIME*1000000); -#endif -#endif + if (driver_setup.settle_delay > 2) { + printf("%s: waiting %d seconds for scsi devices to settle...\n", + ncr_name(np), driver_setup.settle_delay); + DELAY(1000000UL * driver_setup.settle_delay); + } /* ** Now let the generic SCSI driver @@ -3642,8 +4542,8 @@ /* ** start the timeout daemon */ - ncr_timeout (np); np->lasttime=0; + ncr_timeout (np); /* ** use SIMPLE TAG messages by default @@ -3664,14 +4564,25 @@ attach_error: if (!instance) return -1; + printf("%s: detaching...\n", ncr_name(np)); #ifndef NCR_IOMAPPED - if (np->reg_remapped) { - printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128); - unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128); + if (np->vaddr) { +#ifdef DEBUG_NCR53C8XX + printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128); +#endif + unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128); + } + if (np->vaddr2) { +#ifdef DEBUG_NCR53C8XX + printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096); +#endif + unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096); } #endif if (np->port) { +#ifdef DEBUG_NCR53C8XX printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128); +#endif release_region(np->port, 128); } scsi_unregister(instance); @@ -3682,47 +4593,6 @@ /*========================================================== ** ** -** Process pending device interrupts. -** -** -**========================================================== -*/ -int ncr_intr(np) - ncb_p np; -{ - int n = 0; - u_long flags; - - save_flags(flags); cli(); - - if (DEBUG_FLAGS & DEBUG_TINY) printf ("["); - -#ifdef SCSI_NCR_PARANOIA - if (INB(nc_istat) & (INTF|SIP|DIP)) { - /* - ** Repeat until no outstanding ints - */ - do { -#endif - ncr_exception (np); -#ifdef SCSI_NCR_PARANOIA - } while (INB(nc_istat) & (INTF|SIP|DIP)); - - n=1; - np->ticks = 5 * HZ; - }; -#endif - - if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n"); - - restore_flags(flags); - - return (n); -} - -/*========================================================== -** -** ** Start execution of a SCSI command. ** This is called from the generic SCSI driver. ** @@ -3734,7 +4604,7 @@ struct Scsi_Host *host = cmd->host; /* Scsi_Device *device = cmd->device; */ struct host_data *host_data = (struct host_data *) host->hostdata; - ncb_p np = &host_data->ncb_data; + ncb_p np = host_data->ncb; tcb_p tp = &np->target[cmd->target]; ccb_p cp; @@ -3742,7 +4612,7 @@ int segments; u_char qidx, nego, idmsg, *msgptr; - u_long msglen, msglen2; + u_int msglen, msglen2; u_long flags; int xfer_direction; @@ -3753,22 +4623,6 @@ /*--------------------------------------------- ** - ** Reset SCSI bus - ** - ** Interrupt handler does the real work. - ** - **--------------------------------------------- - */ -#if 0 - if (flags & SCSI_RESET) { - OUTB (nc_scntl1, CRST); - DELAY (1000); - return(COMPLETE); - } -#endif - - /*--------------------------------------------- - ** ** Some shortcuts ... ** **--------------------------------------------- @@ -3779,6 +4633,19 @@ return(DID_BAD_TARGET); } + /*--------------------------------------------- + ** + ** Complete the 1st TEST UNIT READY command + ** with error condition if the device is + ** flagged NOSCAN, in order to speed up + ** the boot. + ** + **--------------------------------------------- + */ + if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) { + tp->usrflag &= ~UF_NOSCAN; + return DID_BAD_TARGET; + } if (DEBUG_FLAGS & DEBUG_TINY) { PRINT_ADDR(cmd); @@ -3788,13 +4655,14 @@ /*--------------------------------------------------- ** ** Assign a ccb / bind cmd - ** If no free ccb, insert cmd into the waiting list. + ** If resetting or no free ccb, + ** insert cmd into the waiting list. ** **---------------------------------------------------- */ save_flags(flags); cli(); - if (!(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) { + if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) { insert_into_waiting_list(np, cmd); restore_flags(flags); return(DID_OK); @@ -3803,16 +4671,14 @@ /*--------------------------------------------------- ** - ** Enable tagged queue if asked by user + ** Enable tagged queue if asked by scsi ioctl ** **---------------------------------------------------- */ -#ifdef SCSI_NCR_TAGGED_QUEUE_DISABLED - if (cmd->device && cmd->device->tagged_queue && - (lp = tp->lp[cmd->lun]) && (!lp->usetags)) { + if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) { + tp->usrtags = SCSI_NCR_MAX_TAGS; ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS); } -#endif /*--------------------------------------------------- ** @@ -3820,9 +4686,10 @@ ** **---------------------------------------------------- */ - +#ifdef SCSI_NCR_PROFILE_SUPPORT bzero (&cp->phys.header.stamp, sizeof (struct tstamp)); cp->phys.header.stamp.start = jiffies; +#endif /*---------------------------------------------------- ** @@ -3857,7 +4724,8 @@ nego = 0; - if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) { + if (cmd->lun == 0 && !tp->nego_cp && + (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) { /* ** negotiate wide transfers ? */ @@ -3874,7 +4742,7 @@ */ if (!nego && !tp->period) { - if (SCSI_NCR_MAX_SYNC + if ( 1 #if defined (CDROM_ASYNC) && ((tp->inqdata[0] & 0x1f) != 5) #endif @@ -3887,6 +4755,15 @@ printf ("asynchronous.\n"); }; }; + + /* + ** remember nego is pending for the target. + ** Avoid to start a nego for all queued commands + ** when tagged command queuing is enabled. + */ + + if (nego) + tp->nego_cp = cp; }; /*--------------------------------------------------- @@ -3925,7 +4802,7 @@ idmsg = M_IDENTIFY | cmd->lun; - if ((cp!=&np->ccb) && (np->disc)) + if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag)) idmsg |= 0x40; msgptr = cp->scsi_smsg; @@ -4013,7 +4890,7 @@ segments = ncr_scatter (cp, cp->cmd); if (segments < 0) { - ncr_free_ccb(np, cp); + ncr_free_ccb(np, cp, cmd->target, cmd->lun); restore_flags(flags); return(DID_ERROR); } @@ -4028,10 +4905,12 @@ switch((int) cmd->cmnd[0]) { case 0x08: /* READ(6) 08 */ case 0x28: /* READ(10) 28 */ + case 0xA8: /* READ(12) A8 */ xfer_direction = XferIn; break; case 0x0A: /* WRITE(6) 0A */ case 0x2A: /* WRITE(10) 2A */ + case 0xAA: /* WRITE(12) AA */ xfer_direction = XferOut; break; default: @@ -4046,20 +4925,31 @@ **---------------------------------------------------- */ + cp->segments = segments; + if (!cp->data_len) + xfer_direction = XferNone; + switch (xfer_direction) { + u_long endp; default: + case XferBoth: + cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io); + cp->phys.header.goalp = cp->phys.header.savep; + break; case XferIn: - cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_in); - cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16; - break; + endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16; + cp->phys.header.goalp = endp + 8; + cp->phys.header.savep = endp - segments*16; + break; case XferOut: - cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out); - cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16; - break; + endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16; + cp->phys.header.goalp = endp + 8; + cp->phys.header.savep = endp - segments*16; + break; case XferNone: - cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data); - cp->phys.header.goalp = cp->phys.header.savep; - break; + cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data); + cp->phys.header.goalp = cp->phys.header.savep; + break; } cp->phys.header.lastp = cp->phys.header.savep; @@ -4161,12 +5051,15 @@ printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np), np->squeueput, (unsigned)(np->script->startpos[0]- - (NCB_SCRIPT_PHYS (np, tryloop)))); + (NCB_SCRIPTH_PHYS (np, tryloop)))); /* ** Script processor may be waiting for reselect. ** Wake it up. */ +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if (!np->stalling) +#endif OUTB (nc_istat, SIGP); /* @@ -4184,28 +5077,112 @@ /*========================================================== ** ** +** Start reset process. +** If reset in progress do nothing. +** The interrupt handler will reinitialize the chip. +** The timeout handler will wait for settle_time before +** clearing it and so resuming command processing. +** +** +**========================================================== +*/ +static void ncr_start_reset(ncb_p np, int settle_delay) +{ + u_long flags; + + save_flags(flags); cli(); + + if (!np->settle_time) { + if (bootverbose > 1) + printf("%s: resetting, command processing suspended for %d seconds\n", + ncr_name(np), settle_delay); + np->settle_time = jiffies + settle_delay * HZ; + OUTB (nc_istat, SRST); + DELAY (1000); + OUTB (nc_istat, 0); + OUTW (nc_sien, RST); + OUTB (nc_scntl1, CRST); + DELAY (100); + } + + restore_flags(flags); +} + +/*========================================================== +** +** ** Reset the SCSI BUS. ** This is called from the generic SCSI driver. ** ** **========================================================== */ -int ncr_reset_bus (Scsi_Cmnd *cmd) +int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset) { struct Scsi_Host *host = cmd->host; /* Scsi_Device *device = cmd->device; */ struct host_data *host_data = (struct host_data *) host->hostdata; - ncb_p np = &host_data->ncb_data; + ncb_p np = host_data->ncb; + ccb_p cp; u_long flags; + int found; - save_flags(flags); cli(); +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if (np->stalling) + np->stalling = 0; +#endif + save_flags(flags); cli(); +/* + * Return immediately if reset is in progress. + */ + if (np->settle_time) { + restore_flags(flags); + return SCSI_RESET_PUNT; + } +/* + * Start the reset process. + * The script processor is then assumed to be stopped. + * Commands will now be queued in the waiting list until a settle + * delay of 2 seconds will be completed. + */ + ncr_start_reset(np, 2); +/* + * First, look in the wakeup list + */ + for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) { + /* + ** look for the ccb of this command. + */ + if (cp->host_status == HS_IDLE) continue; + if (cp->cmd == cmd) { + found = 1; + break; + } + } +/* + * Then, look in the waiting list + */ + if (!found && retrieve_from_waiting_list(0, np, cmd)) + found = 1; +/* + * Wake-up all awaiting commands with DID_RESET. + */ reset_waiting_list(np); - ncr_init(np, "scsi bus reset", HS_RESET); - -#ifndef SCSI_NCR_NO_DISCONNECT - np->disc = 1; -#endif +/* + * Wake-up all pending commands with HS_RESET -> DID_RESET. + */ + ncr_wakeup(np, HS_RESET); +/* + * If the involved command was not in a driver queue, and the + * scsi driver told us reset is synchronous, and the command is not + * currently in the waiting list, complete it with DID_RESET status, + * in order to keep it alive. + */ + if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) { + cmd->result = ScsiResult(DID_RESET, 0); + cmd->scsi_done(cmd); + } restore_flags(flags); @@ -4221,17 +5198,22 @@ ** **========================================================== */ -int ncr_abort_command (Scsi_Cmnd *cmd) +static int ncr_abort_command (Scsi_Cmnd *cmd) { struct Scsi_Host *host = cmd->host; /* Scsi_Device *device = cmd->device; */ struct host_data *host_data = (struct host_data *) host->hostdata; - ncb_p np = &host_data->ncb_data; + ncb_p np = host_data->ncb; ccb_p cp; u_long flags; int found; int retv; +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if (np->stalling == 2) + np->stalling = 0; +#endif + save_flags(flags); cli(); /* * First, look for the scsi command in the waiting list @@ -4246,7 +5228,7 @@ /* * Then, look in the wakeup list */ - for (found=0, cp=&np->ccb; cp; cp=cp->link_ccb) { + for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) { /* ** look for the ccb of this command. */ @@ -4256,14 +5238,23 @@ break; } } + if (!found) { restore_flags(flags); return SCSI_ABORT_NOT_RUNNING; } + if (np->settle_time) { + restore_flags(flags); + return SCSI_ABORT_SNOOZE; + } + /* ** Disable reselect. ** Remove it from startqueue. + ** Set cp->tlimit to 0. The ncr_timeout() handler will use + ** this condition in order to complete the canceled command + ** after the script skipped the ccb, if necessary. */ cp->jump_ccb.l_cmd = (SCR_JUMP); if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) { @@ -4271,35 +5262,18 @@ cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip); } - switch (cp->host_status) { - case HS_BUSY: - case HS_NEGOTIATE: - /* - ** still in start queue ? - */ - if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, skip)) { - retv = SCSI_ABORT_BUSY; - break; - } - /* fall through */ - case HS_DISCONNECT: - cp->host_status=HS_ABORTED; - cp->tag = 0; - /* - ** wakeup this ccb. - */ - ncr_complete (np, cp); - retv = SCSI_ABORT_SUCCESS; - break; - default: - cp->tag = 0; - /* - ** wakeup this ccb. - */ - ncr_complete (np, cp); - retv = SCSI_ABORT_SUCCESS; - break; - } + cp->tlimit = 0; + retv = SCSI_ABORT_PENDING; + + /* + ** If there are no requests, the script + ** processor will sleep on SEL_WAIT_RESEL. + ** Let's wake it up, since it may have to work. + */ +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if (!np->stalling) +#endif + OUTB (nc_istat, SIGP); restore_flags(flags); @@ -4325,7 +5299,6 @@ lcb_p lp; int target, lun; int i; - u_char scntl3; printf("%s: releasing host resources\n", ncr_name(np)); @@ -4334,7 +5307,7 @@ ** Set release_stage to 1 and wait that ncr_timeout() set it to 2. */ -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printf("%s: stopping the timer\n", ncr_name(np)); #endif np->release_stage = 1; @@ -4347,7 +5320,7 @@ ** Disable chip interrupts */ -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printf("%s: disabling chip interrupts\n", ncr_name(np)); #endif OUTW (nc_sien , 0); @@ -4357,7 +5330,7 @@ ** Free irq */ -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printf("%s: freeing irq %d\n", ncr_name(np), irq); #endif #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70) @@ -4372,28 +5345,40 @@ /* ** Reset NCR chip - ** Preserve scntl3 for automatic clock detection. + ** Restore bios setting for automatic clock detection. */ printf("%s: resetting chip\n", ncr_name(np)); - scntl3 = INB (nc_scntl3); OUTB (nc_istat, SRST); DELAY (1000); OUTB (nc_istat, 0 ); - OUTB (nc_scntl3, scntl3); + + OUTB(nc_dmode, np->sv_dmode); + OUTB(nc_dcntl, np->sv_dcntl); + OUTB(nc_ctest3, np->sv_ctest3); + OUTB(nc_ctest4, np->sv_ctest4); + OUTB(nc_ctest5, np->sv_ctest5); + OUTB(nc_gpcntl, np->sv_gpcntl); + OUTB(nc_stest2, np->sv_stest2); + + ncr_selectclock(np, np->sv_scntl3); /* ** Release Memory mapped IO region and IO mapped region */ #ifndef NCR_IOMAPPED -#ifdef DEBUG - printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128); +#ifdef DEBUG_NCR53C8XX + printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128); +#endif + unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128); +#ifdef DEBUG_NCR53C8XX + printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096); #endif - unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128); + unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096); #endif -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128); #endif release_region(np->port, 128); @@ -4402,13 +5387,13 @@ ** Free allocated ccb(s) */ - while ((cp=np->ccb.link_ccb) != NULL) { - np->ccb.link_ccb = cp->link_ccb; + while ((cp=np->ccb->link_ccb) != NULL) { + np->ccb->link_ccb = cp->link_ccb; if (cp->host_status) { printf("%s: shall free an active ccb (host_status=%d)\n", ncr_name(np), cp->host_status); } -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printf("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp); #endif m_free(cp, sizeof(*cp)); @@ -4423,7 +5408,7 @@ for (lun = 0 ; lun < MAX_LUN ; lun++) { lp = tp->lp[lun]; if (lp) { -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printf("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp); #endif m_free(lp, sizeof(*lp)); @@ -4476,7 +5461,7 @@ ** timestamp ** Optional, spare some CPU time */ -#ifdef SCSI_NCR_PROFILE +#ifdef SCSI_NCR_PROFILE_SUPPORT ncb_profile (np, cp); #endif @@ -4487,6 +5472,16 @@ cmd = cp->cmd; cp->cmd = NULL; tp = &np->target[cmd->target]; + lp = tp->lp[cmd->lun]; + + /* + ** We donnot queue more than 1 ccb per target + ** with negotiation at any time. If this ccb was + ** used for negotiation, clear this info in the tcb. + */ + + if (cp == tp->nego_cp) + tp->nego_cp = 0; /* ** Check for parity errors. @@ -4555,11 +5550,11 @@ */ if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) { if (np->unit < SCSI_NCR_MAX_HOST) { -#ifdef SCSI_NCR_FORCE_SYNC_NEGO - ((char *) cmd->request_buffer)[7] |= INQ7_SYNC; -#endif - ((char *) cmd->request_buffer)[7] &= - (target_capabilities[np->unit].and_map[cmd->target]); + if (driver_setup.force_sync_nego) + ((char *) cmd->request_buffer)[7] |= INQ7_SYNC; + else + ((char *) cmd->request_buffer)[7] &= + (target_capabilities[np->unit].and_map[cmd->target]); } bcopy ( cmd->request_buffer, &tp->inqdata, @@ -4568,12 +5563,7 @@ /* ** set number of tags */ - lp = tp->lp[cmd->lun]; -#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED - if (lp && !lp->usetags) { - ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS); - } -#endif + ncr_setmaxtags (np, tp, driver_setup.default_tags); /* ** prepare negotiation of synch and wide. */ @@ -4585,8 +5575,33 @@ tp->quirks |= QUIRK_UPDATE; } + /* + ** Announce changes to the generic driver. + */ + if (lp) { + ncr_settags (tp, lp); + if (lp->reqlink != lp->actlink) + ncr_opennings (np, lp, cmd); + }; + tp->bytes += cp->data_len; tp->transfers ++; + + /* + ** If tags was reduced due to queue full, + ** increase tags if 100 good status received. + */ + if (tp->numtags < tp->maxtags) { + ++tp->num_good; + if (tp->num_good >= 100) { + tp->num_good = 0; + ++tp->numtags; + if (tp->numtags == 1) { + PRINT_ADDR(cmd); + printf("tagged command queueing resumed\n"); + } + } + } } else if ((cp->host_status == HS_COMPLETE) && (cp->scsi_status == (S_SENSE|S_GOOD) || cp->scsi_status == (S_SENSE|S_CHECK_COND))) { @@ -4612,6 +5627,29 @@ */ cmd->result = ScsiResult(DID_OK, cp->scsi_status); + } else if ((cp->host_status == HS_COMPLETE) + && (cp->scsi_status == S_QUEUE_FULL)) { + + /* + ** Target is stuffed. + */ + cmd->result = ScsiResult(DID_OK, cp->scsi_status); + + /* + ** Suspend tagged queuing and start good status counter. + ** Announce changes to the generic driver. + */ + if (tp->numtags) { + PRINT_ADDR(cmd); + printf("QUEUE FULL! suspending tagged command queueing\n"); + tp->numtags = 0; + tp->num_good = 0; + if (lp) { + ncr_settags (tp, lp); + if (lp->reqlink != lp->actlink) + ncr_opennings (np, lp, cmd); + }; + } } else if ((cp->host_status == HS_SEL_TIMEOUT) || (cp->host_status == HS_TIMEOUT)) { @@ -4680,7 +5718,7 @@ /* ** Free this ccb */ - ncr_free_ccb (np, cp); + ncr_free_ccb (np, cp, cmd->target, cmd->lun); /* ** requeue awaiting scsi commands @@ -4713,7 +5751,7 @@ ** complete all jobs that are not IDLE. */ - ccb_p cp = &np->ccb; + ccb_p cp = np->ccb; while (cp) { switch (cp->host_status) { @@ -4751,11 +5789,6 @@ void ncr_init (ncb_p np, char * msg, u_long code) { int i; - u_long usrsync; - u_char usrwide; -#if 0 - u_char burstlen; -#endif /* ** Reset chip. @@ -4768,7 +5801,7 @@ ** Message. */ - if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg); + if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg); /* ** Clear Start Queue @@ -4779,10 +5812,9 @@ /* ** Start at first entry. */ - np->squeueput = 0; - np->script->startpos[0] = NCB_SCRIPT_PHYS (np, tryloop); - np->script->start0 [0] = SCR_INT ^ IFFALSE (0); + np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop); + np->script0->start0 [0] = SCR_INT ^ IFFALSE (0); /* ** Wakeup all pending jobs. @@ -4790,122 +5822,73 @@ ncr_wakeup (np, code); /* - ** Remove Reset, abort ... - */ - OUTB (nc_istat, 0 ); - - /* ** Init chip. */ -/** NCR53C810 **/ - if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) { - OUTB(nc_dmode, 0x80); /* Set 8-transfer burst */ - } - else -/** NCR53C815 **/ - if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) { - OUTB(nc_dmode, 0x80); /* Set 8-transfer burst */ - } - else -/** NCR53C825 **/ - if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) { - OUTB(nc_dmode, 0x80); /* Set 8-transfer burst */ - } - else -/** NCR53C810A or NCR53C860 **/ - if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) || - ChipDevice == PCI_DEVICE_ID_NCR_53C860) { - OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */ -#if 0 - OUTB(nc_ctest3, 0x01); /* Set write and invalidate */ - OUTB(nc_dcntl, 0xa1); /* Cache line size enable, */ - /* pre-fetch enable and 700 comp */ -#endif - } - else -/** NCR53C825A or NCR53C875 **/ - if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) || - ChipDevice == PCI_DEVICE_ID_NCR_53C875) { - OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */ -#if 0 - OUTB(nc_ctest5, 0x04); /* Set DMA FIFO to 88 */ - OUTB(nc_ctest5, 0x24); /* Set DMA FIFO to 536 */ - OUTB(nc_dmode, 0x40); /* Set 64-transfer burst */ - OUTB(nc_ctest3, 0x01); /* Set write and invalidate */ - OUTB(nc_dcntl, 0x81); /* Cache line size enable and 700 comp*/ -#endif - } -/** OTHERS **/ - else { - OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */ - } -#if 0 - burstlen = 0xc0; -#endif - -#ifdef SCSI_NCR_DISABLE_PARITY_CHECK - OUTB (nc_scntl0, 0xc0 ); /* full arb., (no parity) */ -#else - OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */ -#endif - OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */ - OUTB (nc_scntl3, np->rv_scntl3);/* timing prescaler */ - OUTB (nc_scid , RRE|np->myaddr);/* host adapter SCSI address */ - OUTW (nc_respid, 1ul<myaddr);/* id to respond to */ - OUTB (nc_istat , SIGP ); /* Signal Process */ -#if 0 - OUTB (nc_dmode , burstlen); /* Burst length = 2 .. 16 transfers */ -#endif - OUTB (nc_dcntl , NOCOM ); /* no single step mode, protect SFBR*/ + OUTB (nc_istat, 0x00 ); /* Remove Reset, abort */ + OUTB (nc_scntl0, np->rv_scntl0 | 0xc0); + /* full arb., ena parity, par->ATN */ + OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */ + + ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */ + + OUTB (nc_scid , RRE|np->myaddr); /* Adapter SCSI address */ + OUTW (nc_respid, 1ul<myaddr); /* Id to respond to */ + OUTB (nc_istat , SIGP ); /* Signal Process */ + OUTB (nc_dmode , np->rv_dmode); /* Burst length, dma mode */ + OUTB (nc_ctest5, np->rv_ctest5); /* Large fifo + large burst */ + + OUTB (nc_dcntl , NOCOM|np->rv_dcntl); /* Protect SFBR */ + OUTB (nc_ctest3, np->rv_ctest3); /* Write and invalidate */ + OUTB (nc_ctest4, np->rv_ctest4); /* Master parity checking */ + + OUTB (nc_stest2, EXT|np->rv_stest2); /* Extended Sreq/Sack filtering */ + OUTB (nc_stest3, TE); /* TolerANT enable */ + OUTB (nc_stime0, 0x0d ); /* HTH disabled STO 0.4 sec. */ -#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK - OUTB (nc_ctest4, 0x00 ); /* disable master parity checking */ -#else - OUTB (nc_ctest4, 0x08 ); /* enable master parity checking */ -#endif + /* + ** Disable disconnects. + */ - OUTB (nc_stest2, EXT ); /* Extended Sreq/Sack filtering */ - OUTB (nc_stest3, TE ); /* TolerANT enable */ - OUTB (nc_stime0, 0x0d ); /* HTH = disable STO = 0.4 sec. */ - /* 0.25 sec recommended for scsi 1 */ + np->disc = 0; /* - ** Reinitialize usrsync. - ** Have to renegotiate synch mode. + ** Enable GPIO0 pin for writing if LED support. */ - usrsync = 255; + if (np->features & FE_LED0) { + OUTOFFB (nc_gpcntl, 0x01); + } -#ifndef SCSI_NCR_FORCE_ASYNCHRONOUS - if (SCSI_NCR_MAX_SYNC) { - u_long period; - period =1000000/SCSI_NCR_MAX_SYNC; /* ns = 10e6 / kHz */ - if (period <= 11 * np->ns_sync) { - if (period < 4 * np->ns_sync) - usrsync = np->ns_sync; - else - usrsync = period / 4; - }; - }; -#endif + /* + ** Upload the script into on-board RAM + */ + if (np->vaddr2) { + if (bootverbose) + printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np)); + bcopy(np->script0, np->script, sizeof(struct script)); + } /* - ** Reinitialize usrwide. - ** Have to renegotiate wide mode. + ** enable ints */ - usrwide = (SCSI_NCR_MAX_WIDE); - if (usrwide > np->maxwide) usrwide=np->maxwide; + OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST); + OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID); /* - ** Disable disconnects. + ** For 895/6 enable SBMC interrupt and save current SCSI bus mode. */ - - np->disc = 0; + if (np->features & FE_ULTRA2) { + OUTONW (nc_sien, SBMC); + np->scsi_mode = INB (nc_stest4) & SMODE; + } /* ** Fill in target structure. + ** Reinitialize usrsync. + ** Reinitialize usrwide. + ** Prepare sync negotiation according to actual SCSI bus mode. */ for (i=0;isval = 0; tp->wval = np->rv_scntl3; - tp->usrsync = usrsync; - tp->usrwide = usrwide; + if (tp->usrsync != 255) { + if (tp->usrsync <= np->maxsync) { + if (tp->usrsync < np->minsync) { + tp->usrsync = np->minsync; + } + } + else + tp->usrsync = 255; + }; + + if (tp->usrwide > np->maxwide) + tp->usrwide = np->maxwide; ncr_negotiate (np, tp); } /* - ** enable ints - */ - - OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST); - OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID); - - /* ** Start script processor. */ @@ -4950,7 +5936,13 @@ u_long minsync = tp->usrsync; - if (minsync < 25) minsync=25; + /* + ** SCSI bus mode limit + */ + + if (np->scsi_mode && np->scsi_mode == SMODE_SE) { + if (minsync < 12) minsync = 12; + } /* ** if not scsi 2 @@ -4964,18 +5956,18 @@ ** our limit .. */ - if (minsync < np->ns_sync) - minsync = np->ns_sync; + if (minsync < np->minsync) + minsync = np->minsync; /* ** divider limit */ - if (minsync > (np->ns_sync * 11) / 4) + if (minsync > np->maxsync) minsync = 255; tp->minsync = minsync; - tp->maxoffs = (minsync<255 ? 8 : 0); + tp->maxoffs = (minsync<255 ? np->maxoffs : 0); /* ** period=0: has to negotiate sync transfer @@ -4991,16 +5983,120 @@ /*========================================================== ** +** Get clock factor and sync divisor for a given +** synchronous factor period. +** Returns the clock factor (in sxfer) and scntl3 +** synchronous divisor field. +** +**========================================================== +*/ + +static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p) +{ + u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */ + int div = np->clock_divn; /* Number of divisors supported */ + u_long fak; /* Sync factor in sxfer */ + u_long per; /* Period in tenths of ns */ + u_long kpc; /* (per * clk) */ + + /* + ** Compute the synchronous period in tenths of nano-seconds + */ + if (sfac <= 10) per = 250; + else if (sfac == 11) per = 303; + else if (sfac == 12) per = 500; + else per = 40 * sfac; + + /* + ** Look for the greatest clock divisor that allows an + ** input speed faster than the period. + */ + kpc = per * clk; + while (--div >= 0) + if (kpc >= (div_10M[div] << 2)) break; + + /* + ** Calculate the lowest clock factor that allows an output + ** speed not faster than the period. + */ + fak = (kpc - 1) / div_10M[div] + 1; + +#if 0 /* This optimization does not seem very usefull */ + + per = (fak * div_10M[div]) / clk; + + /* + ** Why not to try the immediate lower divisor and to choose + ** the one that allows the fastest output speed ? + ** We dont want input speed too much greater than output speed. + */ + if (div >= 1 && fak < 8) { + u_long fak2, per2; + fak2 = (kpc - 1) / div_10M[div-1] + 1; + per2 = (fak2 * div_10M[div-1]) / clk; + if (per2 < per && fak2 <= 8) { + fak = fak2; + per = per2; + --div; + } + } +#endif + + if (fak < 4) fak = 4; /* Should never happen, too bad ... */ + + /* + ** Compute and return sync parameters for the ncr + */ + *fakp = fak - 4; + *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0); +} + + +/*========================================================== +** +** Set actual values, sync status and patch all ccbs of +** a target according to new sync/wide agreement. +** +**========================================================== +*/ + +static void ncr_set_sync_wide_status (ncb_p np, u_char target) +{ + ccb_p cp; + tcb_p tp = &np->target[target]; + + /* + ** set actual value and sync_status + */ + OUTB (nc_sxfer, tp->sval); + np->sync_st = tp->sval; + OUTB (nc_scntl3, tp->wval); + np->wide_st = tp->wval; + + /* + ** patch ALL ccbs of this target. + */ + for (cp = np->ccb; cp; cp = cp->link_ccb) { + if (!cp->cmd) continue; + if (cp->cmd->target != target) continue; + cp->sync_status = tp->sval; + cp->wide_status = tp->wval; + }; +} + +/*========================================================== +** ** Switch sync mode for current job and it's target ** **========================================================== */ -static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer) +static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer) { Scsi_Cmnd *cmd; tcb_p tp; - u_char target = INB (nc_ctest0)&7; + u_char target = INB (nc_ctest0) & 0x0f; + u_char idiv; assert (cp); if (!cp) return; @@ -5011,57 +6107,81 @@ assert (target == (cmd->target & 0xf)); tp = &np->target[target]; - tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff; - if (tp->sval == sxfer) return; + if (!scntl3 || !(sxfer & 0x1f)) + scntl3 = np->rv_scntl3; + scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07); + + /* + ** Deduce the value of controller sync period from scntl3. + ** period is in tenths of nano-seconds. + */ + + idiv = ((scntl3 >> 4) & 0x7); + if ((sxfer & 0x1f) && idiv) + tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz; + else + tp->period = 0xffff; + + /* + ** Stop there if sync parameters are unchanged + */ + if (tp->sval == sxfer && tp->wval == scntl3) return; tp->sval = sxfer; + tp->wval = scntl3; /* ** Bells and whistles ;-) */ PRINT_ADDR(cmd); - if (sxfer & 0x0f) { + if (sxfer & 0x01f) { + unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0); + unsigned mb10 = (f10 + tp->period/2) / tp->period; + char *scsi; + /* ** Disable extended Sreq/Sack filtering */ - if (tp->period <= 200) OUTB (nc_stest2, 0); + if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT); - printf ("%s%dns (%d Mb/sec) offset %d.\n", - tp->period<200 ? "FAST SCSI-2 ":"", - tp->period, - (((tp->wval & EWS)? 2:1)*1000+tp->period/2)/tp->period, - sxfer & 0x0f); - } else printf ("asynchronous.\n"); + /* + ** Bells and whistles ;-) + */ + if (tp->period < 500) scsi = "FAST-40"; + else if (tp->period < 1000) scsi = "FAST-20"; + else if (tp->period < 2000) scsi = "FAST-10"; + else scsi = "SLOW"; - /* - ** set actual value and sync_status - */ - OUTB (nc_sxfer, sxfer); - np->sync_st = sxfer; + printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi, + tp->widedone > 1 ? "WIDE " : "", + mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f); + } else + printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : ""); /* + ** set actual value and sync_status ** patch ALL ccbs of this target. */ - for (cp = &np->ccb; cp; cp = cp->link_ccb) { - if (!cp->cmd) continue; - if (cp->cmd->target != target) continue; - cp->sync_status = sxfer; - }; + ncr_set_sync_wide_status(np, target); } /*========================================================== ** ** Switch wide mode for current job and it's target +** SCSI specs say: a SCSI device that accepts a WDTR +** message shall reset the synchronous agreement to +** asynchronous mode. ** **========================================================== */ -static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide) +static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack) { Scsi_Cmnd *cmd; - u_short target = INB (nc_ctest0)&7; + u_short target = INB (nc_ctest0) & 0x0f; tcb_p tp; - u_char scntl3 = np->rv_scntl3 | (wide ? EWS : 0); + u_char scntl3; + u_char sxfer; assert (cp); if (!cp) return; @@ -5073,32 +6193,33 @@ tp = &np->target[target]; tp->widedone = wide+1; - if (tp->wval == scntl3) return; - tp->wval = scntl3; + scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0); + + sxfer = ack ? 0 : tp->sval; /* - ** Bells and whistles ;-) + ** Stop there if sync/wide parameters are unchanged */ - PRINT_ADDR(cmd); - if (scntl3 & EWS) - printf ("WIDE SCSI (16 bit) enabled.\n"); - else - printf ("WIDE SCSI disabled.\n"); + if (tp->sval == sxfer && tp->wval == scntl3) return; + tp->sval = sxfer; + tp->wval = scntl3; /* - ** set actual value and sync_status + ** Bells and whistles ;-) */ - OUTB (nc_scntl3, scntl3); - np->wide_st = scntl3; + if (bootverbose >= 2) { + PRINT_ADDR(cmd); + if (scntl3 & EWS) + printf ("WIDE SCSI (16 bit) enabled.\n"); + else + printf ("WIDE SCSI disabled.\n"); + } /* + ** set actual value and sync_status ** patch ALL ccbs of this target. */ - for (cp = &np->ccb; cp; cp = cp->link_ccb) { - if (!cp->cmd) continue; - if (cp->cmd->target != target) continue; - cp->wide_status = scntl3; - }; + ncr_set_sync_wide_status(np, target); } /*========================================================== @@ -5108,19 +6229,32 @@ **========================================================== */ -static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags) +static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags) { int l; - tp->usrtags = usrtags; + if (numtags > tp->usrtags) + numtags = tp->usrtags; + tp->numtags = numtags; + tp->maxtags = numtags; + for (l=0; llp[l]; if (!lp) continue; + + wastags = lp->usetags; ncr_settags (tp, lp); - if (lp->usetags > 0) { + + if (numtags > 1 && lp->reqccbs > 1) { + PRINT_LUN(np, tp - np->target, l); + printf("using tagged command queueing, up to %ld cmds/lun\n", numtags); + } + else if (numtags <= 1 && wastags) { PRINT_LUN(np, tp - np->target, l); - printf("using tagged command queueing, up to %d cmds/lun\n", lp->usetags); + printf("disabling tagged command queueing\n"); } }; } @@ -5139,8 +6273,8 @@ */ if (( tp->inqdata[2] & 0x7) >= 2 && ( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00) - && tp->usrtags) { - reqtags = tp->usrtags; + && tp->numtags > 1) { + reqtags = tp->numtags; if (lp->actlink <= 1) lp->usetags=reqtags; } else { @@ -5171,7 +6305,7 @@ **---------------------------------------------------- */ -#ifdef SCSI_NCR_USER_COMMAND +#ifdef SCSI_NCR_USER_COMMAND_SUPPORT static void ncr_usercmd (ncb_p np) { @@ -5196,13 +6330,13 @@ np->user.data = SCSI_NCR_MAX_TAGS; for (t=0; tuser.target>>t)&1)) continue; + np->target[t].usrtags = np->user.data; ncr_setmaxtags (np, &np->target[t], np->user.data); }; - np->disc = 1; break; case UC_SETDEBUG: -#ifdef SCSI_NCR_DEBUG +#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT ncr_debug = np->user.data; #endif break; @@ -5234,12 +6368,106 @@ case UC_CLEARPROF: bzero(&np->profile, sizeof(np->profile)); break; +#ifdef UC_DEBUG_ERROR_RECOVERY + case UC_DEBUG_ERROR_RECOVERY: + np->debug_error_recovery = np->user.data; + break; +#endif } np->user.cmd=0; } #endif +/*===================================================================== +** +** Embedded error recovery debugging code. +** +**===================================================================== +** +** This code is conditionned by SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT. +** It only can be enabled after boot-up with a control command. +** +** Every 30 seconds the timer handler of the driver decides to +** change the behaviour of the driver in order to trigger errors. +** +** If last command was "debug_error_recovery sge", the driver +** sets sync offset of all targets that use sync transfers to 2, +** and so hopes a SCSI gross error at the next read operation. +** +** If last command was "debug_error_recovery abort", the driver +** does not signal new scsi commands to the script processor, until +** it is asked to abort or reset a command by the mid-level driver. +** +** If last command was "debug_error_recovery reset", the driver +** does not signal new scsi commands to the script processor, until +** it is asked to reset a command by the mid-level driver. +** +** If last command was "debug_error_recovery parity", the driver +** will assert ATN on the next DATA IN phase mismatch, and so will +** behave as if a parity error had been detected. +** +** The command "debug_error_recovery none" makes the driver behave +** normaly. +** +**===================================================================== +*/ + +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT +static void ncr_trigger_errors (ncb_p np) +{ + /* + ** If np->debug_error_recovery is not zero, we want to + ** simulate common errors in order to test error recovery. + */ + do { + static u_long last = 0l; + + if (!np->debug_error_recovery) + break; + if (!last) + last = jiffies; + else if (jiffies < last + 30*HZ) + break; + last = jiffies; + /* + * This one triggers SCSI gross errors. + */ + if (np->debug_error_recovery == 1) { + int i; + printf("%s: testing error recovery from SCSI gross error...\n", ncr_name(np)); + for (i = 0 ; i < MAX_TARGET ; i++) { + if (np->target[i].sval & 0x1f) { + np->target[i].sval &= ~0x1f; + np->target[i].sval += 2; + } + } + } + /* + * This one triggers abort from the mid-level driver. + */ + else if (np->debug_error_recovery == 2) { + printf("%s: testing error recovery from mid-level driver abort()...\n", ncr_name(np)); + np->stalling = 2; + } + /* + * This one triggers reset from the mid-level driver. + */ + else if (np->debug_error_recovery == 3) { + printf("%s: testing error recovery from mid-level driver reset()...\n", ncr_name(np)); + np->stalling = 3; + } + /* + * This one set ATN on phase mismatch in DATA IN phase and so + * will behave as on scsi parity error detected. + */ + else if (np->debug_error_recovery == 4) { + printf("%s: testing data in parity error...\n", ncr_name(np)); + np->assert_atn = 1; + } + } while (0); +} +#endif /*========================================================== ** @@ -5267,7 +6495,7 @@ ** If release process in progress, let's go ** Set the release stage from 1 to 2 to synchronize ** with the release process. - **/ + */ if (np->release_stage) { if (np->release_stage == 1) np->release_stage = 2; @@ -5282,7 +6510,33 @@ add_timer(&np->timer); - if (np->lasttime + HZ < thistime) { +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + ncr_trigger_errors (np); +#endif + + /* + ** If we are resetting the ncr, wait for settle_time before + ** clearing it. Then command processing will be resumed. + */ + if (np->settle_time) { + if (np->settle_time <= thistime) { + if (bootverbose > 1) + printf("%s: command processing resumed\n", ncr_name(np)); + save_flags(flags); cli(); + np->settle_time = 0; + np->disc = 1; + requeue_waiting_list(np); + restore_flags(flags); + } + return; + } + + /* + ** Since the generic scsi driver only allows us 0.5 second + ** to perform abort of a command, we must look at ccbs about + ** every 0.25 second. + */ + if (np->lasttime + (HZ>>2) <= thistime) { /* ** block ncr interrupts */ @@ -5311,44 +6565,15 @@ t = (thistime - np->heartbeat) / HZ; if (t<2) np->latetime=0; else np->latetime++; - if (np->latetime>5) { - /* - ** If there are no requests, the script - ** processor will sleep on SEL_WAIT_RESEL. - ** But we have to check whether it died. - ** Let's wake it up. - */ - OUTB (nc_istat, SIGP); - } - if (np->latetime>10) { - /* - ** Although we tried to wake it up, - ** the script processor didn't respond. - ** - ** May be a target is hanging, - ** or another initator lets a tape device - ** rewind with disconnect disabled :-( - ** - ** We won't accept that. - */ - if (INB (nc_sbcl) & CBSY) - OUTB (nc_scntl1, CRST); - DELAY (1000); - ncr_init (np, "ncr dead ?", HS_TIMEOUT); -#ifndef SCSI_NCR_NO_DISCONNECT - np->disc = 1; -#endif - np->heartbeat = thistime; - } /*---------------------------------------------------- ** - ** should handle ccb timeouts - ** Let the middle scsi driver manage timeouts. + ** handle ccb timeouts + ** **---------------------------------------------------- */ - for (cp=&np->ccb; cp; cp=cp->link_ccb) { + for (cp=np->ccb; cp; cp=cp->link_ccb) { /* ** look for timed out ccbs. */ @@ -5357,7 +6582,7 @@ /* ** Have to force ordered tag to avoid timeouts */ - if (cp->cmd && cp->tlimit <= + if (cp->cmd && cp->tlimit && cp->tlimit <= thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) { lcb_p lp; lp = np->target[cp->cmd->target].lp[cp->cmd->lun]; @@ -5365,24 +6590,14 @@ lp->force_ordered_tag = 1; } } -/* -** Let the middle scsi driver manage timeouts -*/ -#if 0 - if (cp->tlimit > thistime) continue; - /* - ** Disable reselect. - ** Remove it from startqueue. + ** ncr_abort_command() cannot complete canceled + ** commands immediately. It sets tlimit to zero + ** and ask the script to skip the scsi process if + ** necessary. We have to complete this work here. */ - cp->jump_ccb.l_cmd = (SCR_JUMP); - if (cp->phys.header.launch.l_paddr == - NCB_SCRIPT_PHYS (np, select)) { - printf ("%s: timeout ccb=%p (skip)\n", - ncr_name (np), cp); - cp->phys.header.launch.l_paddr - = NCB_SCRIPT_PHYS (np, skip); - }; + + if (cp->tlimit) continue; switch (cp->host_status) { @@ -5397,7 +6612,7 @@ /* fall through */ case HS_DISCONNECT: - cp->host_status=HS_TIMEOUT; + cp->host_status=HS_ABORTED; }; cp->tag = 0; @@ -5405,7 +6620,11 @@ ** wakeup this ccb. */ ncr_complete (np, cp); + +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if (!np->stalling) #endif + OUTB (nc_istat, SIGP); } restore_flags(flags); } @@ -5425,43 +6644,146 @@ #endif /* SCSI_NCR_BROKEN_INTR */ } -/*========================================================== -** +/*========================================================== +** +** log message for real hard errors +** +** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)." +** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf." +** +** exception register: +** ds: dstat +** si: sist +** +** SCSI bus lines: +** so: control lines as driver by NCR. +** si: control lines as seen by NCR. +** sd: scsi data lines as seen by NCR. +** +** wide/fastmode: +** sxfer: (see the manual) +** scntl3: (see the manual) +** +** current script command: +** dsp: script adress (relative to start of script). +** dbc: first word of script command. +** +** First 16 register of the chip: +** r0..rf +** +**========================================================== +*/ + +static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat) +{ + u_int32 dsp; + int script_ofs; + int script_size; + char *script_name; + u_char *script_base; + int i; + + dsp = INL (nc_dsp); + + if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) { + script_ofs = dsp - np->p_script; + script_size = sizeof(struct script); + script_base = (u_char *) np->script; + script_name = "script"; + } + else { + script_ofs = dsp - np->p_scripth; + script_size = sizeof(struct scripth); + script_base = (u_char *) np->scripth; + script_name = "scripth"; + } + + printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n", + ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist, + (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl), + (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs, + (unsigned)INL (nc_dbc)); + + if (((script_ofs & 3) == 0) && + (unsigned)script_ofs < script_size) { + printf ("%s: script cmd = %08x\n", ncr_name(np), + (int) *(ncrcmd *)(script_base + script_ofs)); + } + + printf ("%s: regdump:", ncr_name(np)); + for (i=0; i<16;i++) + printf (" %02x", (unsigned)INB_OFF(i)); + printf (".\n"); +} + +/*============================================================ ** ** ncr chip exception handler. ** +**============================================================ ** -**========================================================== +** In normal cases, interrupt conditions occur one at a +** time. The ncr is able to stack in some extra registers +** other interrupts that will occurs after the first one. +** But severall interrupts may occur at the same time. +** +** We probably should only try to deal with the normal +** case, but it seems that multiple interrupts occur in +** some cases that are not abnormal at all. +** +** The most frequent interrupt condition is Phase Mismatch. +** We should want to service this interrupt quickly. +** A SCSI parity error may be delivered at the same time. +** The SIR interrupt is not very frequent in this driver, +** since the INTFLY is likely used for command completion +** signaling. +** The Selection Timeout interrupt may be triggered with +** IID and/or UDC. +** The SBMC interrupt (SCSI Bus Mode Change) may probably +** occur at any time. +** +** This handler try to deal as cleverly as possible with all +** the above. +** +**============================================================ */ void ncr_exception (ncb_p np) { u_char istat, dstat; u_short sist; - u_int32 dsp, dsa; - int script_ofs; int i; /* ** interrupt on the fly ? */ while ((istat = INB (nc_istat)) & INTF) { - if (DEBUG_FLAGS & DEBUG_TINY) printf ("F"); + if (DEBUG_FLAGS & DEBUG_TINY) printf ("F "); +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if (np->stalling) + OUTB (nc_istat, INTF); + else +#endif OUTB (nc_istat, (istat & SIGP) | INTF); np->profile.num_fly++; ncr_wakeup (np, 0); }; - if (!(istat & (SIP|DIP))) return; + if (!(istat & (SIP|DIP))) + return; + + np->profile.num_int++; + + if (istat & CABRT) + OUTB (nc_istat, CABRT); /* ** Steinbach's Guideline for Systems Programming: ** Never test for an error condition you don't know how to handle. */ - dstat = (istat & DIP) ? INB (nc_dstat) : 0; sist = (istat & SIP) ? INW (nc_sist) : 0; - np->profile.num_int++; + dstat = (istat & DIP) ? INB (nc_dstat) : 0; if (DEBUG_FLAGS & DEBUG_TINY) printf ("<%d|%x:%x|%x:%x>", @@ -5469,297 +6791,121 @@ dstat,sist, (unsigned)INL(nc_dsp), (unsigned)INL(nc_dbc)); - if ((dstat==DFE) && (sist==PAR)) return; - -/*========================================================== -** -** First the normal cases. -** -**========================================================== -*/ - /*------------------------------------------- - ** SCSI reset - **------------------------------------------- - */ - - if (sist & RST) { - ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET); - return; - }; - /*------------------------------------------- - ** selection timeout + /*======================================================== + ** First, interrupts we want to service cleanly. ** - ** IID excluded from dstat mask! - ** (chip bug) - **------------------------------------------- - */ - - if ((sist & STO) && - !(sist & (GEN|HTH|MA|SGE|UDC|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|SIR))) { - ncr_int_sto (np); - return; - }; - - /*------------------------------------------- - ** Phase mismatch. - **------------------------------------------- - */ - - if ((sist & MA) && - !(sist & (STO|GEN|HTH|SGE|UDC|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|SIR|IID))) { - ncr_int_ma (np); + ** Phase mismatch is the most frequent interrupt, and + ** so we have to service it as quickly and as cleanly + ** as possible. + ** Programmed interrupts are rarely used in this driver, + ** but we must handle them cleanly anyway. + ** We try to deal with PAR and SBMC combined with + ** some other interrupt(s). + **========================================================= + */ + + if (!(sist & (STO|GEN|HTH|SGE|UDC|RST)) && + !(dstat & (MDPE|BF|ABRT|IID))) { + if ((sist & SBMC) && ncr_int_sbmc (np)) + return; + if ((sist & PAR) && ncr_int_par (np)) + return; + if (sist & MA) { + ncr_int_ma (np); + return; + } + if (dstat & SIR) { + ncr_int_sir (np); + return; + } + if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) + printf("%s: unknown interrupt(s) ignored sist=%x dstat=%x\n", + ncr_name(np), sist, dstat); + OUTONB (nc_dcntl, (STD|NOCOM)); return; }; - /*---------------------------------------- - ** move command with length 0 - **---------------------------------------- + /*======================================================== + ** Now, interrupts that need some fixing up. + ** Order and multiple interrupts is so less important. + ** + ** If SRST has been asserted, we just reset the chip. + ** + ** Selection is intirely handled by the chip. If the + ** chip says STO, we trust it. Seems some other + ** interrupts may occur at the same time (UDC, IID), so + ** we ignore them. In any case we do enough fix-up + ** in the service routine. + ** We just exclude some fatal dma errors. + **========================================================= */ - if ((dstat & IID) && - !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|SIR)) && - ((INL(nc_dbc) & 0xf8000000) == SCR_MOVE_TBL)) { - /* - ** Target wants more data than available. - ** The "no_data" script will do it. - */ - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, no_data)); + if (sist & RST) { + ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET); return; }; - /*------------------------------------------- - ** Programmed interrupt - **------------------------------------------- - */ - - if ((dstat & SIR) && - !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|IID)) && - (INB(nc_dsps) <= SIR_MAX)) { - ncr_int_sir (np); + if ((sist & STO) && + !(dstat & (MDPE|BF|ABRT))) { + ncr_int_sto (np); return; }; - /*======================================== - ** do the register dump - **======================================== + /*========================================================= + ** Now, interrupts we are not able to recover cleanly. + ** (At least for the moment). + ** + ** Do the register dump. + ** Log message for real hard errors. + ** Clear all fifos. + ** For MDPE, BF, ABORT, IID, SGE and HTH we reset the + ** BUS and the chip. + ** We are more soft for UDC. + **========================================================= */ if (jiffies - np->regtime > 10*HZ) { - int i; np->regtime = jiffies; - for (i=0; iregdump); i++) + for (i = 0; iregdump); i++) ((char*)&np->regdump)[i] = INB_OFF(i); np->regdump.nc_dstat = dstat; np->regdump.nc_sist = sist; }; - /*========================================= - ** log message for real hard errors - **========================================= - - "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ (dsp:dbc)." - " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf." - - exception register: - ds: dstat - si: sist - - SCSI bus lines: - so: control lines as driver by NCR. - si: control lines as seen by NCR. - sd: scsi data lines as seen by NCR. - - wide/fastmode: - sxfer: (see the manual) - scntl3: (see the manual) - - current script command: - dsp: script adress (relative to start of script). - dbc: first word of script command. - - First 16 register of the chip: - r0..rf - - ============================================= - */ - - dsp = (unsigned) INL (nc_dsp); - dsa = (unsigned) INL (nc_dsa); - - script_ofs = dsp - np->p_script; - - printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%x:%08x).\n", - ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist, - (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl), - (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_ofs, - (unsigned) INL (nc_dbc)); - - if (((script_ofs & 3) == 0) && - (unsigned)script_ofs < sizeof(struct script)) { - printf ("\tscript cmd = %08x\n", - (int) *(ncrcmd *)((char*)np->script +script_ofs)); - } - - printf ("\treg:\t"); - for (i=0; i<16;i++) - printf (" %02x", (unsigned)INB_OFF(i)); - printf (".\n"); - - /*---------------------------------------- - ** clean up the dma fifo - **---------------------------------------- - */ - - if ( (INB(nc_sstat0) & (ILF|ORF|OLF) ) || - (INB(nc_sstat1) & (FF3210) ) || - (INB(nc_sstat2) & (ILF1|ORF1|OLF1)) || /* wide .. */ - !(dstat & DFE)) { - printf ("%s: have to clear fifos.\n", ncr_name (np)); - OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ - OUTB (nc_ctest3, CLF); /* clear dma fifo */ - } + ncr_log_hard_error(np, sist, dstat); - /*---------------------------------------- - ** handshake timeout - **---------------------------------------- - */ + printf ("%s: have to clear fifos.\n", ncr_name (np)); + OUTB (nc_stest3, TE|CSF); + OUTONB (nc_ctest3, CLF); + + if ((sist & (SGE)) || + (dstat & (MDPE|BF|ABORT|IID))) { + ncr_start_reset(np, 2); + return; + }; if (sist & HTH) { printf ("%s: handshake timeout\n", ncr_name(np)); - OUTB (nc_scntl1, CRST); - DELAY (1000); - OUTB (nc_scntl1, 0x00); - OUTB (nc_scr0, HS_FAIL); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); - return; - } - - /*---------------------------------------- - ** unexpected disconnect - **---------------------------------------- - */ - - if ((sist & UDC) && - !(sist & (STO|GEN|HTH|MA|SGE|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|SIR|IID))) { - OUTB (nc_scr0, HS_UNEXPECTED); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); + ncr_start_reset(np, 2); return; }; - /*---------------------------------------- - ** cannot disconnect - **---------------------------------------- - */ - - if ((dstat & IID) && - !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|SIR)) && - ((INL(nc_dbc) & 0xf8000000) == SCR_WAIT_DISC)) { - /* - ** Unexpected data cycle while waiting for disconnect. - */ - if (INB(nc_sstat2) & LDSC) { - /* - ** It's an early reconnect. - ** Let's continue ... - */ - OUTB (nc_dcntl, (STD|NOCOM)); - /* - ** info message - */ - printf ("%s: INFO: LDSC while IID.\n", - ncr_name (np)); - return; + if (sist & UDC) { + printf ("%s: unexpected disconnect\n", ncr_name(np)); + if (INB (nc_scr1) != 0xff) { + OUTB (nc_scr1, HS_UNEXPECTED); + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup)); }; - printf ("%s: target %d doesn't release the bus.\n", - ncr_name (np), (int)INB (nc_ctest0)&0x0f); - /* - ** return without restarting the NCR. - ** timeout will do the real work. - */ - return; - }; - - /*---------------------------------------- - ** single step - **---------------------------------------- - */ - - if ((dstat & SSI) && - !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) && - !(dstat & (MDPE|BF|ABRT|SIR|IID))) { - OUTB (nc_dcntl, (STD|NOCOM)); - return; - }; - -/* -** @RECOVER@ HTH, SGE, ABRT. -** -** We should try to recover from these interrupts. -** They may occur if there are problems with synch transfers, or -** if targets are switched on or off while the driver is running. -*/ - - if (sist & SGE) { - OUTB (nc_ctest3, CLF); /* clear scsi offsets */ - } - - /* - ** Freeze controller to be able to read the messages. - */ - - if (DEBUG_FLAGS & DEBUG_FREEZE) { - unsigned char val; - for (i=0; i<0x60; i++) { - switch (i%16) { - - case 0: - printf ("%s: reg[%d0]: ", - ncr_name(np),i/16); - break; - case 4: - case 8: - case 12: - printf (" "); - break; - }; - val = INB_OFF(i); - printf (" %x%x", val/16, val%16); - if (i%16==15) printf (".\n"); - } - - del_timer(&np->timer); - - printf ("%s: halted!\n", ncr_name(np)); - /* - ** don't restart controller ... - */ - OUTB (nc_istat, SRST); + ncr_start_reset(np, 2); return; }; -#ifdef NCR_FREEZE - /* - ** Freeze system to be able to read the messages. - */ - printf ("ncr: fatal error: system halted - press reset to reboot ..."); - cli(); - for (;;); -#endif - - /* - ** sorry, have to kill ALL jobs ... + /*========================================================= + ** We just miss the cause of the interrupt. :( + ** Print a message. The timeout will do the real work. + **========================================================= */ - - ncr_init (np, "fatal error", HS_FAIL); -#ifndef SCSI_NCR_NO_DISCONNECT - np->disc = 1; -#endif + printf ("%s: unknown interrupt\n", ncr_name(np)); } /*========================================================== @@ -5789,7 +6935,7 @@ */ dsa = INL (nc_dsa); - cp = &np->ccb; + cp = np->ccb; while (cp && (CCB_PHYS (cp, phys) != dsa)) cp = cp->link_ccb; @@ -5803,7 +6949,7 @@ */ scratcha = INL (nc_scratcha); - diff = scratcha - NCB_SCRIPT_PHYS (np, tryloop); + diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop); /* assert ((diff <= MAX_START * 20) && !(diff % 20));*/ @@ -5813,9 +6959,55 @@ return; }; ncr_init (np, "selection timeout", HS_FAIL); -#ifndef SCSI_NCR_NO_DISCONNECT np->disc = 1; -#endif +} + +/*========================================================== +** +** ncr chip exception handler for SCSI bus mode change +** +**========================================================== +** +** spi2-r12 11.2.3 says a transceiver mode change must +** generate a reset event and a device that detects a reset +** event shall initiate a hard reset. It says also that a +** device that detects a mode change shall set data transfer +** mode to eight bit asynchronous, etc... +** So, just resetting should be enough. +** +** +**---------------------------------------------------------- +*/ + +static int ncr_int_sbmc (ncb_p np) +{ + u_char scsi_mode = INB (nc_stest4) & SMODE; + + printf("%s: SCSI bus mode change from %x to %x, resetting ...\n", + ncr_name(np), np->scsi_mode, scsi_mode); + + np->scsi_mode = scsi_mode; + ncr_start_reset(np, 2); + + return 1; +} + +/*========================================================== +** +** ncr chip exception handler for SCSI parity error. +** +**========================================================== +** +** SCSI parity errors are handled by the SCSI script. +** So, we just print some message. +** +**---------------------------------------------------------- +*/ + +static int ncr_int_par (ncb_p np) +{ + printf("%s: SCSI parity error detected\n", ncr_name(np)); + return 0; } /*========================================================== @@ -5836,49 +7028,76 @@ { u_int32 dbc; u_int32 rest; - u_int32 dsa; u_int32 dsp; + u_int32 dsa; u_int32 nxtdsp; u_int32 *vdsp; u_int32 oadr, olen; u_int32 *tblp; ncrcmd *newcmd; - u_char cmd, sbcl, delta, ss0, ss2; + u_char cmd, sbcl; ccb_p cp; - dsp = INL (nc_dsp); - dsa = INL (nc_dsa); - dbc = INL (nc_dbc); - ss0 = INB (nc_sstat0); - ss2 = INB (nc_sstat2); - sbcl= INB (nc_sbcl); - - cmd = dbc >> 24; - rest= dbc & 0xffffff; - delta=(INB (nc_dfifo) - rest) & 0x7f; - - /* - ** The data in the dma fifo has not been transfered to - ** the target -> add the amount to the rest - ** and clear the data. - ** Check the sstat2 register in case of wide transfer. - */ - - if (! (INB(nc_dstat) & DFE)) rest += delta; - if (ss0 & OLF) rest++; - if (ss0 & ORF) rest++; - if (INB(nc_scntl3) & EWS) { - if (ss2 & OLF1) rest++; - if (ss2 & ORF1) rest++; - }; - OUTB (nc_ctest3, CLF ); /* clear dma fifo */ - OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ + dsp = INL (nc_dsp); + dbc = INL (nc_dbc); + sbcl = INB (nc_sbcl); + + cmd = dbc >> 24; + rest = dbc & 0xffffff; + + /* + ** Take into account dma fifo and various buffers and latches, + ** only if the interrupted phase is an OUTPUT phase. + */ + + if ((cmd & 1) == 0) { + u_char ctest5, ss0, ss2; + u_short delta; + + ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0; + if (ctest5 & DFS) + delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff; + else + delta=(INB (nc_dfifo) - rest) & 0x7f; + + /* + ** The data in the dma fifo has not been transfered to + ** the target -> add the amount to the rest + ** and clear the data. + ** Check the sstat2 register in case of wide transfer. + */ + + rest += delta; + ss0 = INB (nc_sstat0); + if (ss0 & OLF) rest++; + if (ss0 & ORF) rest++; + if (INB(nc_scntl3) & EWS) { + ss2 = INB (nc_sstat2); + if (ss2 & OLF1) rest++; + if (ss2 & ORF1) rest++; + }; + + OUTONB (nc_ctest3, CLF ); /* clear dma fifo */ + OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */ + + if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) + printf ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7, + (unsigned) rest, (unsigned) delta, ss0); + + } else { + if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) + printf ("P%x%x RL=%d ", cmd&7, sbcl&7, rest); + if ((cmd & 7) != 1) { + OUTONB (nc_ctest3, CLF ); + OUTB (nc_stest3, TE|CSF); + } + } /* ** locate matching cp */ dsa = INL (nc_dsa); - cp = &np->ccb; + cp = np->ccb; while (cp && (CCB_PHYS (cp, phys) != dsa)) cp = cp->link_ccb; @@ -5904,19 +7123,18 @@ } else if (dsp == vtophys (&cp->patch[6])) { vdsp = &cp->patch[4]; nxtdsp = vdsp[3]; - } else { + } else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) { vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8); nxtdsp = dsp; + } else { + vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8); + nxtdsp = dsp; }; /* ** log the information */ - if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) { - printf ("P%x%x ",cmd&7, sbcl&7); - printf ("RL=%d D=%d SS0=%x ", - (unsigned) rest, (unsigned) delta, ss0); - }; + if (DEBUG_FLAGS & DEBUG_PHASE) { printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ", cp, np->header.cp, @@ -5948,7 +7166,7 @@ }; /* - ** if old phase not dataphase, leave here. + ** check cmd against assumed interrupted script command. */ if (cmd != (vdsp[0] >> 24)) { @@ -5958,13 +7176,25 @@ return; } + +#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT + if ((cmd & 7) == 1 && np->assert_atn) { + np->assert_atn = 0; + OUTONB(nc_socl, CATN); + } +#endif + + /* + ** if old phase not dataphase, leave here. + */ + if (cmd & 0x06) { PRINT_ADDR(cp->cmd); printf ("phase change %x-%x %d@%08x resid=%d.\n", cmd&7, sbcl&7, (unsigned)olen, (unsigned)oadr, (unsigned)rest); - OUTB (nc_dcntl, (STD|NOCOM)); + OUTONB (nc_dcntl, (STD|NOCOM)); return; }; @@ -6000,7 +7230,10 @@ */ np->profile.num_break++; OUTL (nc_temp, vtophys (newcmd)); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); + if ((cmd & 7) == 0) + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch)); + else + OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn)); } /*========================================================== @@ -6031,11 +7264,12 @@ void ncr_int_sir (ncb_p np) { + u_char scntl3; u_char chg, ofs, per, fak, wide; u_char num = INB (nc_dsps); ccb_p cp=0; u_long dsa; - u_char target = INB (nc_ctest0) & 7; + u_char target = INB (nc_ctest0) & 0x0f; tcb_p tp = &np->target[target]; int i; if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num); @@ -6044,13 +7278,15 @@ case SIR_SENSE_RESTART: case SIR_STALL_RESTART: break; + case SIR_STALL_QUEUE: /* Ignore, just restart the script */ + goto out; default: /* ** lookup the ccb */ dsa = INL (nc_dsa); - cp = &np->ccb; + cp = np->ccb; while (cp && (CCB_PHYS (cp, phys) != dsa)) cp = cp->link_ccb; @@ -6063,6 +7299,32 @@ } switch (num) { + u_long endp; + case SIR_DATA_IO_IS_OUT: + case SIR_DATA_IO_IS_IN: +/* +** We did not guess the direction of transfer. We have to wait for +** actual data direction driven by the target before setting +** pointers. We must patch the global header too. +*/ + if (num == SIR_DATA_IO_IS_OUT) { + endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16; + cp->phys.header.goalp = endp + 8; + cp->phys.header.savep = endp - cp->segments*16; + } else { + endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16; + cp->phys.header.goalp = endp + 8; + cp->phys.header.savep = endp - cp->segments*16; + } + + cp->phys.header.lastp = cp->phys.header.savep; + np->header.savep = cp->phys.header.savep; + np->header.goalp = cp->phys.header.goalp; + np->header.lastp = cp->phys.header.lastp; + OUTL (nc_temp, np->header.savep); + OUTL (nc_dsp, np->header.savep); + return; + /* break; */ /*-------------------------------------------------------------------- ** @@ -6099,7 +7361,7 @@ if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+ restart job ..\n"); OUTL (nc_dsa, CCB_PHYS (cp, phys)); - OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, getcc)); + OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc)); return; }; @@ -6230,11 +7492,11 @@ switch (cp->nego_status) { case NS_SYNC: - ncr_setsync (np, cp, 0xe0); + ncr_setsync (np, cp, 0, 0xe0); break; case NS_WIDE: - ncr_setwide (np, cp, 0); + ncr_setwide (np, cp, 0, 0); break; }; @@ -6277,8 +7539,8 @@ ** check values against driver limits. */ - if (per < np->ns_sync) - {chg = 1; per = np->ns_sync;} + if (per < np->minsync) + {chg = 1; per = np->minsync;} if (per < tp->minsync) {chg = 1; per = tp->minsync;} if (ofs > tp->maxoffs) @@ -6287,23 +7549,26 @@ /* ** Check against controller limits. */ + fak = 7; + scntl3 = 0; if (ofs != 0) { - fak = (4ul * per - 1) / np->ns_sync - 3; - if (fak>7) { + ncr_getsync(np, per, &fak, &scntl3); + if (fak > 7) { chg = 1; ofs = 0; } } if (ofs == 0) { - fak = 7; - per = 0; + fak = 7; + per = 0; + scntl3 = 0; tp->minsync = 0; } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->cmd); - printf ("sync: per=%d ofs=%d fak=%d chg=%d.\n", - per, ofs, fak, chg); + printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n", + per, scntl3, ofs, fak, chg); } if (INB (HS_PRT) == HS_NEGOTIATE) { @@ -6318,19 +7583,19 @@ /* ** Answer wasn't acceptable. */ - ncr_setsync (np, cp, 0xe0); + ncr_setsync (np, cp, 0, 0xe0); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ - ncr_setsync (np, cp, (fak<<5)|ofs); + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; return; case NS_WIDE: - ncr_setwide (np, cp, 0); + ncr_setwide (np, cp, 0, 0); break; }; }; @@ -6354,7 +7619,7 @@ ** prepare an answer message */ - ncr_setsync (np, cp, (fak<<5)|ofs); + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); np->msgout[0] = M_EXTENDED; np->msgout[1] = 3; @@ -6367,7 +7632,7 @@ if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->cmd); printf ("sync msgout: "); - (void) ncr_show_msg (np->msgin); + (void) ncr_show_msg (np->msgout); printf (".\n"); } @@ -6429,19 +7694,19 @@ /* ** Answer wasn't acceptable. */ - ncr_setwide (np, cp, 0); + ncr_setwide (np, cp, 0, 1); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ - ncr_setwide (np, cp, wide); + ncr_setwide (np, cp, wide, 1); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; return; case NS_SYNC: - ncr_setsync (np, cp, 0xe0); + ncr_setsync (np, cp, 0, 0xe0); break; }; }; @@ -6451,7 +7716,7 @@ ** prepare an answer message */ - ncr_setwide (np, cp, wide); + ncr_setwide (np, cp, wide, 1); np->msgout[0] = M_EXTENDED; np->msgout[1] = 2; @@ -6534,13 +7799,14 @@ */ PRINT_ADDR(cp->cmd); - printf ("M_DISCONNECT received, but datapointer not saved:\n" - "\tdata=%x save=%x goal=%x.\n", + printf ("M_DISCONNECT received, but datapointer not saved: " + "data=%x save=%x goal=%x.\n", (unsigned) INL (nc_temp), (unsigned) np->header.savep, (unsigned) np->header.goalp); break; +#if 0 /* This stuff does not work */ /*-------------------------------------------------------------------- ** ** Processing of a "S_QUEUE_FULL" status. @@ -6571,14 +7837,9 @@ np->script->start1[0] = SCR_INT; /* - ** For the moment tagged transfers cannot be disabled. - */ -#if 0 - /* ** Try to disable tagged transfers. */ ncr_setmaxtags (np, &np->target[target], 0); -#endif /* ** @QUEUE@ @@ -6601,7 +7862,7 @@ /* ** Look for a disconnected job. */ - cp = &np->ccb; + cp = np->ccb; while (cp && cp->host_status != HS_DISCONNECT) cp = cp->link_ccb; @@ -6623,10 +7884,11 @@ printf ("%s: queue empty.\n", ncr_name (np)); np->script->start1[0] = SCR_INT ^ IFFALSE (0); break; +#endif /* This stuff does not work */ }; out: - OUTB (nc_dcntl, (STD|NOCOM)); + OUTONB (nc_dcntl, (STD|NOCOM)); } /*========================================================== @@ -6649,7 +7911,9 @@ */ lp = np->target[target].lp[lun]; - if (lp) { + + if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) { + cp = lp->next_ccb; /* @@ -6657,6 +7921,15 @@ */ while (cp && cp->magic) cp = cp->next_ccb; + + /* + ** Increment active commands and decrement credit. + */ + + if (cp) { + ++lp->active; + --lp->opennings; + } } /* @@ -6668,7 +7941,7 @@ if ((!cp) && lp && lp->actccbs > 0) return ((ccb_p) 0); - if (!cp) cp = &np->ccb; + if (!cp) cp = np->ccb; /* ** Wait until available. @@ -6697,18 +7970,30 @@ **========================================================== */ -void ncr_free_ccb (ncb_p np, ccb_p cp) +void ncr_free_ccb (ncb_p np, ccb_p cp, u_long target, u_long lun) { + lcb_p lp; + /* ** sanity */ assert (cp != NULL); + /* + ** Decrement active commands and increment credit. + */ + + lp = np->target[target].lp[lun]; + if (lp) { + --lp->active; + ++lp->opennings; + } + cp -> host_status = HS_IDLE; cp -> magic = 0; #if 0 - if (cp == &np->ccb) + if (cp == np->ccb) wakeup ((caddr_t) cp); #endif } @@ -6743,10 +8028,12 @@ tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target))); tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr; - tp->getscr[0] = SCR_COPY (1); + tp->getscr[0] = + (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1); tp->getscr[1] = vtophys (&tp->sval); tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer); - tp->getscr[3] = SCR_COPY (1); + tp->getscr[3] = + (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1); tp->getscr[4] = vtophys (&tp->wval); tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3); @@ -6759,7 +8046,7 @@ tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun); tp->jump_lcb.l_cmd = (SCR_JUMP); - tp->jump_lcb.l_paddr = NCB_SCRIPT_PHYS (np, abort); + tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort); np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb); } @@ -6771,7 +8058,7 @@ /* ** Allocate a lcb */ - lp = (lcb_p) m_alloc (sizeof (struct lcb)); + lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT); if (!lp) return; if (DEBUG_FLAGS & DEBUG_ALLOC) { @@ -6790,31 +8077,25 @@ lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag); lp->jump_ccb.l_cmd = (SCR_JUMP); - lp->jump_ccb.l_paddr = NCB_SCRIPT_PHYS (np, aborttag); + lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag); lp->actlink = 1; + lp->active = 1; + /* ** Chain into LUN list */ tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb); tp->lp[lun] = lp; -#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED - if (!lp->usetags) { - ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS); - } -#endif + ncr_setmaxtags (np, tp, driver_setup.default_tags); } /* ** Allocate ccbs up to lp->reqccbs. - ** - ** This modification will be reworked in a future release. */ -loop_alloc_ccb: - /* ** Limit possible number of ccbs. ** @@ -6828,7 +8109,7 @@ /* ** Allocate a ccb */ - cp = (ccb_p) m_alloc (sizeof (struct ccb)); + cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT); if (!cp) return; @@ -6866,16 +8147,68 @@ /* ** Chain into wakeup list */ - cp->link_ccb = np->ccb.link_ccb; - np->ccb.link_ccb = cp; + cp->link_ccb = np->ccb->link_ccb; + np->ccb->link_ccb = cp; /* ** Chain into CCB list */ - cp->next_ccb = lp->next_ccb; - lp->next_ccb = cp; + cp->next_ccb = lp->next_ccb; + lp->next_ccb = cp; +} + +/*========================================================== +** +** +** Announce the number of ccbs/tags to the scsi driver. +** +** +**========================================================== +*/ + +static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd) +{ + /* + ** want to reduce the number ... + */ + if (lp->actlink > lp->reqlink) { + + /* + ** Try to reduce the count. + ** We assume to run at splbio .. + */ + u_char diff = lp->actlink - lp->reqlink; + + if (!diff) return; + + if (diff > lp->opennings) + diff = lp->opennings; + + lp->opennings -= diff; + + lp->actlink -= diff; + if (DEBUG_FLAGS & DEBUG_TAGS) + printf ("%s: actlink: diff=%d, new=%d, req=%d\n", + ncr_name(np), diff, lp->actlink, lp->reqlink); + return; + }; + + /* + ** want to increase the number ? + */ + if (lp->reqlink > lp->actlink) { + u_char diff = lp->reqlink - lp->actlink; -goto loop_alloc_ccb; + lp->opennings += diff; + + lp->actlink += diff; +#if 0 + wakeup ((caddr_t) xp->sc_link); +#endif + if (DEBUG_FLAGS & DEBUG_TAGS) + printf ("%s: actlink: diff=%d, new=%d, req=%d\n", + ncr_name(np), diff, lp->actlink, lp->reqlink); + }; } /*========================================================== @@ -6894,8 +8227,7 @@ **---------------------------------------------------------- */ -/* FreeBSD driver important comments -** --------------------------------- +/* ** We try to reduce the number of interrupts caused ** by unexpected phase changes due to disconnects. ** A typical harddisk may disconnect before ANY block. @@ -6903,158 +8235,49 @@ ** we had to use a break point every 512 bytes. ** Of course the number of scatter/gather blocks is ** limited. +** Under Linux, the scatter/gatter blocks are provided by +** the generic driver. We just have to copy addresses and +** sizes to the data segment array. */ -/* -** The scatterlist passed by the linux middle-level scsi drivers -** may contain blocks of any size (Generaly < 1024 bytes blocks, -** can be 4096 with a 4K fs). -*/ - -#if defined(SCSI_NCR_SEGMENT_SIZE) static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd) { - struct scatterlist *scatter; - struct dsb *phys; - register u_short segment = 0; - register u_short o_segment = 0; - u_short chunk, chunk_min; - u_long segaddr; - int segsize; - int datalen; - - phys = &cp->phys; - cp->data_len = 0; - - /* - ** Compute a good value for chunk size - ** If SCSI_NCR_SEGMENT_SIZE is OK, we will try to use it. - */ - - if (!cmd->use_sg) - cp->data_len = cmd->request_bufflen; - else { - scatter = (struct scatterlist *)cmd->buffer; - for (segment = 0 ; segment < cmd->use_sg ; segment++) - cp->data_len += scatter[segment].length; - } - - - if (!cp->data_len) { - bzero (&phys->data, sizeof (phys->data)); - return 0; - } - - chunk_min = cp->data_len / MAX_SCATTER; - for (chunk = SCSI_NCR_SEGMENT_SIZE ; chunk < chunk_min ; chunk += chunk); - - /* - ** If the linux scsi command is not a scatterlist, - ** the computed chunk size is OK. - */ - - if (!cmd->use_sg) { - bzero (&phys->data, sizeof (phys->data)); - datalen = cmd->request_bufflen; - segaddr = vtophys(cmd->request_buffer); - segsize = chunk; - o_segment = 0; - -if (DEBUG_FLAGS & DEBUG_SCATTER) - printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n", - (unsigned) segaddr, (int) datalen, (int) chunk); - - while (datalen && (o_segment < MAX_SCATTER)) { - if (segsize > datalen) segsize = datalen; - phys->data[o_segment].addr = segaddr; - phys->data[o_segment].size = segsize; + struct scr_tblmove *data; + int segment = 0; + int use_sg = (int) cmd->use_sg; - datalen -= segsize; - -if(DEBUG_FLAGS & DEBUG_SCATTER) - printf ("ncr53c8xx: seg #%d addr=%lx size=%d (rest=%d).\n", - o_segment, segaddr, (int) segsize, (int) datalen); +#if 0 + bzero (cp->phys.data, sizeof (cp->phys.data)); +#endif + data = cp->phys.data; + cp->data_len = 0; - segaddr += segsize; - o_segment++; + if (!use_sg) { + if (cmd->request_bufflen) { + data = &data[MAX_SCATTER - 1]; + data[0].addr = vtophys(cmd->request_buffer); + data[0].size = cmd->request_bufflen; + cp->data_len = data[0].size; + segment = 1; } - - return datalen ? -1 : o_segment; } + else if (use_sg <= MAX_SCATTER) { + struct scatterlist *scatter = (struct scatterlist *)cmd->buffer; - /* - ** Else, the computed chunk size is not so good - ** and we have to iterate. - ** Rescatter the Linux scatterlist into the data block descriptor. - ** Loop if necessary, beginning with the not so good chunk size and - ** doubling it if the scatter process fails. - */ - - scatter = (struct scatterlist *)cmd->buffer; - for (segment = 0; segment < cmd->use_sg; chunk += chunk) { - o_segment = 0; - bzero (&phys->data, sizeof (phys->data)); - for (segment = 0 ; segment < cmd->use_sg ; segment++) { - datalen = scatter[segment].length; - segaddr = vtophys(scatter[segment].address); - segsize = chunk; - -if (DEBUG_FLAGS & DEBUG_SCATTER) - printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n", - (unsigned) segaddr, (int) datalen, (int) chunk); - - while (datalen && (o_segment < MAX_SCATTER)) { - if (segsize > datalen) segsize = datalen; - phys->data[o_segment].addr = segaddr; - phys->data[o_segment].size = segsize; - - datalen -= segsize; - -if(DEBUG_FLAGS & DEBUG_SCATTER) - printf ("ncr53c8xx: seg #%d addr=%lx size=%d (rest=%d).\n", - o_segment, segaddr, (int) segsize, (int) datalen); - - segaddr += segsize; - o_segment++; - } - - if (datalen) break; + data = &data[MAX_SCATTER - use_sg]; + while (segment < use_sg) { + data[segment].addr = vtophys(scatter[segment].address); + data[segment].size = scatter[segment].length; + cp->data_len += data[segment].size; + ++segment; } } - - return segment < cmd->use_sg ? -1 : o_segment; -} - -#else /* !defined SCSI_NCR_SEGMENT_SIZE */ - -static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd) -{ - struct dsb *phys = &cp->phys; - u_short segment = 0; - - cp->data_len = 0; - bzero (&phys->data, sizeof (phys->data)); - - if (!cmd->use_sg) { - phys->data[segment].addr = vtophys(cmd->request_buffer); - phys->data[segment].size = cmd->request_bufflen; - cp->data_len += phys->data[segment].size; - segment++; - return segment; - } - - while (segment < cmd->use_sg && segment < MAX_SCATTER) { - struct scatterlist *scatter = (struct scatterlist *)cmd->buffer; - - phys->data[segment].addr = vtophys(scatter[segment].address); - phys->data[segment].size = scatter[segment].length; - cp->data_len += phys->data[segment].size; - ++segment; + else { + return -1; } - return segment < cmd->use_sg ? -1 : segment; + return segment; } -#endif /* SCSI_NCR_SEGMENT_SIZE */ /*========================================================== ** @@ -7068,7 +8291,9 @@ */ #ifndef NCR_IOMAPPED +__initfunc( static int ncr_regtest (struct ncb* np) +) { register volatile u_long data; /* @@ -7092,12 +8317,14 @@ } #endif +__initfunc( static int ncr_snooptest (struct ncb* np) +) { u_long ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc, err=0; int i; #ifndef NCR_IOMAPPED - if (np->use_mmio) { + if (np->reg) { err |= ncr_regtest (np); if (err) return (err); } @@ -7105,7 +8332,7 @@ /* ** init */ - pc = NCB_SCRIPT_PHYS (np, snooptest); + pc = NCB_SCRIPTH_PHYS (np, snooptest); host_wr = 1; ncr_wr = 2; /* @@ -7149,8 +8376,11 @@ /* ** Check termination position. */ - if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) { + if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) { printf ("CACHE TEST FAILED: script execution failed.\n"); + printf ("start=%08lx, pc=%08lx, end=%08lx\n", + (u_long) NCB_SCRIPTH_PHYS (np, snooptest), pc, + (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8); return (0x40); }; /* @@ -7183,23 +8413,19 @@ **========================================================== */ -/* -** Compute the difference in milliseconds. -**/ +#ifdef SCSI_NCR_PROFILE_SUPPORT -#ifdef SCSI_NCR_PROFILE +/* +** Compute the difference in jiffies ticks. +*/ -static int ncr_delta (u_long from, u_long to) -{ - if (!from) return (-1); - if (!to) return (-2); - return ((to - from) * 1000 / HZ ); -} +#define ncr_delta(from, to) \ + ( ((to) && (from))? (to) - (from) : -1 ) #define PROFILE cp->phys.header.stamp static void ncb_profile (ncb_p np, ccb_p cp) { - int co, da, st, en, di, se, post,work,disc; + int co, st, en, di, se, post,work,disc; u_long diff; PROFILE.end = jiffies; @@ -7207,9 +8433,6 @@ st = ncr_delta (PROFILE.start,PROFILE.status); if (st<0) return; /* status not reached */ - da = ncr_delta (PROFILE.start,PROFILE.data); - if (da<0) return; /* No data transfer phase */ - co = ncr_delta (PROFILE.start,PROFILE.command); if (co<0) return; /* command not executed */ @@ -7246,7 +8469,7 @@ } #undef PROFILE -#endif /* SCSI_NCR_PROFILE */ +#endif /* SCSI_NCR_PROFILE_SUPPORT */ /*========================================================== ** @@ -7307,7 +8530,7 @@ /*========================================================== ** ** Determine the ncr's clock frequency. -** This is important for the negotiation +** This is essential for the negotiation ** of the synchronous transfer rate. ** **========================================================== @@ -7315,84 +8538,161 @@ ** Note: we have to return the correct value. ** THERE IS NO SAVE DEFAULT VALUE. ** -** We assume that all NCR based boards are delivered -** with a 40Mhz clock. Because we have to divide -** by an integer value greater than 3, only clock -** frequencies of 40Mhz (/4) or 50MHz (/5) permit -** the FAST-SCSI rate of 10MHz. +** Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock. +** 53C860 and 53C875 rev. 1 support fast20 transfers but +** do not have a clock doubler and so are provided with a +** 80 MHz clock. All other fast20 boards incorporate a doubler +** and so should be delivered with a 40 MHz clock. +** The future fast40 chips (895/895) use a 40 Mhz base clock +** and provide a clock quadrupler (160 Mhz). The code below +** tries to deal as cleverly as possible with all this stuff. ** **---------------------------------------------------------- */ -#ifndef NCR_CLOCK -# define NCR_CLOCK 40 -#endif /* NCR_CLOCK */ +/* + * Select NCR SCSI clock frequency + */ +static void ncr_selectclock(ncb_p np, u_char scntl3) +{ + if (np->multiplier < 2) { + OUTB(nc_scntl3, scntl3); + return; + } + + if (bootverbose >= 2) + printf ("%s: enabling clock multiplier\n", ncr_name(np)); + OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */ + if (np->multiplier > 2) { /* Poll bit 5 of stest4 for quadrupler */ + int i = 20; + while (!(INB(nc_stest4) & LCKFRQ) && --i > 0) + DELAY(20); + if (!i) + printf("%s: the chip cannot lock the frequency\n", ncr_name(np)); + } else /* Wait 20 micro-seconds for doubler */ + DELAY(20); + OUTB(nc_stest3, HSC); /* Halt the scsi clock */ + OUTB(nc_scntl3, scntl3); + OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */ + OUTB(nc_stest3, 0x00); /* Restart scsi clock */ +} -static void ncr_getclock (ncb_p np, u_char scntl3) -{ -#if 0 - u_char tbl[5] = {6,2,3,4,6}; - u_char f; - u_char ns_clock = (1000/NCR_CLOCK); - - /* - ** Compute the best value for scntl3. - */ - - f = (2 * MIN_SYNC_PD - 1) / ns_clock; - if (!f ) f=1; - if (f>4) f=4; - np -> ns_sync = (ns_clock * tbl[f]) / 2; - np -> rv_scntl3 = f<<4; - - f = (2 * MIN_ASYNC_PD - 1) / ns_clock; - if (!f ) f=1; - if (f>4) f=4; - np -> ns_async = (ns_clock * tbl[f]) / 2; - np -> rv_scntl3 |= f; - if (DEBUG_FLAGS & DEBUG_TIMING) - printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n", - ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3); -#else - /* - * If NCR53C875 chip with clock doubler enabled, - * disable clock doubler and assume 40 MHz clock. - * If NCR53C860 chip assume 80 MHz clock. + +/* + * calculate NCR SCSI clock frequency (in KHz) + */ +__initfunc( +static unsigned ncrgetfreq (ncb_p np, int gen) +) +{ + unsigned ms = 0; + + /* + * Measure GEN timer delay in order + * to calculate SCSI clock frequency + * + * This code will never execute too + * many loop iterations (if DELAY is + * reasonably correct). It could get + * too low a delay (too high a freq.) + * if the CPU is slow executing the + * loop for some reason (an NMI, for + * example). For this reason we will + * if multiple measurements are to be + * performed trust the higher delay + * (lower frequency returned). */ + OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */ + OUTW (nc_sien , 0); /* mask all scsi interrupts */ + (void) INW (nc_sist); /* clear pending scsi interrupt */ + OUTB (nc_dien , 0); /* mask all dma interrupts */ + (void) INW (nc_sist); /* another one, just to be sure :) */ + OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */ + OUTB (nc_stime1, 0); /* disable general purpose timer */ + OUTB (nc_stime1, gen); /* set to nominal delay of 1<= 2) + printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms); + /* + * adjust for prescaler, and convert into KHz + */ + return ms ? ((1 << gen) * 4340) / ms : 0; +} - switch(np->device_id) { - case PCI_DEVICE_ID_NCR_53C875: - if ((INB(nc_stest1) & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) { - if (bootverbose) - printf ("%s: disabling clock doubler\n", ncr_name(np)); - OUTB(nc_stest1, 0); - scntl3 = 3; - } - break; - case PCI_DEVICE_ID_NCR_53C860: - scntl3 = 5; - break; +/* + * Get/probe NCR SCSI clock frequency + */ +__initfunc( +static void ncr_getclock (ncb_p np, int mult) +) +{ + unsigned char scntl3 = INB(nc_scntl3); + unsigned char stest1 = INB(nc_stest1); + unsigned f1; + + np->multiplier = 1; + f1 = 40000; + + /* + ** True with 875 or 895 with clock multiplier selected + */ + if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) { + if (bootverbose >= 2) + printf ("%s: clock multiplier found\n", ncr_name(np)); + np->multiplier = mult; } /* - * For now just preserve the BIOS setting ... - */ + ** If multiplier not found or scntl3 not 7,5,3, + ** reset chip and get frequency from general purpose timer. + ** Otherwise trust scntl3 BIOS setting. + */ + if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) { + unsigned f2; - if ((scntl3 & 7) < 3) { - printf ("%s: assuming 40MHz clock\n", ncr_name(np)); - scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */ - } + OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0); - np->ns_sync = 25; - np->ns_async = 50; - np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7); - - if (bootverbose) { - printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n", - ncr_name(np), scntl3, np->rv_scntl3); + (void) ncrgetfreq (np, 11); /* throw away first result */ + f1 = ncrgetfreq (np, 11); + f2 = ncrgetfreq (np, 11); + + if (bootverbose) + printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2); + + if (f1 > f2) f1 = f2; /* trust lower result */ + + if (f1 < 45000) f1 = 40000; + else if (f1 < 55000) f1 = 50000; + else f1 = 80000; + + if (f1 < 80000 && mult > 1) { + if (bootverbose >= 2) + printf ("%s: clock multiplier assumed\n", ncr_name(np)); + np->multiplier = mult; + } + } else { + if ((scntl3 & 7) == 3) f1 = 40000; + else if ((scntl3 & 7) == 5) f1 = 80000; + else f1 = 160000; + + f1 /= np->multiplier; } -#endif + + /* + ** Compute controller synchronous parameters. + */ + f1 *= np->multiplier; + np->clock_khz = f1; } /*===================== LINUX ENTRY POINTS SECTION ==========================*/ @@ -7409,29 +8709,108 @@ #define ulong unsigned long #endif -static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip, - uchar bus, uchar device_fn, int options); - -/* -** NCR53C8XX devices description table +/* --------------------------------------------------------------------- +** +** Driver setup from the boot command line +** +** --------------------------------------------------------------------- */ -static struct { - ushort pci_device_id; - int chip; - int max_revision; - int min_revision; -} pci_chip_ids[] = { - {PCI_DEVICE_ID_NCR_53C810, 810, -1, -1}, -/* {PCI_DEVICE_ID_NCR_53C810AP, 810, -1, -1}, */ - {PCI_DEVICE_ID_NCR_53C815, 815, -1, -1}, - {PCI_DEVICE_ID_NCR_53C820, 820, -1, -1}, - {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1}, - {PCI_DEVICE_ID_NCR_53C860, 860, -1, -1}, - {PCI_DEVICE_ID_NCR_53C875, 875, -1, -1} -}; +__initfunc( +void ncr53c8xx_setup(char *str, int *ints) +) +{ +#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT + char *cur = str; + char *pc, *pv; + int val; + int base; + int c; + + while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { + val = 0; + pv = pc; + c = *++pv; + if (c == 'n') + val = 0; + else if (c == 'y') + val = 1; + else { + base = 0; +#if 0 + if (c == '0') { + c = *pv++; + base = 8; + } + if (c == 'x') { + ++pv; + base = 16; + } + else if (c >= '0' && c <= '9') + base = 10; + else + break; +#endif + val = (int) simple_strtoul(pv, NULL, base); + } -#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0])) + if (!strncmp(cur, "mpar:", 5)) + driver_setup.master_parity = val; + else if (!strncmp(cur, "spar:", 5)) + driver_setup.scsi_parity = val; + else if (!strncmp(cur, "disc:", 5)) + driver_setup.disconnection = val; + else if (!strncmp(cur, "specf:", 6)) + driver_setup.special_features = val; + else if (!strncmp(cur, "ultra:", 6)) + driver_setup.ultra_scsi = val; + else if (!strncmp(cur, "fsn:", 4)) + driver_setup.force_sync_nego = val; + else if (!strncmp(cur, "revprob:", 8)) + driver_setup.reverse_probe = val; + else if (!strncmp(cur, "tags:", 5)) { + if (val > SCSI_NCR_MAX_TAGS) + val = SCSI_NCR_MAX_TAGS; + driver_setup.default_tags = val; + } + else if (!strncmp(cur, "sync:", 5)) + driver_setup.default_sync = val; + else if (!strncmp(cur, "verb:", 5)) + driver_setup.verbose = val; + else if (!strncmp(cur, "debug:", 6)) + driver_setup.debug = val; + else if (!strncmp(cur, "burst:", 6)) + driver_setup.burst_max = val; + else if (!strncmp(cur, "led:", 4)) + driver_setup.led_pin = val; + else if (!strncmp(cur, "wide:", 5)) + driver_setup.max_wide = val? 1:0; + else if (!strncmp(cur, "settle:", 7)) + driver_setup.settle_delay= val; + else if (!strncmp(cur, "diff:", 5)) + driver_setup.diff_support= val; + else if (!strncmp(cur, "irqm:", 5)) + driver_setup.irqm = val; + else if (!strncmp(cur, "pcifix:", 7)) + driver_setup.pci_fix_up = val; +#ifdef SCSI_NCR_NVRAM_SUPPORT + else if (!strncmp(cur, "nvram:", 6)) + driver_setup.use_nvram = val; +#endif + + else if (!strncmp(cur, "safe:", 5) && val) + memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup)); + else + printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", pc-cur+1, cur); + + if ((cur = strchr(cur, ',')) != NULL) + ++cur; + } +#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */ +} + +static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, + uchar bus, uchar device_fn, ncr_device *device); /* ** Linux entry point for NCR53C8XX devices detection routine. @@ -7442,15 +8821,156 @@ ** Read the PCI configuration and try to attach each ** detected NCR board. ** +** If NVRAM is present, try to attach boards according to +** the used defined boot order. +** ** Returns the number of boards successfully attached. */ +__initfunc( +static void ncr_print_driver_setup(void) +) +{ +#define YesNo(y) y ? 'y' : 'n' + printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n", + YesNo(driver_setup.disconnection), + YesNo(driver_setup.special_features), + YesNo(driver_setup.ultra_scsi), + driver_setup.default_tags, + driver_setup.default_sync, + driver_setup.burst_max, + YesNo(driver_setup.max_wide), + driver_setup.diff_support); + printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,led:%c,settle:%d,irqm:%d\n", + YesNo(driver_setup.master_parity), + YesNo(driver_setup.scsi_parity), + YesNo(driver_setup.force_sync_nego), + driver_setup.verbose, + driver_setup.debug, + YesNo(driver_setup.led_pin), + driver_setup.settle_delay, + driver_setup.irqm); +#undef YesNo +} + +/* +** NCR53C8XX devices description table and chip ids list. +*/ + +static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE; +static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS; + +#ifdef SCSI_NCR_NVRAM_SUPPORT +__initfunc( +static int +ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[]) +) +{ + int i, j; + int attach_count = 0; + ncr_nvram *nvram; + ncr_device *devp; + + if (!nvram_index) + return 0; + + /* find first Symbios NVRAM if there is one as we need to check it for host boot order */ + for (i = 0, nvram_index = -1; i < count; i++) { + devp = &device[i]; + nvram = devp->nvram; + if (!nvram) + continue; + if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) { + if (nvram_index == -1) + nvram_index = i; +#ifdef SCSI_NCR_DEBUG_NVRAM + printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n", + devp->chip.name, devp->slot.bus, + (int) (devp->slot.device_fn & 0xf8) >> 3, + (int) devp->slot.device_fn & 7); + for (j = 0 ; j < 4 ; j++) { + Symbios_host *h = &nvram->data.Symbios.host[j]; + printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n", + j, h->device_id, h->vendor_id, + h->device_fn, h->io_port, + (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : ""); + } + } + else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) { + /* display Tekram nvram data */ + printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n", + devp->chip.name, devp->slot.bus, + (int) (devp->slot.device_fn & 0xf8) >> 3, + (int) devp->slot.device_fn & 7); +#endif + } + } + + if (nvram_index >= 0 && nvram_index < count) + nvram = device[nvram_index].nvram; + else + nvram = 0; + + if (!nvram) + goto out; + + /* + ** check devices in the boot record against devices detected. + ** attach devices if we find a match. boot table records that + ** do not match any detected devices will be ignored. + ** devices that do not match any boot table will not be attached + ** here but will attempt to be attached during the device table + ** rescan. + */ + for (i = 0; i < 4; i++) { + Symbios_host *h = &nvram->data.Symbios.host[i]; + for (j = 0 ; j < count ; j++) { + devp = &device[j]; + if (h->device_fn == devp->slot.device_fn && +#if 0 /* bus number location in nvram ? */ + h->bus == devp->slot.bus && +#endif + h->device_id == devp->chip.device_id) + break; + } + if (j < count && !devp->attached && + !ncr_attach (tpnt, attach_count, devp)) { + attach_count++; + devp->attached = 1; + } + } + +out: + return attach_count; +} +#endif /* SCSI_NCR_NVRAM_SUPPORT */ + +__initfunc( int ncr53c8xx_detect(Scsi_Host_Template *tpnt) +) { - int i; - int count = 0; /* Number of boards detected */ - uchar pci_bus, pci_device_fn; - short pci_index; /* Device index to PCI BIOS calls */ + int i, j; + int chips; + int count = 0; + uchar bus, device_fn; + short index; + int attach_count = 0; + ncr_device device[8]; +#ifdef SCSI_NCR_NVRAM_SUPPORT + ncr_nvram nvram[4]; + int k, nvrams; +#endif + int hosts; + +#ifdef SCSI_NCR_NVRAM_SUPPORT + int nvram_index = 0; +#endif + if (initverbose >= 2) + ncr_print_driver_setup(); + +#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT + ncr_debug = driver_setup.debug; +#endif #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) tpnt->proc_dir = &proc_scsi_ncr53c8xx; @@ -7459,131 +8979,363 @@ # endif #endif - if (pcibios_present()) { - for (i = 0; i < NPCI_CHIP_IDS; ++i) - for (pci_index = 0; - !pcibios_find_device(PCI_VENDOR_ID_NCR, - pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, - &pci_device_fn); - ++pci_index) - if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip, - pci_bus, pci_device_fn, /* no options */ 0)) - ++count; - } + /* + ** Detect all 53c8xx hosts and then attach them. + ** + ** If we are using NVRAM, once all hosts are detected, we need to check + ** any NVRAM for boot order in case detect and boot order differ and + ** attach them using the order in the NVRAM. + ** + ** If no NVRAM is found or data appears invalid attach boards in the + ** the order they are detected. + */ - return count; -} + if (!pcibios_present()) + return 0; + + chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]); + hosts = sizeof(device) / sizeof(device[0]); +#ifdef SCSI_NCR_NVRAM_SUPPORT + k = 0; + if (driver_setup.use_nvram & 0x1) + nvrams = sizeof(nvram) / sizeof(nvram[0]); + else + nvrams = 0; +#endif + + for (j = 0; j < chips ; ++j) { + i = driver_setup.reverse_probe ? chips-1 - j : j; + for (index = 0; ; index++) { + char *msg = ""; + if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i], + index, &bus, &device_fn)) || + (count == hosts)) + break; +#ifdef SCSI_NCR_NVRAM_SUPPORT + device[count].nvram = k < nvrams ? &nvram[k] : 0; +#else + device[count].nvram = 0; +#endif + if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) { + device[count].nvram = 0; + continue; + } +#ifdef SCSI_NCR_NVRAM_SUPPORT + if (device[count].nvram) { + ++k; + nvram_index |= device[count].nvram->type; + switch (device[count].nvram->type) { + case SCSI_NCR_TEKRAM_NVRAM: + msg = "with Tekram NVRAM"; + break; + case SCSI_NCR_SYMBIOS_NVRAM: + msg = "with Symbios NVRAM"; + break; + default: + msg = ""; + device[count].nvram = 0; + --k; + } + } +#endif + printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n", + device[count].chip.name, msg); + + device[count].attached = 0; + ++count; + } + } +#ifdef SCSI_NCR_NVRAM_SUPPORT + attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device); +#endif + /* + ** rescan device list to make sure all boards attached. + ** devices without boot records will not be attached yet + ** so try to attach them here. + */ + for (i= 0; i < count; i++) { + if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) { + attach_count++; + device[i].attached = 1; + } + } + return attach_count; +} /* -** Read the PCI configuration of a found NCR board and -** try yo attach it. +** Read and check the PCI configuration for any detected NCR +** boards and save data for attaching after all boards have +** been detected. */ -static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip, - uchar bus, uchar device_fn, int options) -{ - ushort vendor_id, device_id, command; -#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) - uint base, io_port; +__initfunc( +static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, + uchar bus, uchar device_fn, ncr_device *device) +) +{ + ushort vendor_id, device_id, command; + uchar cache_line_size, latency_timer; + uchar irq, revision; +#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) + uint base, io_port; +#else + ulong base, io_port; +#endif + int i, error; + +#ifdef SCSI_NCR_NVRAM_SUPPORT + ncr_nvram *nvram = device->nvram; +#endif + ncr_chip *chip; + + printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n", + bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7); + /* + * Read info from the PCI config space + */ + if ( + (error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id)) || + (error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id)) || + (error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) || + (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port)) || + (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) || + (error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision)) || + (error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq)) || + (error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) || + (error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer)) + ) + goto err_pcibios; + + /* + * Check if the chip is supported + */ + chip = 0; + for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) { + if (device_id != ncr_chip_table[i].device_id) + continue; + if (revision > ncr_chip_table[i].revision_id) + continue; + chip = &device->chip; + memcpy(chip, &ncr_chip_table[i], sizeof(*chip)); + chip->revision_id = revision; + break; + } + if (!chip) { + printk("ncr53c8xx: not initializing, device not supported\n"); + return -1; + } + + /* + * Check availability of IO space, memory space and master capability. + */ + if (command & PCI_COMMAND_IO) { + if ((io_port & 3) != 1) { + printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n" + " bits 0..1 indicate a non-IO mapping\n", (int) io_port); + io_port = 0; + } + else + io_port &= PCI_BASE_ADDRESS_IO_MASK; + } + else + io_port = 0; + + if (command & PCI_COMMAND_MEMORY) { + if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { + printk("ncr53c8xx: disabling memory mapping since base address 1\n" + " contains a non-memory mapping\n"); + base = 0; + } + else + base &= PCI_BASE_ADDRESS_MEM_MASK; + } + else + base = 0; + + if (!io_port && !base) { + printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n"); + return -1; + } + + if (io_port && check_region (io_port, 128)) { + printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n", + (int) io_port, (int) (io_port + 127)); + return -1; + } + + if (!(command & PCI_COMMAND_MASTER)) { + printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n"); + return -1; + } + + /* + * Fix some features according to driver setup. + */ + if (!driver_setup.special_features) + chip->features &= ~FE_SPECIAL_SET; + if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) { + chip->features |= FE_ULTRA; + chip->features &= ~FE_ULTRA2; + } + if (driver_setup.ultra_scsi < 1) + chip->features &= ~FE_ULTRA; + if (!driver_setup.max_wide) + chip->features &= ~FE_WIDE; + + +#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT + + /* + * Try to fix up PCI config according to wished features. + */ +#if defined(__i386) && !defined(MODULE) + if ((driver_setup.pci_fix_up & 1) && + (chip->features & FE_CLSE) && cache_line_size == 0) { + extern char x86; + switch(x86) { + case 4: cache_line_size = 4; break; + case 5: cache_line_size = 8; break; + } + if (cache_line_size) + error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size); + if (error) + goto err_pcibios; + if (initverbose) + printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size); + } + + if ((driver_setup.pci_fix_up & 2) && cache_line_size && + (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) { + command |= PCI_COMMAND_INVALIDATE; + error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command); + if (error) + goto err_pcibios; + if (initverbose) + printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n"); + } +#endif + /* + * Fix up for old chips that support READ LINE but not CACHE LINE SIZE. + * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords + * and donnot enable READ LINE. + * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed). + */ + + if (!(chip->features & FE_CLSE)) { + int burst_max = chip->burst_max; + if (cache_line_size == 0) { + chip->features &= ~FE_ERL; + if (burst_max > 3) + burst_max = 3; + } + else { + while (cache_line_size < (1 << burst_max)) + --burst_max; + } + chip->burst_max = burst_max; + } + + /* + * Tune PCI LATENCY TIMER according to burst max length transfer. + * (latency timer >= burst length + 6, we add 10 to be quite sure) + * If current value is zero, the device has probably been configured + * for no bursting due to some broken hardware. + */ + + if (latency_timer == 0 && chip->burst_max) + printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n"); + + if ((driver_setup.pci_fix_up & 4) && chip->burst_max) { + uchar lt = (1 << chip->burst_max) + 6 + 10; + if (latency_timer < lt) { + latency_timer = lt; + if (initverbose) + printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer); + error = pcibios_write_config_byte(bus, device_fn, + PCI_LATENCY_TIMER, latency_timer); + if (error) + goto err_pcibios; + } + } + + /* + * Fix up for recent chips that support CACHE LINE SIZE. + * If PCI config space is not OK, remove features that shall not be + * used by the chip. No need to trigger possible chip bugs. + */ + + if ((chip->features & FE_CLSE) && cache_line_size == 0) { + chip->features &= ~FE_CACHE_SET; + printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n"); + } + + if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) { + chip->features &= ~FE_WRIE; + printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n"); + } + +#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */ + + /* initialise ncr_device structure with items required by ncr_attach */ + device->slot.bus = bus; + device->slot.device_fn = device_fn; + device->slot.base = base; + device->slot.io_port = io_port; + device->slot.irq = irq; + device->attached = 0; +#ifdef SCSI_NCR_NVRAM_SUPPORT + if (!nvram) + goto out; + + /* + ** Get access to chip IO registers + */ +#ifdef NCR_IOMAPPED + request_region(io_port, 128, "ncr53c8xx"); + device->slot.port = ioport; +#else + device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128); + if (!device->slot.reg) + goto out; +#endif + + /* + ** Try to read SYMBIOS nvram. + ** Data can be used to order booting of boards. + ** + ** Data is saved in ncr_device structure if NVRAM found. This + ** is then used to find drive boot order for ncr_attach(). + ** + ** NVRAM data is passed to Scsi_Host_Template later during ncr_attach() + ** for any device set up. + ** + ** Try to read TEKRAM nvram if Symbios nvram not found. + */ + + if (!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios)) + nvram->type = SCSI_NCR_SYMBIOS_NVRAM; + else if (!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram)) + nvram->type = SCSI_NCR_TEKRAM_NVRAM; + else + nvram->type = 0; +out: + /* + ** Release access to chip IO registers + */ +#ifdef NCR_IOMAPPED + release_region(device->slot.port, 128); #else - ulong base, io_port; + unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128); #endif - uchar irq, revision; - int error, expected_chip; - int expected_id = -1, max_revision = -1, min_revision = -1; - int i; - - printk("ncr53c8xx : at PCI bus %d, device %d, function %d\n", - bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7); - - if (!pcibios_present()) { - printk("ncr53c8xx : not initializing due to lack of PCI BIOS,\n"); - return -1; - } - - if ((error = pcibios_read_config_word( bus, device_fn, PCI_VENDOR_ID, &vendor_id)) || - (error = pcibios_read_config_word( bus, device_fn, PCI_DEVICE_ID, &device_id)) || - (error = pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) || - (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, &io_port)) || - (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) || - (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION, &revision)) || - (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE, &irq))) { - printk("ncr53c8xx : error %s not initializing due to error reading configuration space\n", - pcibios_strerror(error)); - return -1; - } - - if (vendor_id != PCI_VENDOR_ID_NCR) { - printk("ncr53c8xx : not initializing, 0x%04x is not NCR vendor ID\n", (int) vendor_id); - return -1; - } - - - if (command & PCI_COMMAND_IO) { - if ((io_port & 3) != 1) { - printk("ncr53c8xx : disabling I/O mapping since base address 0 (0x%x)\n" - " bits 0..1 indicate a non-IO mapping\n", (int) io_port); - io_port = 0; - } - else - io_port &= PCI_BASE_ADDRESS_IO_MASK; - } - else - io_port = 0; - if (command & PCI_COMMAND_MEMORY) { - if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { - printk("ncr53c8xx : disabling memory mapping since base address 1\n" - " contains a non-memory mapping\n"); - base = 0; - } - else - base &= PCI_BASE_ADDRESS_MEM_MASK; - } - else - base = 0; - - if (!io_port && !base) { - printk("ncr53c8xx : not initializing, both I/O and memory mappings disabled\n"); - return -1; - } - - if (!(command & PCI_COMMAND_MASTER)) { - printk ("ncr53c8xx : not initializing, BUS MASTERING was disabled\n"); - return -1; - } - - for (i = 0; i < NPCI_CHIP_IDS; ++i) { - if (device_id == pci_chip_ids[i].pci_device_id) { - max_revision = pci_chip_ids[i].max_revision; - min_revision = pci_chip_ids[i].min_revision; - expected_chip = pci_chip_ids[i].chip; - } - if (chip == pci_chip_ids[i].chip) - expected_id = pci_chip_ids[i].pci_device_id; - } - - if (chip && device_id != expected_id) - printk("ncr53c8xx : warning : device id of 0x%04x doesn't\n" - " match expected 0x%04x\n", - (unsigned int) device_id, (unsigned int) expected_id ); - - if (max_revision != -1 && revision > max_revision) - printk("ncr53c8xx : warning : revision %d is greater than expected.\n", - (int) revision); - else if (min_revision != -1 && revision < min_revision) - printk("ncr53c8xx : warning : revision %d is lower than expected.\n", - (int) revision); - - if (io_port && check_region (io_port, 128)) { - printk("ncr53c8xx : IO region 0x%x to 0x%x is in use\n", - (int) io_port, (int) (io_port + 127)); - return -1; - } +#endif /* SCSI_NCR_NVRAM_SUPPORT */ + return 0; - return ncr_attach (tpnt, unit, device_id, revision, chip, base, io_port, - (int) irq, bus, (uchar) device_fn); +err_pcibios: + printk("ncr53c8xx: error %s reading configuration space\n", + pcibios_strerror(error)); + return -1; } #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0) @@ -7596,13 +9348,18 @@ for (device = devlist; device; device = device->next) { if (device->host == host) { +#if SCSI_NCR_MAX_TAGS > 1 if (device->tagged_supported) { device->queue_depth = SCSI_NCR_MAX_TAGS; } else { - device->queue_depth = 1; + device->queue_depth = 2; } -#ifdef DEBUG +#else + device->queue_depth = 1; +#endif + +#ifdef DEBUG_NCR53C8XX printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n", device->id, device->lun, device->queue_depth); #endif @@ -7618,19 +9375,19 @@ int ncr53c8xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) { int sts; -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printk("ncr53c8xx_queue_command\n"); #endif if ((sts = ncr_queue_command(cmd, done)) != DID_OK) { cmd->result = ScsiResult(sts, 0); done(cmd); -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printk("ncr53c8xx : command not queued - result=%d\n", sts); #endif return sts; } -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printk("ncr53c8xx : command successfully queued\n"); #endif return sts; @@ -7648,8 +9405,13 @@ { struct Scsi_Host *host; struct host_data *host_data; +#if 0 + u_long flags; + + save_flags(flags); cli(); +#endif -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printk("ncr53c8xx : interrupt received\n"); #endif @@ -7658,12 +9420,20 @@ host_data = (struct host_data *) host->hostdata; #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70) # ifdef SCSI_NCR_SHARE_IRQ - if (dev_id == &host_data->ncb_data) + if (dev_id == host_data->ncb) { +#else + if (1) { # endif #endif - ncr_intr(&host_data->ncb_data); + if (DEBUG_FLAGS & DEBUG_TINY) printf ("["); + ncr_exception(host_data->ncb); + if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n"); + } } } +#if 0 + restore_flags(flags); +#endif } /* @@ -7679,40 +9449,106 @@ ** Linux entry point of reset() function */ -#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98) +#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS + int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags) +{ + int sts; + unsigned long flags; + + printk("ncr53c8xx_reset: pid=%lu reset_flags=%x serial_number=%ld serial_number_at_timeout=%ld\n", + cmd->pid, reset_flags, cmd->serial_number, cmd->serial_number_at_timeout); + + save_flags(flags); cli(); + + /* + * We have to just ignore reset requests in some situations. + */ +#if defined SCSI_RESET_NOT_RUNNING + if (cmd->serial_number != cmd->serial_number_at_timeout) { + sts = SCSI_RESET_NOT_RUNNING; + goto out; + } +#endif + /* + * If the mid-level driver told us reset is synchronous, it seems + * that we must call the done() callback for the involved command, + * even if this command was not queued to the low-level driver, + * before returning SCSI_RESET_SUCCESS. + */ + + sts = ncr_reset_bus(cmd, + (reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS); + /* + * Since we always reset the controller, when we return success, + * we add this information to the return code. + */ +#if defined SCSI_RESET_HOST_RESET + if (sts == SCSI_RESET_SUCCESS) + sts |= SCSI_RESET_HOST_RESET; +#endif + +out: + restore_flags(flags); + return sts; +} #else int ncr53c8xx_reset(Scsi_Cmnd *cmd) -#endif { -#ifdef DEBUG -printk("ncr53c8xx_reset : reset call\n"); -#endif - return ncr_reset_bus(cmd); + printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid); + return ncr_reset_bus(cmd, 1); } +#endif /* ** Linux entry point of abort() function */ +#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS + +int ncr53c8xx_abort(Scsi_Cmnd *cmd) +{ + int sts; + unsigned long flags; + + printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n", + cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout); + + save_flags(flags); cli(); + + /* + * We have to just ignore abort requests in some situations. + */ + if (cmd->serial_number != cmd->serial_number_at_timeout) { + sts = SCSI_ABORT_NOT_RUNNING; + goto out; + } + + sts = ncr_abort_command(cmd); +out: + restore_flags(flags); + return sts; +} +#else int ncr53c8xx_abort(Scsi_Cmnd *cmd) { -printk("ncr53c8xx_abort : abort call\n"); + printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid); return ncr_abort_command(cmd); } +#endif #ifdef MODULE int ncr53c8xx_release(struct Scsi_Host *host) { struct host_data *host_data; -#ifdef DEBUG +#ifdef DEBUG_NCR53C8XX printk("ncr53c8xx : release\n"); #endif for (host = first_host; host; host = host->next) { if (host->hostt == the_template) { host_data = (struct host_data *) host->hostdata; - ncr_detach(&host_data->ncb_data, host->irq); + ncr_detach(host_data->ncb, host->irq); } } @@ -7752,17 +9588,19 @@ } } -static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd) +static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd) { Scsi_Cmnd *wcmd; if (!(wcmd = np->waiting_list)) return 0; while (wcmd->next_wcmd) { if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) { - wcmd->next_wcmd = cmd->next_wcmd; - cmd->next_wcmd = 0; + if (to_remove) { + wcmd->next_wcmd = cmd->next_wcmd; + cmd->next_wcmd = 0; + } #ifdef DEBUG_WAITING_LIST - printf("%s: cmd %lx removed from waiting list\n", ncr_name(np), (u_long) cmd); + printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd); #endif return cmd; } @@ -7802,13 +9640,7 @@ #undef next_wcmd /* -** In order to patch the SCSI script for SAVE/RESTORE DATA POINTER, -** we need the direction of transfer. -** Linux middle-level scsi driver does not provide this information. -** So we have to guess it. -** My documentation about SCSI-II standard is old. Probably some opcode -** are missing. -** If I do'nt know the command code, I assume input transfer direction. +** Returns data transfer direction for common op-codes. */ static int guess_xfer_direction(int opcode) @@ -7816,111 +9648,31 @@ int d; switch(opcode) { - case 0x00: /* TEST UNIT READY 00 */ - case 0x08: /* READ(6) 08 */ case 0x12: /* INQUIRY 12 */ case 0x4D: /* LOG SENSE 4D */ case 0x5A: /* MODE SENSE(10) 5A */ case 0x1A: /* MODE SENSE(6) 1A */ - case 0x28: /* READ(10) 28 */ - case 0xA8: /* READ(12) A8 */ case 0x3C: /* READ BUFFER 3C */ case 0x1C: /* RECEIVE DIAGNOSTIC RESULTS 1C */ - case 0xB7: /* READ DEFECT DATA(12) B7 */ - case 0xB8: /* READ ELEMENT STATUS B8 */ - /* GET WINDOW 25 */ - case 0x25: /* READ CAPACITY 25 */ - case 0x29: /* READ GENERATION 29 */ - case 0x3E: /* READ LONG 3E */ - /* GET DATA BUFFER STATUS 34 */ - /* PRE-FETCH 34 */ - case 0x34: /* READ POSITION 34 */ case 0x03: /* REQUEST SENSE 03 */ - case 0x05: /* READ BLOCK LIMITS 05 */ - case 0x0F: /* READ REVERSE 0F */ - case 0x14: /* RECOVER BUFFERED DATA 14 */ - case 0x2D: /* READ UPDATED BLOCK 2D */ - case 0x37: /* READ DEFECT DATA(10) 37 */ - case 0x42: /* READ SUB-CHANNEL 42 */ - case 0x43: /* READ TOC 43 */ - case 0x44: /* READ HEADER 44 */ - case 0xC7: /* ??? ??? C7 */ d = XferIn; break; case 0x39: /* COMPARE 39 */ case 0x3A: /* COPY AND VERIFY 3A */ - /* PRINT 0A */ - /* SEND MESSAGE(6) 0A */ - case 0x0A: /* WRITE(6) 0A */ case 0x18: /* COPY 18 */ case 0x4C: /* LOG SELECT 4C */ case 0x55: /* MODE SELECT(10) 55 */ case 0x3B: /* WRITE BUFFER 3B */ case 0x1D: /* SEND DIAGNOSTIC 1D */ case 0x40: /* CHANGE DEFINITION 40 */ - /* SEND MESSAGE(12) AA */ - case 0xAA: /* WRITE(12) AA */ - case 0xB6: /* SEND VOLUME TAG B6 */ - case 0x3F: /* WRITE LONG 3F */ - case 0x04: /* FORMAT UNIT 04 */ - /* INITIALIZE ELEMENT STATUS 07 */ - case 0x07: /* REASSIGN BLOCKS 07 */ case 0x15: /* MODE SELECT(6) 15 */ - case 0x24: /* SET WINDOW 24 */ - case 0x2A: /* WRITE(10) 2A */ - case 0x2E: /* WRITE AND VERIFY(10) 2E */ - case 0xAE: /* WRITE AND VERIFY(12) AE */ - case 0xB0: /* SEARCH DATA HIGH(12) B0 */ - case 0xB1: /* SEARCH DATA EQUAL(12) B1 */ - case 0xB2: /* SEARCH DATA LOW(12) B2 */ - /* OBJECT POSITION 31 */ - case 0x30: /* SEARCH DATA HIGH(10) 30 */ - case 0x31: /* SEARCH DATA EQUAL(10) 31 */ - case 0x32: /* SEARCH DATA LOW(10) 32 */ - case 0x38: /* MEDIUM SCAN 38 */ - case 0x3D: /* UPDATE BLOCK 3D */ - case 0x41: /* WRITE SAME 41 */ - /* LOAD UNLOAD 1B */ - /* SCAN 1B */ - case 0x1B: /* START STOP UNIT 1B */ d = XferOut; break; - case 0x01: /* REZERO UNIT 01 */ - /* SEEK(6) 0B */ - case 0x0B: /* SLEW AND PRINT 0B */ - /* SYNCHRONIZE BUFFER 10 */ - case 0x10: /* WRITE FILEMARKS 10 */ - case 0x11: /* SPACE 11 */ - case 0x13: /* VERIFY 13 */ - case 0x16: /* RESERVE UNIT 16 */ - case 0x17: /* RELEASE UNIT 17 */ - case 0x19: /* ERASE 19 */ - /* LOCATE 2B */ - /* POSITION TO ELEMENT 2B */ - case 0x2B: /* SEEK(10) 2B */ - case 0x1E: /* PREVENT ALLOW MEDIUM REMOVAL 1E */ - case 0x2C: /* ERASE(10) 2C */ - case 0xAC: /* ERASE(12) AC */ - case 0x2F: /* VERIFY(10) 2F */ - case 0xAF: /* VERIFY(12) AF */ - case 0x33: /* SET LIMITS(10) 33 */ - case 0xB3: /* SET LIMITS(12) B3 */ - case 0x35: /* SYNCHRONIZE CACHE 35 */ - case 0x36: /* LOCK UNLOCK CACHE 36 */ - case 0x45: /* PLAY AUDIO(10) 45 */ - case 0x47: /* PLAY AUDIO MSF 47 */ - case 0x48: /* PLAY AUDIO TRACK/INDEX 48 */ - case 0x49: /* PLAY TRACK RELATIVE(10) 49 */ - case 0xA9: /* PLAY TRACK RELATIVE(12) A9 */ - case 0x4B: /* PAUSE/RESUME 4B */ - /* MOVE MEDIUM A5 */ - case 0xA5: /* PLAY AUDIO(12) A5 */ - case 0xA6: /* EXCHANGE MEDIUM A6 */ - case 0xB5: /* REQUEST VOLUME ELEMENT ADDRESS B5 */ + case 0x00: /* TEST UNIT READY 00 */ d = XferNone; break; default: - d = XferIn; + d = XferBoth; break; } @@ -7940,6 +9692,8 @@ **========================================================================= */ +#ifdef SCSI_NCR_USER_COMMAND_SUPPORT + #define is_digit(c) ((c) >= '0' && (c) <= '9') #define digit_to_bin(c) ((c) - '0') #define is_space(c) ((c) == ' ' || (c) == '\t') @@ -8021,6 +9775,10 @@ uc->cmd = UC_SETFLAG; else if ((arg_len = is_keyword(ptr, len, "clearprof")) != 0) uc->cmd = UC_CLEARPROF; +#ifdef UC_DEBUG_ERROR_RECOVERY + else if ((arg_len = is_keyword(ptr, len, "debug_error_recovery")) != 0) + uc->cmd = UC_DEBUG_ERROR_RECOVERY; +#endif else arg_len = 0; @@ -8038,13 +9796,16 @@ case UC_SETWIDE: case UC_SETFLAG: SKIP_SPACES(1); - GET_INT_ARG(target); + if ((arg_len = is_keyword(ptr, len, "all")) != 0) { + ptr += arg_len; len -= arg_len; + uc->target = ~0; + } else { + GET_INT_ARG(target); + uc->target = (1< MAX_TARGET) - return -EINVAL; - uc->target = (1<data |= DEBUG_ALLOC; + else if ((arg_len = is_keyword(ptr, len, "phase"))) + uc->data |= DEBUG_PHASE; else if ((arg_len = is_keyword(ptr, len, "poll"))) uc->data |= DEBUG_POLL; else if ((arg_len = is_keyword(ptr, len, "queue"))) @@ -8086,6 +9849,8 @@ uc->data |= DEBUG_SCRIPT; else if ((arg_len = is_keyword(ptr, len, "tiny"))) uc->data |= DEBUG_TINY; + else if ((arg_len = is_keyword(ptr, len, "timing"))) + uc->data |= DEBUG_TIMING; else if ((arg_len = is_keyword(ptr, len, "nego"))) uc->data |= DEBUG_NEGO; else if ((arg_len = is_keyword(ptr, len, "tags"))) @@ -8107,24 +9872,37 @@ SKIP_SPACES(1); if ((arg_len = is_keyword(ptr, len, "trace"))) uc->data |= UF_TRACE; + else if ((arg_len = is_keyword(ptr, len, "no_disc"))) + uc->data |= UF_NODISC; else return -EINVAL; ptr += arg_len; len -= arg_len; } break; +#ifdef UC_DEBUG_ERROR_RECOVERY + case UC_DEBUG_ERROR_RECOVERY: + SKIP_SPACES(1); + if ((arg_len = is_keyword(ptr, len, "sge"))) + uc->data = 1; + else if ((arg_len = is_keyword(ptr, len, "abort"))) + uc->data = 2; + else if ((arg_len = is_keyword(ptr, len, "reset"))) + uc->data = 3; + else if ((arg_len = is_keyword(ptr, len, "parity"))) + uc->data = 4; + else if ((arg_len = is_keyword(ptr, len, "none"))) + uc->data = 0; + else + return -EINVAL; + ptr += arg_len; len -= arg_len; + break; +#endif default: break; } - /* - ** Not allow to disable tagged queue - */ - if (uc->cmd == UC_SETTAGS && uc->data < 1) - return -EINVAL; - if (len) return -EINVAL; -#ifdef SCSI_NCR_USER_COMMAND else { long flags; @@ -8132,10 +9910,13 @@ ncr_usercmd (np); restore_flags(flags); } -#endif return length; } +#endif /* SCSI_NCR_USER_COMMAND_SUPPORT */ + +#ifdef SCSI_NCR_USER_INFO_SUPPORT + struct info_str { char *buffer; @@ -8182,6 +9963,8 @@ ** Copy formatted profile information into the input buffer. */ +#define to_ms(t) ((t) * 1000 / HZ) + static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len) { struct info_str info; @@ -8192,7 +9975,7 @@ info.pos = 0; copy_info(&info, "General information:\n"); - copy_info(&info, " Chip NCR53C%03d, ", np->chip); + copy_info(&info, " Chip NCR53C%s, ", np->chip_name); copy_info(&info, "device id 0x%x, ", np->device_id); copy_info(&info, "revision id 0x%x\n", np->revision_id); @@ -8200,12 +9983,19 @@ copy_info(&info, "IRQ number %d\n", (int) np->irq); #ifndef NCR_IOMAPPED - if (np->use_mmio) + if (np->reg) copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n", - (u_long) np->reg_remapped); + (u_long) np->reg); #endif + copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync); + copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS); + + if (driver_setup.debug || driver_setup.verbose > 1) { + copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug); + copy_info(&info, "verbosity level %d\n", driver_setup.verbose); + } -#ifdef SCSI_NCR_PROFILE +#ifdef SCSI_NCR_PROFILE_SUPPORT copy_info(&info, "Profiling information:\n"); copy_info(&info, " %-12s = %lu\n", "num_trans",np->profile.num_trans); copy_info(&info, " %-12s = %lu\n", "num_kbytes",np->profile.num_kbytes); @@ -8213,15 +10003,17 @@ copy_info(&info, " %-12s = %lu\n", "num_break",np->profile.num_break); copy_info(&info, " %-12s = %lu\n", "num_int", np->profile.num_int); copy_info(&info, " %-12s = %lu\n", "num_fly", np->profile.num_fly); - copy_info(&info, " %-12s = %lu\n", "ms_setup", np->profile.ms_setup); - copy_info(&info, " %-12s = %lu\n", "ms_data", np->profile.ms_data); - copy_info(&info, " %-12s = %lu\n", "ms_disc", np->profile.ms_disc); - copy_info(&info, " %-12s = %lu\n", "ms_post", np->profile.ms_post); + copy_info(&info, " %-12s = %lu\n", "ms_setup", to_ms(np->profile.ms_setup)); + copy_info(&info, " %-12s = %lu\n", "ms_data", to_ms(np->profile.ms_data)); + copy_info(&info, " %-12s = %lu\n", "ms_disc", to_ms(np->profile.ms_disc)); + copy_info(&info, " %-12s = %lu\n", "ms_post", to_ms(np->profile.ms_post)); #endif return info.pos > info.offset? info.pos - info.offset : 0; } +#endif /* SCSI_NCR_USER_INFO_SUPPORT */ + /* ** Entry point of the scsi proc fs of the driver. ** - func = 0 means read (returns profile data) @@ -8243,7 +10035,7 @@ for (host = first_host; host; host = host->next) { if (host->hostt == the_template && host->host_no == hostno) { host_data = (struct host_data *) host->hostdata; - ncb = &host_data->ncb_data; + ncb = host_data->ncb; break; } } @@ -8252,26 +10044,486 @@ return -EINVAL; if (func) { +#ifdef SCSI_NCR_USER_COMMAND_SUPPORT retv = ncr_user_command(ncb, buffer, length); -#ifdef DEBUG_PROC_INFO -printf("ncr_user_command: retv=%d\n", retv); +#else + retv = -EINVAL; #endif } else { if (start) *start = buffer; +#ifdef SCSI_NCR_USER_INFO_SUPPORT retv = ncr_host_info(ncb, buffer, offset, length); +#else + retv = -EINVAL; +#endif } return retv; } + /*========================================================================= ** End of proc file system stuff **========================================================================= */ #endif + +#ifdef SCSI_NCR_NVRAM_SUPPORT + +/* --------------------------------------------------------------------- +** +** Try reading Symbios format nvram +** +** --------------------------------------------------------------------- +** +** GPOI0 - data in/data out +** GPIO1 - clock +** +** return 0 if NVRAM data OK, 1 if NVRAM data not OK +** --------------------------------------------------------------------- +*/ + +#define SET_BIT 0 +#define CLR_BIT 1 +#define SET_CLK 2 +#define CLR_CLK 3 + +static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl); +static void nvram_start(ncr_slot *np, u_char *gpreg); +static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl); +static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl); +static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl); +static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl); +static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg); +static void nvram_stop(ncr_slot *np, u_char *gpreg); +static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode); + +__initfunc( +static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram) +) +{ + static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0}; + u_char gpcntl, gpreg; + u_char old_gpcntl, old_gpreg; + u_short csum; + u_char ack_data; + int retv = 1; + + /* save current state of GPCNTL and GPREG */ + old_gpreg = INB (nc_gpreg); + old_gpcntl = INB (nc_gpcntl); + gpcntl = old_gpcntl & 0xfc; + + /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */ + OUTB (nc_gpreg, old_gpreg); + OUTB (nc_gpcntl, gpcntl); + + /* this is to set NVRAM into a known state with GPIO0/1 both low */ + gpreg = old_gpreg; + nvram_setBit(np, 0, &gpreg, CLR_CLK); + nvram_setBit(np, 0, &gpreg, CLR_BIT); + + /* now set NVRAM inactive with GPIO0/1 both high */ + nvram_stop(np, &gpreg); + + /* activate NVRAM */ + nvram_start(np, &gpreg); + + /* write device code and random address MSB */ + nvram_write_byte(np, &ack_data, + 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl); + if (ack_data & 0x01) + goto out; + + /* write random address LSB */ + nvram_write_byte(np, &ack_data, + (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl); + if (ack_data & 0x01) + goto out; + + /* regenerate START state to set up for reading */ + nvram_start(np, &gpreg); + + /* rewrite device code and address MSB with read bit set (lsb = 0x01) */ + nvram_write_byte(np, &ack_data, + 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl); + if (ack_data & 0x01) + goto out; + + /* now set up GPIO0 for inputting data */ + gpcntl |= 0x01; + OUTB (nc_gpcntl, gpcntl); + + /* input all active data - only part of total NVRAM */ + csum = nvram_read_data(np, + (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl); + + /* finally put NVRAM back in inactive mode */ + gpcntl &= 0xfe; + OUTB (nc_gpcntl, gpcntl); + nvram_stop(np, &gpreg); + +#ifdef SCSI_NCR_DEBUG_NVRAM +printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n", + nvram->start_marker, + nvram->trailer[0], nvram->trailer[1], nvram->trailer[2], + nvram->trailer[3], nvram->trailer[4], nvram->trailer[5], + nvram->byte_count, sizeof(*nvram) - 12, + nvram->checksum, csum); +#endif + + /* check valid NVRAM signature, verify byte count and checksum */ + if (nvram->start_marker == 0 && + !memcmp(nvram->trailer, Symbios_trailer, 6) && + nvram->byte_count == sizeof(*nvram) - 12 && + csum == nvram->checksum) + retv = 0; +out: + /* return GPIO0/1 to original states after having accessed NVRAM */ + OUTB (nc_gpcntl, old_gpcntl); + OUTB (nc_gpreg, old_gpreg); + + return retv; +} + +/* + * Read Symbios NvRAM data and compute checksum. + */ +__initfunc( +static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl) +) +{ + int x; + u_short csum; + + for (x = 0; x < len; x++) + nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl); + + for (x = 6, csum = 0; x < len - 6; x++) + csum += data[x]; + + return csum; +} + +/* + * Send START condition to NVRAM to wake it up. + */ +__initfunc( +static void nvram_start(ncr_slot *np, u_char *gpreg) +) +{ + nvram_setBit(np, 1, gpreg, SET_BIT); + nvram_setBit(np, 0, gpreg, SET_CLK); + nvram_setBit(np, 0, gpreg, CLR_BIT); + nvram_setBit(np, 0, gpreg, CLR_CLK); +} + +/* + * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK, + * GPIO0 must already be set as an output + */ +__initfunc( +static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl) +) +{ + int x; + + for (x = 0; x < 8; x++) + nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg); + + nvram_readAck(np, ack_data, gpreg, gpcntl); +} + +/* + * READ a byte from the NVRAM and then send an ACK to say we have got it, + * GPIO0 must already be set as an input + */ +__initfunc( +static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl) +) +{ + int x; + u_char read_bit; + + *read_data = 0; + for (x = 0; x < 8; x++) { + nvram_doBit(np, &read_bit, 1, gpreg); + *read_data |= ((read_bit & 0x01) << (7 - x)); + } + + nvram_writeAck(np, ack_data, gpreg, gpcntl); +} + +/* + * Output an ACK to the NVRAM after reading, + * change GPIO0 to output and when done back to an input + */ +__initfunc( +static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl) +) +{ + OUTB (nc_gpcntl, *gpcntl & 0xfe); + nvram_doBit(np, 0, write_bit, gpreg); + OUTB (nc_gpcntl, *gpcntl); +} + +/* + * Input an ACK from NVRAM after writing, + * change GPIO0 to input and when done back to an output + */ +__initfunc( +static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl) +) +{ + OUTB (nc_gpcntl, *gpcntl | 0x01); + nvram_doBit(np, read_bit, 1, gpreg); + OUTB (nc_gpcntl, *gpcntl); +} + +/* + * Read or write a bit to the NVRAM, + * read if GPIO0 input else write if GPIO0 output + */ +__initfunc( +static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg) +) +{ + nvram_setBit(np, write_bit, gpreg, SET_BIT); + nvram_setBit(np, 0, gpreg, SET_CLK); + if (read_bit) + *read_bit = INB (nc_gpreg); + nvram_setBit(np, 0, gpreg, CLR_CLK); + nvram_setBit(np, 0, gpreg, CLR_BIT); +} + +/* + * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!! + */ +__initfunc( +static void nvram_stop(ncr_slot *np, u_char *gpreg) +) +{ + nvram_setBit(np, 0, gpreg, SET_CLK); + nvram_setBit(np, 1, gpreg, SET_BIT); +} + +/* + * Set/clear data/clock bit in GPIO0 + */ +__initfunc( +static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode) +) +{ + DELAY(5); + switch (bit_mode){ + case SET_BIT: + *gpreg |= write_bit; + break; + case CLR_BIT: + *gpreg &= 0xfe; + break; + case SET_CLK: + *gpreg |= 0x02; + break; + case CLR_CLK: + *gpreg &= 0xfd; + break; + + } + OUTB (nc_gpreg, *gpreg); + DELAY(5); +} + +#undef SET_BIT 0 +#undef CLR_BIT 1 +#undef SET_CLK 2 +#undef CLR_CLK 3 + + +/* --------------------------------------------------------------------- +** +** Try reading Tekram format nvram +** +** --------------------------------------------------------------------- +** +** GPOI0 - data in +** GPIO1 - data out +** GPIO2 - clock +** GPIO4 - chip select +** +** return 0 if NVRAM data OK, 1 if NVRAM data not OK +** --------------------------------------------------------------------- +*/ + +static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg); +static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg); +static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg); +static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg); +static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg); +static void Tnvram_Stop(ncr_slot *np, u_char *gpreg); +static void Tnvram_Clk(ncr_slot *np, u_char *gpreg); + +__initfunc( +static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram) +) +{ + u_char gpcntl, gpreg; + u_char old_gpcntl, old_gpreg; + u_short csum; + + /* save current state of GPCNTL and GPREG */ + old_gpreg = INB (nc_gpreg); + old_gpcntl = INB (nc_gpcntl); + + /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in, + 1/2/4 out */ + gpreg = old_gpreg & 0xe9; + OUTB (nc_gpreg, gpreg); + gpcntl = (old_gpcntl & 0xe9) | 0x09; + OUTB (nc_gpcntl, gpcntl); + + /* input all of NVRAM, 64 words */ + csum = Tnvram_read_data(np, (u_short *) nvram, + sizeof(*nvram) / sizeof(short), &gpreg); + + /* return GPIO0/1/2/4 to original states after having accessed NVRAM */ + OUTB (nc_gpcntl, old_gpcntl); + OUTB (nc_gpreg, old_gpreg); + + /* check data valid */ + if (csum != 0x1234) + return 1; + + return 0; +} + +/* + * Read Tekram NvRAM data and compute checksum. + */ +__initfunc( +static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg) +) +{ + u_char read_bit; + u_short csum; + int x; + + for (x = 0, csum = 0; x < len; x++) { + + /* output read command and address */ + Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg); + if (read_bit & 0x01) + return 0; /* Force bad checksum */ + + Tnvram_Read_Word(np, &data[x], gpreg); + csum += data[x]; + + Tnvram_Stop(np, gpreg); + } + + return csum; +} + +/* + * Send read command and address to NVRAM + */ +__initfunc( +static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg) +) +{ + int x; + + /* send 9 bits, start bit (1), command (2), address (6) */ + for (x = 0; x < 9; x++) + Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg); + + *read_bit = INB (nc_gpreg); +} + +/* + * READ a byte from the NVRAM + */ +__initfunc( +static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg) +) +{ + int x; + u_char read_bit; + + *nvram_data = 0; + for (x = 0; x < 16; x++) { + Tnvram_Read_Bit(np, &read_bit, gpreg); + + if (read_bit & 0x01) + *nvram_data |= (0x01 << (15 - x)); + else + *nvram_data &= ~(0x01 << (15 - x)); + } +} + +/* + * Read bit from NVRAM + */ +__initfunc( +static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg) +) +{ + DELAY(2); + Tnvram_Clk(np, gpreg); + *read_bit = INB (nc_gpreg); +} + +/* + * Write bit to GPIO0 + */ +__initfunc( +static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg) +) +{ + if (write_bit & 0x01) + *gpreg |= 0x02; + else + *gpreg &= 0xfd; + + *gpreg |= 0x10; + + OUTB (nc_gpreg, *gpreg); + DELAY(2); + + Tnvram_Clk(np, gpreg); +} + +/* + * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!! + */ +__initfunc( +static void Tnvram_Stop(ncr_slot *np, u_char *gpreg) +) +{ + *gpreg &= 0xef; + OUTB (nc_gpreg, *gpreg); + DELAY(2); + + Tnvram_Clk(np, gpreg); +} + +/* + * Pulse clock bit in GPIO0 + */ +__initfunc( +static void Tnvram_Clk(ncr_slot *np, u_char *gpreg) +) +{ + OUTB (nc_gpreg, *gpreg | 0x04); + DELAY(2); + OUTB (nc_gpreg, *gpreg); +} + +#endif /* SCSI_NCR_NVRAM_SUPPORT */ + /* ** Module stuff */ diff -urN linux-2.0.31-pre5/drivers/scsi/ncr53c8xx.h linux-2.0.31-pre6/drivers/scsi/ncr53c8xx.h --- linux-2.0.31-pre5/drivers/scsi/ncr53c8xx.h 1997-03-11 15:40:02.000000000 -0800 +++ linux-2.0.31-pre6/drivers/scsi/ncr53c8xx.h 2003-08-15 15:04:01.000000000 -0700 @@ -42,7 +42,10 @@ #ifndef NCR53C8XX_H #define NCR53C8XX_H -/*********** LINUX SPECIFIC SECTION ******************/ +/* +** Name and revision of the driver +*/ +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.4" /* ** Check supported Linux versions @@ -65,28 +68,6 @@ #define LINUX_VERSION_CODE LinuxVersionCode(1,2,13) #endif -#if !defined(VERSION) -#define VERSION ((LINUX_VERSION_CODE >> 16) & 0xff) -#define PATCHLEVEL ((LINUX_VERSION_CODE >> 8) & 0xff) -#define SUBLEVEL ((LINUX_VERSION_CODE >> 0) & 0xff) -#endif - -#if VERSION == 0 || VERSION > 3 -# error Only Linux version 1 and probable 2 or 3 supported. -#endif - -#if VERSION == 1 && PATCHLEVEL == 2 -# if SUBLEVEL != 13 -# error Only sublevel 13 of Linux 1.2 is supported. -# endif -#endif - -#if VERSION == 1 && PATCHLEVEL == 3 -# if SUBLEVEL < 45 -# error Only sublevels >=45 of Linux 1.3 are supported. -# endif -#endif - /* ** Normal IO or memory mapped IO. ** @@ -108,53 +89,168 @@ #endif /* -** Avoid to change these constants, unless you know what you are doing. +** If you want a driver as small as possible, donnot define the +** following options. +*/ + +#define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT +#define SCSI_NCR_DEBUG_INFO_SUPPORT +#define SCSI_NCR_PCI_FIX_UP_SUPPORT +#ifdef SCSI_NCR_PROC_INFO_SUPPORT +# define SCSI_NCR_PROFILE_SUPPORT +# define SCSI_NCR_USER_COMMAND_SUPPORT +# define SCSI_NCR_USER_INFO_SUPPORT +/* # define SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */ +#endif + +/*========================================================== +** +** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable +** +**========================================================== +*/ + +#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT +#define SCSI_NCR_NVRAM_SUPPORT +/* #define SCSI_NCR_DEBUG_NVRAM */ +#endif + +/* --------------------------------------------------------------------- +** Take into account kernel configured parameters. +** Most of these options can be overridden at startup by a command line. +** --------------------------------------------------------------------- */ +/* + * For Ultra2 SCSI support option, use special features and allow 40Mhz + * synchronous data transfers. + */ +#define SCSI_NCR_SETUP_SPECIAL_FEATURES (1) +#define SCSI_NCR_SETUP_ULTRA_SCSI (2) +#define SCSI_NCR_MAX_SYNC (40) + +/* + * Allow tags from 2 to 12, default 4 + */ +#ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS +#if CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2 +#define SCSI_NCR_MAX_TAGS (2) +#elif CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 12 +#define SCSI_NCR_MAX_TAGS (12) +#else +#define SCSI_NCR_MAX_TAGS CONFIG_SCSI_NCR53C8XX_MAX_TAGS +#endif +#else #define SCSI_NCR_MAX_TAGS (4) -#define SCSI_NCR_ALWAYS_SIMPLE_TAG +#endif -#ifdef CONFIG_SCSI_NCR53C8XX_IOMAPPED +/* + * Allow tagged command queuing support if configured with default number + * of tags set to max (see above). + */ +#ifdef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE +#define SCSI_NCR_SETUP_DEFAULT_TAGS SCSI_NCR_MAX_TAGS +#else +#define SCSI_NCR_SETUP_DEFAULT_TAGS (0) +#endif + +/* + * Use normal IO if configured. Forced for alpha. + */ +#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__) #define SCSI_NCR_IOMAPPED #endif -#ifndef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE -#define SCSI_NCR_TAGGED_QUEUE_DISABLED +/* + * Sync transfer frequency at startup. + * Allow from 5Mhz to 40Mhz default 10 Mhz. + */ +#ifndef CONFIG_SCSI_NCR53C8XX_SYNC +#define CONFIG_SCSI_NCR53C8XX_SYNC (5) +#elif CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC +#define SCSI_NCR_SETUP_DEFAULT_SYNC SCSI_NCR_MAX_SYNC #endif -#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT -#define SCSI_NCR_NO_DISCONNECT +#if CONFIG_SCSI_NCR53C8XX_SYNC == 0 +#define SCSI_NCR_SETUP_DEFAULT_SYNC (255) +#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 5 +#define SCSI_NCR_SETUP_DEFAULT_SYNC (50) +#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 20 +#define SCSI_NCR_SETUP_DEFAULT_SYNC (250/(CONFIG_SCSI_NCR53C8XX_SYNC)) +#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 33 +#define SCSI_NCR_SETUP_DEFAULT_SYNC (11) +#else +#define SCSI_NCR_SETUP_DEFAULT_SYNC (10) #endif -#ifdef CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS -#define SCSI_NCR_FORCE_ASYNCHRONOUS +/* + * Disallow disconnections at boot-up + */ +#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT +#define SCSI_NCR_SETUP_DISCONNECTION (0) +#else +#define SCSI_NCR_SETUP_DISCONNECTION (1) #endif +/* + * Force synchronous negotiation for all targets + */ #ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO -#define SCSI_NCR_FORCE_SYNC_NEGO +#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (1) +#else +#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (0) #endif +/* + * Disable master parity checking (flawed hardwares need that) + */ #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK -#define SCSI_NCR_DISABLE_MPARITY_CHECK +#define SCSI_NCR_SETUP_MASTER_PARITY (0) +#else +#define SCSI_NCR_SETUP_MASTER_PARITY (1) #endif +/* + * Disable scsi parity checking (flawed devices may need that) + */ #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK -#define SCSI_NCR_DISABLE_PARITY_CHECK +#define SCSI_NCR_SETUP_SCSI_PARITY (0) +#else +#define SCSI_NCR_SETUP_SCSI_PARITY (1) #endif -#if 0 -#define SCSI_NCR_SEGMENT_SIZE (512) +/* + * Vendor specific stuff + */ +#ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT +#define SCSI_NCR_SETUP_LED_PIN (1) +#define SCSI_NCR_SETUP_DIFF_SUPPORT (3) +#else +#define SCSI_NCR_SETUP_LED_PIN (0) +#define SCSI_NCR_SETUP_DIFF_SUPPORT (0) #endif -#define SCSI_NCR_MAX_SCATTER (128) +/* + * Settle time after reset at boot-up + */ +#define SCSI_NCR_SETUP_SETTLE_TIME (2) + +/* +** Other parameters not configurable with "make config" +** Avoid to change these constants, unless you know what you are doing. +*/ + +#define SCSI_NCR_ALWAYS_SIMPLE_TAG +#define SCSI_NCR_MAX_SCATTER (127) #define SCSI_NCR_MAX_TARGET (16) #define SCSI_NCR_MAX_HOST (2) -#define SCSI_NCR_SETTLE_TIME (2) #define SCSI_NCR_TIMEOUT_ALERT (3*HZ) #define SCSI_NCR_CAN_QUEUE (7*SCSI_NCR_MAX_TAGS) #define SCSI_NCR_CMD_PER_LUN (SCSI_NCR_MAX_TAGS) -#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER-1) +#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER) + +#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5) #if 1 /* defined CONFIG_SCSI_MULTI_LUN */ #define SCSI_NCR_MAX_LUN (8) @@ -162,8 +258,6 @@ #define SCSI_NCR_MAX_LUN (1) #endif -#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5) - /* ** Define Scsi_Host_Template parameters ** @@ -196,7 +290,7 @@ #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) -#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12d)", ncr53c8xx_detect,\ +#define NCR53C8XX {NULL,NULL,NULL,NULL,SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\ ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \ ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \ NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\ @@ -207,7 +301,7 @@ #else -#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12d)", ncr53c8xx_detect,\ +#define NCR53C8XX {NULL, NULL, SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\ ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \ ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \ NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\ @@ -222,6 +316,200 @@ #ifndef HOSTS_C /* +** NCR53C8XX Device Ids +*/ + +#ifndef PCI_DEVICE_ID_NCR_53C810 +#define PCI_DEVICE_ID_NCR_53C810 1 +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C810AP +#define PCI_DEVICE_ID_NCR_53C810AP 5 +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C815 +#define PCI_DEVICE_ID_NCR_53C815 4 +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C820 +#define PCI_DEVICE_ID_NCR_53C820 2 +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C825 +#define PCI_DEVICE_ID_NCR_53C825 3 +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C860 +#define PCI_DEVICE_ID_NCR_53C860 6 +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C875 +#define PCI_DEVICE_ID_NCR_53C875 0xf +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C875J +#define PCI_DEVICE_ID_NCR_53C875J 0x8f +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C885 +#define PCI_DEVICE_ID_NCR_53C885 0xd +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C895 +#define PCI_DEVICE_ID_NCR_53C895 0xc +#endif + +#ifndef PCI_DEVICE_ID_NCR_53C896 +#define PCI_DEVICE_ID_NCR_53C896 0xb +#endif + +/* +** NCR53C8XX devices features table. +*/ +typedef struct { + unsigned short device_id; + unsigned short revision_id; + char *name; + unsigned char burst_max; + unsigned char offset_max; + unsigned char nr_divisor; + unsigned int features; +#define FE_LED0 (1<<0) +#define FE_WIDE (1<<1) +#define FE_ULTRA (1<<2) +#define FE_ULTRA2 (1<<3) +#define FE_DBLR (1<<4) +#define FE_QUAD (1<<5) +#define FE_ERL (1<<6) +#define FE_CLSE (1<<7) +#define FE_WRIE (1<<8) +#define FE_ERMP (1<<9) +#define FE_BOF (1<<10) +#define FE_DFS (1<<11) +#define FE_PFEN (1<<12) +#define FE_LDSTR (1<<13) +#define FE_RAM (1<<14) +#define FE_CLK80 (1<<15) +#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP) +#define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80) +#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM) +} ncr_chip; + +#define SCSI_NCR_CHIP_TABLE \ +{ \ + {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810", 4, 8, 4, \ + FE_ERL} \ + , \ + {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4, 8, 4, \ + FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF} \ + , \ + {PCI_DEVICE_ID_NCR_53C815, 0xff, "815", 4, 8, 4, \ + FE_ERL|FE_BOF} \ + , \ + {PCI_DEVICE_ID_NCR_53C820, 0xff, "820", 4, 8, 4, \ + FE_WIDE|FE_ERL} \ + , \ + {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 4, 8, 4, \ + FE_WIDE|FE_ERL|FE_BOF} \ + , \ + {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7, 8, 4, \ + FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \ + , \ + {PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, \ + FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN|FE_RAM} \ + , \ + {PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 7, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ + {PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 7, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ + {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J", 7, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ + {PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 7, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ + {PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 7, 31, 7, \ + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ + {PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 7, 31, 7, \ + FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ +} + +/* + * List of supported NCR chip ids + */ +#define SCSI_NCR_CHIP_IDS \ +{ \ + PCI_DEVICE_ID_NCR_53C810, \ + PCI_DEVICE_ID_NCR_53C815, \ + PCI_DEVICE_ID_NCR_53C820, \ + PCI_DEVICE_ID_NCR_53C825, \ + PCI_DEVICE_ID_NCR_53C860, \ + PCI_DEVICE_ID_NCR_53C875, \ + PCI_DEVICE_ID_NCR_53C875J, \ + PCI_DEVICE_ID_NCR_53C885, \ + PCI_DEVICE_ID_NCR_53C895, \ + PCI_DEVICE_ID_NCR_53C896 \ +} + +/* +** Initial setup. +** Can be overriden at startup by a command line. +*/ +#define SCSI_NCR_DRIVER_SETUP \ +{ \ + SCSI_NCR_SETUP_MASTER_PARITY, \ + SCSI_NCR_SETUP_SCSI_PARITY, \ + SCSI_NCR_SETUP_DISCONNECTION, \ + SCSI_NCR_SETUP_SPECIAL_FEATURES, \ + SCSI_NCR_SETUP_ULTRA_SCSI, \ + SCSI_NCR_SETUP_FORCE_SYNC_NEGO, \ + 0, \ + 0, \ + 1, \ + 1, \ + SCSI_NCR_SETUP_DEFAULT_TAGS, \ + SCSI_NCR_SETUP_DEFAULT_SYNC, \ + 0x00, \ + 7, \ + SCSI_NCR_SETUP_LED_PIN, \ + 1, \ + SCSI_NCR_SETUP_SETTLE_TIME, \ + SCSI_NCR_SETUP_DIFF_SUPPORT, \ + 0 \ +} + +/* +** Boot fail safe setup. +** Override initial setup from boot command line: +** ncr53c8xx=safe:y +*/ +#define SCSI_NCR_DRIVER_SAFE_SETUP \ +{ \ + 0, \ + 1, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 1, \ + 2, \ + 0, \ + 255, \ + 0x00, \ + 255, \ + 0, \ + 0, \ + 10, \ + 1, \ + 1 \ +} + +/* ** Define the table of target capabilities by host and target ** ** If you have problems with a scsi device, note the host unit and the @@ -309,38 +597,6 @@ #endif #endif -/* -** NCR53C8XX Device Ids -*/ - -#ifndef PCI_DEVICE_ID_NCR_53C810 -#define PCI_DEVICE_ID_NCR_53C810 1 -#endif - -#ifndef PCI_DEVICE_ID_NCR_53C810AP -#define PCI_DEVICE_ID_NCR_53C810AP 5 -#endif - -#ifndef PCI_DEVICE_ID_NCR_53C815 -#define PCI_DEVICE_ID_NCR_53C815 4 -#endif - -#ifndef PCI_DEVICE_ID_NCR_53C820 -#define PCI_DEVICE_ID_NCR_53C820 2 -#endif - -#ifndef PCI_DEVICE_ID_NCR_53C825 -#define PCI_DEVICE_ID_NCR_53C825 3 -#endif - -#ifndef PCI_DEVICE_ID_NCR_53C860 -#define PCI_DEVICE_ID_NCR_53C860 6 -#endif - -#ifndef PCI_DEVICE_ID_NCR_53C875 -#define PCI_DEVICE_ID_NCR_53C875 0xf -#endif - /**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/ /*----------------------------------------------------------------- @@ -365,6 +621,7 @@ /*03*/ u_char nc_scntl3; /* cnf system clock dependent */ #define EWS 0x08 /* cmd: enable wide scsi [W]*/ + #define ULTRA 0x80 /* cmd: ULTRA enable */ /*04*/ u_char nc_scid; /* cnf host adapter scsi address */ #define RRE 0x40 /* r/w:e enable response to resel. */ @@ -418,6 +675,7 @@ #define ILF1 0x80 /* sta: data in SIDL register msb[W]*/ #define ORF1 0x40 /* sta: data in SODR register msb[W]*/ #define OLF1 0x20 /* sta: data in SODL register msb[W]*/ + #define DM 0x04 /* sta: DIFFSENS mismatch (895/6 only) */ #define LDSC 0x02 /* sta: disconnect & reconnect */ /*10*/ u_int32 nc_dsa; /* --> Base page */ @@ -443,13 +701,20 @@ #define CSIGP 0x40 /*1b*/ u_char nc_ctest3; - #define CLF 0x04 /* clear scsi fifo */ + #define FLF 0x08 /* cmd: flush dma fifo */ + #define CLF 0x04 /* cmd: clear dma fifo */ + #define FM 0x02 /* mod: fetch pin mode */ + #define WRIE 0x01 /* mod: write and invalidate enable */ /*1c*/ u_int32 nc_temp; /* ### Temporary stack */ /*20*/ u_char nc_dfifo; /*21*/ u_char nc_ctest4; + #define BDIS 0x80 /* mod: burst disable */ + #define MPEE 0x08 /* mod: master parity error enable */ + /*22*/ u_char nc_ctest5; + #define DFS 0x20 /* mod: dma fifo size */ /*23*/ u_char nc_ctest6; /*24*/ u_int32 nc_dbc; /* ### Byte count and command */ @@ -459,18 +724,31 @@ /*34*/ u_int32 nc_scratcha; /* ??? Temporary register a */ /*38*/ u_char nc_dmode; + #define BL_2 0x80 /* mod: burst length shift value +2 */ + #define BL_1 0x40 /* mod: burst length shift value +1 */ + #define ERL 0x08 /* mod: enable read line */ + #define ERMP 0x04 /* mod: enable read multiple */ + #define BOF 0x02 /* mod: burst op code fetch */ + /*39*/ u_char nc_dien; /*3a*/ u_char nc_dwt; /*3b*/ u_char nc_dcntl; /* --> Script execution control */ - #define SSM 0x10 /* mod: single step mode */ - #define STD 0x04 /* cmd: start dma mode */ - #define NOCOM 0x01 /* cmd: protect sfbr while reselect */ + + #define CLSE 0x80 /* mod: cache line size enable */ + #define PFF 0x40 /* cmd: pre-fetch flush */ + #define PFEN 0x20 /* mod: pre-fetch enable */ + #define SSM 0x10 /* mod: single step mode */ + #define IRQM 0x08 /* mod: irq mode (1 = totem pole !) */ + #define STD 0x04 /* cmd: start dma mode */ + #define IRQD 0x02 /* mod: irq disable */ + #define NOCOM 0x01 /* cmd: protect sfbr while reselect */ /*3c*/ u_int32 nc_adder; /*40*/ u_short nc_sien; /* -->: interrupt enable */ /*42*/ u_short nc_sist; /* <--: interrupt status */ + #define SBMC 0x1000/* sta: SCSI Bus Mode Change (895/6 only) */ #define STO 0x0400/* sta: timeout (select) */ #define GEN 0x0200/* sta: timeout (general) */ #define HTH 0x0100/* sta: timeout (handshake) */ @@ -496,6 +774,7 @@ /*4d*/ u_char nc_stest1; #define DBLEN 0x08 /* clock doubler running */ #define DBLSEL 0x04 /* clock doubler selected */ + /*4e*/ u_char nc_stest2; #define ROF 0x40 /* reset scsi offset (after gross error!) */ @@ -503,10 +782,18 @@ /*4f*/ u_char nc_stest3; #define TE 0x80 /* c: tolerAnt enable */ + #define HSC 0x20 /* c: Halt SCSI Clock */ #define CSF 0x02 /* c: clear scsi fifo */ /*50*/ u_short nc_sidl; /* Lowlevel: latched from scsi data */ -/*52*/ u_short nc_52_; +/*52*/ u_char nc_stest4; + #define SMODE 0xc0 /* SCSI bus mode (895/6 only) */ + #define SMODE_HVD 0x40 /* High Voltage Differential */ + #define SMODE_SE 0x80 /* Single Ended */ + #define SMODE_LVD 0xc0 /* Low Voltage Differential */ + #define LCKFRQ 0x20 /* Frequency Lock (895/6 only) */ + +/*53*/ u_char nc_53_; /*54*/ u_short nc_sodl; /* Lowlevel: data out to scsi data */ /*56*/ u_short nc_56_; /*58*/ u_short nc_sbdl; /* Lowlevel: data from scsi data */ @@ -659,10 +946,18 @@ ** << source_address >> ** << destination_address >> ** +** SCR_COPY sets the NO FLUSH option by default. +** SCR_COPY_F does not set this option. +** +** For chips which do not support this option, +** ncr_copy_and_bind() will remove this bit. **----------------------------------------------------------- */ -#define SCR_COPY(n) (0xc0000000 | (n)) +#define SCR_NO_FLUSH 0x01000000 + +#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n)) +#define SCR_COPY_F(n) (0xc0000000 | (n)) /*----------------------------------------------------------- ** @@ -768,6 +1063,7 @@ **----------------------------------------------------------- */ +#define SCR_NO_OP 0x80000000 #define SCR_JUMP 0x80080000 #define SCR_JUMPR 0x80880000 #define SCR_CALL 0x88080000 diff -urN linux-2.0.31-pre5/drivers/scsi/README.ncr53c8xx linux-2.0.31-pre6/drivers/scsi/README.ncr53c8xx --- linux-2.0.31-pre5/drivers/scsi/README.ncr53c8xx 1996-09-20 07:00:34.000000000 -0700 +++ linux-2.0.31-pre6/drivers/scsi/README.ncr53c8xx 2003-08-15 15:04:01.000000000 -0700 @@ -1,10 +1,10 @@ -The linux NCR53C8XX driver README file +The Linux NCR53C8XX driver README file Written by Gerard Roudier 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -12 June 1995 +19 June 1997 =============================================================================== 1. Introduction @@ -21,15 +21,31 @@ 8.4 Set order type for tagged command 8.5 Set debug mode 8.6 Clear profile counters + 8.7 Set flag (no_sync) + 8.8 Debug error recovery 9. Configuration parameters -10. Some constants and flags of the ncr53c8xx.h header files -11. Provided files -12. Installation procedure for Linux version 1 -13. Installation procedure for Linux version 2 -14. Control commands under linux-1.2.13 -15. Known problems - 15.1 Tagged commands with Iomega Jaz device - 15.2 Tagged command queueing cannot be disabled at run time +10. Boot setup commands + 10.1 Syntax + 10.2 Available arguments + 10.3 Advised boot setup commands + 10.4 PCI configuration fix-up boot option + 10.5 Serial NVRAM support boot option +11. Some constants and flags of the ncr53c8xx.h header file +12. Installation + 12.1 Provided files + 12.2 Installation procedure +13. Control commands under linux-1.2.13 +14. Known problems + 14.1 Tagged commands with Iomega Jaz device + 14.2 Device names change when another controller is added +15. SCSI problem troubleshooting +16. Synchonous transfer negotiation tables + 16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers + 16.2 Synchronous timings for fast SCSI-2 53C8XX controllers +17. Serial NVRAM support (by Richard Waltham) + 17.1 Features + 17.2 Symbios NVRAM layout + 17.3 Tekram NVRAM layout =============================================================================== @@ -43,21 +59,37 @@ Wolfgang Stanglmeier Stefan Esser -You can find technical information about the NCR 8xx family in the PCI-HOWTO -written by Michael Will and in the SCSI-HOWTO written by Drew Eckhardt. +You can find technical information about the NCR 8xx family in the +PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by +Drew Eckhardt. Information about new chips is available at SYMBIOS web server: - http://www.symbios.com -This short documentation only describes the features of the NCR53C8XX driver, -configuration parameters and control commands available through the proc SCSI -file system read / write operations. + http://www.symbios.com/ -This driver has been tested OK with linux/i386 and is currently untested -under linux/Alpha. If you intend to use this driver under linux/Alpha, just -try it first with read-only or mounted read-only devices. +SCSI standard documentations are available at SYMBIOS ftp server: -I am not a native speaker of English and there are probably lots of + ftp://ftp.symbios.com/ + +Usefull SCSI tools written by Eric Youngdale are available at tsx-11: + + ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsiinfo-X.Y.tar.gz + ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsidev-X.Y.tar.gz + +These tools are not ALPHA but quite clean and work quite well. +It is essential you have the 'scsiinfo' package. + +This short documentation only describes the features of the NCR53C8XX +driver, configuration parameters and control commands available +through the proc SCSI file system read / write operations. + +This driver has been tested OK with linux/i386 and Linux/Alpha. + +Latest driver version and patches are available at: + + ftp://linux.wauug.org/pub/roudier + +I am not a native speaker of English and there are probably lots of mistakes in this README file. Any help will be welcome. @@ -71,8 +103,8 @@ SCSI parity checking Master parity checking -"Wide negotiation" is supported for chips that allow it. -The following table shows some characteristics of NCR 8xx family chips: +"Wide negotiation" is supported for chips that allow it. The +following table shows some characteristics of NCR 8xx family chips: On board Supported by Tested with Chip SDMS BIOS Wide Ultra SCSI the driver the driver @@ -81,12 +113,12 @@ 810A N N N Y Y 815 Y N N Y Y 825 Y Y N Y Y -825A Y Y N Y Not yet -875 Y Y Y(1) Y Not yet - -(1) Ultra SCSI extensions will be supported in a future release of the - driver. +825A Y Y N Y Y +860 N N Y Y Y +875 Y Y Y Y Y +895 Y Y Y(1) Y not yet +(1) The 895 chip is supported 'on paper'. 3. Summary of other supported features. @@ -97,45 +129,58 @@ Debugging information: written to syslog (expert only) Scatter / gather Shared interrupt + Boot setup commands + Serial NVRAM: Symbios and Tekram formats 4. Memory mapped I/O versus normal I/O -Memory mapped I/O has less latency than normal I/O. -Since linux-1.3.x, memory mapped I/O is used rather than normal I/O. -Memory mapped I/O seems to work fine on most hardware configurations, but some -poorly designed motherboards may break this feature. - -During the initialization phase, the driver first tries to use memory mapped -I/O. If nothing seems wrong, it will use memory mapped I/O. -If a flaw is detected, it will use normal I/O. +Memory mapped I/O has less latency than normal I/O. Since +linux-1.3.x, memory mapped I/O is used rather than normal I/O. Memory +mapped I/O seems to work fine on most hardware configurations, but +some poorly designed motherboards may break this feature. -However, it's possible that memory mapped I/O does not work properly and the -driver has not detected the problem. - -The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the +The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the driver to use normal I/O in all cases. 5. Tagged command queueing -Some SCSI devices do not properly support tagged command queuing. -A safe configuration is to not enable tagged command queuing support at -boot-up, and to enable support of it with the control command "settags" -described further in this text. - -Once you are sure that all your devices properly support tagged command queuing, -you can enable it by default with the CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE -configuration option. +Some SCSI devices do not properly support tagged command queuing. A +safe configuration is to not enable tagged command queuing support at +boot-up, and to enable support of it with the control command +"settags" described further in this text. + +Once you are sure that all your devices properly support tagged +command queuing, you can enable it by default with the +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE configuration option. + +The maximum number of simultaneous tagged commands queued to a device +is currently set to 4 by default. It is defined in the file +ncr53c8xx.h by SCSI_NCR_MAX_TAGS. This value is suitable for most SCSI +disks. With large SCSI disks (> 2GB, cache > 512KB average seek time +< 10 ms), 8 tagged commands may give better performance. + +In some special conditions, some SCSI disk firmwares may return a +QUEUE FULL status for a SCSI command. This behaviour is managed by the +driver by the following heuristic: + +- Each time a QUEUE FULL status is returned, tagged command queueing is + temporarily disabled. + +- Every 100 successfully completed SCSI commands, if allowed by the + current limit, the maximum number of queueable commands is + incremented and tagged command queueing is reenabled. 6. Parity checking -The driver supports SCSI parity checking and PCI bus master parity checking. -These features must be enabled in order to ensure safe data transfers. -However, some flawed devices or mother boards will have problems with -parity. You can disable parity by choosing first "CONFIG_EXPERIMENTAL". -Then, "make config" will allow to set the following configuration options: +The driver supports SCSI parity checking and PCI bus master parity +checking. These features must be enabled in order to ensure safe data +transfers. However, some flawed devices or mother boards will have +problems with parity. You can disable parity by choosing first +"CONFIG_EXPERIMENTAL". Then, "make config" will allow to set the +following configuration options: CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK (disable SCSI parity checking) CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK (disable master parity checking) @@ -145,16 +190,19 @@ Profiling information is available through the proc SCSI file system. The device associated with a host has the following pathname: + /proc/scsi/ncr53c8xx/N (N=0,1,2 ....) Generally, only 1 board is used on hardware configuration, and that device is: /proc/scsi/ncr53c8xx/0 -However, if the driver has been made as module, the number of the hosts is -incremented each time the driver is loaded. +However, if the driver has been made as module, the number of the +hosts is incremented each time the driver is loaded. In order to display profiling information, just enter: + cat /proc/scsi/ncr53c8xx/0 + and you will get something like the following text: ------------------------------------------------------- @@ -162,6 +210,7 @@ Chip NCR53C810, device id 0x1, revision id 0x2 IO port address 0x6000, IRQ number 10 Using memory mapped IO at virtual address 0x282c000 + Synchronous transfer period 25, max commands per lun 4 Profiling information: num_trans = 18014 num_kbytes = 671314 @@ -175,7 +224,7 @@ ms_post = 1320 ------------------------------------------------------- -General information is easy to understand. The device ID and the +General information is easy to understand. The device ID and the revision ID identify the SCSI chip as follows: Chip Device id Revision Id @@ -187,15 +236,18 @@ 860 0x6 825A 0x3 >= 0x10 875 0xf +895 0xc The profiling information is updated upon completion of SCSI commands. -A data structure is allocated and zeroed when the host adapter is -attached. So, if the driver is a module, the profile counters are cleared each -time the driver is loaded. -The "clearprof" command allows you to clear these counters at any time. +A data structure is allocated and zeroed when the host adapter is +attached. So, if the driver is a module, the profile counters are +cleared each time the driver is loaded. The "clearprof" command +allows you to clear these counters at any time. The following counters are available: -("num" prefix means "number of", "ms" means milli-seconds) + +("num" prefix means "number of", +"ms" means milli-seconds) num_trans Number of completed commands @@ -238,46 +290,43 @@ (time from SCSI status get to command completion call) Example above: 1.32 seconds spent for post processing -Due to the 1/100 second tick of the system clock, "ms_post" time may be -wrong. +Due to the 1/100 second tick of the system clock, "ms_post" time may +be wrong. -In the example above, we got 18038 interrupts "on the fly" and only 1673 script -breaks probably due to disconnections inside a segment of the scatter list. -This is an excellent result due to the fact that the driver tries to use small -data segments (512) for the scatter list. The CPU load of this rescatter process -is acceptable. Unlike other SCSI processors, NCR53C8XX controllers do not need -large data chunks in order to get better performance, and it seems that it -is just the opposite. -The scatter/gather algorithm of the middle SCSI driver is not optimal for -NCR SCSI processors and should be tunable according to host type. - -You can tune the "wished" segment size for the scatterlist by changing the -following "define" in the file ncr53c8xx.h. -Use only power of 2 greater than 512 (1024, 2048 or 4096). - -SCSI_NCR_SEGMENT_SIZE (default: 512) +In the example above, we got 18038 interrupts "on the fly" and only +1673 script breaks generally due to disconnections inside a segment +of the scatter list. 8. Control commands -Control commands can be sent to the driver with write operations to the -proc SCSI file system. The generic command syntax is the following: +Control commands can be sent to the driver with write operations to +the proc SCSI file system. The generic command syntax is the +following: echo " " >/proc/scsi/ncr53c8xx/0 (assumes controller number is 0) +Using "all" for "" parameter with the commands below will +apply to all targets of the SCSI chain (except the controller). + Available commands: -8.1 Set minimum synchronous period +8.1 Set minimum synchronous period factor - setsync + setsync target: target number - period: minimum synchronous period in nano-seconds. - Maximum speed = 1000/(4*period) MB/second + period: minimum synchronous period. + Maximum speed = 1000/(4*period factor) except for special + cases below. Specify a period of 255, to force asynchronous transfer mode. + 10 means 25 nano-seconds synchronous period + 11 means 30 nano-seconds synchronous period + 12 means 50 nano-seconds synchronous period + 8.2 Set wide size setwide @@ -292,7 +341,6 @@ target: target number tags: number of concurrent tagged commands must not be greater than SCSI_NCR_MAX_TAGS (default: 4) - must not be lower that 1 (see: known problems) 8.4 Set order type for tagged command @@ -321,23 +369,63 @@ nego: print information about SCSI negotiations phase: print information on script interruptions + Use "setdebug" with no argument to reset debug flags. + 8.6 Clear profile counters clearprof - The profile counters are automatically cleared when the amount of data - transfered reaches 1000 GB in order to avoid overflow. + The profile counters are automatically cleared when the amount of + data transfered reaches 1000 GB in order to avoid overflow. The "clearprof" command allows you to clear these counters at any time. +8.7 Set flag (no_sync) + + setflag + + target: target number + + For the moment, only one flag is available: + + no_sync: not allow target to disconnect. + + Do not specify any flag in order to reset the flag. For example: + - setflag 4 + will reset no_sync flag for target 4, so will allow it disconnections. + - setflag all + will allow disconnection for all devices on the SCSI bus. + + +8.8 Debug error recovery + + debug_error_recovery + + Available error type to trigger: + sge: SCSI gross error + abort: abort command from the middle-level driver + reset: reset command from the middle-level driver + parity: scsi parity detected in DATA IN phase + none: restore driver normal behaviour + + The code corresponding to this feature is normally not compiled. + Its purpose is driver testing only. In order to compile the code + that allows to trigger error recovery you must define at compile time + SCSI_NCR_DEBUG_ERROR_RECOVERY. + If you have compiled the driver with this option, nothing will happen + as long as you donnot use the control command 'debug_error_recovery' + with sge, abort, reset or parity as argument. + If you select an error type, it will be triggered by the driver every + 30 seconds. + 9. Configuration parameters -If the firmware of all your devices is perfect enough, all the features -supported by the driver can be enabled at start-up. -However, if only one has a flaw for some SCSI feature, you can disable the -support by the driver of this feature at linux start-up and enable this -feature after boot-up only for devices that support it safely. +If the firmware of all your devices is perfect enough, all the +features supported by the driver can be enabled at start-up. However, +if only one has a flaw for some SCSI feature, you can disable the +support by the driver of this feature at linux start-up and enable +this feature after boot-up only for devices that support it safely. CONFIG_SCSI_NCR53C8XX_IOMAPPED (default answer: n) Answer "y" if you suspect your mother board to not allow memory mapped I/O. @@ -347,9 +435,16 @@ Answer "y" if you are sure that all your SCSI devices that are able to accept tagged commands will proceed safely. -CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS (default answer: n) - This option forces asynchronous transfer mode for all SCSI devices. - +CONFIG_SCSI_NCR53C8XX_MAX_TAGS (default answer: 4) + This option allows you to specify the maximum number of tagged commands + that can be queued to a device. + +CONFIG_SCSI_NCR53C8XX_SYNC (default answer: 5) + This option allows you to specify the frequency in MHz the driver + will use at boot time for synchronous data transfer negotiations. + This frequency can be changed later with the "setsync" control command. + 0 means "asynchronous data transfers". + CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO (default answer: n) Force synchronous negotiation for all SCSI-2 devices. Some SCSI-2 devices do not report this feature in byte 7 of inquiry @@ -360,12 +455,331 @@ you can answer "y". Then, all SCSI devices will never disconnect the bus even while performing long SCSI operations. - -10. Some constants and flags of the ncr53c8xx.h header files - -Some of these are defined from the configuration parameters. -To change other "defines", you must edit the header file. -Do that only if you know what you are doing. +CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 + bit as a flag indicating singled-ended/differential interface. + If all the boards of your system are genuine SYMBIOS boards or use + BIOS and drivers from SYMBIOS, you would want to enable this option. + This option must NOT be enabled if your system has at least one 53C8XX + based scsi board with a vendor-specific BIOS. + For example, Tekram DC-390/U, DC-390/W and DC-390/F scsi controllers + use a vendor-specific BIOS and are known to not use SYMBIOS compatible + GPIO wiring. So, this option must not be enabled if your system has + such a board installed. + +CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT + Enable support for reading the serial NVRAM data on Symbios and + some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for + systems with more than one Symbios compatible controller where at least + one has a serial NVRAM, or for a system with a mixture of Symbios and + Tekram cards. Enables setting the boot order of host adaptors + to something other than the default order or "reverse probe" order. + Also enables Symbios and Tekram cards to be distinguished so + CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a + mixture of Symbios and Tekram cards so the Symbios cards can make use of + the full range of Symbios features, differential, led pin, without + causing problems for the Tekram card(s). + +10. Boot setup commands + +10.1 Syntax + +Setup commands can be passed to the driver at boot time. +A boot setup command for the ncr53c8xx driver begins with the driver name +"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of +integers separated with comma followed by an optionnal list of comma- +separated strings. Example of boot setup command under lilo prompt: + +lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200 + +- enable tagged commands, up to 4 tagged commands queued. +- set synchronous negotiation speed to 10 Mega-transfers / second. +- set DEBUG_NEGO flag. + +For the moment, the integer list of arguments is disgarded by the driver. +It will be used in the future in order to allow a per controller setup. + +Each string argument must be specified as "keyword:value". Only lower-case +characters and digits are allowed. + +10.2 Available arguments + +Master parity checking + mpar:y enabled + mpar:n disabled + +Scsi parity checking + spar:y enabled + spar:n disabled + +Scsi disconnections + disc:y enabled + disc:n disabled + +Special features + Only apply to 810A, 825A, 860 and 875 controllers. + Have no effect with normal 810 and 825. + specf:y enabled + specf:n disabled + +Ultra SCSI support + Only apply to 860 and 875 controllers. + Have no effect with other ones. + ultra:y enabled + ultra:n disabled + +Number of tagged commands + tags:0 (or tags:1 ) tagged command queuing disabled + tags:#tags (#tags > 1) tagged command queuing enabled + #tags will be truncated to the max queued commands configuration parameter. + If the driver is configured with a maximum of 4 queued commands, tags:4 is + the right argument to specify. + +Default synchronous period factor + sync:255 disabled (asynchronous transfer mode) + sync:#factor + #factor = 10 Ultra-2 SCSI 40 Mega-transfers / second + #factor = 11 Ultra-2 SCSI 33 Mega-transfers / second + #factor < 25 Ultra SCSI 20 Mega-transfers / second + #factor < 50 Fast SCSI-2 + + In all cases, the driver will use the minimum transfer period supported by + controllers according to NCR53C8XX chip type. + +Negotiate synchronous with all devices + (force sync nego) + fsn:y enabled + fsn:n disabled + +Verbosity level + verb:0 minimal + verb:1 normal + verb:2 too much + +Debug mode + debug:0 clear debug flags + debug:#x set debug flags + #x is an integer value combining the following power-of-2 values: + DEBUG_ALLOC 0x1 + DEBUG_PHASE 0x2 + DEBUG_POLL 0x4 + DEBUG_QUEUE 0x8 + DEBUG_RESULT 0x10 + DEBUG_SCATTER 0x20 + DEBUG_SCRIPT 0x40 + DEBUG_TINY 0x80 + DEBUG_TIMING 0x100 + DEBUG_NEGO 0x200 + DEBUG_TAGS 0x400 + DEBUG_FREEZE 0x800 + DEBUG_RESTART 0x1000 + + You can play safely with DEBUG_NEGO. However, some of these flags may + generate bunches of syslog messages. + +Burst max + burst:0 burst disabled + burst:255 get burst length from initial IO register settings. + burst:#x burst enabled (1<<#x burst transfers max) + #x is an integer value which is log base 2 of the burst transfers max. + The NCR53C875 and NCR53C825A support up to 128 burst transfers (#x = 7). + Other chips only support up to 16 (#x = 4). + This is a maximum value. The driver set the burst length according to chip + and revision ids. By default the driver uses the maximum value supported + by the chip. + +LED support + led:1 enable LED support + led:0 disable LED support + Donnot enable LED support if your scsi board does not use SDMS BIOS. + (See 'Configuration parameters') + +Max wide + wide:1 wide scsi enabled + wide:0 wide scsi disabled + Some scsi boards use a 875 (ultra wide) and only supply narrow connectors. + If you have connected a wide device with a 50 pins to 68 pins cable + converter, any accepted wide negotiation will break further data transfers. + In such a case, using "wide:0" in the bootup command will be helpfull. + +Differential mode + diff:0 never set up diff mode + diff:1 set up diff mode if BIOS set it + diff:2 always set up diff mode + diff:3 set diff mode if GPIO3 is not set + +IRQ mode + irqm:0 always open drain + irqm:1 same as initial settings (assumed BIOS settings) + irqm:2 always totem pole + +Reverse probe + revprob:n probe chip ids from the PCI configuration in this order: + 810, 815, 820, 860, 875, 885, 895, 896 + revprob:y probe chip ids in the reverse order. + +Fix up PCI configuration space + pcifix: