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
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
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
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.
-
-force normal IO
+ 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.
+
+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
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
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
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
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("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
return -1;
}
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);
}
/*
* 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;
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) {
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;
}
* 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.
*
* Restrict vt switching via ioctl()
* -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Rewrote init_dev and release_dev to eliminate races.
+ * -- Bill Hawes <whawes@star.net>, June 97
*/
#include <linux/config.h>
(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;
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];
- 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) {
+ if (driver->type == TTY_DRIVER_TYPE_PTY) {
+ 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;
+
+ 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;
- }
- }
- 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;
+ 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_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);
}
/*
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;
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",
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;
}
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]) {
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) {
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);
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;
-
- 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;
+ 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;
+ }
}
}
+ 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);
}
/*
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);
{
struct tty_struct * real_tty;
int retval;
- int opt = 0;
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
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)
* 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;
}
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);
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");
{
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) );
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
Setting to > 1512 effectively disables this feature. */
static const rx_copybreak = 200;
-#include <linux/config.h>
-#ifdef MODULE
#include <linux/version.h>
#include <linux/module.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/kernel.h>
#include <linux/sched.h>
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
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
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
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 *);
#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
#include <linux/skbuff.h>
#include <linux/types.h>
-#include <linux/config.h> /* for CONFIG_PCI */
/*
* API changed at linux version 2.1.0
--- /dev/null
+/* 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
+ <base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
+*/
+
+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 <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* A nominally proper method to handle version dependencies is to use
+ LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
+#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,0))
+#define KERNEL_1_2
+#else /* 1.3.0 */
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+#define NEW_MULTICAST
+#define LINUX_1_4
+#else
+#warning "This driver is tested for 1.3.44 and later development kernels only."
+#endif /* 1.3.44 */
+#endif
+#else
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef HAVE_HEADER_CACHE
+#define LINUX_1_4
+#define NEW_MULTICAST
+#else
+#ifdef ETH_P_DDCMP /* Warning: Bogus! This means IS_LINUX_1_3. */
+#define KERNEL_1_3
+#else
+#define KERNEL_1_2
+#endif
+#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: <Ethernet MTU> + 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);
+
+\f
+
+#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<<i))
+ printk(connectors[i]);
+ printk("\n"KERN_INFO" Primary interface chip %s PHY #%d.\n",
+ phys[(eeprom[6]>>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;
+}
+\f
+/* 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;
+}
+
+\f
+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;
+}
+\f
+#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 */
+\f
+/*
+ * 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:
+ */
/* 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... */
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. <cough> */
#include <linux/module.h>
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
+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.
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
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
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
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
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
-The linux NCR53C8XX driver README file
+The Linux NCR53C8XX driver README file
Written by Gerard Roudier <groudier@club-internet.fr>
21 Rue Carnot
95170 DEUIL LA BARRE - FRANCE
-12 June 1995
+19 June 1997
===============================================================================
1. Introduction
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
===============================================================================
Wolfgang Stanglmeier <wolf@cologne.de>
Stefan Esser <se@mi.Uni-Koeln.de>
-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.
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
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.
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.
+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.
-
-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.
+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:
-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.
+- 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)
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:
-------------------------------------------------------
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
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
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
(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.
-
-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).
+Due to the 1/100 second tick of the system clock, "ms_post" time may
+be wrong.
-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 "<verb> <parameters>" >/proc/scsi/ncr53c8xx/0
(assumes controller number is 0)
+Using "all" for "<target>" 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 <target> <period>
+ setsync <target> <period factor>
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 <target> <size>
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
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> <flag>
+
+ 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 <error to trigger>
+
+ 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.
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
you can answer "y". Then, all SCSI devices will never disconnect the bus
even while performing long SCSI operations.
+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:<option bits>
+
+ Available option bits:
+ 0x1: Set PCI cache-line size register if not set.
+ 0x2: Set write and invalidate bit in PCI command register.
+ 0x4: Increase if necessary PCI latency timer according to burst max.
+
+ Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
+
+Serial NVRAM
+ nvram:n do not look for serial NVRAM
+ nvram:y test controllers for onboard serial NVRAM
+
+Boot fail safe
+ safe:y load the following assumed fail safe initial setup
+
+ master parity disabled mpar:n
+ scsi parity enabled spar:y
+ disconnections not allowed disc:n
+ special features disabled specf:n
+ ultra scsi disabled ultra:n
+ force sync negotiation disabled fsn:n
+ reverse probe disabled revprob:n
+ PCI fix up disabled pcifix:0
+ serial NVRAM enabled nvram:y
+ verbosity level 2 verb:2
+ tagged command queuing disabled tags:0
+ synchronous negotiation disabled sync:255
+ debug flags none debug:0
+ burst length from BIOS settings burst:255
+ LED support disabled led:0
+ wide support disabled wide:0
+ settle time 10 seconds settle:10
+ differential support from BIOS settings diff:1
+ irq mode from BIOS settings irqm:1
+
+10.3 Advised boot setup commands
+
+If the driver has been configured with default options, the equivalent
+boot setup is:
+
+ ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+ tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
+
+For an installation diskette or a safe but not fast system,
+boot setup can be:
+
+ ncr53c8xx=safe:y,mpar:y,disc:y
+ ncr53c8xx=safe:y,disc:y
+ ncr53c8xx=safe:y,mpar:y
+ ncr53c8xx=safe:y
+
+My personnal system works flawlessly with the following equivalent setup:
+
+ ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+ tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
+
+The driver prints its actual setup when verbosity level is 2. You can try
+"ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2"
+to your boot setup command in order to check the actual setup the driver is
+using.
+
+10.4 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+ 0x1: Set PCI cache-line size register if not set.
+ 0x2: Set write and invalidate bit in PCI command register.
+
+Use 'pcifix:3' in order to allow the driver to fix both PCI features.
+
+These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875
+and are only supported for Pentium and 486 class processors.
+Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple
+and PCI write and invalidate commands. These features require the
+cache line size register to be properly set in the PCI configuration
+space of the chips. On the other hand, chips will use PCI write and
+invalidate commands only if the corresponding bit is set to 1 in the
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and
+invalidate bit in the PCI configuration space of 53C8XX chips.
+Optimized PCI accesses may be broken for some PCI/memory controllers or
+make problems with some PCI boards.
+
+This fix-up works flawlessly on my system.
+(MB Triton HX / 53C875 / 53C810A)
+I use these options at my own risks as you will do if you decide to
+use them too.
+
+
+10.5 Serial NVRAM support boot option
+
+nvram:n do not look for serial NVRAM
+nvram:y test controllers for onboard serial NVRAM
+
+This option is described below (see 17. Serial NVRAM support).
+When this option is enabled, the driver tries to detect all boards using
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the
+data format used, as follow:
+
+ Tekram format Symbios format
+General and host parameters
+ Boot order N Y
+ Host SCSI ID Y Y
+ SCSI parity checking Y Y
+ Verbose boot messages N Y
+SCSI devices parameters
+ Synchronous transfer speed Y Y
+ Wide 16 / Narrow Y Y
+ Tagged Command Queuing enabled Y Y
+ Disconnections enabled Y Y
+ Scan at boot time N Y
+
+In order to speed up the system boot, for each device configured without
+the "scan at boot time" option, the driver forces an error on the
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast
+hard disks. In such a situation you cannot configure the NVRAM with
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+ 0x01 look for NVRAM (equivalent to nvram=y)
+ 0x02 ignore NVRAM "Synchronous negotiation" parameters for all devices
+ 0x04 ignore NVRAM "Wide negotiation" parameter for all devices
+ 0x08 ignore NVRAM "Scan at boot time" parameter for all devices
+
+My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer
+mode. However, it works flawlessly at 20 MB/second with the driver.
+Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver
+use its setup for synchronous and wide negotiations.
-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.
+11. Some constants and flags of the ncr53c8xx.h header file
+
+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.
+
+SCSI_NCR_SETUP_ULTRA_SUPPORT (default: defined)
+ Ultra SCSI support.
+ Can be changed by the following boot setup command:
+ ncr53c8xx=ultra:n
+
+SCSI_NCR_SETUP_SPECIAL_FEATURES (default: defined)
+ If defined, the driver will enable some special features according
+ to chip and revision id.
+ For 810A, 860, 825A and 875 scsi chips, this option enables support
+ of features that reduce load of PCI bus and memory accesses during
+ scsi transfer processing: burst op-code fetch, read multiple,
+ read line, prefetch, cache line line, write and invalidate,
+ burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only).
+ Can be changed by the following boot setup command:
+ ncr53c8xx=specf:n
SCSI_NCR_IOMAPPED (default: not defined)
If defined, normal I/O is forced.
Maximum number of simultaneous tagged commands to a device.
Can be changed by "settags <target> <maxtags>"
+SCSI_NCR_SETUP_DEFAULT_SYNC (default: 50)
+ Transfer period factor the driver will use at boot time for synchronous
+ negotiation. 0 means asynchronous.
+ Can be changed by "setsync <target> <period factor>"
+
+SCSI_NCR_SETUP_DEFAULT_TAGS (default: 4)
+ Default number of simultaneous tagged commands to a device.
+ < 1 means tagged command queuing disabled at start-up.
+
SCSI_NCR_ALWAYS_SIMPLE_TAG (default: defined)
Use SIMPLE TAG for read and write commands.
Can be changed by "setorder <ordered|simple|default>"
-SCSI_NCR_TAGGED_QUEUE_DISABLED (default: defined)
- If defined, tagged command queuing is disable at start-up.
- Can be changed by "settags <target> <maxtags>"
-
-SCSI_NCR_NO_DISCONNECT (default: not defined)
- If defined, targets are not allowed to disconnect.
+SCSI_NCR_SETUP_DISCONNECTION (default: defined)
+ If defined, targets are allowed to disconnect.
-SCSI_NCR_FORCE_SYNC_NEGO (default: not defined)
+SCSI_NCR_SETUP_FORCE_SYNC_NEGO (default: not defined)
If defined, synchronous negotiation is tried for all SCSI-2 devices.
Can be changed by "setsync <target> <period>"
-SCSI_NCR_DISABLE_MPARITY_CHECK (default: not defined)
- If defined, master parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY (default: defined)
+ If defined, master parity checking is enabled.
-SCSI_NCR_DISABLE_PARITY_CHECK (default: not defined)
- If defined, SCSI parity checking is disabled.
+SCSI_NCR_SETUP_MASTER_PARITY (default: defined)
+ If defined, SCSI parity checking is enabled.
SCSI_NCR_PROFILE (default: defined)
If defined, profiling information is gathered.
SCSI_NCR_MAX_SCATTER (default: 128)
Scatter list size of the driver ccb.
-SCSI_NCR_SEGMENT_SIZE (default: 512)
- If defined, the driver will try to use scatter segments of this size.
- If not defined, the Linux scatter list is used as is.
-
SCSI_NCR_MAX_TARGET (default: 16)
Max number of targets per host.
Max number of LUNs per target.
-11. Provided files
+12. Installation
+
+12.1 Provided files
Driver and common files:
README.ncr53c8xx : this file
ChangeLog.ncr53c8xx : change log
+ ConfigHelp.ncr53c8xx : Part of Configure.help about the driver
ncr53c8xx.h : definitions
ncr53c8xx.c : the driver code
scsitag.c : command tool to enable tagged queue
conf.modules : sample of /etc/conf.modules
-Installation procedure 1 replacing the standard NCR53c7,8xx driver:
-
Install.ncr53c8xx : installation script
- Uninstall.ncr53c8xx : uninstallation script
- 53c7,8xx.h : included by hosts.c, override the standard driver
-
- Patch-1.2.13.ncr53c8xx : patch for linux-1.2.13
- Patch-1.3.45-49.ncr53c8xx : patch for linux-1.3.45 to linux-1.3.49
- Patch-1.3.50-100.ncr53c8xx : patch for linux-1.3.50 to linux-1.3.100
-Installation procedure 2 adding the driver to the kernel tree:
+ Patch-1.2.13.ncr53c8xx : patch for linux-1.2.13
+ Patch-2.0.29.ncr53c8xx : patch for linux-2.0.29
- Install2.ncr53c8xx : installation script
+You must untar the distribution with the following command:
- Patch2-2.0.0-1.ncr53c8xx : patch for linux-2.0.0 and linux-2.0.1
- Patch2-Current.ncr53c8xx : patch used for sub-levels > 1
+ tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
-Prior to installing the driver, you must untar the distribution, as follow:
+The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
- mkdir ncrB2L
- cd ncrB2L
- tar zxvf ncrBsd2Linux-1.12a-src.tar.gz
+12.2 Installation procedure
-12. Installation procedure for Linux version 1
+This install script has been tested with linux-1.2.13 and 2.0.29.
-This install script only supports linux-1.2.13 and linux-1.3.45 to 1.3.100.
-
-This procedure just moves the standard driver files to SAVE_53 sub-directory
-of linux/drivers/scsi, copies the drivers file to that directory and patches
-the SCSI Makefile.
-The standard driver can be restored with Uninstall.ncr53c8xx
+This procedure copies the new driver files to the kernel tree and
+applies a patch to some files of the kernel tree.
If your linux directory is at the standard location
"/usr/src/linux", just enter:
Make the kernel:
Change to linux source directory
- Configure with 53c7,8xx support (Y or m) for Install.ncr53c8xx
+ Configure with NCR53C7,8XX support = N
+ Configure with NCR53C8XX support = Y (or m)
Make dependancies
Make the kernel (use make zdisk first)
Make and install modules if you have configured with 'm'
- Notes:
- Since release 1.3.90, additionnal configuation parameters
- have been added for the standard NCR driver.
- Just reply Y or N as you want to these questions;
- The NCR53C8XX driver ignores those parameters.
-
- If you prefer the standard NCR driver of Linux:
- just enter:
- Uninstall.ncr53c8xx
- or
- Uninstall.ncr53c8xx <your_linux_directory>
- if your linux root is not /usr/src/linux.
+13. Control commands under linux-1.2.13
-13. Installation procedure for Linux version 2
-
-This procedure adds the driver to the kernel tree.
-Using "make config" you can choose between the standard driver and the BSD one.
-It is possible to configure both drivers as modules and to switch from one
-to the other at run time.
-Take care to unload the current driver module before loading the other one.
-
- If your linux directory is at the standard location
- "/usr/src/linux", just enter:
- Install2.ncr53c8xx
-
- Else enter:
- Install2.ncr53c8xx <your_linux_directory>
-
- Make the kernel:
- Change to linux source directory
- Configure with NCR53C8XX support (Y or m)
- Make dependancies
- Make the kernel (use make zdisk first)
- Make and install modules if you have configured with 'm'
-
-
-14. Control commands under linux-1.2.13
-
-Profiling data and control commands using the proc SCSI file system are not
-available for linux-1.2.13.
-The only control command available is "scsitag" which allows you to enable
-tagged command queuing support after linux boot-up.
+Profiling data and control commands using the proc SCSI file system
+are not available for linux-1.2.13. The only control command
+available is "scsitag" which allows you to enable tagged command
+queuing support after linux boot-up.
Tagged command queueing is disabled by default at system startup.
You can enable tagged queue per device with the following command:
+
scsitag device_name (ex: scsitag /dev/sda)
Use "cc -o scsitag scsitag.c" to create the "scsitag" executable.
-15. Known problems
-
-15.1 Tagged commands with Iomega Jaz device
+14. Known problems
-I have not tried this device, however it has been reported to me the following:
-This device is capable of Tagged command queuing. However while spinning up,
-it rejects Tagged commands. This behaviour is conforms to 6.8.2 of SCSI-2
-specifications. The current behaviour of the driver in that situation is not
-satisfying. So do not enable Tagged command queuing for devices that are able
-to spin down.
-The other problem that may appear is timeouts. The only way to avoid timeouts
-seems to edit linux/drivers/scsi/sd.c and to increase the current timeout
-values.
+14.1 Tagged commands with Iomega Jaz device
-15.2 Tagged command queuing cannot be disabled at run time
-
-Once Tagged command queuing has been enabled, the driver will not allow you to
-disable this feature ("settags <target> 0" is not supported).
-This problem is due to some limitations of the code added to the Linux version
-of the driver.
+I have not tried this device, however it has been reported to me the
+following: This device is capable of Tagged command queuing. However
+while spinning up, it rejects Tagged commands. This behaviour is
+conforms to 6.8.2 of SCSI-2 specifications. The current behaviour of
+the driver in that situation is not satisfying. So do not enable
+Tagged command queuing for devices that are able to spin down. The
+other problem that may appear is timeouts. The only way to avoid
+timeouts seems to edit linux/drivers/scsi/sd.c and to increase the
+current timeout values.
+
+14.2 Device names change when another controller is added.
+
+When you add a new NCR53C8XX chip based controller to a system that already
+has one or more controllers of this family, it may happen that the order
+the driver registers them to the kernel causes problems due to device
+name changes.
+When at least one controller uses NvRAM, SDMS BIOS version 4 allows you to
+define the order the BIOS will scan the scsi boards. The driver attaches
+controllers according to BIOS information if NvRAM detect option is set.
+
+If your controllers do not have NvRAM, you can:
+
+- Ask the driver to probe chip ids in reverse order from the boot command
+ line: ncr53c8xx=revprob:y
+- Make appropriate changes in the fstab.
+- Use the 'scsidev' tool from Eric Youngdale.
+
+15. SCSI problem troubleshooting
+
+Most SCSI problems are due to a non conformant SCSI bus or to buggy
+devices. If infortunately you have SCSI problems, you can check the
+following things:
+
+- SCSI bus cables
+- terminations at both end of the SCSI chain
+- linux syslog messages (some of them may help you)
+
+If you donnot find the source of problems, you can configure the
+driver with no features enabled.
+
+- only asynchronous data transfers
+- tagged commands disabled
+- disconnections not allowed
+
+Now, if your SCSI bus is ok, your system have every chance to work
+with this safe configuration but performances will not be optimal.
+
+If it still fails, then you can send your problem description to
+appropriate mailing lists or news-groups. Send me a copy in order to
+be sure I will receive it. Obviously, a bug in the driver code is
+possible.
+
+ My email address: Gerard Roudier <groudier@club-internet.fr>
+
+Allowing disconnections is important if you use several devices on
+your SCSI bus but often causes problems with buggy devices.
+Synchronous data transfers increases throughput of fast devices like
+hard disks. Good SCSI hard disks with a large cache gain advantage of
+tagged commands queuing.
+
+Try to enable one feature at a time with control commands. For example:
+
+- echo "setsync all 25" >/proc/scsi/ncr53c8xx/0
+ Will enable fast synchronous data transfer negotiation for all targets.
+
+- echo "setflag 3" >/proc/scsi/ncr53c8xx/0
+ Will reset flags (no_sync) for target 3, and so will allow it to disconnect
+ the SCSI Bus.
+
+- echo "settags 3 4" >/proc/scsi/ncr53c8xx/0
+ Will enable tagged command queuing for target 3 if that device supports it.
+
+Once you have found the device and the feature that cause problems, just
+disable that feature for that device.
+
+
+16. Synchonous transfer negotiation tables
+
+Tables below have been created by calling the routine the driver uses
+for synchronisation negotiation timing calculation and chip setting.
+The first table corresponds to Ultra chips 53875 and 53C860 with 80 MHz
+clock and 5 clock divisors.
+The second one has been calculated by setting the scsi clock to 40 Mhz
+and using 4 clock divisors and so applies to all NCR53C8XX chips in fast
+SCSI-2 mode.
+
+Periods are in nano-seconds and speeds are in Mega-transfers per second.
+1 Mega-transfers/second means 1 MB/s with 8 bits SCSI and 2 MB/s with
+Wide16 SCSI.
+
+16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+
+ ----------------------------------------------
+ Negotiated NCR settings
+ Factor Period Speed Period Speed
+ ------ ------ ------ ------ ------
+ 12 50 20.000 50 20.000
+ 13 52 19.230 62 16.000
+ 14 56 17.857 62 16.000
+ 15 60 16.666 62 16.000
+ 16 64 15.625 75 13.333
+ 17 68 14.705 75 13.333
+ 18 72 13.888 75 13.333
+ 19 76 13.157 87 11.428
+ 20 80 12.500 87 11.428
+ 21 84 11.904 87 11.428
+ 22 88 11.363 93 10.666
+ 23 92 10.869 93 10.666
+ 24 96 10.416 100 10.000
+ 25 100 10.000 100 10.000
+ 26 104 9.615 112 8.888
+ 27 108 9.259 112 8.888
+ 28 112 8.928 112 8.888
+ 29 116 8.620 125 8.000
+ 30 120 8.333 125 8.000
+ 31 124 8.064 125 8.000
+ 32 128 7.812 131 7.619
+ 33 132 7.575 150 6.666
+ 34 136 7.352 150 6.666
+ 35 140 7.142 150 6.666
+ 36 144 6.944 150 6.666
+ 37 148 6.756 150 6.666
+ 38 152 6.578 175 5.714
+ 39 156 6.410 175 5.714
+ 40 160 6.250 175 5.714
+ 41 164 6.097 175 5.714
+ 42 168 5.952 175 5.714
+ 43 172 5.813 175 5.714
+ 44 176 5.681 187 5.333
+ 45 180 5.555 187 5.333
+ 46 184 5.434 187 5.333
+ 47 188 5.319 200 5.000
+ 48 192 5.208 200 5.000
+ 49 196 5.102 200 5.000
+
+
+16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+
+ ----------------------------------------------
+ Negotiated NCR settings
+ Factor Period Speed Period Speed
+ ------ ------ ------ ------ ------
+ 25 100 10.000 100 10.000
+ 26 104 9.615 125 8.000
+ 27 108 9.259 125 8.000
+ 28 112 8.928 125 8.000
+ 29 116 8.620 125 8.000
+ 30 120 8.333 125 8.000
+ 31 124 8.064 125 8.000
+ 32 128 7.812 131 7.619
+ 33 132 7.575 150 6.666
+ 34 136 7.352 150 6.666
+ 35 140 7.142 150 6.666
+ 36 144 6.944 150 6.666
+ 37 148 6.756 150 6.666
+ 38 152 6.578 175 5.714
+ 39 156 6.410 175 5.714
+ 40 160 6.250 175 5.714
+ 41 164 6.097 175 5.714
+ 42 168 5.952 175 5.714
+ 43 172 5.813 175 5.714
+ 44 176 5.681 187 5.333
+ 45 180 5.555 187 5.333
+ 46 184 5.434 187 5.333
+ 47 188 5.319 200 5.000
+ 48 192 5.208 200 5.000
+ 49 196 5.102 200 5.000
+
+
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT
+configuration parameter is set enabling both Symbios and Tekram boards to be
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62
+04 00 03 00 00 10 00 58 00 00 01 00 00 63
+04 00 01 00 00 10 00 48 00 00 01 00 00 61
+00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+ 0x100-0x26f initialised data
+ 0x270-0x7ff not used
+
+general layout
+
+ header - 6 bytes,
+ data - 356 bytes (checksum is byte sum of this data)
+ trailer - 6 bytes
+ ---
+ total 368 bytes
+
+data area layout
+
+ controller set up - 20 bytes
+ boot configuration - 56 bytes (4x14 bytes)
+ device set up - 128 bytes (16x8 bytes)
+ unused (spare?) - 152 bytes (19x8 bytes)
+ ---
+ total 356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00 - ?? start marker
+64 01 - byte count (lsb/msb excludes header/trailer)
+8e 0b - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+controller set up
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+ | | |
+ | | -- host ID
+ | --flag bits 2
+ | 0x00000001= scan order hi->low
+ | (default 0x00 - scan low->hi)
+ --flag bits 1
+ 0x00000001 scam enable
+ 0x00000010 parity enable
+ 0x00000100 verbose boot msgs
+
+remaining bytes unknown - they do not appear to change in my
+current set up for any of the controllers.
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 4th controller
+ | | | | | | | |
+ | | | | | | ---- PCI io port adr
+ | | | | | --0x01 init/scan at boot time
+ | | | | --PCI device/function number (0xdddddfff)
+ | | ----- ?? PCI vendor ID (lsb/msb)
+ ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 - id 15
+ | | | | | |
+ | | | | ----timeout (lsb/msb)
+ | | | --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ | | | (0x30 20 Mtrans/sec- fast 20)
+ | | | (0x64 10 Mtrans/sec- fast )
+ | | | (0xc8 5 Mtrans/sec)
+ | | | (0x00 asynchronous)
+ | | -- ?? max sync offset (0x08 in NVRAM on 53c810a)
+ | | (0x10 in NVRAM on 53c875)
+ | --device bus width (0x08 narrow)
+ | (0x10 16 bit wide)
+ --flag bits
+ 0x00000001 - disconnect enabled
+ 0x00000010 - scan at boot time
+ 0x00000100 - scan luns
+ 0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width - 0x10
+ - sync offset ? - 0x10
+ - sync period - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00 (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+ (addr 0x0yyyy1 = 0x0000)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | | | | |
+ | | | | | | | | ----- parity check 0 - off
+ | | | | | | | | 1 - on
+ | | | | | | | |
+ | | | | | | | ------- sync neg 0 - off
+ | | | | | | | 1 - on
+ | | | | | | |
+ | | | | | | --------- disconnect 0 - off
+ | | | | | | 1 - on
+ | | | | | |
+ | | | | | ----------- start cmd 0 - off
+ | | | | | 1 - on
+ | | | | |
+ | | | | -------------- tagged cmds 0 - off
+ | | | | 1 - on
+ | | | |
+ | | | ---------------- wide neg 0 - off
+ | | | 1 - on
+ | | |
+ --------------------------- sync rate 0 - 10.0 Mtrans/sec
+ 1 - 8.0
+ 2 - 6.6
+ 3 - 5.7
+ 4 - 5.0
+ 5 - 4.0
+ 6 - 3.0
+ 7 - 2.0
+ 7 - 2.0
+ 8 - 20.0
+ 9 - 16.7
+ a - 13.9
+ b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | | | | | | | |
+ | | | | | | | | ----------- host ID 0x00 - 0x0f
+ | | | | | | | |
+ | | | | | | | ----------------------- support for 0 - off
+ | | | | | | | > 2 drives 1 - on
+ | | | | | | |
+ | | | | | | ------------------------- support drives 0 - off
+ | | | | | | > 1Gbytes 1 - on
+ | | | | | |
+ | | | | | --------------------------- bus reset on 0 - off
+ | | | | | power on 1 - on
+ | | | | |
+ | | | | ----------------------------- active neg 0 - off
+ | | | | 1 - on
+ | | | |
+ | | | -------------------------------- imm seek 0 - off
+ | | | 1 - on
+ | | |
+ | | ---------------------------------- scan luns 0 - off
+ | | 1 - on
+ | |
+ -------------------------------------- removable 0 - disable
+ as BIOS dev 1 - boot device
+ 2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+ x x x x x x x x x x x x x x x x
+ | | | | | |
+ | | | --------- boot delay 0 - 3 sec
+ | | | 1 - 5
+ | | | 2 - 10
+ | | | 3 - 20
+ | | | 4 - 30
+ | | | 5 - 60
+ | | | 6 - 120
+ | | |
+ --------------------------- max tag cmds 0 - 2
+ 1 - 4
+ 2 - 8
+ 3 - 16
+ 4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+ x x x x x x x x x x x x x x x x
+ |
+ ----- F2/F6 enable 0 - off ???
+ 1 - on ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
===============================================================================
End of NCR53C8XX driver README file
+
+
+
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 */
#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);
{
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);
*/
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.
*/
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
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;
#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
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,
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,
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,
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,
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,
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,
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,
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,
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,
{ 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 }
};
--- /dev/null
+/***********************************************************************
+ * 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 <linux/version.h>
+/* 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 <scsi/scsicam.h>
+#else
+#include <linux/scsicam.h>
+#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 */
#include "NCR53c406a.h"
#endif
+#ifdef CONFIG_SCSI_DC390T
+#include "dc390.h"
+#endif
+
#ifdef CONFIG_SCSI_AM53C974
#include "AM53C974.h"
#endif
#ifdef CONFIG_SCSI_DTC3280
DTC3x80,
#endif
+#ifdef CONFIG_SCSI_DC390T
+ DC390_T,
+#endif
#ifdef CONFIG_SCSI_NCR53C7xx
NCR53c7xx,
#endif
** And has been ported to NetBSD by
** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
**
+**-----------------------------------------------------------------------------
+**
+** 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 <dormouse@farsrobt.demon.co.uk>:
+** Support for NvRAM detection and reading.
+**
*******************************************************************************
*/
/*
-** 30 August 1996, version 1.12c
+** 26 July 1997, version 2.4
**
** Supported SCSI-II features:
** Synchronous negotiation
** 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
/*==========================================================
#include <linux/version.h>
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#include "linux/blk.h"
+#include <linux/blk.h>
#else
#include "../block/blk.h"
#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
+#include <linux/init.h>
+#else
+#ifndef __initdata
+#define __initdata
+#endif
+#ifndef __initfunc
+#define __initfunc(__arginit) __arginit
+#endif
+#endif
+
#include "scsi.h"
#include "hosts.h"
#include "constants.h"
**==========================================================
*/
-/*
-** 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.
#define SCSI_NCR_MYADDR (7)
#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.
#define SCSI_NCR_MAX_TAGS (4)
#endif
-/*==========================================================
-**
-** Configuration and Debugging
-**
-**==========================================================
-*/
-
/*
** Number of targets supported by the driver.
** n permits target numbers 0..n-1.
#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.
#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
/*
#define NCR_SNOOP_TIMEOUT (1000000)
-#if defined(SCSI_NCR_IOMAPPED) || defined(__alpha__)
-#define NCR_IOMAPPED
-#endif
-
/*==========================================================
**
** Defines for Linux.
** 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().
/*
** 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)
+
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
{
- if (vaddr) vfree((void *) (vaddr & PAGE_MASK));
+ if (vaddr)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+ iounmap((void *) (vaddr & PAGE_MASK));
+#else
+ vfree((void *) (vaddr & PAGE_MASK));
+#endif
}
-#else
+#else /* linux-1.2.13 */
/*
** Linux 1.2.X assumes that addresses (virtual, physical, bus)
#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);
** 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))
-static inline void *m_alloc(int size)
+#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)
{
- void *ptr = (void *) kmalloc(size, GFP_ATOMIC);
- if (((unsigned long) ptr) & 3)
- panic("ncr53c8xx: kmalloc returns misaligned address %lx\n", (unsigned long) ptr);
+ 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;
+ }
+
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
** 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;
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
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;
/*==========================================================
**
** 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
** 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_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_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_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_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_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)
#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 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) (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 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.
#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)
/*==========================================================
**
*/
#define CCB_MAGIC (0xf2691ad2)
-#define MAX_TAGS (16) /* hard limit */
/*==========================================================
**
#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)
/*---------------------------------------
**
u_long end;
u_long select;
u_long command;
- u_long data;
u_long status;
u_long disconnect;
u_long reselect;
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
*/
/*
** 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.
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
** 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
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,
*/
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))
*/
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 ..
vm_offset_t vaddr;
vm_offset_t paddr;
+ vm_offset_t vaddr2;
+ vm_offset_t paddr2;
+
/*
** pointer to the chip's registers.
*/
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.
*/
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
/*
** Start queue.
*/
- u_long squeue [MAX_START];
+ u_int32 squeue [MAX_START];
u_short squeueput;
u_short actccbs;
u_long disc_phys;
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.
*/
u_char msgout[8];
u_char msgin [8];
- u_long lastmsg;
+ u_int32 lastmsg;
/*
** Buffer for STATUS_IN phase.
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))
/*==========================================================
**
**----------------------------------------------------------
*/
+/*
+** 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];
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];
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];
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];
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
+
/*==========================================================
**
**
+ (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;
}
#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))
* 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 ...
*/
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<MAX_START >===========
-** || SCR_COPY (4),
-** || NADDR (squeue[i]),
-** || RADDR (dsa),
-** || SCR_CALL,
-** || PADDR (trysel),
-** ##==========================================
-**
-** SCR_JUMP,
-** PADDR(tryloop),
-**
-**-----------------------------------------------------------
-*/
-0
-
+ PADDRH(tryloop),
}/*-------------------------< TRYSEL >----------------------*/,{
/*
** Now:
** patch the launch field.
** should look like an idle process.
*/
- SCR_COPY (4),
+ SCR_COPY_F (4),
RADDR (dsa),
PADDR (skip2),
SCR_COPY (8),
** We patch the address part of a
** COPY command with the DSA-register.
*/
- SCR_COPY (4),
+ SCR_COPY_F (4),
RADDR (dsa),
PADDR (loadpos),
/*
SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
PADDR (msg_in),
SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
- PADDR (msg_reject),
+ PADDRH (msg_reject),
/*
** normal processing
*/
SCR_FROM_REG (socl),
0,
SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
+ PADDRH (msg_parity),
SCR_FROM_REG (scratcha),
0,
/*
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 ...
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
+ ** ... and wait for the disconnect.
*/
- SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
- SIR_REJECT_RECEIVED,
+ SCR_WAIT_DISC,
+ 0,
+}/*-------------------------< CLEANUP >-------------------*/,{
+ /*
+ ** dsa: Pointer to ccb
+ ** or xxxxxxFF (no ccb)
+ **
+ ** HS_REG: Host-Status (<>0!)
+ */
+ SCR_FROM_REG (dsa),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (0xff)),
+ PADDR (signal),
+ /*
+ ** dsa is valid.
+ ** save the status registers
+ */
+ SCR_COPY (4),
+ RADDR (scr0),
+ NADDR (header.status),
+ /*
+ ** and copy back the header to the ccb.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (cleanup0),
+ SCR_COPY (sizeof (struct head)),
+ NADDR (header),
+}/*-------------------------< CLEANUP0 >--------------------*/,{
+ 0,
+
+ /*
+ ** If command resulted in "check condition"
+ ** status and is not yet completed,
+ ** try to get the condition code.
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
+ 16,
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+ PADDRH(getcc2),
+ /*
+ ** And make the DSA register invalid.
+ */
+/*>>>*/ SCR_LOAD_REG (dsa, 0xff), /* invalid */
+ 0,
+}/*-------------------------< SIGNAL >----------------------*/,{
+ /*
+ ** if status = queue full,
+ ** reinsert in startqueue and stall queue.
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+ SIR_STALL_QUEUE,
+ /*
+ ** if job completed ...
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ /*
+ ** ... signal completion to the host
+ */
+ SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
+ 0,
+ /*
+ ** Auf zu neuen Schandtaten!
+ */
+ SCR_JUMP,
+ PADDR(start),
+
+}/*-------------------------< SAVE_DP >------------------*/,{
+ /*
+ ** SAVE_DP message:
+ ** Copy TEMP register to SAVEP in header.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+ SCR_JUMP,
+ PADDR (clrack),
+}/*-------------------------< RESTORE_DP >---------------*/,{
+ /*
+ ** RESTORE_DP message:
+ ** Copy SAVEP in header to TEMP register.
+ */
+ SCR_COPY (4),
+ NADDR (header.savep),
+ RADDR (temp),
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< DISCONNECT >---------------*/,{
+ /*
+ ** If QUIRK_AUTOSAVE is set,
+ ** do an "save pointer" operation.
+ */
+ SCR_FROM_REG (QU_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
+ 12,
+ /*
+ ** like SAVE_DP message:
+ ** Copy TEMP register to SAVEP in header.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+/*>>>*/ /*
+ ** Check if temp==savep or temp==goalp:
+ ** if not, log a missing save pointer message.
+ ** In fact, it's a comparison mod 256.
+ **
+ ** Hmmm, I hadn't thought that I would be urged to
+ ** write this kind of ugly self modifying code.
+ **
+ ** It's unbelievable, but the ncr53c8xx isn't able
+ ** to subtract one register from another.
+ */
+ SCR_FROM_REG (temp),
+ 0,
+ /*
+ ** You are not expected to understand this ..
+ **
+ ** CAUTION: only little endian architectures supported! XXX
+ */
+ SCR_COPY_F (1),
+ NADDR (header.savep),
+ PADDR (disconnect0),
+}/*-------------------------< DISCONNECT0 >--------------*/,{
+/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (1)),
+ 20,
+ /*
+ ** neither this
+ */
+ SCR_COPY_F (1),
+ NADDR (header.goalp),
+ PADDR (disconnect1),
+}/*-------------------------< DISCONNECT1 >--------------*/,{
+ SCR_INT ^ IFFALSE (DATA (1)),
+ SIR_MISSING_SAVE,
+/*>>>*/
+
+ /*
+ ** DISCONNECTing ...
+ **
+ ** disable the "unexpected disconnect" feature,
+ ** and remove the ACK signal.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ ** Wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** Profiling:
+ ** Set a time stamp,
+ ** and count the disconnects.
+ */
+ SCR_COPY (sizeof (u_long)),
+ KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (header.stamp.disconnect),
+ SCR_COPY (4),
+ NADDR (disc_phys),
+ RADDR (temp),
+ SCR_REG_REG (temp, SCR_ADD, 0x01),
+ 0,
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (disc_phys),
+ /*
+ ** Status is: DISCONNECTED.
+ */
+ SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< MSG_OUT >-------------------*/,{
+ /*
+ ** The target requests a message.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ /*
+ ** If it was no ABORT message ...
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
+ PADDRH (msg_out_abort),
+ /*
+ ** ... wait for the next phase
+ ** if it's a message out, send it again, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR (msg_out),
+}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+ /*
+ ** ... else clear the message ...
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (4),
+ RADDR (scratcha),
+ NADDR (msgout),
+ /*
+ ** ... and process the next phase
+ */
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*------------------------< BADGETCC >---------------------*/,{
+ /*
+ ** If SIGP was set, clear it and try again.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+ 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),
+ 0,
+ SCR_CLR (SCR_TRG),
+ 0,
+ /*
+ ** Sleep waiting for a reselection.
+ ** If SIGP is set, special treatment.
+ **
+ ** Zu allem bereit ..
+ */
+ 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 ?
+ **
+ ** load the target id into the SFBR
+ ** and jump to the control block.
+ **
+ ** Look at the declarations of
+ ** - struct ncb
+ ** - struct tcb
+ ** - struct lcb
+ ** - struct ccb
+ ** to understand what's going on.
+ */
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ SCR_JUMP,
+ 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.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+ PADDR (start),
+ SCR_JUMP,
+ PADDR (reselect),
+
+}/*-------------------------< RESEL_TMP >-------------------*/,{
+ /*
+ ** The return address in TEMP
+ ** is in fact the data structure address,
+ ** so copy it to the DSA register.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ RADDR (dsa),
+ SCR_JUMP,
+ PADDR (prepare),
+
+}/*-------------------------< RESEL_LUN >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get an IDENTIFY message
+ ** Wait for a msg_in phase.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 48,
+ /*
+ ** message phase
+ ** It's not a sony, it's a trick:
+ ** read the data without acknowledging it.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
+ 32,
+ /*
+ ** It WAS an Identify message.
+ ** get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Mask out the lun.
+ */
+ SCR_REG_REG (sfbr, SCR_AND, 0x07),
+ 0,
+ SCR_RETURN,
+ 0,
+ /*
+ ** No message phase or no IDENTIFY message:
+ ** return 0.
+ */
+/*>>>*/ SCR_LOAD_SFBR (0),
+ 0,
+ SCR_RETURN,
+ 0,
+
+}/*-------------------------< RESEL_TAG >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get a SIMPLE_TAG message
+ ** Wait for a MSG_IN phase.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 64,
+ /*
+ ** message phase
+ ** It's a trick - read the data
+ ** without acknowledging it.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
+ 48,
+ /*
+ ** 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<MAX_SCATTER >=========
+** || 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<MAX_START >===========
+** || 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),
SCR_FROM_REG (socl),
0,
SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
+ PADDRH (msg_parity),
SCR_FROM_REG (scratcha),
0,
/*
SCR_FROM_REG (socl),
0,
SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
+ PADDRH (msg_parity),
SCR_FROM_REG (scratcha),
0,
/*
*/
SCR_JUMP ^ IFTRUE (DATA (3)),
- PADDR (msg_ext_3),
+ PADDRH (msg_ext_3),
SCR_JUMP ^ IFFALSE (DATA (2)),
PADDR (msg_bad),
}/*-------------------------< MSG_EXT_2 >----------------*/,{
SCR_FROM_REG (socl),
0,
SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
+ PADDRH (msg_parity),
SCR_FROM_REG (scratcha),
0,
SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
- PADDR (msg_wdtr),
+ PADDRH (msg_wdtr),
/*
** unknown extended message
*/
SCR_FROM_REG (socl),
0,
SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
+ PADDRH (msg_parity),
/*
** let the host do the real work.
*/
}/*-------------------------< 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 >-------------------*/,{
- /*
- ** dsa: Pointer to ccb
- ** or xxxxxxFF (no ccb)
- **
- ** HS_REG: Host-Status (<>0!)
- */
- SCR_FROM_REG (dsa),
- 0,
- SCR_JUMP ^ IFTRUE (DATA (0xff)),
- PADDR (signal),
- /*
- ** dsa is valid.
- ** save the status registers
- */
- SCR_COPY (4),
- RADDR (scr0),
- NADDR (header.status),
- /*
- ** and copy back the header to the ccb.
- */
- SCR_COPY (4),
- RADDR (dsa),
- PADDR (cleanup0),
- SCR_COPY (sizeof (struct head)),
- NADDR (header),
-}/*-------------------------< CLEANUP0 >--------------------*/,{
- 0,
-
- /*
- ** If command resulted in "check condition"
- ** status and is not yet completed,
- ** try to get the condition code.
- */
- SCR_FROM_REG (HS_REG),
- 0,
-/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
- 16,
- SCR_FROM_REG (SS_REG),
- 0,
- SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
- PADDR(getcc2),
- /*
- ** And make the DSA register invalid.
- */
-/*>>>*/ SCR_LOAD_REG (dsa, 0xff), /* invalid */
- 0,
-}/*-------------------------< SIGNAL >----------------------*/,{
- /*
- ** if status = queue full,
- ** reinsert in startqueue and stall queue.
- */
- SCR_FROM_REG (SS_REG),
- 0,
- SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
- SIR_STALL_QUEUE,
- /*
- ** if job completed ...
- */
- SCR_FROM_REG (HS_REG),
- 0,
- /*
- ** ... signal completion to the host
- */
- SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
- 0,
- /*
- ** Auf zu neuen Schandtaten!
- */
- SCR_JUMP,
- PADDR(start),
-
-}/*-------------------------< SAVE_DP >------------------*/,{
- /*
- ** SAVE_DP message:
- ** Copy TEMP register to SAVEP in header.
- */
- SCR_COPY (4),
- RADDR (temp),
- NADDR (header.savep),
- SCR_JUMP,
- PADDR (clrack),
-}/*-------------------------< RESTORE_DP >---------------*/,{
- /*
- ** RESTORE_DP message:
- ** Copy SAVEP in header to TEMP register.
- */
- SCR_COPY (4),
- NADDR (header.savep),
- RADDR (temp),
- SCR_JUMP,
- PADDR (clrack),
-
-}/*-------------------------< DISCONNECT >---------------*/,{
- /*
- ** If QUIRK_AUTOSAVE is set,
- ** do an "save pointer" operation.
- */
- SCR_FROM_REG (QU_REG),
- 0,
-/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
- 12,
- /*
- ** like SAVE_DP message:
- ** Copy TEMP register to SAVEP in header.
- */
- SCR_COPY (4),
- RADDR (temp),
- NADDR (header.savep),
-/*>>>*/ /*
- ** Check if temp==savep or temp==goalp:
- ** if not, log a missing save pointer message.
- ** In fact, it's a comparison mod 256.
- **
- ** Hmmm, I hadn't thought that I would be urged to
- ** write this kind of ugly self modifying code.
- **
- ** It's unbelievable, but the ncr53c8xx isn't able
- ** to subtract one register from another.
- */
- SCR_FROM_REG (temp),
- 0,
- /*
- ** You are not expected to understand this ..
- **
- ** CAUTION: only little endian architectures supported! XXX
- */
- SCR_COPY (1),
- NADDR (header.savep),
- PADDR (disconnect0),
-}/*-------------------------< DISCONNECT0 >--------------*/,{
-/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (1)),
- 20,
- /*
- ** neither this
- */
- SCR_COPY (1),
- NADDR (header.goalp),
- PADDR (disconnect1),
-}/*-------------------------< DISCONNECT1 >--------------*/,{
- SCR_INT ^ IFFALSE (DATA (1)),
- SIR_MISSING_SAVE,
-/*>>>*/
-
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
/*
- ** DISCONNECTing ...
- **
- ** disable the "unexpected disconnect" feature,
- ** and remove the ACK signal.
+ ** get extended message code.
*/
- SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
0,
- SCR_CLR (SCR_ACK|SCR_ATN),
+ 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),
/*
- ** Wait for the disconnect.
+ ** unknown extended message
*/
- SCR_WAIT_DISC,
+ SCR_JUMP,
+ PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
/*
- ** Profiling:
- ** Set a time stamp,
- ** and count the disconnects.
+ ** get period and offset
*/
- SCR_COPY (sizeof (u_long)),
- KVAR(SCRIPT_KVAR_JIFFIES),
- NADDR (header.stamp.disconnect),
- SCR_COPY (4),
- NADDR (disc_phys),
- RADDR (temp),
- SCR_REG_REG (temp, SCR_ADD, 0x01),
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ SCR_FROM_REG (socl),
0,
- SCR_COPY (4),
- RADDR (temp),
- NADDR (disc_phys),
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
/*
- ** Status is: DISCONNECTED.
+ ** let the host do the real work.
*/
- SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+ SCR_INT,
+ SIR_NEGO_SYNC,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
0,
- SCR_JUMP,
- PADDR (cleanup),
-}/*-------------------------< MSG_OUT >-------------------*/,{
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_NEGO_PROTO,
/*
- ** The target requests a message.
+ ** Send the M_X_SYNC_REQ
*/
- SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
NADDR (msgout),
+ SCR_CLR (SCR_ATN),
+ 0,
SCR_COPY (1),
RADDR (sfbr),
NADDR (lastmsg),
- /*
- ** If it was no ABORT message ...
- */
- SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
- PADDR (msg_out_abort),
- /*
- ** ... wait for the next phase
- ** if it's a message out, send it again, ...
- */
- SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
- PADDR (msg_out),
-}/*-------------------------< MSG_OUT_DONE >--------------*/,{
- /*
- ** ... else clear the message ...
- */
- SCR_LOAD_REG (scratcha, M_NOOP),
- 0,
- SCR_COPY (4),
- RADDR (scratcha),
- NADDR (msgout),
- /*
- ** ... and process the next phase
- */
SCR_JUMP,
- PADDR (dispatch),
+ PADDR (msg_out_done),
+
}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
/*
** After ABORT message,
** We patch the address part of a COPY command
** with the address of the dsa register ...
*/
- SCR_COPY (4),
+ SCR_COPY_F (4),
RADDR (dsa),
- PADDR (getcc1),
+ PADDRH (getcc1),
/*
** ... then we do the actual copy.
*/
SCR_FROM_REG (QU_REG),
0,
SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
- PADDR(getcc3),
+ PADDRH(getcc3),
/*
** Then try to connect to the target.
** If we are reselected, special treatment
** 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 >---------------------*/,{
- /*
- ** If SIGP was set, clear it and try again.
- */
- SCR_FROM_REG (ctest2),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
- PADDR (getcc2),
- SCR_INT,
- SIR_SENSE_FAILED,
-}/*-------------------------< RESELECT >--------------------*/,{
- /*
- ** make the DSA invalid.
- */
- SCR_LOAD_REG (dsa, 0xff),
- 0,
- SCR_CLR (SCR_TRG),
- 0,
- /*
- ** Sleep waiting for a reselection.
- ** If SIGP is set, special treatment.
- **
- ** Zu allem bereit ..
- */
- SCR_WAIT_RESEL,
- PADDR(reselect2),
- /*
- ** ... zu nichts zu gebrauchen ?
- **
- ** load the target id into the SFBR
- ** and jump to the control block.
- **
- ** Look at the declarations of
- ** - struct ncb
- ** - struct tcb
- ** - struct lcb
- ** - struct ccb
- ** to understand what's going on.
- */
- SCR_REG_SFBR (ssid, SCR_AND, 0x87),
- 0,
- SCR_TO_REG (ctest0),
- 0,
- SCR_JUMP,
- NADDR (jump_tcb),
-}/*-------------------------< RESELECT2 >-------------------*/,{
- /*
- ** If it's not connected :(
- ** -> interrupted by SIGP bit.
- ** Jump to start.
- */
- SCR_FROM_REG (ctest2),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
- PADDR (start),
- SCR_JUMP,
- PADDR (reselect),
-
-}/*-------------------------< RESEL_TMP >-------------------*/,{
- /*
- ** The return address in TEMP
- ** is in fact the data structure address,
- ** so copy it to the DSA register.
- */
- SCR_COPY (4),
- RADDR (temp),
- RADDR (dsa),
- SCR_JUMP,
- PADDR (prepare),
-
-}/*-------------------------< RESEL_LUN >-------------------*/,{
- /*
- ** come back to this point
- ** to get an IDENTIFY message
- ** Wait for a msg_in phase.
- */
-/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
- 48,
- /*
- ** message phase
- ** It's not a sony, it's a trick:
- ** read the data without acknowledging it.
- */
- SCR_FROM_REG (sbdl),
- 0,
-/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
- 32,
- /*
- ** It WAS an Identify message.
- ** get it and ack it!
- */
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin),
- SCR_CLR (SCR_ACK),
- 0,
- /*
- ** Mask out the lun.
- */
- SCR_REG_REG (sfbr, SCR_AND, 0x07),
- 0,
- SCR_RETURN,
- 0,
- /*
- ** No message phase or no IDENTIFY message:
- ** return 0.
- */
-/*>>>*/ SCR_LOAD_SFBR (0),
- 0,
- SCR_RETURN,
- 0,
-
-}/*-------------------------< RESEL_TAG >-------------------*/,{
- /*
- ** come back to this point
- ** to get a SIMPLE_TAG message
- ** Wait for a MSG_IN phase.
- */
-/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
- 64,
- /*
- ** message phase
- ** It's a trick - read the data
- ** without acknowledging it.
- */
- SCR_FROM_REG (sbdl),
- 0,
-/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
- 48,
- /*
- ** 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)
+ **
+ ** 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<MAX_SCATTER >=========
-** || 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<MAX_SCATTER >=========
+** ##===========< i=0; i<MAX_SCATTER >=========
** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
** || PADDR (dispatch),
** || SCR_MOVE_TBL ^ SCR_DATA_OUT,
**
**---------------------------------------------------------
*/
-0 /* was (u_long)&ident ? */
-
+0
}/*-------------------------< ABORTTAG >-------------------*/,{
/*
** Abort a bad reselection.
**==========================================================
*/
-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; i<MAX_START; i++) {
*p++ =SCR_COPY (4);
*p++ =NADDR (squeue[i]);
*p++ =PADDR (trysel);
};
*p++ =SCR_JUMP;
- *p++ =PADDR(tryloop);
+ *p++ =PADDRH(tryloop);
- assert ((u_long)p == (u_long)&scr->tryloop + 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; i<MAX_SCATTER; i++) {
+ for (i=0; i<MAX_SCATTER; i++) {
*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
*p++ =PADDR (checkatn);
*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
assert ((u_long)p == (u_long)&scr->data_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; i<MAX_SCATTER; i++) {
+ for (i=0; i<MAX_SCATTER; i++) {
*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
*p++ =PADDR (dispatch);
*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
*p++ =SCR_JUMP;
*p++ =PADDR (no_data);
- assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
+ assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
}
/*==========================================================
**==========================================================
*/
-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) {
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:
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;
*dst++ = *src++;
};
+ if (bootverbose > 1 && opchanged)
+ printf("%s: NO FLUSH bit removed from %d script instructions\n",
+ ncr_name(np), opchanged);
}
/*==========================================================
**==========================================================
*/
-#define MIN_ASYNC_PD 40
-#define MIN_SYNC_PD 20
-
-
/*
** Linux host data structure
**
*/
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-<target %d, lun %d>: ", 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.
** 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
/*
** Initialize structure.
*/
- instance->irq = irq;
- host_data = (struct host_data *) instance->hostdata;
+ host_data = (struct host_data *) instance->hostdata;
+
+ /*
+ ** 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->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 = &host_data->ncb_data;
- 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->script0 =
+ (struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
+ np->scripth0 = &host_data->scripth_data;
/*
** Initialize timer structure
** 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
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)
/*
** 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 );
*/
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;
};
** 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
/*
** start the timeout daemon
*/
- ncr_timeout (np);
np->lasttime=0;
+ ncr_timeout (np);
/*
** use SIMPLE TAG messages by default
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);
return -1;
}
-/*==========================================================
-**
-**
-** 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);
-}
-
/*==========================================================
**
**
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;
int segments;
u_char qidx, nego, idmsg, *msgptr;
- u_long msglen, msglen2;
+ u_int msglen, msglen2;
u_long flags;
int xfer_direction;
cmd->SCp.ptr = NULL;
cmd->SCp.buffer = NULL;
- /*---------------------------------------------
- **
- ** 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 ...
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);
/*---------------------------------------------------
**
** 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);
/*---------------------------------------------------
**
- ** 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
/*---------------------------------------------------
**
**
**----------------------------------------------------
*/
-
+#ifdef SCSI_NCR_PROFILE_SUPPORT
bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
cp->phys.header.stamp.start = jiffies;
+#endif
/*----------------------------------------------------
**
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 ?
*/
*/
if (!nego && !tp->period) {
- if (SCSI_NCR_MAX_SYNC
+ if ( 1
#if defined (CDROM_ASYNC)
&& ((tp->inqdata[0] & 0x1f) != 5)
#endif
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;
};
/*---------------------------------------------------
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;
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);
}
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:
**----------------------------------------------------
*/
+ 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;
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);
/*
return(DID_OK);
}
+/*==========================================================
+**
+**
+** 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);
+}
+
/*==========================================================
**
**
**
**==========================================================
*/
-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);
**
**==========================================================
*/
-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
/*
* 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.
*/
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)) {
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);
lcb_p lp;
int target, lun;
int i;
- u_char scntl3;
printf("%s: releasing host resources\n", ncr_name(np));
** 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;
** Disable chip interrupts
*/
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: disabling chip interrupts\n", ncr_name(np));
#endif
OUTW (nc_sien , 0);
** 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)
/*
** 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);
** 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));
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));
** timestamp
** Optional, spare some CPU time
*/
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
ncb_profile (np, cp);
#endif
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.
*/
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,
/*
** 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.
*/
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))) {
*/
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)) {
/*
** Free this ccb
*/
- ncr_free_ccb (np, cp);
+ ncr_free_ccb (np, cp, cmd->target, cmd->lun);
/*
** requeue awaiting scsi commands
** complete all jobs that are not IDLE.
*/
- ccb_p cp = &np->ccb;
+ ccb_p cp = np->ccb;
while (cp) {
switch (cp->host_status) {
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.
** 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
/*
** 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.
*/
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_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!! */
- 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<<np->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*/
+ ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
-#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK
- OUTB (nc_ctest4, 0x00 ); /* disable master parity checking */
-#else
- OUTB (nc_ctest4, 0x08 ); /* enable master parity checking */
-#endif
+ OUTB (nc_scid , RRE|np->myaddr); /* Adapter SCSI address */
+ OUTW (nc_respid, 1ul<<np->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 ); /* 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 */
+ 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. */
/*
- ** Reinitialize usrsync.
- ** Have to renegotiate synch mode.
+ ** Disable disconnects.
*/
- usrsync = 255;
-
-#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
+ np->disc = 0;
/*
- ** Reinitialize usrwide.
- ** Have to renegotiate wide mode.
+ ** Enable GPIO0 pin for writing if LED support.
*/
- usrwide = (SCSI_NCR_MAX_WIDE);
- if (usrwide > np->maxwide) usrwide=np->maxwide;
+ if (np->features & FE_LED0) {
+ OUTOFFB (nc_gpcntl, 0x01);
+ }
/*
- ** Disable disconnects.
+ ** 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));
+ }
- np->disc = 0;
+ /*
+ ** enable ints
+ */
+
+ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+ OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
+
+ /*
+ ** For 895/6 enable SBMC interrupt and save current SCSI bus mode.
+ */
+ 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;i<MAX_TARGET;i++) {
tp->sval = 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.
*/
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
** 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
tp->widedone=0;
}
+/*==========================================================
+**
+** 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;
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);
+
+ /*
+ ** 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";
- 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");
+ 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
- */
- OUTB (nc_sxfer, sxfer);
- np->sync_st = sxfer;
-
- /*
** 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;
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);
}
/*==========================================================
**==========================================================
*/
-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; l<MAX_LUN; l++) {
lcb_p lp;
+ u_char wastags;
+
if (!tp) break;
lp=tp->lp[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 %d cmds/lun\n", lp->usetags);
+ 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("disabling tagged command queueing\n");
}
};
}
*/
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 {
**----------------------------------------------------
*/
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
static void ncr_usercmd (ncb_p np)
{
np->user.data = SCSI_NCR_MAX_TAGS;
for (t=0; t<MAX_TARGET; t++) {
if (!((np->user.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;
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
/*==========================================================
**
** 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;
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
*/
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.
*/
/*
** 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];
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) {
/* fall through */
case HS_DISCONNECT:
- cp->host_status=HS_TIMEOUT;
+ cp->host_status=HS_ABORTED;
};
cp->tag = 0;
** 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);
}
if (DEBUG_FLAGS & DEBUG_TINY) printf ("}");
restore_flags(flags);
}
-#endif /* SCSI_NCR_BROKEN_INTR */
+#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>",
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; i<sizeof(np->regdump); i++)
+ for (i = 0; i<sizeof(np->regdump); 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)
+ ncr_log_hard_error(np, sist, dstat);
- current script command:
- dsp: script adress (relative to start of script).
- dbc: first word of script command.
+ printf ("%s: have to clear fifos.\n", ncr_name (np));
+ OUTB (nc_stest3, TE|CSF);
+ OUTONB (nc_ctest3, CLF);
- 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 */
- }
-
- /*----------------------------------------
- ** handshake timeout
- **----------------------------------------
- */
+ 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));
}
/*==========================================================
*/
dsa = INL (nc_dsa);
- cp = &np->ccb;
+ cp = np->ccb;
while (cp && (CCB_PHYS (cp, phys) != dsa))
cp = cp->link_ccb;
*/
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));*/
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;
}
/*==========================================================
{
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);
+ dsp = INL (nc_dsp);
+ dbc = INL (nc_dbc);
+ sbcl = INB (nc_sbcl);
- cmd = dbc >> 24;
- rest= dbc & 0xffffff;
- delta=(INB (nc_dfifo) - rest) & 0x7f;
+ cmd = dbc >> 24;
+ rest = dbc & 0xffffff;
/*
- ** 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.
+ ** Take into account dma fifo and various buffers and latches,
+ ** only if the interrupted phase is an OUTPUT phase.
*/
- 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 */
+ 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;
} 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,
};
/*
- ** if old phase not dataphase, leave here.
+ ** check cmd against assumed interrupted script command.
*/
if (cmd != (vdsp[0] >> 24)) {
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;
};
*/
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));
}
/*==========================================================
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);
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;
}
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; */
/*--------------------------------------------------------------------
**
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;
};
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;
};
** 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)
/*
** 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) {
/*
** 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;
};
};
** 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;
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");
}
/*
** 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;
};
};
** prepare an answer message
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
np->msgout[0] = M_EXTENDED;
np->msgout[1] = 2;
*/
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.
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@
/*
** Look for a disconnected job.
*/
- cp = &np->ccb;
+ cp = np->ccb;
while (cp && cp->host_status != HS_DISCONNECT)
cp = cp->link_ccb;
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));
}
/*==========================================================
*/
lp = np->target[target].lp[lun];
- if (lp) {
+
+ if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) {
+
cp = lp->next_ccb;
/*
*/
while (cp && cp->magic) cp = cp->next_ccb;
+
+ /*
+ ** Increment active commands and decrement credit.
+ */
+
+ if (cp) {
+ ++lp->active;
+ --lp->opennings;
+ }
}
/*
if ((!cp) && lp && lp->actccbs > 0)
return ((ccb_p) 0);
- if (!cp) cp = &np->ccb;
+ if (!cp) cp = np->ccb;
/*
** Wait until available.
**==========================================================
*/
-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
}
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);
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);
}
/*
** 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) {
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.
**
/*
** 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;
cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp);
/*
- ** Chain into wakeup list
+ ** Chain into wakeup list
+ */
+ 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;
+}
+
+/*==========================================================
+**
+**
+** 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 ...
*/
- cp->link_ccb = np->ccb.link_ccb;
- np->ccb.link_ccb = cp;
+ 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;
+ };
/*
- ** Chain into CCB list
+ ** want to increase the number ?
*/
- cp->next_ccb = lp->next_ccb;
- lp->next_ccb = cp;
+ if (lp->reqlink > lp->actlink) {
+ u_char diff = lp->reqlink - lp->actlink;
+
+ lp->opennings += diff;
-goto loop_alloc_ccb;
+ 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);
+ };
}
/*==========================================================
**----------------------------------------------------------
*/
-/* 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.
** 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;
- }
+ struct scr_tblmove *data;
+ int segment = 0;
+ int use_sg = (int) cmd->use_sg;
- 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;
-
- 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 0
+ bzero (cp->phys.data, sizeof (cp->phys.data));
+#endif
+ data = cp->phys.data;
+ cp->data_len = 0;
+
+ 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, 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;
+ else if (use_sg <= MAX_SCATTER) {
+ struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+
+ 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 */
/*==========================================================
**
*/
#ifndef NCR_IOMAPPED
+__initfunc(
static int ncr_regtest (struct ncb* np)
+)
{
register volatile u_long data;
/*
}
#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);
}
/*
** init
*/
- pc = NCB_SCRIPT_PHYS (np, snooptest);
+ pc = NCB_SCRIPTH_PHYS (np, snooptest);
host_wr = 1;
ncr_wr = 2;
/*
/*
** 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);
};
/*
**==========================================================
*/
-/*
-** 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;
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 */
}
#undef PROFILE
-#endif /* SCSI_NCR_PROFILE */
+#endif /* SCSI_NCR_PROFILE_SUPPORT */
/*==========================================================
**
/*==========================================================
**
** Determine the ncr's clock frequency.
-** This is important for the negotiation
+** This is essential for the negotiation
** of the synchronous transfer rate.
**
**==========================================================
** 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)
+/*
+ * calculate NCR SCSI clock frequency (in KHz)
+ */
+__initfunc(
+static unsigned ncrgetfreq (ncb_p np, int gen)
+)
{
-#if 0
- u_char tbl[5] = {6,2,3,4,6};
- u_char f;
- u_char ns_clock = (1000/NCR_CLOCK);
+ 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<<gen * 125us */
+ while (!(INW(nc_sist) & GEN) && ms++ < 100000)
+ DELAY(1000); /* count ms */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ /*
+ * set prescaler to divide by whatever 0 means
+ * 0 ought to choose divide by 2, but appears
+ * to set divide by 3.5 mode in my 53c810 ...
+ */
+ OUTB (nc_scntl3, 0);
+
+ if (bootverbose >= 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;
+}
- /*
- ** Compute the best value for scntl3.
- */
+/*
+ * 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;
- 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;
+ np->multiplier = 1;
+ f1 = 40000;
- 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.
- */
-
- 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;
+ ** 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);
+
+ (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;
- np->ns_sync = 25;
- np->ns_async = 50;
- np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
+ 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;
- if (bootverbose) {
- printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
- ncr_name(np), scntl3, np->rv_scntl3);
+ f1 /= np->multiplier;
}
-#endif
+
+ /*
+ ** Compute controller synchronous parameters.
+ */
+ f1 *= np->multiplier;
+ np->clock_khz = f1;
}
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
#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);
+ }
+
+ 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 */
+}
-#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+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.
** 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;
# 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)
+__initfunc(
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device)
+)
{
- ushort vendor_id, device_id, command;
+ 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;
+ uint base, io_port;
#else
- ulong base, io_port;
+ ulong base, io_port;
#endif
- uchar irq, revision;
- int error, expected_chip;
- int expected_id = -1, max_revision = -1, min_revision = -1;
- int i;
+ int i, error;
- printk("ncr53c8xx : at PCI bus %d, device %d, function %d\n",
- bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram *nvram = device->nvram;
+#endif
+ ncr_chip *chip;
- if (!pcibios_present()) {
- printk("ncr53c8xx : not initializing due to lack of PCI BIOS,\n");
- return -1;
- }
+ 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;
+ }
- 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;
- }
+ /*
+ * 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 (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_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
- 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;
- }
+ /*
+ ** 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.
+ */
- 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 (!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
+ unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
- 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)
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
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;
{
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
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
}
/*
** 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);
}
}
}
}
-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;
}
#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)
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;
}
**=========================================================================
*/
+#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')
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;
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<<target);
#ifdef DEBUG_PROC_INFO
printf("ncr_user_command: target=%ld\n", target);
#endif
- if (target > MAX_TARGET)
- return -EINVAL;
- uc->target = (1<<target);
+ }
break;
}
SKIP_SPACES(1);
if ((arg_len = is_keyword(ptr, len, "alloc")))
uc->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")))
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")))
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;
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;
** 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;
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);
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);
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)
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;
}
}
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
*/
#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
#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.
**
#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)
#define SCSI_NCR_MAX_LUN (1)
#endif
-#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5)
-
/*
** Define Scsi_Host_Template parameters
**
#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,\
#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,\
#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
**
#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 ******************/
/*-----------------------------------------------------------------
/*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. */
#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 */
#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 */
/*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) */
/*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!) */
/*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 */
** << 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))
/*-----------------------------------------------------------
**
**-----------------------------------------------------------
*/
+#define SCR_NO_OP 0x80000000
#define SCR_JUMP 0x80080000
#define SCR_JUMPR 0x80880000
#define SCR_CALL 0x88080000
--- /dev/null
+/***********************************************************************
+ * FILE NAME : SCSIIOM.C *
+ * BY : C.L. Huang, ching@tekram.com.tw *
+ * Description: Device Driver for Tekram DC-390 (T) PCI SCSI *
+ * Bus Master Host Adapter *
+ ***********************************************************************/
+
+
+static USHORT
+DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ USHORT ioport, rc;
+ UCHAR bval, bval1, i, cnt;
+ PUCHAR ptr;
+ ULONG wlval;
+
+ pSRB->TagNumber = 31;
+ ioport = pACB->IOPortBase;
+ bval = pDCB->UnitSCSIID;
+ outb(bval,ioport+Scsi_Dest_ID);
+ bval = pDCB->SyncPeriod;
+ outb(bval,ioport+Sync_Period);
+ bval = pDCB->SyncOffset;
+ outb(bval,ioport+Sync_Offset);
+ bval = pDCB->CtrlR1;
+ outb(bval,ioport+CtrlReg1);
+ bval = pDCB->CtrlR3;
+ outb(bval,ioport+CtrlReg3);
+ bval = pDCB->CtrlR4;
+ outb(bval,ioport+CtrlReg4);
+ bval = CLEAR_FIFO_CMD; /* Flush FIFO */
+ outb(bval,ioport+ScsiCmd);
+
+ pSRB->ScsiPhase = SCSI_NOP0;
+ bval = pDCB->IdentifyMsg;
+ if( !(pDCB->SyncMode & EN_ATN_STOP) )
+ {
+ if( (pSRB->CmdBlock[0] == INQUIRY) ||
+ (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ bval &= 0xBF; /* NO disconnection */
+ outb(bval,ioport+ScsiFifo);
+ bval1 = SELECT_W_ATN;
+ pSRB->SRBState = SRB_START_;
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ {
+ if( !(pDCB->IdentifyMsg & 7) ||
+ (pSRB->CmdBlock[0] != INQUIRY) )
+ {
+ bval1 = SEL_W_ATN_STOP;
+ pSRB->SRBState = SRB_MSGOUT;
+ }
+ }
+ }
+ else
+ {
+ if(pDCB->SyncMode & EN_TAG_QUEUING)
+ {
+ outb(bval,ioport+ScsiFifo);
+ bval = MSG_SIMPLE_QTAG;
+ outb(bval,ioport+ScsiFifo);
+ wlval = 1;
+ bval = 0;
+ while( wlval & pDCB->TagMask )
+ {
+ wlval = wlval << 1;
+ bval++;
+ }
+ outb(bval,ioport+ScsiFifo);
+ pDCB->TagMask |= wlval;
+ pSRB->TagNumber = bval;
+ bval1 = SEL_W_ATN2;
+ pSRB->SRBState = SRB_START_;
+ }
+ else
+ {
+ outb(bval,ioport+ScsiFifo);
+ bval1 = SELECT_W_ATN;
+ pSRB->SRBState = SRB_START_;
+ }
+ }
+
+ if( pSRB->SRBFlag & AUTO_REQSENSE )
+ {
+ bval = REQUEST_SENSE;
+ outb(bval,ioport+ScsiFifo);
+ bval = pDCB->IdentifyMsg << 5;
+ outb(bval,ioport+ScsiFifo);
+ bval = 0;
+ outb(bval,ioport+ScsiFifo);
+ outb(bval,ioport+ScsiFifo);
+ bval = sizeof(pSRB->pcmd->sense_buffer);
+ outb(bval,ioport+ScsiFifo);
+ bval = 0;
+ outb(bval,ioport+ScsiFifo);
+ }
+ else
+ {
+ cnt = pSRB->ScsiCmdLen;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ for(i=0; i<cnt; i++)
+ {
+ bval = *ptr++;
+ outb(bval,ioport+ScsiFifo);
+ }
+ }
+ }
+ else /* ATN_STOP */
+ {
+ if( (pSRB->CmdBlock[0] == INQUIRY) ||
+ (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ bval &= 0xBF;
+ outb(bval,ioport+ScsiFifo);
+ bval1 = SELECT_W_ATN;
+ pSRB->SRBState = SRB_START_;
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ {
+ if( !(pDCB->IdentifyMsg & 7) ||
+ (pSRB->CmdBlock[0] != INQUIRY) )
+ {
+ bval1 = SEL_W_ATN_STOP;
+ pSRB->SRBState = SRB_MSGOUT;
+ }
+ }
+ }
+ else
+ {
+ if(pDCB->SyncMode & EN_TAG_QUEUING)
+ {
+ outb(bval,ioport+ScsiFifo);
+ pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
+ wlval = 1;
+ bval = 0;
+ while( wlval & pDCB->TagMask )
+ {
+ wlval = wlval << 1;
+ bval++;
+ }
+ pDCB->TagMask |= wlval;
+ pSRB->TagNumber = bval;
+ pSRB->MsgOutBuf[1] = bval;
+ pSRB->MsgCnt = 2;
+ bval1 = SEL_W_ATN_STOP;
+ pSRB->SRBState = SRB_START_;
+ }
+ else
+ {
+ outb(bval,ioport+ScsiFifo);
+ pSRB->MsgOutBuf[0] = MSG_NOP;
+ pSRB->MsgCnt = 1;
+ pSRB->SRBState = SRB_START_;
+ bval1 = SEL_W_ATN_STOP;
+ }
+ }
+ }
+ bval = inb( ioport+Scsi_Status );
+ if( bval & INTERRUPT )
+ {
+ pSRB->SRBState = SRB_READY;
+ pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
+ rc = 1;
+ }
+ else
+ {
+ pSRB->ScsiPhase = SCSI_NOP1;
+ pACB->pActiveDCB = pDCB;
+ pDCB->pActiveSRB = pSRB;
+ rc = 0;
+ outb(bval1,ioport+ScsiCmd);
+ }
+ return( rc );
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+static void
+DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void
+DC390_Interrupt( int irq, struct pt_regs *regs)
+#endif
+{
+ PACB pACB;
+ PDCB pDCB;
+ PSRB pSRB;
+ USHORT ioport = 0;
+ USHORT phase, i;
+ void (*stateV)( PACB, PSRB, PUCHAR );
+ UCHAR istate = 0;
+ UCHAR sstatus=0, istatus;
+
+ pACB = pACB_start;
+ if( pACB == NULL )
+ return;
+ for( i=0; i < adapterCnt; i++ )
+ {
+ if( pACB->IRQLevel == (UCHAR) irq )
+ {
+ ioport = pACB->IOPortBase;
+ sstatus = inb( ioport+Scsi_Status );
+ if( sstatus & INTERRUPT )
+ break;
+ else
+ pACB = pACB->pNextACB;
+ }
+ else
+ {
+ pACB = pACB->pNextACB;
+ }
+ }
+
+#ifdef DC390_DEBUG1
+ printk("sstatus=%2x,",sstatus);
+#endif
+
+ if( pACB == (PACB )-1 )
+ {
+ printk("DC390: Spurious interrupt detected!\n");
+ return;
+ }
+
+ istate = inb( ioport+Intern_State );
+ istatus = inb( ioport+INT_Status );
+
+#ifdef DC390_DEBUG1
+ printk("Istatus=%2x,",istatus);
+#endif
+
+ if(istatus & DISCONNECTED)
+ {
+ DC390_Disconnect( pACB );
+ return;
+ }
+
+ if(istatus & RESELECTED)
+ {
+ DC390_Reselect( pACB );
+ return;
+ }
+
+ if(istatus & INVALID_CMD)
+ {
+ DC390_InvalidCmd( pACB );
+ return;
+ }
+
+ if(istatus & SCSI_RESET)
+ {
+ DC390_ScsiRstDetect( pACB );
+ return;
+ }
+
+ if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) )
+ {
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ if( pDCB )
+ {
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ EnableMsgOut( pACB, pSRB );
+ }
+
+ phase = (USHORT) pSRB->ScsiPhase;
+ stateV = (void *) DC390_phase0[phase];
+ stateV( pACB, pSRB, &sstatus );
+
+ pSRB->ScsiPhase = sstatus & 7;
+ phase = (USHORT) sstatus & 7;
+ stateV = (void *) DC390_phase1[phase];
+ stateV( pACB, pSRB, &sstatus );
+ }
+}
+
+
+static void
+DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR sstatus, bval;
+ USHORT ioport;
+ PSGL psgl;
+ ULONG ResidCnt, xferCnt;
+
+ ioport = pACB->IOPortBase;
+ sstatus = *psstatus;
+
+ if( !(pSRB->SRBState & SRB_XFERPAD) )
+ {
+ if( sstatus & PARITY_ERR )
+ pSRB->SRBStatus |= PARITY_ERROR;
+
+ if( sstatus & COUNT_2_ZERO )
+ {
+ bval = inb(ioport+DMA_Status);
+ while( !(bval & DMA_XFER_DONE) )
+ bval = inb(ioport+DMA_Status);
+ pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+
+#ifndef VERSION_ELF_1_2_13
+ pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+ pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+ pSRB->SGToBeXferLen = (ULONG) psgl->length;
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ else
+ {
+ bval = inb( ioport+Current_Fifo );
+ bval &= 0x1f;
+ ResidCnt = (ULONG) inb(ioport+CtcReg_High);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+ ResidCnt += (ULONG) bval;
+
+ xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+ pSRB->SGPhysAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = ResidCnt;
+ }
+ }
+ bval = WRITE_DIRECTION+DMA_IDLE_CMD;
+ outb( bval, ioport+DMA_Cmd);
+}
+
+static void
+DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR sstatus, bval;
+ USHORT i, ioport, residual;
+ PSGL psgl;
+ ULONG ResidCnt, xferCnt;
+ PUCHAR ptr;
+
+
+ ioport = pACB->IOPortBase;
+ sstatus = *psstatus;
+
+ if( !(pSRB->SRBState & SRB_XFERPAD) )
+ {
+ if( sstatus & PARITY_ERR )
+ pSRB->SRBStatus |= PARITY_ERROR;
+
+ if( sstatus & COUNT_2_ZERO )
+ {
+ bval = inb(ioport+DMA_Status);
+ while( !(bval & DMA_XFER_DONE) )
+ bval = inb(ioport+DMA_Status);
+
+ bval = READ_DIRECTION+DMA_IDLE_CMD;
+ outb( bval, ioport+DMA_Cmd);
+
+ pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+
+#ifndef VERSION_ELF_1_2_13
+ pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+ pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+ pSRB->SGToBeXferLen = (ULONG) psgl->length;
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ else /* phase changed */
+ {
+ residual = 0;
+ bval = inb(ioport+Current_Fifo);
+ while( bval & 0x1f )
+ {
+ if( (bval & 0x1f) == 1 )
+ {
+ for(i=0; i< 0x100; i++)
+ {
+ bval = inb(ioport+Current_Fifo);
+ if( !(bval & 0x1f) )
+ goto din_1;
+ else if( i == 0x0ff )
+ {
+ residual = 1; /* ;1 residual byte */
+ goto din_1;
+ }
+ }
+ }
+ else
+ bval = inb(ioport+Current_Fifo);
+ }
+din_1:
+ bval = READ_DIRECTION+DMA_BLAST_CMD;
+ outb(bval, ioport+DMA_Cmd);
+ for(i=0; i<0x8000; i++)
+ {
+ bval = inb(ioport+DMA_Status);
+ if(bval & BLAST_COMPLETE)
+ break;
+ }
+ bval = READ_DIRECTION+DMA_IDLE_CMD;
+ outb(bval, ioport+DMA_Cmd);
+
+ ResidCnt = (ULONG) inb(ioport+CtcReg_High);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+
+ xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+ pSRB->SGPhysAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = ResidCnt;
+
+ if( residual )
+ {
+ bval = inb(ioport+ScsiFifo); /* get residual byte */
+#ifndef VERSION_ELF_1_2_13
+ ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr );
+#else
+ ptr = (PUCHAR) pSRB->SGPhysAddr;
+#endif
+ *ptr = bval;
+ pSRB->SGPhysAddr++;
+ pSRB->TotalXferredLen++;
+ pSRB->SGToBeXferLen--;
+ }
+ }
+ }
+}
+
+static void
+DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+ScsiFifo);
+ pSRB->TargetStatus = bval;
+ bval++;
+ bval = inb(ioport+ScsiFifo); /* get message */
+ pSRB->EndMessage = bval;
+
+ *psstatus = SCSI_NOP0;
+ pSRB->SRBState = SRB_COMPLETED;
+ bval = MSG_ACCEPTED_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
+ *psstatus = SCSI_NOP0;
+}
+
+static void
+DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport, wval, wval1;
+ PDCB pDCB;
+ PSRB psrb;
+
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+
+ bval = inb( ioport+ScsiFifo );
+ if( !(pSRB->SRBState & SRB_MSGIN_MULTI) )
+ {
+ if(bval == MSG_DISCONNECT)
+ {
+ pSRB->SRBState = SRB_DISCONNECT;
+ }
+ else if( bval == MSG_SAVE_PTR )
+ goto min6;
+ else if( (bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_QTAG) &&
+ (bval <= MSG_ORDER_QTAG)) )
+ {
+ pSRB->SRBState |= SRB_MSGIN_MULTI;
+ pSRB->MsgInBuf[0] = bval;
+ pSRB->MsgCnt = 1;
+ pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
+ }
+ else if(bval == MSG_REJECT_)
+ {
+ bval = RESET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+ if( pSRB->SRBState & DO_SYNC_NEGO)
+ goto set_async;
+ }
+ else if( bval == MSG_RESTORE_PTR)
+ goto min6;
+ else
+ goto min6;
+ }
+ else
+ { /* minx: */
+
+ *pSRB->pMsgPtr = bval;
+ pSRB->MsgCnt++;
+ pSRB->pMsgPtr++;
+ if( (pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) &&
+ (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG) )
+ {
+ if( pSRB->MsgCnt == 2)
+ {
+ pSRB->SRBState = 0;
+ bval = pSRB->MsgInBuf[1];
+ pSRB = pDCB->pGoingSRB;
+ psrb = pDCB->pGoingLast;
+ if( pSRB )
+ {
+ for( ;; )
+ {
+ if(pSRB->TagNumber != bval)
+ {
+ if( pSRB == psrb )
+ goto mingx0;
+ pSRB = pSRB->pNextSRB;
+ }
+ else
+ break;
+ }
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ {
+ pSRB->SRBState = SRB_ABORT_SENT;
+ EnableMsgOut( pACB, pSRB );
+ }
+ if( !(pSRB->SRBState & SRB_DISCONNECT) )
+ goto mingx0;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->SRBState = SRB_DATA_XFER;
+ }
+ else
+ {
+mingx0:
+ pSRB = pACB->pTmpSRB;
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
+ EnableMsgOut2( pACB, pSRB );
+ }
+ }
+ }
+ else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) )
+ {
+ pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO);
+ if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) )
+ { /* reject_msg: */
+ pSRB->MsgCnt = 1;
+ pSRB->MsgInBuf[0] = MSG_REJECT_;
+ bval = SET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+ }
+ else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) )
+ {
+set_async:
+ pDCB = pSRB->pSRBDCB;
+ pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->CtrlR3 = FAST_CLK; /* ;non_fast */
+ pDCB->CtrlR4 &= 0x3f;
+ pDCB->CtrlR4 |= EATER_25NS; /* ; 25ns glitch eater */
+ goto re_prog;
+ }
+ else
+ { /* set_sync: */
+
+ pDCB = pSRB->pSRBDCB;
+ pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
+ pDCB->SyncOffset &= 0x0f0;
+ pDCB->SyncOffset |= pSRB->MsgInBuf[4];
+ pDCB->NegoPeriod = pSRB->MsgInBuf[3];
+ wval = (USHORT) pSRB->MsgInBuf[3];
+ wval = wval << 2;
+ wval--;
+ wval1 = wval / 25;
+ if( (wval1 * 25) != wval)
+ wval1++;
+ bval = FAST_CLK+FAST_SCSI;
+ pDCB->CtrlR4 &= 0x3f;
+ if(wval1 >= 8)
+ {
+ wval1--;
+ bval = FAST_CLK; /* ;fast clock/normal scsi */
+ pDCB->CtrlR4 |= EATER_25NS; /* ;25 ns glitch eater */
+ }
+ pDCB->CtrlR3 = bval;
+ pDCB->SyncPeriod = (UCHAR)wval1;
+re_prog:
+ bval = pDCB->SyncPeriod;
+ outb(bval, ioport+Sync_Period);
+ bval = pDCB->SyncOffset;
+ outb(bval, ioport+Sync_Offset);
+ bval = pDCB->CtrlR3;
+ outb(bval, ioport+CtrlReg3);
+ bval = pDCB->CtrlR4;
+ outb(bval, ioport+CtrlReg4);
+ SetXferRate( pACB, pDCB);
+ }
+ }
+ }
+min6:
+ *psstatus = SCSI_NOP0;
+ bval = MSG_ACCEPTED_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir)
+{
+ PSGL psgl;
+ UCHAR bval;
+ USHORT ioport;
+ ULONG lval;
+
+
+ ioport = pACB->IOPortBase;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */
+ outb( bval, ioport+DMA_Cmd);
+ if( !pSRB->SGToBeXferLen )
+ {
+ psgl = pSRB->pSegmentList;
+#ifndef VERSION_ELF_1_2_13
+ pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+ pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+ pSRB->SGToBeXferLen = (ULONG) psgl->length;
+ }
+ lval = pSRB->SGToBeXferLen;
+ bval = (UCHAR) lval;
+ outb(bval,ioport+CtcReg_Low);
+ lval = lval >> 8;
+ bval = (UCHAR) lval;
+ outb(bval,ioport+CtcReg_Mid);
+ lval = lval >> 8;
+ bval = (UCHAR) lval;
+ outb(bval,ioport+CtcReg_High);
+
+ lval = pSRB->SGToBeXferLen;
+ outl(lval, ioport+DMA_XferCnt);
+
+ lval = pSRB->SGPhysAddr;
+ outl( lval, ioport+DMA_XferAddr);
+
+ bval = DMA_COMMAND+INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+
+ pSRB->SRBState = SRB_DATA_XFER;
+
+ bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */
+ outb(bval, ioport+DMA_Cmd);
+
+ bval = DMA_START_CMD | ioDir; /* ;+EN_DMA_INT */
+ outb(bval, ioport+DMA_Cmd);
+ }
+ else /* xfer pad */
+ {
+ if( pSRB->SGcount )
+ {
+ pSRB->AdaptStatus = H_OVER_UNDER_RUN;
+ pSRB->SRBStatus |= OVER_RUN;
+ }
+ bval = 0;
+ outb(bval,ioport+CtcReg_Low);
+ outb(bval,ioport+CtcReg_Mid);
+ outb(bval,ioport+CtcReg_High);
+
+ pSRB->SRBState |= SRB_XFERPAD;
+ bval = DMA_COMMAND+XFER_PAD_BYTE;
+ outb(bval, ioport+ScsiCmd);
+/*
+ bval = DMA_IDLE_CMD | ioDir; ;+EN_DMA_INT
+ outb(bval, ioport+DMA_Cmd);
+ bval = DMA_START_CMD | ioDir; ;+EN_DMA_INT
+ outb(bval, ioport+DMA_Cmd);
+*/
+ }
+}
+
+
+static void
+DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR ioDir;
+
+ ioDir = WRITE_DIRECTION;
+ DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR ioDir;
+
+ ioDir = READ_DIRECTION;
+ DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ PDCB pDCB;
+ UCHAR bval;
+ PUCHAR ptr;
+ USHORT ioport, i, cnt;
+
+
+ ioport = pACB->IOPortBase;
+ bval = RESET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ cnt = (USHORT) pSRB->ScsiCmdLen;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ for(i=0; i < cnt; i++)
+ {
+ outb(*ptr, ioport+ScsiFifo);
+ ptr++;
+ }
+ }
+ else
+ {
+ bval = REQUEST_SENSE;
+ outb(bval, ioport+ScsiFifo);
+ pDCB = pACB->pActiveDCB;
+ bval = pDCB->IdentifyMsg << 5;
+ outb(bval, ioport+ScsiFifo);
+ bval = 0;
+ outb(bval, ioport+ScsiFifo);
+ outb(bval, ioport+ScsiFifo);
+ bval = sizeof(pSRB->pcmd->sense_buffer);
+ outb(bval, ioport+ScsiFifo);
+ bval = 0;
+ outb(bval, ioport+ScsiFifo);
+ }
+ pSRB->SRBState = SRB_COMMAND;
+ bval = INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ pSRB->SRBState = SRB_STATUS;
+ bval = INITIATOR_CMD_CMPLTE;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport, i, cnt;
+ PUCHAR ptr;
+ PDCB pDCB;
+
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ pDCB = pACB->pActiveDCB;
+ if( !(pSRB->SRBState & SRB_MSGOUT) )
+ {
+ cnt = pSRB->MsgCnt;
+ if( cnt )
+ {
+ ptr = (PUCHAR) pSRB->MsgOutBuf;
+ for(i=0; i < cnt; i++)
+ {
+ outb(*ptr, ioport+ScsiFifo);
+ ptr++;
+ }
+ pSRB->MsgCnt = 0;
+ if( (pDCB->DCBFlag & ABORT_DEV_) &&
+ (pSRB->MsgOutBuf[0] == MSG_ABORT) )
+ pSRB->SRBState = SRB_ABORT_SENT;
+ }
+ else
+ {
+ bval = MSG_ABORT; /* ??? MSG_NOP */
+ if( (pSRB->CmdBlock[0] == INQUIRY ) ||
+ (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ goto mop1;
+ }
+ outb(bval, ioport+ScsiFifo);
+ }
+ bval = INFO_XFER_CMD;
+ outb( bval, ioport+ScsiCmd);
+ }
+ else
+ {
+mop1:
+ bval = MSG_EXTENDED;
+ outb(bval, ioport+ScsiFifo);
+ bval = 3; /* ;length of extended msg */
+ outb(bval, ioport+ScsiFifo);
+ bval = 1; /* ; sync nego */
+ outb(bval, ioport+ScsiFifo);
+ bval = pDCB->NegoPeriod;
+ outb(bval, ioport+ScsiFifo);
+ bval = SYNC_NEGO_OFFSET;
+ outb(bval, ioport+ScsiFifo);
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ bval = INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+ }
+}
+
+static void
+DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ if( !(pSRB->SRBState & SRB_MSGIN) )
+ {
+ pSRB->SRBState &= SRB_DISCONNECT;
+ pSRB->SRBState |= SRB_MSGIN;
+ }
+ bval = INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+
+static void
+SetXferRate( PACB pACB, PDCB pDCB )
+{
+ UCHAR bval;
+ USHORT cnt, i;
+ PDCB ptr;
+
+ if( !(pDCB->IdentifyMsg & 0x07) )
+ {
+ if( pACB->scan_devices )
+ {
+ CurrSyncOffset = pDCB->SyncOffset;
+ }
+ else
+ {
+ ptr = pACB->pLinkDCB;
+ cnt = pACB->DeviceCnt;
+ bval = pDCB->UnitSCSIID;
+ for(i=0; i<cnt; i++)
+ {
+ if( ptr->UnitSCSIID == bval )
+ {
+ ptr->SyncPeriod = pDCB->SyncPeriod;
+ ptr->SyncOffset = pDCB->SyncOffset;
+ ptr->CtrlR3 = pDCB->CtrlR3;
+ ptr->CtrlR4 = pDCB->CtrlR4;
+ ptr->SyncMode = pDCB->SyncMode;
+ }
+ ptr = ptr->pNextDCB;
+ }
+ }
+ }
+ return;
+}
+
+
+static void
+DC390_Disconnect( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB, psrb;
+ ULONG flags;
+ USHORT ioport, i, cnt;
+ UCHAR bval;
+
+#ifdef DC390_DEBUG0
+ printk("DISC,");
+#endif
+
+ save_flags(flags);
+ cli();
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ if (!pDCB)
+ {
+#ifdef DC390_DEBUG0
+ printk("ACB:%08lx->ActiveDCB:%08lx !,",(ULONG)pACB,(ULONG)pDCB);
+#endif
+ restore_flags(flags); return;
+ }
+ pSRB = pDCB->pActiveSRB;
+ pACB->pActiveDCB = 0;
+ pSRB->ScsiPhase = SCSI_NOP0;
+ bval = EN_SEL_RESEL;
+ outb(bval, ioport+ScsiCmd);
+ if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+ {
+ pSRB->SRBState = 0;
+ DoWaitingSRB( pACB );
+ }
+ else if( pSRB->SRBState & SRB_ABORT_SENT )
+ {
+ pDCB->TagMask = 0;
+ pDCB->DCBFlag = 0;
+ cnt = pDCB->GoingSRBCnt;
+ pDCB->GoingSRBCnt = 0;
+ pSRB = pDCB->pGoingSRB;
+ for( i=0; i < cnt; i++)
+ {
+ psrb = pSRB->pNextSRB;
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ pSRB = psrb;
+ }
+ pDCB->pGoingSRB = 0;
+ DoWaitingSRB( pACB );
+ }
+ else
+ {
+ if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
+ !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
+ { /* Selection time out */
+ if( !(pACB->scan_devices) )
+ {
+ pSRB->SRBState = SRB_READY;
+ RewaitSRB( pDCB, pSRB);
+ }
+ else
+ {
+ pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
+ goto disc1;
+ }
+ }
+ else if( pSRB->SRBState & SRB_DISCONNECT )
+ {
+ DoWaitingSRB( pACB );
+ }
+ else if( pSRB->SRBState & SRB_COMPLETED )
+ {
+disc1:
+ if(pDCB->MaxCommand > 1)
+ {
+ bval = pSRB->TagNumber;
+ pDCB->TagMask &= (~(1 << bval)); /* free tag mask */
+ }
+ pDCB->pActiveSRB = 0;
+ pSRB->SRBState = SRB_FREE;
+ SRBdone( pACB, pDCB, pSRB);
+ }
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+DC390_Reselect( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB pSRB;
+ USHORT ioport, wval;
+ UCHAR bval, bval1;
+
+
+#ifdef DC390_DEBUG0
+ printk("RSEL,");
+#endif
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ if( pDCB )
+ { /* Arbitration lost but Reselection win */
+ pSRB = pDCB->pActiveSRB;
+ if( !( pACB->scan_devices ) )
+ {
+ pSRB->SRBState = SRB_READY;
+ RewaitSRB( pDCB, pSRB);
+ }
+ }
+ bval = inb(ioport+ScsiFifo); /* get ID */
+ bval = bval ^ pACB->HostID_Bit;
+ wval = 0;
+ bval1 = 1;
+ for(;;)
+ {
+ if( !(bval & bval1) )
+ {
+ bval1 = bval1 << 1;
+ wval++;
+ }
+ else
+ break;
+ }
+ wval |= ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8; /* get LUN */
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
+ {
+ pDCB = pDCB->pNextDCB;
+ if( pDCB == pdcb )
+ return;
+ }
+ pACB->pActiveDCB = pDCB;
+ if( pDCB->SyncMode & EN_TAG_QUEUING )
+ {
+ pSRB = pACB->pTmpSRB;
+ pDCB->pActiveSRB = pSRB;
+ }
+ else
+ {
+ pSRB = pDCB->pActiveSRB;
+ if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
+ {
+ pSRB= pACB->pTmpSRB;
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ EnableMsgOut( pACB, pSRB );
+ }
+ else
+ {
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ {
+ pSRB->SRBState = SRB_ABORT_SENT;
+ EnableMsgOut( pACB, pSRB );
+ }
+ else
+ pSRB->SRBState = SRB_DATA_XFER;
+ }
+ }
+ pSRB->ScsiPhase = SCSI_NOP0;
+ bval = pDCB->UnitSCSIID;
+ outb( bval, ioport+Scsi_Dest_ID);
+ bval = pDCB->SyncPeriod;
+ outb(bval, ioport+Sync_Period);
+ bval = pDCB->SyncOffset;
+ outb( bval, ioport+Sync_Offset);
+ bval = pDCB->CtrlR1;
+ outb(bval, ioport+CtrlReg1);
+ bval = pDCB->CtrlR3;
+ outb(bval, ioport+CtrlReg3);
+ bval = pDCB->CtrlR4; /* ; Glitch eater */
+ outb(bval, ioport+CtrlReg4);
+ bval = MSG_ACCEPTED_CMD; /* ;to rls the /ACK signal */
+ outb(bval, ioport+ScsiCmd);
+}
+
+
+static void
+SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb;
+ UCHAR bval, bval1, i, j, status;
+ PSCSICMD pcmd;
+ PSCSI_INQDATA ptr;
+ USHORT disable_tag;
+ ULONG flags;
+ PSGL ptr2;
+ ULONG swlval;
+
+ pcmd = pSRB->pcmd;
+ status = pSRB->TargetStatus;
+ if(pSRB->SRBFlag & AUTO_REQSENSE)
+ {
+ pSRB->SRBFlag &= ~AUTO_REQSENSE;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+ if(status == SCSI_STAT_CHECKCOND)
+ {
+ pcmd->result = DID_BAD_TARGET << 16;
+ goto ckc_e;
+ }
+ if(pSRB->RetryCnt == 0)
+ {
+ *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+ pSRB->TotalXferredLen = pSRB->Segment1[1];
+ if( (pSRB->TotalXferredLen) &&
+ (pSRB->TotalXferredLen >= pcmd->underflow) )
+ pcmd->result |= (DID_OK << 16);
+ else
+ pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+ SCSI_STAT_CHECKCOND;
+#ifdef DC390_DEBUG0
+ printk("Cmd=%2x,Result=%8x,XferL=%8x,",pSRB->CmdBlock[0],
+ (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);
+#endif
+ goto ckc_e;
+ }
+ else
+ {
+ pSRB->RetryCnt--;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+ *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
+ if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+ {
+ pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+ SCSI_STAT_CHECKCOND;
+ goto ckc_e;
+ }
+ pcmd->result |= (DRIVER_SENSE << 24);
+ pSRB->SGcount = (UCHAR) pSRB->Segment1[0];
+ pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8);
+ pSRB->SGIndex = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ if( pcmd->use_sg )
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ else if( pcmd->request_buffer )
+ {
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+ RewaitSRB( pDCB, pSRB );
+ return;
+ }
+ }
+ if( status )
+ {
+ if( status == SCSI_STAT_CHECKCOND)
+ {
+ if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
+ {
+ bval = pSRB->SGcount;
+ swlval = 0;
+ ptr2 = pSRB->pSegmentList;
+ for( i=pSRB->SGIndex; i < bval; i++)
+ {
+ swlval += ptr2->length;
+ ptr2++;
+ }
+#ifdef DC390_DEBUG0
+ printk("XferredLen=%8x,NotXferLen=%8x,",
+ (UINT) pSRB->TotalXferredLen, (UINT) swlval);
+#endif
+ }
+ RequestSense( pACB, pDCB, pSRB );
+ return;
+ }
+ else if( status == SCSI_STAT_QUEUEFULL )
+ {
+ bval = (UCHAR) pDCB->GoingSRBCnt;
+ bval--;
+ pDCB->MaxCommand = bval;
+ RewaitSRB( pDCB, pSRB );
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ return;
+ }
+ else if(status == SCSI_STAT_SEL_TIMEOUT)
+ {
+ pSRB->AdaptStatus = H_SEL_TIMEOUT;
+ pSRB->TargetStatus = 0;
+ pcmd->result = DID_BAD_TARGET << 16;
+ }
+ else
+ {
+ pSRB->AdaptStatus = 0;
+ if( pSRB->RetryCnt )
+ {
+ pSRB->RetryCnt--;
+ pSRB->TargetStatus = 0;
+ pSRB->SGIndex = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ if( pcmd->use_sg )
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ else if( pcmd->request_buffer )
+ {
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+ RewaitSRB( pDCB, pSRB );
+ return;
+ }
+ else
+ {
+ pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) |
+ (ULONG) status;
+ }
+ }
+ }
+ else
+ {
+ status = pSRB->AdaptStatus;
+ if(status & H_OVER_UNDER_RUN)
+ {
+ pSRB->TargetStatus = 0;
+ pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8);
+ }
+ else if( pSRB->SRBStatus & PARITY_ERROR)
+ {
+ pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 8);
+ }
+ else /* No error */
+ {
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pcmd->result |= (DID_OK << 16);
+ }
+ }
+
+ckc_e:
+ if( pACB->scan_devices )
+ {
+ if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+ {
+ if(pcmd->result != (DID_OK << 16))
+ {
+ if( pcmd->result & SCSI_STAT_CHECKCOND )
+ {
+ goto RTN_OK;
+ }
+ else
+ {
+ pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ if( (pcmd->target == pACB->max_id) &&
+ ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) )
+ {
+ pACB->scan_devices = 0;
+ }
+ }
+ }
+ else
+ {
+RTN_OK:
+ pPrevDCB->pNextDCB = pDCB;
+ pDCB->pNextDCB = pACB->pLinkDCB;
+ if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) )
+ pACB->scan_devices = END_SCAN;
+ }
+ }
+ else if( pSRB->CmdBlock[0] == INQUIRY )
+ {
+ if( (pcmd->target == pACB->max_id) &&
+ (pcmd->lun == pACB->max_lun) )
+ {
+ pACB->scan_devices = 0;
+ }
+ ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
+ if( pcmd->use_sg )
+ ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
+ bval1 = ptr->DevType & SCSI_DEVTYPE;
+ if(bval1 == SCSI_NODEV)
+ {
+ pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+ else
+ {
+ pACB->DeviceCnt++;
+ pPrevDCB = pDCB;
+ pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB ));
+ pDCB->DevType = bval1;
+ if(bval1 == TYPE_DISK || bval1 == TYPE_MOD)
+ {
+ if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) &&
+ (ptr->Flags & SCSI_INQ_CMDQUEUE) &&
+ (pDCB->DevMode & TAG_QUEUING_) &&
+ (pDCB->DevMode & EN_DISCONNECT_) )
+ {
+ disable_tag = 0;
+ for(i=0; i<BADDEVCNT; i++)
+ {
+ for(j=0; j<28; j++)
+ {
+ if( ((PUCHAR)ptr)[8+j] != baddevname1[i][j])
+ break;
+ }
+ if(j == 28)
+ {
+ disable_tag = 1;
+ break;
+ }
+ }
+
+ if( !disable_tag )
+ {
+ pDCB->MaxCommand = pACB->TagMaxNum;
+ pDCB->SyncMode |= EN_TAG_QUEUING;
+ pDCB->TagMask = 0;
+ }
+ else
+ {
+ pDCB->SyncMode |= EN_ATN_STOP;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ save_flags( flags );
+ cli();
+/* ReleaseSRB( pDCB, pSRB ); */
+
+ if(pSRB == pDCB->pGoingSRB )
+ {
+ pDCB->pGoingSRB = pSRB->pNextSRB;
+ }
+ else
+ {
+ psrb = pDCB->pGoingSRB;
+ while( psrb->pNextSRB != pSRB )
+ psrb = psrb->pNextSRB;
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pGoingLast )
+ pDCB->pGoingLast = psrb;
+ }
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ pDCB->GoingSRBCnt--;
+
+ DoWaitingSRB( pACB );
+ restore_flags(flags);
+
+/* Notify cmd done */
+ pcmd->scsi_done( pcmd );
+
+ if( pDCB->QIORBCnt )
+ DoNextCmd( pACB, pDCB );
+ return;
+}
+
+
+static void
+DoingSRB_Done( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB psrb, psrb2;
+ USHORT cnt, i;
+ PSCSICMD pcmd;
+
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ do
+ {
+ cnt = pdcb->GoingSRBCnt;
+ psrb = pdcb->pGoingSRB;
+ for( i=0; i<cnt; i++)
+ {
+ psrb2 = psrb->pNextSRB;
+ pcmd = psrb->pcmd;
+ pcmd->result = DID_RESET << 16;
+
+/* ReleaseSRB( pDCB, pSRB ); */
+
+ psrb->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = psrb;
+
+ pcmd->scsi_done( pcmd );
+ psrb = psrb2;
+ }
+ pdcb->GoingSRBCnt = 0;;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+static void
+DC390_ResetSCSIBus( PACB pACB )
+{
+ USHORT ioport;
+ UCHAR bval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ pACB->ACBFlag |= RESET_DEV;
+ ioport = pACB->IOPortBase;
+
+ bval = DMA_IDLE_CMD;
+ outb(bval,ioport+DMA_Cmd);
+
+ bval = RST_SCSI_BUS_CMD;
+ outb(bval,ioport+ScsiCmd);
+
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+DC390_ScsiRstDetect( PACB pACB )
+{
+ ULONG wlval, flags;
+ USHORT ioport;
+ UCHAR bval;
+
+#ifdef DC390_DEBUG0
+ printk("RST_DETEC");
+#endif
+ save_flags(flags);
+ sti();
+ wlval = jiffies + HZ;
+ while( jiffies < wlval ); /* delay 1 sec */
+
+ cli();
+ ioport = pACB->IOPortBase;
+ bval = DMA_IDLE_CMD;
+ outb(bval,ioport+DMA_Cmd);
+ bval = CLEAR_FIFO_CMD;
+ outb(bval,ioport+ScsiCmd);
+
+ if( pACB->ACBFlag & RESET_DEV )
+ pACB->ACBFlag |= RESET_DONE;
+ else
+ {
+ pACB->ACBFlag |= RESET_DETECT;
+
+ ResetDevParam( pACB );
+/* DoingSRB_Done( pACB ); ???? */
+ RecoverSRB( pACB );
+ pACB->pActiveDCB = NULL;
+ pACB->ACBFlag = 0;
+ DoWaitingSRB( pACB );
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ PSCSICMD pcmd;
+
+ pSRB->SRBFlag |= AUTO_REQSENSE;
+ pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0]));
+ pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4]));
+ pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
+ pSRB->Segment1[1] = pSRB->TotalXferredLen;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+
+ pcmd = pSRB->pcmd;
+
+ pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer);
+ pSRB->Segmentx.length = sizeof(pcmd->sense_buffer);
+ pSRB->pSegmentList = &pSRB->Segmentx;
+ pSRB->SGcount = 1;
+ pSRB->SGIndex = 0;
+
+ *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
+ pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
+ *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer);
+ pSRB->ScsiCmdLen = 6;
+
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+ RewaitSRB( pDCB, pSRB );
+}
+
+
+static void
+EnableMsgOut2( PACB pACB, PSRB pSRB )
+{
+ USHORT ioport;
+ UCHAR bval;
+
+ ioport = pACB->IOPortBase;
+ pSRB->MsgCnt = 1;
+ bval = SET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+
+static void
+EnableMsgOut( PACB pACB, PSRB pSRB )
+{
+ pSRB->MsgOutBuf[0] = MSG_ABORT;
+ EnableMsgOut2( pACB, pSRB );
+}
+
+
+static void
+DC390_InvalidCmd( PACB pACB )
+{
+ UCHAR bval;
+ USHORT ioport;
+ PSRB pSRB;
+
+ pSRB = pACB->pActiveDCB->pActiveSRB;
+ if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) )
+ {
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval,(ioport+ScsiCmd));
+ }
+}
+
* Time out in seconds for disks and Magneto-opticals (which are slower).
*/
-#define SD_TIMEOUT (15 * HZ)
-#define SD_MOD_TIMEOUT (15 * HZ)
+#define SD_TIMEOUT (20 * HZ)
+#define SD_MOD_TIMEOUT (25 * HZ)
#define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
SC->device->type != TYPE_MOD)
--- /dev/null
+/***********************************************************************
+ * FILE NAME : TMSCSIM.C *
+ * BY : C.L. Huang, ching@tekram.com.tw *
+ * Description: Device Driver for Tekram DC-390(T) PCI SCSI *
+ * Bus Master Host Adapter *
+ * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. *
+ ***********************************************************************/
+/* Minor enhancements and bugfixes by *
+ * Kurt Garloff <K.Garloff@ping.de> *
+ ***********************************************************************/
+/* HISTORY: *
+ * *
+ * REV# DATE NAME DESCRIPTION *
+ * 1.00 04/24/96 CLH First release *
+ * 1.01 06/12/96 CLH Fixed bug of Media Change for Removable *
+ * Device, scan all LUN. Support Pre2.0.10 *
+ * 1.02 06/18/96 CLH Fixed bug of Command timeout ... *
+ * 1.03 09/25/96 KG Added tmscsim_proc_info() *
+ * 1.04 10/11/96 CLH Updating for support KV 2.0.x *
+ * 1.05 10/18/96 KG Fixed bug in DC390_abort(null ptr deref)*
+ * 1.06 10/25/96 KG Fixed module support *
+ * 1.07 11/09/96 KG Fixed tmscsim_proc_info() *
+ * 1.08 11/18/96 KG Fixed null ptr in DC390_Disconnect() *
+ * 1.09 11/30/96 KG Added register the allocated IO space *
+ * 1.10 12/05/96 CLH Modified tmscsim_proc_info(), and reset *
+ * pending interrupt in DC390_detect() *
+ ***********************************************************************/
+
+
+#define DC390_DEBUG
+
+#define SCSI_MALLOC
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/config.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */
+#include "../block/blk.h"
+#else
+#include <linux/blk.h>
+#endif
+
+#include "scsi.h"
+#include "hosts.h"
+#include "tmscsim.h"
+#include "constants.h"
+#include "sd.h"
+#include <linux/stat.h>
+
+#include "dc390.h"
+
+#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI
+
+
+#ifndef VERSION_ELF_1_2_13
+struct proc_dir_entry proc_scsi_tmscsim ={
+ PROC_SCSI_DC390T, 7 ,"tmscsim",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+ };
+#endif
+
+static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+
+static void SetXferRate( PACB pACB, PDCB pDCB );
+static void DC390_Disconnect( PACB pACB );
+static void DC390_Reselect( PACB pACB );
+static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DoingSRB_Done( PACB pACB );
+static void DC390_ScsiRstDetect( PACB pACB );
+static void DC390_ResetSCSIBus( PACB pACB );
+static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void EnableMsgOut2( PACB pACB, PSRB pSRB );
+static void EnableMsgOut( PACB pACB, PSRB pSRB );
+static void DC390_InvalidCmd( PACB pACB );
+
+int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index );
+void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd );
+
+#ifdef MODULE
+static int DC390_release(struct Scsi_Host *host);
+static int DC390_shutdown (struct Scsi_Host *host);
+#endif
+
+
+static PSHT pSHT_start = NULL;
+static PSH pSH_start = NULL;
+static PSH pSH_current = NULL;
+static PACB pACB_start= NULL;
+static PACB pACB_current = NULL;
+static PDCB pPrevDCB = NULL;
+static USHORT adapterCnt = 0;
+static USHORT InitialTime = 0;
+static USHORT CurrSyncOffset = 0;
+static ULONG mech1addr;
+static UCHAR mech2bus, mech2Agent, mech2CfgSPenR;
+
+static PVOID DC390_phase0[]={
+ DC390_DataOut_0,
+ DC390_DataIn_0,
+ DC390_Command_0,
+ DC390_Status_0,
+ DC390_Nop_0,
+ DC390_Nop_0,
+ DC390_MsgOut_0,
+ DC390_MsgIn_0,
+ DC390_Nop_1
+ };
+
+static PVOID DC390_phase1[]={
+ DC390_DataOutPhase,
+ DC390_DataInPhase,
+ DC390_CommandPhase,
+ DC390_StatusPhase,
+ DC390_Nop_0,
+ DC390_Nop_0,
+ DC390_MsgOutPhase,
+ DC390_MsgInPhase,
+ DC390_Nop_1,
+ };
+
+UCHAR eepromBuf[MAX_ADAPTER_NUM][128];
+
+
+UCHAR clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+
+UCHAR baddevname1[2][28] ={
+ "SEAGATE ST3390N 9546",
+ "HP C3323-300 4269"};
+
+#define BADDEVCNT 2
+
+
+/***********************************************************************
+ *
+ *
+ *
+ **********************************************************************/
+static void
+QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+{
+ ULONG flags;
+ PSCSICMD pcmd;
+
+ save_flags(flags);
+ cli();
+
+ if( !pDCB->QIORBCnt )
+ {
+ pDCB->pQIORBhead = cmd;
+ pDCB->pQIORBtail = cmd;
+ pDCB->QIORBCnt++;
+ cmd->next = NULL;
+ }
+ else
+ {
+ pcmd = pDCB->pQIORBtail;
+ pcmd->next = cmd;
+ pDCB->pQIORBtail = cmd;
+ pDCB->QIORBCnt++;
+ cmd->next = NULL;
+ }
+
+ restore_flags(flags);
+}
+
+
+static PSCSICMD
+Getcmd( PDCB pDCB )
+{
+ ULONG flags;
+ PSCSICMD pcmd;
+
+ save_flags(flags);
+ cli();
+
+ pcmd = pDCB->pQIORBhead;
+ pDCB->pQIORBhead = pcmd->next;
+ pcmd->next = NULL;
+ pDCB->QIORBCnt--;
+
+ restore_flags(flags);
+ return( pcmd );
+}
+
+
+static PSRB
+GetSRB( PACB pACB )
+{
+ ULONG flags;
+ PSRB pSRB;
+
+ save_flags(flags);
+ cli();
+
+ pSRB = pACB->pFreeSRB;
+ if( pSRB )
+ {
+ pACB->pFreeSRB = pSRB->pNextSRB;
+ pSRB->pNextSRB = NULL;
+ }
+ restore_flags(flags);
+ return( pSRB );
+}
+
+
+static void
+RewaitSRB0( PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb1;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+
+ if( (psrb1 = pDCB->pWaitingSRB) )
+ {
+ pSRB->pNextSRB = psrb1;
+ pDCB->pWaitingSRB = pSRB;
+ }
+ else
+ {
+ pSRB->pNextSRB = NULL;
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+ restore_flags(flags);
+}
+
+
+static void
+RewaitSRB( PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb1;
+ ULONG flags;
+ UCHAR bval;
+
+ save_flags(flags);
+ cli();
+ pDCB->GoingSRBCnt--;
+ psrb1 = pDCB->pGoingSRB;
+ if( pSRB == psrb1 )
+ {
+ pDCB->pGoingSRB = psrb1->pNextSRB;
+ }
+ else
+ {
+ while( pSRB != psrb1->pNextSRB )
+ psrb1 = psrb1->pNextSRB;
+ psrb1->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pGoingLast )
+ pDCB->pGoingLast = psrb1;
+ }
+ if( (psrb1 = pDCB->pWaitingSRB) )
+ {
+ pSRB->pNextSRB = psrb1;
+ pDCB->pWaitingSRB = pSRB;
+ }
+ else
+ {
+ pSRB->pNextSRB = NULL;
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+
+ bval = pSRB->TagNumber;
+ pDCB->TagMask &= (~(1 << bval)); /* Free TAG number */
+ restore_flags(flags);
+}
+
+
+static void
+DoWaitingSRB( PACB pACB )
+{
+ ULONG flags;
+ PDCB ptr, ptr1;
+ PSRB pSRB;
+
+ save_flags(flags);
+ cli();
+
+ if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
+ {
+ ptr = pACB->pDCBRunRobin;
+ if( !ptr )
+ {
+ ptr = pACB->pLinkDCB;
+ pACB->pDCBRunRobin = ptr;
+ }
+ ptr1 = ptr;
+ for( ;ptr1; )
+ {
+ pACB->pDCBRunRobin = ptr1->pNextDCB;
+ if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) ||
+ !( pSRB = ptr1->pWaitingSRB ) )
+ {
+ if(pACB->pDCBRunRobin == ptr)
+ break;
+ ptr1 = ptr1->pNextDCB;
+ }
+ else
+ {
+ if( !DC390_StartSCSI(pACB, ptr1, pSRB) )
+ {
+ ptr1->GoingSRBCnt++;
+ if( ptr1->pWaitLast == pSRB )
+ {
+ ptr1->pWaitingSRB = NULL;
+ ptr1->pWaitLast = NULL;
+ }
+ else
+ {
+ ptr1->pWaitingSRB = pSRB->pNextSRB;
+ }
+ pSRB->pNextSRB = NULL;
+
+ if( ptr1->pGoingSRB )
+ ptr1->pGoingLast->pNextSRB = pSRB;
+ else
+ ptr1->pGoingSRB = pSRB;
+ ptr1->pGoingLast = pSRB;
+ }
+ break;
+ }
+ }
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+SRBwaiting( PDCB pDCB, PSRB pSRB)
+{
+ if( pDCB->pWaitingSRB )
+ {
+ pDCB->pWaitLast->pNextSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ pSRB->pNextSRB = NULL;
+ }
+ else
+ {
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+}
+
+
+static void
+SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB )
+{
+ ULONG flags;
+ PDCB pDCB;
+
+ save_flags(flags);
+ cli();
+
+ pDCB = pSRB->pSRBDCB;
+ if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
+ (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) )
+ {
+ SRBwaiting(pDCB, pSRB);
+ goto SND_EXIT;
+ }
+
+ if( pDCB->pWaitingSRB )
+ {
+ SRBwaiting(pDCB, pSRB);
+/* pSRB = GetWaitingSRB(pDCB); */
+ pSRB = pDCB->pWaitingSRB;
+ pDCB->pWaitingSRB = pSRB->pNextSRB;
+ pSRB->pNextSRB = NULL;
+ }
+
+ if( !DC390_StartSCSI(pACB, pDCB, pSRB) )
+ {
+ pDCB->GoingSRBCnt++;
+ if( pDCB->pGoingSRB )
+ {
+ pDCB->pGoingLast->pNextSRB = pSRB;
+ pDCB->pGoingLast = pSRB;
+ }
+ else
+ {
+ pDCB->pGoingSRB = pSRB;
+ pDCB->pGoingLast = pSRB;
+ }
+ }
+ else
+ RewaitSRB0( pDCB, pSRB );
+
+SND_EXIT:
+ restore_flags(flags);
+ return;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ ***********************************************************************/
+
+int
+DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+{
+ USHORT ioport, i;
+ Scsi_Cmnd *pcmd;
+ struct Scsi_Host *psh;
+ PACB pACB;
+ PDCB pDCB;
+ PSRB pSRB;
+ ULONG flags;
+ PUCHAR ptr,ptr1;
+
+ psh = cmd->host;
+ pACB = (PACB ) psh->hostdata;
+ ioport = pACB->IOPortBase;
+
+#ifdef DC390_DEBUG0
+/* if(pACB->scan_devices) */
+ printk("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmnd[0],cmd->target,cmd->lun);
+#endif
+
+ if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) )
+ {
+ pACB->scan_devices = 0;
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+ else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) )
+ {
+ pACB->scan_devices = 0;
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+
+ if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) )
+ {
+/* printk("DC390: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return( 0 );
+ }
+
+ if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+ {
+ if( pACB->DeviceCnt < MAX_DEVICES )
+ {
+ pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
+ pDCB = pACB->pDCB_free;
+#ifdef DC390_DEBUG0
+ printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+ DC390_initDCB( pACB, pDCB, cmd );
+ }
+ else /* ???? */
+ {
+/* printk("DC390: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return(0);
+ }
+ }
+ else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+ {
+/* printk("DC390: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return(0);
+ }
+ else
+ {
+ pDCB = pACB->pLinkDCB;
+ while( (pDCB->UnitSCSIID != cmd->target) ||
+ (pDCB->UnitSCSILUN != cmd->lun) )
+ {
+ pDCB = pDCB->pNextDCB;
+ }
+#ifdef DC390_DEBUG0
+ printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+ }
+
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+ save_flags(flags);
+ cli();
+
+ if( pDCB->QIORBCnt )
+ {
+ QLinkcmd( cmd, pDCB );
+ pcmd = Getcmd( pDCB );
+ }
+ else
+ pcmd = cmd;
+
+ pSRB = GetSRB( pACB );
+
+ if( !pSRB )
+ {
+ QLinkcmd( pcmd, pDCB );
+ restore_flags(flags);
+ return(0);
+ }
+
+/* BuildSRB(pSRB); */
+
+ pSRB->pSRBDCB = pDCB;
+ pSRB->pcmd = pcmd;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ ptr1 = (PUCHAR) pcmd->cmnd;
+ pSRB->ScsiCmdLen = pcmd->cmd_len;
+ for(i=0; i< pcmd->cmd_len; i++)
+ {
+ *ptr = *ptr1;
+ ptr++;
+ ptr1++;
+ }
+ if( pcmd->use_sg )
+ {
+ pSRB->SGcount = (UCHAR) pcmd->use_sg;
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ }
+ else if( pcmd->request_buffer )
+ {
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ else
+ pSRB->SGcount = 0;
+
+ pSRB->SGIndex = 0;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pSRB->MsgCnt = 0;
+ if( pDCB->DevType != TYPE_TAPE )
+ pSRB->RetryCnt = 1;
+ else
+ pSRB->RetryCnt = 0;
+ pSRB->SRBStatus = 0;
+ pSRB->SRBFlag = 0;
+ pSRB->SRBState = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGPhysAddr = 0;
+ pSRB->SGToBeXferLen = 0;
+ pSRB->ScsiPhase = 0;
+ pSRB->EndMessage = 0;
+ SendSRB( pcmd, pACB, pSRB );
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+static void
+DoNextCmd( PACB pACB, PDCB pDCB )
+{
+ Scsi_Cmnd *pcmd;
+ PSRB pSRB;
+ ULONG flags;
+ PUCHAR ptr,ptr1;
+ USHORT i;
+
+
+ if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
+ return;
+ save_flags(flags);
+ cli();
+
+ pcmd = Getcmd( pDCB );
+ pSRB = GetSRB( pACB );
+ if( !pSRB )
+ {
+ QLinkcmd( pcmd, pDCB );
+ restore_flags(flags);
+ return;
+ }
+
+ pSRB->pSRBDCB = pDCB;
+ pSRB->pcmd = pcmd;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ ptr1 = (PUCHAR) pcmd->cmnd;
+ pSRB->ScsiCmdLen = pcmd->cmd_len;
+ for(i=0; i< pcmd->cmd_len; i++)
+ {
+ *ptr = *ptr1;
+ ptr++;
+ ptr1++;
+ }
+ if( pcmd->use_sg )
+ {
+ pSRB->SGcount = (UCHAR) pcmd->use_sg;
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ }
+ else if( pcmd->request_buffer )
+ {
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ else
+ pSRB->SGcount = 0;
+
+ pSRB->SGIndex = 0;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pSRB->MsgCnt = 0;
+ if( pDCB->DevType != TYPE_TAPE )
+ pSRB->RetryCnt = 1;
+ else
+ pSRB->RetryCnt = 0;
+ pSRB->SRBStatus = 0;
+ pSRB->SRBFlag = 0;
+ pSRB->SRBState = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGPhysAddr = 0;
+ pSRB->SGToBeXferLen = 0;
+ pSRB->ScsiPhase = 0;
+ pSRB->EndMessage = 0;
+ SendSRB( pcmd, pACB, pSRB );
+
+ restore_flags(flags);
+ return;
+}
+
+
+/***********************************************************************
+ * Function:
+ * DC390_bios_param
+ *
+ * Description:
+ * Return the disk geometry for the given SCSI device.
+ ***********************************************************************/
+#ifdef VERSION_ELF_1_2_13
+int DC390_bios_param(Disk *disk, int devno, int geom[])
+#else
+int DC390_bios_param(Disk *disk, kdev_t devno, int geom[])
+#endif
+{
+ int heads, sectors, cylinders;
+ PACB pACB;
+
+ pACB = (PACB) disk->device->host->hostdata;
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if ( cylinders > 1024)
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (255 * 63);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return (0);
+}
+
+
+/***********************************************************************
+ * Function : int DC390_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command
+ *
+ * Inputs : cmd - command to abort
+ *
+ * Returns : 0 on success, -1 on failure.
+ ***********************************************************************/
+
+int
+DC390_abort (Scsi_Cmnd *cmd)
+{
+ ULONG flags;
+ PACB pACB;
+ PDCB pDCB, pdcb;
+ PSRB pSRB, psrb;
+ USHORT count, i;
+ PSCSICMD pcmd, pcmd1;
+ int status;
+
+
+#ifdef DC390_DEBUG0
+ printk("DC390 : Abort Cmd.");
+#endif
+
+ save_flags(flags);
+ cli();
+
+ pACB = (PACB) cmd->host->hostdata;
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ while( (pDCB->UnitSCSIID != cmd->target) ||
+ (pDCB->UnitSCSILUN != cmd->lun) )
+ {
+ pDCB = pDCB->pNextDCB;
+ if( pDCB == pdcb )
+ goto NOT_RUN;
+ }
+
+ if( pDCB->QIORBCnt )
+ {
+ pcmd = pDCB->pQIORBhead;
+ if( pcmd == cmd )
+ {
+ pDCB->pQIORBhead = pcmd->next;
+ pcmd->next = NULL;
+ pDCB->QIORBCnt--;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+ for( count = pDCB->QIORBCnt, i=0; i<count-1; i++)
+ {
+ if( pcmd->next == cmd )
+ {
+ pcmd1 = pcmd->next;
+ pcmd->next = pcmd1->next;
+ pcmd1->next = NULL;
+ pDCB->QIORBCnt--;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+ else
+ {
+ pcmd = pcmd->next;
+ }
+ }
+ }
+
+ pSRB = pDCB->pWaitingSRB;
+ if( !pSRB )
+ goto ON_GOING;
+ if( pSRB->pcmd == cmd )
+ {
+ pDCB->pWaitingSRB = pSRB->pNextSRB;
+ goto IN_WAIT;
+ }
+ else
+ {
+ psrb = pSRB;
+ if( !(psrb->pNextSRB) )
+ goto ON_GOING;
+ while( psrb->pNextSRB->pcmd != cmd )
+ {
+ psrb = psrb->pNextSRB;
+ if( !(psrb->pNextSRB) )
+ goto ON_GOING;
+ }
+ pSRB = psrb->pNextSRB;
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pWaitLast )
+ pDCB->pWaitLast = psrb; /* No check for psrb == NULL ? */
+IN_WAIT:
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ cmd->next = NULL;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+
+ON_GOING:
+ pSRB = pDCB->pGoingSRB;
+ for( count = pDCB->GoingSRBCnt, i=0; i<count; i++)
+ {
+ if( pSRB->pcmd != cmd )
+ pSRB = pSRB->pNextSRB;
+ else
+ {
+ if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) )
+ {
+ status = SCSI_ABORT_BUSY;
+ goto ABO_X;
+ }
+ else
+ {
+ status = SCSI_ABORT_SNOOZE;
+ goto ABO_X;
+ }
+ }
+ }
+
+NOT_RUN:
+ status = SCSI_ABORT_NOT_RUNNING;
+
+ABO_X:
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+ restore_flags(flags);
+ return( status );
+}
+
+
+static void
+ResetDevParam( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+
+ pDCB = pACB->pLinkDCB;
+ if( pDCB == NULL )
+ return;
+ pdcb = pDCB;
+ do
+ {
+ pDCB->SyncMode &= ~SYNC_NEGO_DONE;
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->CtrlR3 = FAST_CLK;
+ pDCB->CtrlR4 &= NEGATE_REQACKDATA;
+ pDCB->CtrlR4 |= EATER_25NS;
+ pDCB = pDCB->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+static void
+RecoverSRB( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB psrb, psrb2;
+ USHORT cnt, i;
+
+ pDCB = pACB->pLinkDCB;
+ if( pDCB == NULL )
+ return;
+ pdcb = pDCB;
+ do
+ {
+ cnt = pdcb->GoingSRBCnt;
+ psrb = pdcb->pGoingSRB;
+ for (i=0; i<cnt; i++)
+ {
+ psrb2 = psrb;
+ psrb = psrb->pNextSRB;
+/* RewaitSRB( pDCB, psrb ); */
+ if( pdcb->pWaitingSRB )
+ {
+ psrb2->pNextSRB = pdcb->pWaitingSRB;
+ pdcb->pWaitingSRB = psrb2;
+ }
+ else
+ {
+ pdcb->pWaitingSRB = psrb2;
+ pdcb->pWaitLast = psrb2;
+ psrb2->pNextSRB = NULL;
+ }
+ }
+ pdcb->GoingSRBCnt = 0;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+/***********************************************************************
+ * Function : int DC390_reset (Scsi_Cmnd *cmd, ...)
+ *
+ * Purpose : perform a hard reset on the SCSI bus
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ ***********************************************************************/
+
+#ifdef VERSION_2_0_0
+int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
+#else
+int DC390_reset (Scsi_Cmnd *cmd)
+#endif
+{
+ USHORT ioport;
+ unsigned long flags;
+ PACB pACB;
+ UCHAR bval;
+ USHORT i;
+
+
+#ifdef DC390_DEBUG1
+ printk("DC390: RESET,");
+#endif
+
+ pACB = (PACB ) cmd->host->hostdata;
+ ioport = pACB->IOPortBase;
+ save_flags(flags);
+ cli();
+ bval = inb(ioport+CtrlReg1);
+ bval |= DIS_INT_ON_SCSI_RST;
+ outb(bval,ioport+CtrlReg1); /* disable interrupt */
+ DC390_ResetSCSIBus( pACB );
+ for( i=0; i<500; i++ )
+ udelay(1000);
+ bval = inb(ioport+CtrlReg1);
+ bval &= ~DIS_INT_ON_SCSI_RST;
+ outb(bval,ioport+CtrlReg1); /* re-enable interrupt */
+
+ bval = DMA_IDLE_CMD;
+ outb(bval,ioport+DMA_Cmd);
+ bval = CLEAR_FIFO_CMD;
+ outb(bval,ioport+ScsiCmd);
+
+ ResetDevParam( pACB );
+ DoingSRB_Done( pACB );
+ pACB->pActiveDCB = NULL;
+
+ pACB->ACBFlag = 0;
+ DoWaitingSRB( pACB );
+
+ restore_flags(flags);
+#ifdef DC390_DEBUG1
+ printk("DC390: RESET1,");
+#endif
+ return( SCSI_RESET_SUCCESS );
+}
+
+
+#include "scsiiom.c"
+
+
+/***********************************************************************
+ * Function : static void DC390_initDCB
+ *
+ * Purpose : initialize the internal structures for a given DCB
+ *
+ * Inputs : cmd - pointer to this scsi cmd request block structure
+ *
+ ***********************************************************************/
+void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
+{
+ PEEprom prom;
+ UCHAR bval;
+ USHORT index;
+
+ if( pACB->DeviceCnt == 0 )
+ {
+ pACB->pLinkDCB = pDCB;
+ pACB->pDCBRunRobin = pDCB;
+ pDCB->pNextDCB = pDCB;
+ pPrevDCB = pDCB;
+ }
+ else
+ pPrevDCB->pNextDCB = pDCB;
+
+ pDCB->pDCBACB = pACB;
+ pDCB->QIORBCnt = 0;
+ pDCB->UnitSCSIID = cmd->target;
+ pDCB->UnitSCSILUN = cmd->lun;
+ pDCB->pWaitingSRB = NULL;
+ pDCB->pGoingSRB = NULL;
+ pDCB->GoingSRBCnt = 0;
+ pDCB->pActiveSRB = NULL;
+ pDCB->TagMask = 0;
+ pDCB->MaxCommand = 1;
+ pDCB->AdaptIndex = pACB->AdapterIndex;
+ index = pACB->AdapterIndex;
+ pDCB->DCBFlag = 0;
+
+ prom = (PEEprom) &eepromBuf[index][cmd->target << 2];
+ pDCB->DevMode = prom->EE_MODE1;
+ pDCB->AdpMode = eepromBuf[index][EE_MODE2];
+
+ if( pDCB->DevMode & EN_DISCONNECT_ )
+ bval = 0xC0;
+ else
+ bval = 0x80;
+ bval |= cmd->lun;
+ pDCB->IdentifyMsg = bval;
+
+ pDCB->SyncMode = 0;
+ if( pDCB->DevMode & SYNC_NEGO_ )
+ {
+ if( !(cmd->lun) || CurrSyncOffset )
+ pDCB->SyncMode = SYNC_ENABLE;
+ }
+
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2;
+
+ pDCB->CtrlR1 = pACB->AdaptSCSIID;
+ if( pDCB->DevMode & PARITY_CHK_ )
+ pDCB->CtrlR1 |= PARITY_ERR_REPO;
+
+ pDCB->CtrlR3 = FAST_CLK;
+
+ pDCB->CtrlR4 = EATER_25NS;
+ if( pDCB->AdpMode & ACTIVE_NEGATION)
+ pDCB->CtrlR4 |= NEGATE_REQACKDATA;
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initSRB
+ *
+ * Purpose : initialize the internal structures for a given SRB
+ *
+ * Inputs : psrb - pointer to this scsi request block structure
+ *
+ ***********************************************************************/
+void DC390_initSRB( PSRB psrb )
+{
+#ifndef VERSION_ELF_1_2_13
+#ifdef DC390_DEBUG0
+ printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb));
+#endif
+ psrb->PhysSRB = virt_to_bus( psrb );
+#else
+ psrb->PhysSRB = (ULONG) psrb;
+#endif
+}
+
+
+void DC390_linkSRB( PACB pACB )
+{
+ USHORT count, i;
+ PSRB psrb;
+
+ count = pACB->SRBCount;
+
+ for( i=0; i< count; i++)
+ {
+ if( i != count - 1)
+ pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
+ else
+ pACB->SRB_array[i].pNextSRB = NULL;
+ psrb = (PSRB) &pACB->SRB_array[i];
+ DC390_initSRB( psrb );
+ }
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initACB
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+ PACB pACB;
+ USHORT i;
+
+ psh->can_queue = MAX_CMD_QUEUE;
+ psh->cmd_per_lun = MAX_CMD_PER_LUN;
+ psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID];
+ psh->io_port = io_port;
+ psh->n_io_port = 0x80;
+ psh->irq = Irq;
+
+ pACB = (PACB) psh->hostdata;
+
+#ifndef VERSION_ELF_1_2_13
+ psh->max_id = 8;
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ psh->max_lun = 8;
+ else
+#endif
+ psh->max_lun = 1;
+#endif
+
+ pACB->max_id = 7;
+ if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
+ pACB->max_id--;
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ pACB->max_lun = 7;
+ else
+#endif
+ pACB->max_lun = 0;
+
+ pACB->pScsiHost = psh;
+ pACB->IOPortBase = (USHORT) io_port;
+ pACB->pLinkDCB = NULL;
+ pACB->pDCBRunRobin = NULL;
+ pACB->pActiveDCB = NULL;
+ pACB->pFreeSRB = pACB->SRB_array;
+ pACB->SRBCount = MAX_SRB_CNT;
+ pACB->AdapterIndex = index;
+ pACB->status = 0;
+ pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID];
+ pACB->HostID_Bit = (1 << pACB->AdaptSCSIID);
+ pACB->AdaptSCSILUN = 0;
+ pACB->DeviceCnt = 0;
+ pACB->IRQLevel = Irq;
+ pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2;
+ pACB->ACBFlag = 0;
+ pACB->scan_devices = 1;
+ pACB->Gmode2 = eepromBuf[index][EE_MODE2];
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ pACB->LUNchk = 1;
+ pACB->pDCB_free = &pACB->DCB_array[0];
+ DC390_linkSRB( pACB );
+ pACB->pTmpSRB = &pACB->TmpSRB;
+ DC390_initSRB( pACB->pTmpSRB );
+ for(i=0; i<MAX_SCSI_ID; i++)
+ pACB->DCBmap[i] = 0;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_initAdapter
+ *
+ * Purpose : initialize the SCSI chip ctrl registers
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+ USHORT ioport;
+ UCHAR bval;
+ PACB pACB, pacb;
+ USHORT used_irq = 0;
+
+ pacb = pACB_start;
+ if( pacb != NULL )
+ {
+ for ( ; (pacb != (PACB) -1) ; )
+ {
+ if( pacb->IRQLevel == Irq )
+ {
+ used_irq = 1;
+ break;
+ }
+ else
+ pacb = pacb->pNextACB;
+ }
+ }
+
+ if( !used_irq )
+ {
+#ifdef VERSION_ELF_1_2_13
+ if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim"))
+#else
+ if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL))
+#endif
+ {
+ printk("DC390: register IRQ error!\n");
+ return( -1 );
+ }
+ }
+
+ request_region(io_port,psh->n_io_port,"tmscsim");
+
+ ioport = (USHORT) io_port;
+
+ pACB = (PACB) psh->hostdata;
+ bval = SEL_TIMEOUT; /* 250ms selection timeout */
+ outb(bval,ioport+Scsi_TimeOut);
+
+ bval = CLK_FREQ_40MHZ; /* Conversion factor = 0 , 40MHz clock */
+ outb(bval,ioport+Clk_Factor);
+
+ bval = NOP_CMD; /* NOP cmd - clear command register */
+ outb(bval,ioport+ScsiCmd);
+
+ bval = EN_FEATURE+EN_SCSI2_CMD; /* Enable Feature and SCSI-2 */
+ outb(bval,ioport+CtrlReg2);
+
+ bval = FAST_CLK; /* fast clock */
+ outb(bval,ioport+CtrlReg3);
+
+ bval = EATER_25NS;
+ if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION )
+ bval |= NEGATE_REQACKDATA;
+ outb(bval,ioport+CtrlReg4);
+
+ bval = DIS_INT_ON_SCSI_RST; /* Disable SCSI bus reset interrupt */
+ outb(bval,ioport+CtrlReg1);
+
+ return(0);
+}
+
+
+void
+DC390_EnableCfg( USHORT mechnum, UCHAR regval )
+{
+ ULONG wlval;
+
+ if(mechnum == 2)
+ {
+ outb(mech2bus, PCI_CFG2_FORWARD_REG);
+ outb(mech2CfgSPenR, PCI_CFG2_ENABLE_REG);
+ }
+ else
+ {
+ regval &= 0xFC;
+ wlval = mech1addr;
+ wlval |= (((ULONG)regval) & 0xff);
+ outl(wlval, PCI_CFG1_ADDRESS_REG);
+ }
+}
+
+
+void
+DC390_DisableCfg( USHORT mechnum )
+{
+
+ if(mechnum == 2)
+ outb(0, PCI_CFG2_ENABLE_REG);
+ else
+ outl(0, PCI_CFG1_ADDRESS_REG);
+}
+
+
+UCHAR
+DC390_inByte( USHORT mechnum, UCHAR regval )
+{
+ UCHAR bval;
+ ULONG wval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg( mechnum, regval );
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= ((USHORT) regval) & 0xff;
+ bval = inb(wval);
+ }
+ else
+ {
+ regval &= 3;
+ bval = inb(PCI_CFG1_DATA_REG | regval);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+ return(bval);
+}
+
+
+USHORT
+DC390_inWord( USHORT mechnum, UCHAR regval )
+{
+ USHORT wval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg(mechnum,regval);
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= regval;
+ wval = inw(wval);
+ }
+ else
+ {
+ regval &= 3;
+ wval = inw(PCI_CFG1_DATA_REG | regval);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+ return(wval);
+}
+
+
+ULONG
+DC390_inDword(USHORT mechnum, UCHAR regval )
+{
+ ULONG wlval;
+ ULONG flags;
+ USHORT wval;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg(mechnum,regval);
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= regval;
+ wlval = inl(wval);
+ }
+ else
+ {
+ wlval = inl(PCI_CFG1_DATA_REG);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+ return(wlval);
+}
+
+
+void
+DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval )
+{
+
+ USHORT wval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg(mechnum,regval);
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= regval;
+ outb(bval, wval);
+ }
+ else
+ {
+ regval &= 3;
+ outb(bval, PCI_CFG1_DATA_REG | regval);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+}
+
+
+void
+DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval )
+{
+
+ UCHAR bval;
+
+ bval = 0;
+ if(mode == ENABLE_CE)
+ *regval = 0xc0;
+ else
+ *regval = 0x80;
+ DC390_OutB(mechnum,*regval,bval);
+ if(mode == DISABLE_CE)
+ DC390_OutB(mechnum,*regval,bval);
+ udelay(160);
+}
+
+
+void
+DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry )
+{
+ UCHAR bval;
+
+ bval = 0;
+ if(Carry)
+ {
+ bval = 0x40;
+ *regval = 0x80;
+ DC390_OutB(mechnum,*regval,bval);
+ }
+ udelay(160);
+ bval |= 0x80;
+ DC390_OutB(mechnum,*regval,bval);
+ udelay(160);
+ bval = 0;
+ DC390_OutB(mechnum,*regval,bval);
+ udelay(160);
+}
+
+
+UCHAR
+DC390_EEpromInDO( USHORT mechnum )
+{
+ UCHAR bval,regval;
+
+ regval = 0x80;
+ bval = 0x80;
+ DC390_OutB(mechnum,regval,bval);
+ udelay(160);
+ bval = 0x40;
+ DC390_OutB(mechnum,regval,bval);
+ udelay(160);
+ regval = 0x0;
+ bval = DC390_inByte(mechnum,regval);
+ if(bval == 0x22)
+ return(1);
+ else
+ return(0);
+}
+
+
+USHORT
+EEpromGetData1( USHORT mechnum )
+{
+ UCHAR i;
+ UCHAR carryFlag;
+ USHORT wval;
+
+ wval = 0;
+ for(i=0; i<16; i++)
+ {
+ wval <<= 1;
+ carryFlag = DC390_EEpromInDO(mechnum);
+ wval |= carryFlag;
+ }
+ return(wval);
+}
+
+
+void
+DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd )
+{
+ UCHAR i,j;
+ USHORT carryFlag;
+
+ carryFlag = 1;
+ j = 0x80;
+ for(i=0; i<9; i++)
+ {
+ DC390_EEpromOutDI(mechnum,regval,carryFlag);
+ carryFlag = (EEpromCmd & j) ? 1 : 0;
+ j >>= 1;
+ }
+}
+
+
+void
+DC390_ReadEEprom( USHORT mechnum, USHORT index )
+{
+ UCHAR regval,cmd;
+ PUSHORT ptr;
+ USHORT i;
+
+ ptr = (PUSHORT) &eepromBuf[index][0];
+ cmd = EEPROM_READ;
+ for(i=0; i<0x40; i++)
+ {
+ DC390_EnDisableCE(ENABLE_CE, mechnum, ®val);
+ DC390_Prepare(mechnum, ®val, cmd);
+ *ptr = EEpromGetData1(mechnum);
+ ptr++;
+ cmd++;
+ DC390_EnDisableCE(DISABLE_CE,mechnum,®val);
+ }
+}
+
+
+USHORT
+DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index )
+{
+ USHORT wval, rc, *ptr;
+ UCHAR i;
+
+ DC390_ReadEEprom( MechNum, index );
+ wval = 0;
+ ptr = (PUSHORT) &eepromBuf[index][0];
+ for(i=0; i<128 ;i+=2, ptr++)
+ wval += *ptr;
+ if( wval == 0x1234 )
+ rc = 0;
+ else
+ rc = -1;
+ return( rc );
+}
+
+
+USHORT
+DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum )
+{
+ USHORT devnum;
+
+ devnum = BusDevFunNum;
+
+ if(Mechnum == 2)
+ {
+ if(devnum & 0x80)
+ return(-1);
+ mech2bus = (UCHAR)((devnum & 0xff00) >> 8); /* Bus num */
+ mech2Agent = ((UCHAR)(devnum & 0xff)) >> 3; /* Dev num */
+ mech2Agent |= 0xc0;
+ mech2CfgSPenR = ((UCHAR)(devnum & 0xff)) & 0x07; /* Fun num */
+ mech2CfgSPenR = (mech2CfgSPenR << 1) | 0x20;
+ }
+ else /* use mech #1 method */
+ {
+ mech1addr = 0x80000000 | ((ULONG)devnum << 8);
+ }
+ return(0);
+}
+
+/***********************************************************************
+ * Function : static int DC390_init (struct Scsi_Host *host)
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure/
+ *
+ * Preconditions : when this function is called, the chip_type
+ * field of the pACB structure MUST have been set.
+ ***********************************************************************/
+
+static int
+DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
+{
+ PSH psh;
+ PACB pACB;
+
+ if( !DC390_CheckEEpromCheckSum( MechNum, index) )
+ {
+ psh = scsi_register( psht, sizeof(DC390_ACB) );
+ if( !psh )
+ return( -1 );
+ if( !pSH_start )
+ {
+ pSH_start = psh;
+ pSH_current = psh;
+ }
+ else
+ {
+ pSH_current->next = psh;
+ pSH_current = psh;
+ }
+
+#ifdef DC390_DEBUG0
+ printk("DC390: pSH = %8x,", (UINT) psh);
+ printk("DC390: Index %02i,", index);
+#endif
+
+ DC390_initACB( psh, io_port, Irq, index );
+ if( !DC390_initAdapter( psh, io_port, Irq, index ) )
+ {
+ pACB = (PACB) psh->hostdata;
+ if( !pACB_start )
+ {
+ pACB_start = pACB;
+ pACB_current = pACB;
+ pACB->pNextACB = (PACB) -1;
+ }
+ else
+ {
+ pACB_current->pNextACB = pACB;
+ pACB_current = pACB;
+ pACB->pNextACB = (PACB) -1;
+ }
+
+#ifdef DC390_DEBUG0
+ printk("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n",
+ (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array);
+ printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",
+ sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );
+#endif
+
+ }
+ else
+ {
+ pSH_start = NULL;
+ scsi_unregister( psh );
+ return( -1 );
+ }
+ return( 0 );
+ }
+ else
+ {
+ printk("DC390_init: EEPROM reading error!\n");
+ return( -1 );
+ }
+}
+
+
+/***********************************************************************
+ * Function : int DC390_detect(Scsi_Host_Template *psht)
+ *
+ * Purpose : detects and initializes AMD53C974 SCSI chips
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : psht - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ ***********************************************************************/
+
+int
+DC390_detect(Scsi_Host_Template *psht)
+{
+#ifdef FOR_PCI_OK
+ UCHAR pci_bus, pci_device_fn;
+ int error = 0;
+ USHORT chipType = 0;
+ USHORT i;
+#endif
+
+ UCHAR irq;
+ UCHAR istatus;
+#ifndef VERSION_ELF_1_2_13
+ UINT io_port;
+#else
+ ULONG io_port;
+#endif
+ USHORT adaptCnt = 0; /* Number of boards detected */
+ USHORT pci_index = 0; /* Device index to PCI BIOS calls */
+ USHORT MechNum, BusDevFunNum;
+ ULONG wlval;
+
+#ifndef VERSION_ELF_1_2_13
+ psht->proc_dir = &proc_scsi_tmscsim;
+#endif
+
+ InitialTime = 1;
+ pSHT_start = psht;
+ pACB_start = NULL;
+
+ MechNum = 1;
+ for( ; (MechNum < 3) && (!adaptCnt); MechNum++)
+ {
+ BusDevFunNum = 0;
+ for (; adaptCnt < MAX_ADAPTER_NUM ;)
+ {
+ if( !DC390_ToMech( MechNum, BusDevFunNum) )
+ {
+ wlval = DC390_inDword( MechNum, PCI_VENDOR_ID);
+ if(wlval == ( (PCI_DEVICE_ID_AMD53C974 << 16)+
+ PCI_VENDOR_ID_AMD) )
+ {
+ io_port =DC390_inDword(MechNum,PCI_BASE_ADDRESS_0) & 0xFFFE;
+ irq = DC390_inByte( MechNum, PCI_INTERRUPT_LINE);
+#ifdef DC390_DEBUG0
+ printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
+#endif
+ if( !DC390_init(psht, io_port, irq, pci_index, MechNum) )
+ {
+ adaptCnt++;
+ pci_index++;
+ istatus = inb( (USHORT)io_port+INT_Status ); /* Reset Pending INT */
+#ifdef DC390_DEBUG0
+ printk("DC390: Mech=%2x,\n",(UCHAR) MechNum);
+#endif
+ }
+ }
+ }
+ if( BusDevFunNum != 0xfff8 )
+ BusDevFunNum += 8; /* next device # */
+ else
+ break;
+ }
+ }
+
+#ifdef FOR_PCI_OK
+ if ( pcibios_present() )
+ {
+ for (i = 0; i < MAX_ADAPTER_NUM; ++i)
+ {
+ if( !pcibios_find_device( PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD53C974,
+ pci_index, &pci_bus, &pci_device_fn) )
+ {
+ chipType = PCI_DEVICE_ID_AMD53C974;
+ pci_index++;
+ }
+
+ if( chipType )
+ {
+
+ error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ error |= pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ if( error )
+ {
+ printk("DC390_detect: reading configuration registers error!\n");
+ InitialTime = 0;
+ return( 0 );
+ }
+
+ (USHORT) io_port = (USHORT) io_port & 0xFFFE;
+#ifdef DC390_DEBUG0
+ printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
+#endif
+ if( !DC390_init(psht, io_port, irq, i) )
+ adaptCnt++;
+ chipType = 0;
+ }
+ else
+ break;
+ }
+ }
+#endif
+
+ InitialTime = 0;
+ adapterCnt = adaptCnt;
+ return( adaptCnt );
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+
+/********************************************************************
+ * Function: tmscsim_set_info()
+ *
+ * Purpose: Set adapter info (!)
+ *
+ * Not yet implemented
+ *
+ *******************************************************************/
+
+int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+/********************************************************************
+ * Function: tmscsim_proc_info(char* buffer, char **start,
+ * off_t offset, int length, int hostno, int inout)
+ *
+ * Purpose: return SCSI Adapter/Device Info
+ *
+ * Input: buffer: Pointer to a buffer where to write info
+ * start :
+ * offset:
+ * hostno: Host adapter index
+ * inout : Read (=0) or set(!=0) info
+ *
+ * Output: buffer: contains info
+ * length; length of info in buffer
+ *
+ * return value: length
+ *
+ ********************************************************************/
+
+/* KG: proc_info taken from driver aha152x.c */
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+#define YESNO(YN)\
+if (YN) SPRINTF(" Yes ");\
+else SPRINTF(" No ")
+
+int tmscsim_proc_info(char *buffer, char **start,
+ off_t offset, int length, int hostno, int inout)
+{
+ int dev, spd, spd1;
+ char *pos = buffer;
+ PSH shpnt;
+ PACB acbpnt;
+ PDCB dcbpnt;
+ unsigned long flags;
+/* Scsi_Cmnd *ptr; */
+
+ acbpnt = pACB_start;
+
+ while(acbpnt != (PACB)-1)
+ {
+ shpnt = acbpnt->pScsiHost;
+ if (shpnt->host_no == hostno) break;
+ acbpnt = acbpnt->pNextACB;
+ }
+
+ if (acbpnt == (PACB)-1) return(-ESRCH);
+ if(!shpnt) return(-ESRCH);
+
+ if(inout) // Has data been written to the file ?
+ return(tmscsim_set_info(buffer, length, shpnt));
+
+ SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, ");
+ SPRINTF("Driver Version 1.10, 1996/12/05\n");
+
+ save_flags(flags);
+ cli();
+
+ SPRINTF("SCSI Host Nr %i, ", shpnt->host_no);
+ SPRINTF("DC390 Adapter Nr %i\n", acbpnt->AdapterIndex);
+ SPRINTF("IOPortBase 0x%04x, ", acbpnt->IOPortBase);
+ SPRINTF("IRQLevel 0x%02x\n", acbpnt->IRQLevel);
+
+ SPRINTF("MaxID %i, MaxLUN %i, ",acbpnt->max_id, acbpnt->max_lun);
+ SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN);
+
+ SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status);
+
+ SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt);
+
+ SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n");
+
+ dcbpnt = acbpnt->pLinkDCB;
+ for (dev = 0; dev < acbpnt->DeviceCnt; dev++)
+ {
+ SPRINTF("%02i %02i %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN);
+ YESNO(dcbpnt->DevMode & PARITY_CHK_);
+ YESNO(dcbpnt->SyncMode & SYNC_NEGO_DONE);
+ YESNO(dcbpnt->DevMode & EN_DISCONNECT_);
+ YESNO(dcbpnt->DevMode & SEND_START_);
+ YESNO(dcbpnt->SyncMode & EN_TAG_QUEUING);
+ SPRINTF(" %03i ns ", (dcbpnt->NegoPeriod) << 2);
+ if (dcbpnt->SyncOffset & 0x0f)
+ {
+ spd = 1000/(dcbpnt->NegoPeriod <<2);
+ spd1 = 1000%(dcbpnt->NegoPeriod <<2);
+ spd1 = (spd1 * 10)/(dcbpnt->NegoPeriod <<2);
+ SPRINTF(" %2i.%1i M %02i\n", spd, spd1, (dcbpnt->SyncOffset & 0x0f));
+ }
+ else SPRINTF("\n");
+ /* Add more info ...*/
+ dcbpnt = dcbpnt->pNextDCB;
+ }
+
+ restore_flags(flags);
+ *start = buffer + offset;
+
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+#endif /* VERSION_ELF_1_2_13 */
+
+
+#ifdef MODULE
+
+/***********************************************************************
+ * Function : static int DC390_shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the SCSI chip.
+ * Use prior to dumping core, unloading the driver, etc.
+ *
+ * Returns : 0 on success
+ ***********************************************************************/
+static int
+DC390_shutdown (struct Scsi_Host *host)
+{
+ UCHAR bval;
+ USHORT ioport;
+ unsigned long flags;
+ PACB pACB = (PACB)(host->hostdata);
+
+ ioport = (unsigned int) pACB->IOPortBase;
+
+ save_flags (flags);
+ cli();
+
+/* pACB->soft_reset(host); */
+
+#ifdef DC390_DEBUG0
+ printk("DC390: shutdown,");
+#endif
+
+ bval = inb(ioport+CtrlReg1);
+ bval |= DIS_INT_ON_SCSI_RST;
+ outb(bval,ioport+CtrlReg1); /* disable interrupt */
+ DC390_ResetSCSIBus( pACB );
+
+ restore_flags (flags);
+ return( 0 );
+}
+
+
+int DC390_release(struct Scsi_Host *host)
+{
+ int irq_count;
+ struct Scsi_Host *tmp;
+
+ DC390_shutdown (host);
+
+ if (host->irq != IRQ_NONE)
+ {
+ for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next)
+ {
+ if ( tmp->irq == host->irq )
+ ++irq_count;
+ }
+ if (irq_count == 1)
+ {
+#ifdef DC390_DEBUG0
+ printk("DC390: Free IRQ %i.",host->irq);
+#endif
+#ifndef VERSION_ELF_1_2_13
+ free_irq(host->irq,NULL);
+#else
+ free_irq(host->irq);
+#endif
+ }
+ }
+
+ release_region(host->io_port,host->n_io_port);
+
+ return( 1 );
+}
+
+Scsi_Host_Template driver_template = DC390_T;
+#include "scsi_module.c"
+#endif /* def MODULE */
+
--- /dev/null
+/***********************************************************************
+;* File Name : TMSCSIM.H *
+;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter *
+;* Device Driver *
+;***********************************************************************/
+
+#ifndef TMSCSIM_H
+#define TMSCSIM_H
+
+#define IRQ_NONE 255
+
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned long ULONG;
+typedef unsigned int UINT;
+
+typedef UCHAR *PUCHAR;
+typedef USHORT *PUSHORT;
+typedef ULONG *PULONG;
+typedef Scsi_Host_Template *PSHT;
+typedef struct Scsi_Host *PSH;
+typedef Scsi_Device *PSCSIDEV;
+typedef Scsi_Cmnd *PSCSICMD;
+typedef void *PVOID;
+typedef struct scatterlist *PSGL, SGL;
+
+
+/*;-----------------------------------------------------------------------*/
+typedef struct _SyncMsg
+{
+UCHAR ExtendMsg;
+UCHAR ExtMsgLen;
+UCHAR SyncXferReq;
+UCHAR Period;
+UCHAR ReqOffset;
+} SyncMsg;
+/*;-----------------------------------------------------------------------*/
+typedef struct _Capacity
+{
+ULONG BlockCount;
+ULONG BlockLength;
+} Capacity;
+/*;-----------------------------------------------------------------------*/
+typedef struct _SGentry
+{
+ULONG SGXferDataPtr;
+ULONG SGXferDataLen;
+} SGentry;
+
+typedef struct _SGentry1
+{
+ULONG SGXLen;
+ULONG SGXPtr;
+} SGentry1, *PSGE;
+
+
+#define MAX_ADAPTER_NUM 4
+#define MAX_DEVICES 10
+#define MAX_SG_LIST_BUF 16
+#define MAX_CMD_QUEUE 20
+#define MAX_CMD_PER_LUN 8
+#define MAX_SCSI_ID 8
+#define MAX_SRB_CNT MAX_CMD_QUEUE+4
+#define END_SCAN 2
+
+#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */
+
+/*
+;-----------------------------------------------------------------------
+; SCSI Request Block
+;-----------------------------------------------------------------------
+*/
+struct _SRB
+{
+UCHAR CmdBlock[12];
+
+struct _SRB *pNextSRB;
+struct _DCB *pSRBDCB;
+PSCSICMD pcmd;
+PSGL pSegmentList;
+
+ULONG PhysSRB;
+ULONG TotalXferredLen;
+ULONG SGPhysAddr; /*;a segment starting address */
+ULONG SGToBeXferLen; /*; to be xfer length */
+
+SGL Segmentx; /* make a one entry of S/G list table */
+
+PUCHAR pMsgPtr;
+USHORT SRBState;
+USHORT Revxx2; /* ??? */
+
+UCHAR MsgInBuf[6];
+UCHAR MsgOutBuf[6];
+
+UCHAR AdaptStatus;
+UCHAR TargetStatus;
+UCHAR MsgCnt;
+UCHAR EndMessage;
+UCHAR TagNumber;
+UCHAR SGcount;
+UCHAR SGIndex;
+UCHAR IORBFlag; /*;81h-Reset, 2-retry */
+
+UCHAR SRBStatus;
+UCHAR RetryCnt;
+UCHAR SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */
+ /*; b4-settimeout,b5-Residual valid */
+UCHAR ScsiCmdLen;
+UCHAR ScsiPhase;
+UCHAR Reserved3[3]; /*;for dword alignment */
+ULONG Segment0[2];
+ULONG Segment1[2];
+};
+
+typedef struct _SRB DC390_SRB, *PSRB;
+
+/*
+;-----------------------------------------------------------------------
+; Device Control Block
+;-----------------------------------------------------------------------
+*/
+struct _DCB
+{
+struct _DCB *pNextDCB;
+struct _ACB *pDCBACB;
+
+PSCSICMD pQIORBhead;
+PSCSICMD pQIORBtail;
+PSCSICMD AboIORBhead;
+PSCSICMD AboIORBtail;
+USHORT QIORBCnt;
+USHORT AboIORBcnt;
+
+PSRB pWaitingSRB;
+PSRB pWaitLast;
+PSRB pGoingSRB;
+PSRB pGoingLast;
+PSRB pActiveSRB;
+USHORT GoingSRBCnt;
+USHORT WaitSRBCnt; /* ??? */
+
+ULONG TagMask;
+
+USHORT MaxCommand;
+USHORT AdaptIndex; /*; UnitInfo struc start */
+USHORT UnitIndex; /*; nth Unit on this card */
+UCHAR UnitSCSIID; /*; SCSI Target ID (SCSI Only) */
+UCHAR UnitSCSILUN; /*; SCSI Log. Unit (SCSI Only) */
+
+UCHAR IdentifyMsg;
+UCHAR CtrlR1;
+UCHAR CtrlR3;
+UCHAR CtrlR4;
+
+UCHAR InqDataBuf[8];
+UCHAR CapacityBuf[8];
+UCHAR DevMode;
+UCHAR AdpMode;
+UCHAR SyncMode; /*; 0:async mode */
+UCHAR NegoPeriod; /*;for nego. */
+UCHAR SyncPeriod; /*;for reg. */
+UCHAR SyncOffset; /*;for reg. and nego.(low nibble) */
+UCHAR UnitCtrlFlag;
+UCHAR DCBFlag;
+UCHAR DevType;
+UCHAR Reserved2[3]; /*;for dword alignment */
+};
+
+typedef struct _DCB DC390_DCB, *PDCB;
+/*
+;-----------------------------------------------------------------------
+; Adapter Control Block
+;-----------------------------------------------------------------------
+*/
+struct _ACB
+{
+ULONG PhysACB;
+PSH pScsiHost;
+struct _ACB *pNextACB;
+USHORT IOPortBase;
+USHORT Revxx1; /* ??? */
+
+PDCB pLinkDCB;
+PDCB pDCBRunRobin;
+PDCB pActiveDCB;
+PDCB pDCB_free;
+PSRB pFreeSRB;
+PSRB pTmpSRB;
+USHORT SRBCount;
+USHORT AdapterIndex; /*; nth Adapter this driver */
+USHORT max_id;
+USHORT max_lun;
+
+UCHAR msgin123[4];
+UCHAR status;
+UCHAR AdaptSCSIID; /*; Adapter SCSI Target ID */
+UCHAR AdaptSCSILUN; /*; Adapter SCSI LUN */
+UCHAR DeviceCnt;
+UCHAR IRQLevel;
+UCHAR TagMaxNum;
+UCHAR ACBFlag;
+UCHAR Gmode2;
+UCHAR LUNchk;
+UCHAR scan_devices;
+UCHAR HostID_Bit;
+UCHAR Reserved1[1]; /*;for dword alignment */
+UCHAR DCBmap[MAX_SCSI_ID];
+DC390_DCB DCB_array[MAX_DEVICES]; /* +74h, Len=3E8 */
+DC390_SRB SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */
+DC390_SRB TmpSRB;
+};
+
+typedef struct _ACB DC390_ACB, *PACB;
+
+/*;-----------------------------------------------------------------------*/
+
+
+#define BIT31 0x80000000
+#define BIT30 0x40000000
+#define BIT29 0x20000000
+#define BIT28 0x10000000
+#define BIT27 0x08000000
+#define BIT26 0x04000000
+#define BIT25 0x02000000
+#define BIT24 0x01000000
+#define BIT23 0x00800000
+#define BIT22 0x00400000
+#define BIT21 0x00200000
+#define BIT20 0x00100000
+#define BIT19 0x00080000
+#define BIT18 0x00040000
+#define BIT17 0x00020000
+#define BIT16 0x00010000
+#define BIT15 0x00008000
+#define BIT14 0x00004000
+#define BIT13 0x00002000
+#define BIT12 0x00001000
+#define BIT11 0x00000800
+#define BIT10 0x00000400
+#define BIT9 0x00000200
+#define BIT8 0x00000100
+#define BIT7 0x00000080
+#define BIT6 0x00000040
+#define BIT5 0x00000020
+#define BIT4 0x00000010
+#define BIT3 0x00000008
+#define BIT2 0x00000004
+#define BIT1 0x00000002
+#define BIT0 0x00000001
+
+/*;---UnitCtrlFlag */
+#define UNIT_ALLOCATED BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY BIT3
+
+/*;---UnitFlags */
+#define DASD_SUPPORT BIT0
+#define SCSI_SUPPORT BIT1
+#define ASPI_SUPPORT BIT2
+
+/*;----SRBState machine definition */
+#define SRB_FREE 0
+#define SRB_WAIT BIT0
+#define SRB_READY BIT1
+#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/
+#define SRB_MSGIN BIT3
+#define SRB_MSGIN_MULTI BIT4
+#define SRB_COMMAND BIT5
+#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/
+#define SRB_DISCONNECT BIT7
+#define SRB_DATA_XFER BIT8
+#define SRB_XFERPAD BIT9
+#define SRB_STATUS BIT10
+#define SRB_COMPLETED BIT11
+#define SRB_ABORT_SENT BIT12
+#define DO_SYNC_NEGO BIT13
+#define SRB_UNEXPECT_RESEL BIT14
+
+/*;---ACBFlag */
+#define RESET_DEV BIT0
+#define RESET_DETECT BIT1
+#define RESET_DONE BIT2
+
+/*;---DCBFlag */
+#define ABORT_DEV_ BIT0
+
+/*;---SRBstatus */
+#define SRB_OK BIT0
+#define ABORTION BIT1
+#define OVER_RUN BIT2
+#define UNDER_RUN BIT3
+#define PARITY_ERROR BIT4
+#define SRB_ERROR BIT5
+
+/*;---SRBFlag */
+#define DATAOUT BIT7
+#define DATAIN BIT6
+#define RESIDUAL_VALID BIT5
+#define ENABLE_TIMER BIT4
+#define RESET_DEV0 BIT2
+#define ABORT_DEV BIT1
+#define AUTO_REQSENSE BIT0
+
+/*;---Adapter status */
+#define H_STATUS_GOOD 0
+#define H_SEL_TIMEOUT 0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD 0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB 0x19
+#define H_BAD_CCB_OR_SG 0x1A
+#define H_ABORT 0x0FF
+
+/*; SCSI Status byte codes*/
+#define SCSI_STAT_GOOD 0x0 /*; Good status */
+#define SCSI_STAT_CHECKCOND 0x02 /*; SCSI Check Condition */
+#define SCSI_STAT_CONDMET 0x04 /*; Condition Met */
+#define SCSI_STAT_BUSY 0x08 /*; Target busy status */
+#define SCSI_STAT_INTER 0x10 /*; Intermediate status */
+#define SCSI_STAT_INTERCONDMET 0x14 /*; Intermediate condition met */
+#define SCSI_STAT_RESCONFLICT 0x18 /*; Reservation conflict */
+#define SCSI_STAT_CMDTERM 0x22 /*; Command Terminated */
+#define SCSI_STAT_QUEUEFULL 0x28 /*; Queue Full */
+
+#define SCSI_STAT_UNEXP_BUS_F 0xFD /*; Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE /*; Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT 0xFF /*; Selection Time out */
+
+/*;---Sync_Mode */
+#define SYNC_DISABLE 0
+#define SYNC_ENABLE BIT0
+#define SYNC_NEGO_DONE BIT1
+#define WIDE_ENABLE BIT2
+#define WIDE_NEGO_DONE BIT3
+#define EN_TAG_QUEUING BIT4
+#define EN_ATN_STOP BIT5
+
+#define SYNC_NEGO_OFFSET 15
+
+/*;---SCSI bus phase*/
+#define SCSI_DATA_OUT 0
+#define SCSI_DATA_IN 1
+#define SCSI_COMMAND 2
+#define SCSI_STATUS_ 3
+#define SCSI_NOP0 4
+#define SCSI_NOP1 5
+#define SCSI_MSG_OUT 6
+#define SCSI_MSG_IN 7
+
+/*;----SCSI MSG BYTE*/
+#define MSG_COMPLETE 0x00
+#define MSG_EXTENDED 0x01
+#define MSG_SAVE_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INITIATOR_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT_ 0x07
+#define MSG_NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_LINK_CMD_COMPL 0x0A
+#define MSG_LINK_CMD_COMPL_FLG 0x0B
+#define MSG_BUS_RESET 0x0C
+#define MSG_ABORT_TAG 0x0D
+#define MSG_SIMPLE_QTAG 0x20
+#define MSG_HEAD_QTAG 0x21
+#define MSG_ORDER_QTAG 0x22
+#define MSG_IDENTIFY 0x80
+#define MSG_HOST_ID 0x0C0
+
+/*;----SCSI STATUS BYTE*/
+#define STATUS_GOOD 0x00
+#define CHECK_CONDITION_ 0x02
+#define STATUS_BUSY 0x08
+#define STATUS_INTERMEDIATE 0x10
+#define RESERVE_CONFLICT 0x18
+
+/* cmd->result */
+#define STATUS_MASK_ 0xFF
+#define MSG_MASK 0xFF00
+#define RETURN_MASK 0xFF0000
+
+/*
+** Inquiry Data format
+*/
+
+typedef struct _SCSIInqData { /* INQ */
+
+ UCHAR DevType; /* Periph Qualifier & Periph Dev Type*/
+ UCHAR RMB_TypeMod; /* rem media bit & Dev Type Modifier */
+ UCHAR Vers; /* ISO, ECMA, & ANSI versions */
+ UCHAR RDF; /* AEN, TRMIOP, & response data format*/
+ UCHAR AddLen; /* length of additional data */
+ UCHAR Res1; /* reserved */
+ UCHAR Res2; /* reserved */
+ UCHAR Flags; /* RelADr,Wbus32,Wbus16,Sync,etc. */
+ UCHAR VendorID[8]; /* Vendor Identification */
+ UCHAR ProductID[16]; /* Product Identification */
+ UCHAR ProductRev[4]; /* Product Revision */
+
+
+} SCSI_INQDATA, *PSCSI_INQDATA;
+
+
+/* Inquiry byte 0 masks */
+
+
+#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */
+#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */
+
+
+/* Inquiry byte 1 mask */
+
+#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */
+
+
+/* Peripheral Device Type definitions */
+
+#define SCSI_DASD 0x00 /* Direct-access Device */
+#define SCSI_SEQACESS 0x01 /* Sequential-access device */
+#define SCSI_PRINTER 0x02 /* Printer device */
+#define SCSI_PROCESSOR 0x03 /* Processor device */
+#define SCSI_WRITEONCE 0x04 /* Write-once device */
+#define SCSI_CDROM 0x05 /* CD-ROM device */
+#define SCSI_SCANNER 0x06 /* Scanner device */
+#define SCSI_OPTICAL 0x07 /* Optical memory device */
+#define SCSI_MEDCHGR 0x08 /* Medium changer device */
+#define SCSI_COMM 0x09 /* Communications device */
+#define SCSI_NODEV 0x1F /* Unknown or no device type */
+
+/*
+** Inquiry flag definitions (Inq data byte 7)
+*/
+
+#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing*/
+#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */
+#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */
+#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */
+#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */
+#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */
+#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */
+
+
+/*
+;==========================================================
+; EEPROM byte offset
+;==========================================================
+*/
+typedef struct _EEprom
+{
+UCHAR EE_MODE1;
+UCHAR EE_SPEED;
+UCHAR xx1;
+UCHAR xx2;
+} EEprom, *PEEprom;
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2 65
+#define EE_DELAY 66
+#define EE_TAG_CMD_NUM 67
+
+/*; EE_MODE1 bits definition*/
+#define PARITY_CHK_ BIT0
+#define SYNC_NEGO_ BIT1
+#define EN_DISCONNECT_ BIT2
+#define SEND_START_ BIT3
+#define TAG_QUEUING_ BIT4
+
+/*; EE_MODE2 bits definition*/
+#define MORE2_DRV BIT0
+#define GREATER_1G BIT1
+#define RST_SCSI_BUS BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK BIT4
+#define LUN_CHECK BIT5
+
+#define ENABLE_CE 1
+#define DISABLE_CE 0
+#define EEPROM_READ 0x80
+
+/*
+;==========================================================
+; AMD 53C974 Registers bit Definition
+;==========================================================
+*/
+/*
+;====================
+; SCSI Register
+;====================
+*/
+
+/*; Command Reg.(+0CH) */
+#define DMA_COMMAND BIT7
+#define NOP_CMD 0
+#define CLEAR_FIFO_CMD 1
+#define RST_DEVICE_CMD 2
+#define RST_SCSI_BUS_CMD 3
+#define INFO_XFER_CMD 0x10
+#define INITIATOR_CMD_CMPLTE 0x11
+#define MSG_ACCEPTED_CMD 0x12
+#define XFER_PAD_BYTE 0x18
+#define SET_ATN_CMD 0x1A
+#define RESET_ATN_CMD 0x1B
+#define SELECT_W_ATN 0x42
+#define SEL_W_ATN_STOP 0x43
+#define EN_SEL_RESEL 0x44
+#define SEL_W_ATN2 0x46
+#define DATA_XFER_CMD INFO_XFER_CMD
+
+
+/*; SCSI Status Reg.(+10H) */
+#define INTERRUPT BIT7
+#define ILLEGAL_OP_ERR BIT6
+#define PARITY_ERR BIT5
+#define COUNT_2_ZERO BIT4
+#define GROUP_CODE_VALID BIT3
+#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0)
+
+/*; Interrupt Status Reg.(+14H) */
+#define SCSI_RESET BIT7
+#define INVALID_CMD BIT6
+#define DISCONNECTED BIT5
+#define SERVICE_REQUEST BIT4
+#define SUCCESSFUL_OP BIT3
+#define RESELECTED BIT2
+#define SEL_ATTENTION BIT1
+#define SELECTED BIT0
+
+/*; Internal State Reg.(+18H) */
+#define SYNC_OFFSET_FLAG BIT3
+#define INTRN_STATE_MASK (BIT2+BIT1+BIT0)
+
+/*; Clock Factor Reg.(+24H) */
+#define CLK_FREQ_40MHZ 0
+#define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0)
+#define CLK_FREQ_30MHZ (BIT2+BIT1)
+#define CLK_FREQ_25MHZ (BIT2+BIT0)
+#define CLK_FREQ_20MHZ BIT2
+#define CLK_FREQ_15MHZ (BIT1+BIT0)
+#define CLK_FREQ_10MHZ BIT1
+
+/*; Control Reg. 1(+20H) */
+#define EXTENDED_TIMING BIT7
+#define DIS_INT_ON_SCSI_RST BIT6
+#define PARITY_ERR_REPO BIT4
+#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0)
+
+/*; Control Reg. 2(+2CH) */
+#define EN_FEATURE BIT6
+#define EN_SCSI2_CMD BIT3
+
+/*; Control Reg. 3(+30H) */
+#define ID_MSG_CHECK BIT7
+#define EN_QTAG_MSG BIT6
+#define EN_GRP2_CMD BIT5
+#define FAST_SCSI BIT4 /* ;10MB/SEC */
+#define FAST_CLK BIT3 /* ;25 - 40 MHZ */
+
+/*; Control Reg. 4(+34H) */
+#define EATER_12NS 0
+#define EATER_25NS BIT7
+#define EATER_35NS BIT6
+#define EATER_0NS (BIT7+BIT6)
+#define NEGATE_REQACKDATA BIT2
+#define NEGATE_REQACK BIT3
+/*
+;====================
+; DMA Register
+;====================
+*/
+/*; DMA Command Reg.(+40H) */
+#define READ_DIRECTION BIT7
+#define WRITE_DIRECTION 0
+#define EN_DMA_INT BIT6
+#define MAP_TO_MDL BIT5
+#define DIAGNOSTIC BIT4
+#define DMA_IDLE_CMD 0
+#define DMA_BLAST_CMD BIT0
+#define DMA_ABORT_CMD BIT1
+#define DMA_START_CMD (BIT1+BIT0)
+
+/*; DMA Status Reg.(+54H) */
+#define PCI_MS_ABORT BIT6
+#define BLAST_COMPLETE BIT5
+#define SCSI_INTERRUPT BIT4
+#define DMA_XFER_DONE BIT3
+#define DMA_XFER_ABORT BIT2
+#define DMA_XFER_ERROR BIT1
+#define POWER_DOWN BIT0
+
+/*
+; DMA SCSI Bus and Ctrl.(+70H)
+;EN_INT_ON_PCI_ABORT
+*/
+
+/*
+;==========================================================
+; SCSI Chip register address offset
+;==========================================================
+*/
+#define CtcReg_Low 0x00
+#define CtcReg_Mid 0x04
+#define ScsiFifo 0x08
+#define ScsiCmd 0x0C
+#define Scsi_Status 0x10
+#define INT_Status 0x14
+#define Sync_Period 0x18
+#define Sync_Offset 0x1C
+#define CtrlReg1 0x20
+#define Clk_Factor 0x24
+#define CtrlReg2 0x2C
+#define CtrlReg3 0x30
+#define CtrlReg4 0x34
+#define CtcReg_High 0x38
+#define DMA_Cmd 0x40
+#define DMA_XferCnt 0x44
+#define DMA_XferAddr 0x48
+#define DMA_Wk_ByteCntr 0x4C
+#define DMA_Wk_AddrCntr 0x50
+#define DMA_Status 0x54
+#define DMA_MDL_Addr 0x58
+#define DMA_Wk_MDL_Cntr 0x5C
+#define DMA_ScsiBusCtrl 0x70
+
+#define StcReg_Low CtcReg_Low
+#define StcReg_Mid CtcReg_Mid
+#define Scsi_Dest_ID Scsi_Status
+#define Scsi_TimeOut INT_Status
+#define Intern_State Sync_Period
+#define Current_Fifo Sync_Offset
+#define StcReg_High CtcReg_High
+
+#define am_target Scsi_Status
+#define am_timeout INT_Status
+#define am_seq_step Sync_Period
+#define am_fifo_count Sync_Offset
+
+
+#define DC390_read8(address) \
+ inb(DC390_ioport + (address)))
+
+#define DC390_read16(address) \
+ inw(DC390_ioport + (address)))
+
+#define DC390_read32(address) \
+ inl(DC390_ioport + (address)))
+
+#define DC390_write8(address,value) \
+ outb((value), DC390_ioport + (address)))
+
+#define DC390_write16(address,value) \
+ outw((value), DC390_ioport + (address)))
+
+#define DC390_write32(address,value) \
+ outl((value), DC390_ioport + (address)))
+
+
+/* Configuration method #1 */
+#define PCI_CFG1_ADDRESS_REG 0xcf8
+#define PCI_CFG1_DATA_REG 0xcfc
+#define PCI_CFG1_ENABLE 0x80000000
+#define PCI_CFG1_TUPPLE(bus, device, function, register) \
+ (PCI_CFG1_ENABLE | (((bus) << 16) & 0xff0000) | \
+ (((device) << 11) & 0xf800) | (((function) << 8) & 0x700)| \
+ (((register) << 2) & 0xfc))
+
+/* Configuration method #2 */
+#define PCI_CFG2_ENABLE_REG 0xcf8
+#define PCI_CFG2_FORWARD_REG 0xcfa
+#define PCI_CFG2_ENABLE 0x0f0
+#define PCI_CFG2_TUPPLE(function) \
+ (PCI_CFG2_ENABLE | (((function) << 1) & 0xe))
+
+
+#endif /* TMSCSIM_H */
else
printk("US14F: interrupt: unexpected interrupt\n");
- if (config.slot ? inb(config.icm_address - 1) : (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+ if (config.slot ? inb(config.icm_address - 1) :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)
printk("Ux4F: multiple commands completed\n");
+#else
+ ;
+#endif
#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
printk("USx4F: interrupt: returning\n");
!strcmp(this_char,"uid") ||
!strcmp(this_char,"gid"))) {
char * vpnt = value;
- unsigned int ivalue;
- ivalue = 0;
- while(*vpnt){
- if(*vpnt < '0' || *vpnt > '9') break;
- ivalue = ivalue * 10 + (*vpnt - '0');
- vpnt++;
- }
+ unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
if (*vpnt) return 0;
switch(*this_char) {
case 'b':
if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
{
- printk("_isofs_bmap: block>= EOF(%d, %d)", block,
+ printk("_isofs_bmap: block>= EOF(%d, %ld)", block,
inode->i_size);
}
return 0;
SMB_INOP(dir)->nused += 1;
- /* We have to link the new inode_info into the doubly linked
- list of inode_infos to make a complete linear search
- possible. */
-
+ /*
+ * We have to link the new inode_info into the doubly linked
+ * list of inode_infos to make a complete linear search possible.
+ */
root = &(SMB_SERVER(dir)->root);
new_inode_info->prev = root;
if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
{
- new_inode_info->next->prev = new_inode_info->prev;
- new_inode_info->prev->next = new_inode_info->next;
- SMB_INOP(dir)->nused -= 1;
-
printk("smb_iget: iget failed!");
+ /*
+ * If we blocked in iget(), another task may have referenced
+ * the info structure ... clean up with smb_free_inode_info.
+ */
+ smb_free_inode_info(new_inode_info);
return NULL;
}
+
return inode;
}
{
goto in_tree;
}
+
+ if (new_inode_info == NULL)
+ {
+ iput(dir);
+ return -ENOMEM;
+ }
new_inode_info->finfo = finfo;
DPRINTK("attr: %x\n", finfo.attr);
if ((*result = smb_iget(dir, new_inode_info)) == NULL)
{
- smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
iput(dir);
return -EACCES;
}
if ((*result = smb_iget(dir, new_inode_info)) == NULL)
{
- smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
iput(dir);
return error;
}
{
/* Ok, now we're in trouble. The inode info is not
there. What should we do now??? */
- printk("smb_read_inode: inode info not found\n");
+ printk("smb_read_inode: inode %ld info not found\n",
+ inode->i_ino);
return;
}
inode_info->state = SMB_INODE_VALID;
static void
smb_put_inode(struct inode *inode)
{
- struct smb_dirent *finfo = SMB_FINFO(inode);
struct smb_server *server = SMB_SERVER(inode);
struct smb_inode_info *info = SMB_INOP(inode);
+ struct smb_dirent *finfo;
+ __u32 mtime = inode->i_mtime;
+
+ if (inode->i_count > 1) {
+ printk("smb_put_inode: in use device %s, inode %ld count=%d\n",
+ kdevname(inode->i_dev), inode->i_ino, inode->i_count);
+ return;
+ }
if (S_ISDIR(inode->i_mode))
{
smb_invalid_dir_cache(inode->i_ino);
}
- if (finfo->opened != 0)
- {
- if (smb_proc_close(server, finfo->fileid, inode->i_mtime))
+ clear_inode(inode);
+
+ /*
+ * We don't want the inode to be reused as free if we block here,
+ * so temporarily increment i_count.
+ */
+ inode->i_count++;
+ if (info) {
+ finfo = &info->finfo;
+ if (finfo->opened != 0)
{
- /* We can't do anything but complain. */
- DPRINTK("smb_put_inode: could not close\n");
+ if (smb_proc_close(server, finfo->fileid, mtime))
+ {
+ /* We can't do anything but complain. */
+ printk("smb_put_inode: could not close\n");
+ }
}
- }
- smb_free_inode_info(info);
- clear_inode(inode);
+ smb_free_inode_info(info);
+ } else
+ printk("smb_put_inode: no inode info??\n");
+
+ inode->i_count--;
}
static void
if (server->packet != NULL)
{
smb_vfree(server->packet);
+ server->packet = NULL;
server->packet_size = 0;
}
server->packet = smb_vmalloc(max_xmit);
/* Now make a new packet with the correct size. */
smb_vfree(server->packet);
+ server->packet = NULL;
server->packet = smb_vmalloc(server->max_xmit);
if (server->packet == NULL)
DPRINTK("smb_receive: Increase packet size from %d to %d\n",
server->packet_size, len + 4);
smb_vfree(server->packet);
+ server->packet = NULL;
+
server->packet_size = 0;
server->packet = smb_vmalloc(len + 4);
if (server->packet == NULL)
* SHMMAX <= (PAGE_SIZE << _SHM_IDX_BITS).
*/
-#define SHMMAX 0x1000000 /* max shared seg size (bytes) */
+#define SHMMAX 0x2000000 /* max shared seg size (bytes) */
#define SHMMIN 1 /* really PAGE_SIZE */ /* min shared seg size (bytes) */
#define SHMMNI (1<<_SHM_ID_BITS) /* max num of segs system wide */
#define SHMALL /* max shm system wide (pages) */ \
*/
#define fsuser() (current->fsuid == 0)
+/*
+ * Display an IP address in readable format.
+ */
+
+#define NIPQUAD(addr) \
+ (((addr) >> 0) & 0xff), \
+ (((addr) >> 8) & 0xff), \
+ (((addr) >> 16) & 0xff), \
+ (((addr) >> 24) & 0xff)
+
#endif /* __KERNEL__ */
#define SI_LOAD_SHIFT 16
PROC_SCSI_QLOGICISP,
PROC_SCSI_SEAGATE,
PROC_SCSI_T128,
+ PROC_SCSI_DC390T,
PROC_SCSI_NCR53C7xx,
PROC_SCSI_NCR53C8XX,
PROC_SCSI_ULTRASTOR,
extern void aic7xxx_setup(char *str, int *ints);
extern void AM53C974_setup(char *str, int *ints);
extern void BusLogic_Setup(char *str, int *ints);
+extern void ncr53c8xx_setup(char *str, int *ints);
extern void eata2x_setup(char *str, int *ints);
extern void u14_34f_setup(char *str, int *ints);
extern void fdomain_setup(char *str, int *ints);
#ifdef CONFIG_SCSI_BUSLOGIC
{ "BusLogic=", BusLogic_Setup},
#endif
+#ifdef CONFIG_SCSI_NCR53C8XX
+ { "ncr53c8xx=", ncr53c8xx_setup},
+#endif
#ifdef CONFIG_SCSI_EATA
{ "eata=", eata2x_setup},
#endif
#ifdef CONFIG_ROOT_NFS
{KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN,
0644, NULL, &proc_dostring, &sysctl_string },
- {KERN_NFSRNAME, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
+ {KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
0644, NULL, &proc_dostring, &sysctl_string },
#endif
#ifdef CONFIG_BINFMT_JAVA
if (tip != dev->pa_addr && net_alias_has(skb->dev))
{
/*
- * net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
+ * net_alias_dev_rx32 returns main dev if it fails to found other.
*/
- dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+ dev = net_alias_dev_rx32(dev, AF_INET, sip, tip);
if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
{
break;
}
if(icmph->code>NR_ICMP_UNREACH) /* Invalid type */
- return;
+ goto flush_it;
}
/*
/*
* Try to select closest <src,dst> alias device, if any.
- * net_alias_dev_rcv_sel32 returns main device if it
+ * net_alias_dev_rx32 returns main device if it
* fails to found other.
*/
#ifdef CONFIG_NET_ALIAS
if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) {
- skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
+ skb->dev = dev = net_alias_dev_rx32(skb->dev, AF_INET, iph->saddr, iph->daddr);
}
#endif
/* Only let this warning get printed once a minute. */
if (jiffies - warning_time > HZ*60) {
warning_time = jiffies;
- printk(KERN_INFO "Warning: possible SYN flooding on port %d. Sending cookies.\n", ntohs(th->dest));
+ printk(KERN_INFO "Warning: possible SYN flood from %d.%d.%d.%d on %d.%d.%d.%d:%d. Sending cookies.\n",
+ NIPQUAD(saddr), NIPQUAD(daddr), ntohs(th->dest));
}
#ifdef CONFIG_RST_COOKIES
tcp_send_synack_probe(daddr, saddr, th, &tcp_prot,
// * Only resolves operands of jump and call instructions.
#include <fstream.h>
+#include <strstream.h>
#include <iomanip.h>
#include <stdio.h>
#include <string.h>
char buf[1024];
int lines = 0;
+ int eip_seen = 0;
+ long offset;
while (fgets(buf, sizeof(buf), objdump_FILE)) {
+ if (eip_seen && buf[4] == ':') {
+ // assume objdump from binutils 2.8..., reformat to old style
+ offset = strtol(buf, 0, 16);
+ char newbuf[sizeof(buf)];
+ memset(newbuf, '\0', sizeof(newbuf));
+ ostrstream ost(newbuf, sizeof(newbuf));
+ ost.width(8);
+ ost << offset;
+ ost << " <_EIP+" << offset << ">: " << &buf[6] << ends;
+ strcpy(buf, newbuf);
+ }
if (!strnequ(&buf[9], "<_EIP", 5))
continue;
+ eip_seen = 1;
if (strstr(buf, " is out of bounds"))
break;
lines++;
cout << buf;
continue;
}
- long offset = strtol(buf, 0, 16);
- char* bp_0 = strchr(buf, '>') + 2;
+ offset = strtol(buf, 0, 16);
+ char* bp_0 = strchr(buf, '>');
KSym* ksym = find(eip_addr + offset);
+ if (bp_0)
+ bp_0 += 2;
+ else
+ bp_0 = strchr(buf, ':');
if (ksym)
cout << *ksym << ' ';
- char* bp = bp_0;
+ char *bp_1 = strstr(bp_0, "\t"); // objdump from binutils 2.8...
+ if (bp_1)
+ ++bp_1;
+ else
+ bp_1 = bp_0;
+ char *bp = bp_1;
while (!isspace(*bp))
bp++;
while (isspace(*bp))
bp++;
- if (*bp != '0') {
+ if (!isxdigit(*bp)) {
cout << bp_0;
- } else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
+ } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn
long rel_addr = strtol(bp, 0, 16);
ksym = find(eip_addr + rel_addr);
if (ksym) {