patch-2.1.49 linux/drivers/scsi/ncr53c8xx.c

Next file: linux/drivers/scsi/ncr53c8xx.h
Previous file: linux/drivers/scsi/aic7xxx_proc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
@@ -36,11 +36,35 @@
 **  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.
+**
 *******************************************************************************
 */
 
 /*
-**	9 May 1997, version 2.1b
+**	26 July 1997, version 2.4
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -51,13 +75,13 @@
 **	    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		(Narrow fast 20, BIOS required)
-**		53C875		(Wide fast 20 with on board rom BIOS)
-**		53C895		(Ultra2 80 MB/s with on board rom BIOS)
+**		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 and above only)
@@ -67,10 +91,6 @@
 
 #define SCSI_NCR_DEBUG_FLAGS	(0)		
 
-#define NCR_DATE "pl24 96/12/14"
-
-#define NCR_VERSION	(2)
-
 #define NCR_GETCC_WITHMSG
 
 /*==========================================================
@@ -186,7 +206,14 @@
 #else
 #define MAX_LUN    (1)
 #endif
+
+/*
+**    Asynchronous pre-scaler (ns). Shall be 40
+*/
  
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
 
 /*
 **    The maximum number of jobs scheduled for starting.
@@ -279,7 +306,9 @@
 **	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;
@@ -292,7 +321,9 @@
 	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)
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
@@ -472,7 +503,8 @@
 	unsigned ultra_scsi	: 2;
 	unsigned force_sync_nego: 1;
 	unsigned reverse_probe: 1;
-	unsigned pci_fix_up: 2;
+	unsigned pci_fix_up: 4;
+	u_char	use_nvram;
 	u_char	verbose;
 	u_char	default_tags;
 	u_short	default_sync;
@@ -511,7 +543,149 @@
 
 static void ncr53c8xx_timeout(unsigned long np);
 
-#define bootverbose (driver_setup.verbose)
+#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;
 
 /*==========================================================
 **
@@ -800,6 +974,7 @@
 
 #define	UF_TRACE	(0x01)
 #define	UF_NODISC	(0x02)
+#define	UF_NOSCAN	(0x04)
 
 /*---------------------------------------
 **
@@ -897,6 +1072,14 @@
 	ccb_p   hold_cp;
 
 	/*
+	**	pointer to ccb used for negotiating.
+	**	Avoid to start a nego for all queued commands 
+	**	when tagged command queuing is enabled.
+	*/
+
+	ccb_p   nego_cp;
+
+	/*
 	**	statistical data
 	*/
 
@@ -906,13 +1089,15 @@
 	/*
 	**	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;
 
@@ -1341,13 +1526,13 @@
 	int    unit;			/* Unit number                       */
 	char   chip_name[8];		/* Chip name                         */
 	char   inst_name[16];		/* Instance name                     */
-	u_int  features;		/* Chip features map		     */
 	struct timer_list timer;	/* Timer link header                 */
 	int	ncr_cache;		/* Cache test variable               */
 	Scsi_Cmnd *waiting_list;	/* Waiting list header for commands  */
 					/* that we can't put into the squeue */
 	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;
@@ -1365,6 +1550,8 @@
 	*/
 	u_short	device_id;
 	u_char	revision_id;
+
+	u_char	sv_scntl0;
 	u_char	sv_scntl3;
 	u_char	sv_dmode;
 	u_char	sv_dcntl;
@@ -1375,6 +1562,8 @@
 	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;
@@ -1382,9 +1571,7 @@
 	u_char	rv_ctest5;
 	u_char	rv_stest2;
 
-	u_char	maxburst;
 	u_char	scsi_mode;
-	u_char	multiplier;
 
 	/*-----------------------------------------------
 	**	Scripts ..
@@ -1443,21 +1630,21 @@
 	u_char	  myaddr;
 
 	/*
-	**	timing parameters
+	**	Max dwords burst supported by the adapter.
 	*/
-	u_char		ns_sync;
-	u_char		rv_scntl3;
+	u_char		maxburst;	/* log base 2 of dwords burst	*/
 
 	/*
-	**	controller chip dependent maximal offset.
+	**	timing parameters
 	*/
-	u_char		maxoffs;
+	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		*/
 
-	/*
-	**	controller scsi clock frequency and available divisors
-	*/
-	u_long		clock_khz;
-	int		clock_divn;
 
 	/*-----------------------------------------------
 	**	Link to the generic SCSI driver
@@ -1690,11 +1877,11 @@
 				(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	int	ncr_getsync	(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p);
+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);
@@ -1705,9 +1892,7 @@
 static	void	ncr_usercmd	(ncb_p np);
 #endif
 
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, 
-		       ncr_chip *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 *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
@@ -1718,6 +1903,11 @@
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
 #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
+
 /*==========================================================
 **
 **
@@ -3447,7 +3637,7 @@
 			**	If PREFETCH feature not enabled, remove 
 			**	the NO FLUSH bit if present.
 			*/
-			if ((opcode & SCR_NO_FLUSH) && !(np->features & _F_PFEN)) {
+			if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
 				dst[-1] = (opcode & ~SCR_NO_FLUSH);
 				++opchanged;
 			}
@@ -3570,7 +3760,7 @@
 */
 
 #define PRINT_LUN(np, target, lun) \
-printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
 static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
@@ -3579,6 +3769,20 @@
 	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 
@@ -3622,16 +3826,79 @@
 	}
 }
 
+#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)
+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;
@@ -3643,35 +3910,73 @@
 	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 & _F_QUAD)
+	if	(np->features & FE_QUAD)
 		np->multiplier	= 4;
-	else if	(np->features & _F_DBLR)
+	else if	(np->features & FE_DBLR)
 		np->multiplier	= 2;
 	else
 		np->multiplier	= 1;
 
-	np->clock_khz	= (np->features & _F_CLK80)? 80000 : 40000;
+	np->clock_khz	= (np->features & FE_CLK80)? 80000 : 40000;
 	np->clock_khz	*= np->multiplier;
 
 	if (np->clock_khz != 40000)
 		ncr_getclock(np, np->multiplier);
 
-	if	(np->clock_khz <=  25000)	np->rv_scntl3 = 0x01;
-	else if	(np->clock_khz <=  37500)	np->rv_scntl3 = 0x02;
-	else if	(np->clock_khz <=  50000)	np->rv_scntl3 = 0x03;
-	else if	(np->clock_khz <=  75000)	np->rv_scntl3 = 0x04;
-	else if	(np->clock_khz <= 100000)	np->rv_scntl3 = 0x05;
-	else if	(np->clock_khz <= 150000)	np->rv_scntl3 = 0x06;
-	else					np->rv_scntl3 = 0x07;
+	/*
+	 * 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 & _F_RAM) {
+	if (np->features & FE_RAM) {
 		OUTONB(nc_ctest2, 0x8);
 		np->paddr2 = INL(nc_scr0);
 		OUTOFFB(nc_ctest2, 0x8);
@@ -3681,6 +3986,7 @@
 	**	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;
@@ -3688,12 +3994,6 @@
 	np->rv_ctest5	= np->sv_ctest5;
 	burst_max	= burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
 #else
-	np->rv_dmode	= 0;
-	np->rv_dcntl	= 0;
-	np->rv_ctest3	= 0;
-	np->rv_ctest4	= 0;
-	np->rv_ctest5	= 0;
-	np->rv_stest2	= 0;
 
 	/*
 	**	Select burst length (dwords)
@@ -3709,19 +4009,19 @@
 	/*
 	**	Select all supported special features
 	*/
-	if (np->features & _F_ERL)
+	if (np->features & FE_ERL)
 		np->rv_dmode	|= ERL;		/* Enable Read Line */
-	if (np->features & _F_BOF)
+	if (np->features & FE_BOF)
 		np->rv_dmode	|= BOF;		/* Burst Opcode Fetch */
-	if (np->features & _F_ERMP)
+	if (np->features & FE_ERMP)
 		np->rv_dmode	|= ERMP;	/* Enable Read Multiple */
-	if (np->features & _F_PFEN)
+	if (np->features & FE_PFEN)
 		np->rv_dcntl	|= PFEN;	/* Prefetch Enable */
-	if (np->features & _F_CLSE)
+	if (np->features & FE_CLSE)
 		np->rv_dcntl	|= CLSE;	/* Cache Line Size Enable */
-	if (np->features & _F_WRIE)
+	if (np->features & FE_WRIE)
 		np->rv_ctest3	|= WRIE;	/* Write and Invalidate */
-	if (np->features & _F_DFS)
+	if (np->features & FE_DFS)
 		np->rv_ctest5	|= DFS;		/* Dma Fifo Size */
 
 	/*
@@ -3729,6 +4029,34 @@
 	*/
 	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 */
 
@@ -3738,21 +4066,31 @@
 	ncr_init_burst(np, burst_max);
 
 	/*
-	**	Set differential mode.
-	*/
-	switch(driver_setup.diff_support) {
-	case 3:
-		if (INB(nc_gpreg) & 0x08)
+	**	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;
+		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.
@@ -3762,36 +4100,167 @@
 		np->rv_dcntl	|= IRQM;
 		break;
 	case 1:
-		np->rv_stest2	|= (np->sv_dcntl & IRQM);
+		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 value of SCNTL3 = %02x, final = %02x\n",
-			ncr_name(np), np->sv_scntl3, np->rv_scntl3);
-		printf ("%s: initial value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
-			ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
-		printf ("%s: final value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
-			ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
-		if (np->rv_stest2 & 0x20)
-			printf ("%s: DIFF mode set\n", ncr_name(np));
+		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 ("%s: on-board RAM at 0x%lx\n", ncr_name(np), np->paddr2);
-
-	if (bootverbose && np->ns_sync < 25)
-		printf ("%s: Ultra%s SCSI support enabled\n", ncr_name(np),
-			np->ns_sync < 12 ? "-2": "");
+		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.
 **
@@ -3803,18 +4272,18 @@
 */
 
 __initfunc(
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, 
-		       ncr_chip *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)
 )
 {
         struct host_data *host_data;
 	ncb_p np;
         struct Scsi_Host *instance = 0;
 	u_long flags = 0;
+	ncr_nvram *nvram = device->nvram;
 
-printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
-	unit, chip->name, chip->revision_id, 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
@@ -3825,7 +4294,6 @@
 	/*
 	**	Initialize structure.
 	*/
-	instance->irq = irq;
 	host_data = (struct host_data *) instance->hostdata;
 
 	/*
@@ -3842,18 +4310,16 @@
 	/*
 	**	Store input informations in the host data structure.
 	*/
-	strncpy(np->chip_name, chip->name, sizeof(np->chip_name) - 1);
+	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	= chip->device_id;
-	np->revision_id	= chip->revision_id;
-	np->features	= chip->features;
-	np->maxwide	= (np->features & _F_WIDE)? 1 : 0;
-	np->ns_sync	= 25;
-	np->clock_khz	= 40000;
-	np->clock_divn	= chip->nr_divisor;
-	np->maxoffs	= chip->offset_max;
-	np->maxburst	= chip->burst_max;
+	np->device_id	= device->chip.device_id;
+	np->revision_id	= device->chip.revision_id;
+	np->features	= device->chip.features;
+	np->clock_divn	= device->chip.nr_divisor;
+	np->maxoffs	= device->chip.offset_max;
+	np->maxburst	= device->chip.burst_max;
 
 	np->script0  =
 	(struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
@@ -3872,10 +4338,10 @@
 	**	virtual and physical memory.
 	*/
 
-	np->paddr = base;
+	np->paddr = device->slot.base;
 
 #ifndef NCR_IOMAPPED
-	np->vaddr = remap_pci_mem((u_long) base, (u_long) 128);
+	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));
 		goto attach_error;
@@ -3898,13 +4364,35 @@
 	**	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);
+	(void)ncr_prepare_setting(np, nvram);
 
 #ifndef NCR_IOMAPPED
 	if (np->paddr2 && sizeof(struct script) <= 4096) {
@@ -3930,7 +4418,8 @@
 #ifndef NCR_IOMAPPED
 	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)
@@ -3956,8 +4445,7 @@
 	**    Patch the script for LED support.
 	*/
 
-	if (driver_setup.led_pin & (~np->sv_gpcntl) & 0x01) {
-		np->features |= _F_LED0;
+	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);
@@ -3971,19 +4459,9 @@
 	np->jump_tcb.l_paddr	= NCB_SCRIPTH_PHYS (np, abort);
 
 	/*
-	**  Get SCSI addr of host adapter (set by bios?).
-	*/
-
-	np->myaddr = INB(nc_scid) & 0x07;
-	if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
-
-	/*
 	**	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   );
@@ -4001,31 +4479,24 @@
 	**	Install the interrupt handler.
 	*/
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#   ifdef SCSI_NCR_SHARE_IRQ
+#ifdef SCSI_NCR_SHARE_IRQ
 	if (bootverbose > 1)
 		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
+		        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;
-
-	/*
-	**	Not allow disconnections for all targets if asked by config
-	*/
-
-	if (!driver_setup.disconnection) {
-		int i;
-		for (i = 0 ; i < MAX_TARGET ; i++)
-			np->target[i].usrflag |= UF_NODISC;
-	}
+	np->irq = device->slot.irq;
 
 	/*
 	**	After SCSI devices have been opened, we cannot
@@ -4152,6 +4623,19 @@
 		return(DID_BAD_TARGET);
         }
 
+	/*---------------------------------------------
+	**
+	**	Complete the 1st TEST UNIT READY command
+	**	with error condition if the device is 
+	**	flagged NOSCAN, in order to speed up 
+	**	the boot.
+	**
+	**---------------------------------------------
+	*/
+	if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+		tp->usrflag &= ~UF_NOSCAN;
+		return DID_BAD_TARGET;
+	}
 
 	if (DEBUG_FLAGS & DEBUG_TINY) {
 		PRINT_ADDR(cmd);
@@ -4177,15 +4661,13 @@
 
 	/*---------------------------------------------------
 	**
-	**	Enable tagged queue if asked by user
+	**	Enable tagged queue if asked by scsi ioctl
 	**
 	**----------------------------------------------------
 	*/
-	if (driver_setup.default_tags < SCSI_NCR_MAX_TAGS) {
-	 	if (cmd->device && cmd->device->tagged_queue &&
-		    (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
-			ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
-		}
+	if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+		tp->usrtags = SCSI_NCR_MAX_TAGS;
+		ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
 	}
 
 	/*---------------------------------------------------
@@ -4232,7 +4714,8 @@
 
 	nego = 0;
 
-	if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+	if (cmd->lun == 0 && !tp->nego_cp && 
+	    (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
 		/*
 		**	negotiate wide transfers ?
 		*/
@@ -4262,6 +4745,15 @@
 				printf ("asynchronous.\n");
 			};
 		};
+
+		/*
+		**	remember nego is pending for the target.
+		**	Avoid to start a nego for all queued commands 
+		**	when tagged command queuing is enabled.
+		*/
+
+		if (nego)
+			tp->nego_cp = cp;
 	};
 
 	/*---------------------------------------------------
@@ -4973,6 +5465,15 @@
 	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.
 	*/
 
@@ -5080,12 +5581,12 @@
 		**	If tags was reduced due to queue full,
 		**	increase tags if 100 good status received.
 		*/
-		if (tp->usrtags < tp->maxtags) {
+		if (tp->numtags < tp->maxtags) {
 			++tp->num_good;
 			if (tp->num_good >= 100) {
 				tp->num_good = 0;
-				++tp->usrtags;
-				if (tp->usrtags == 1) {
+				++tp->numtags;
+				if (tp->numtags == 1) {
 					PRINT_ADDR(cmd);
 					printf("tagged command queueing resumed\n");
 				}
@@ -5128,10 +5629,10 @@
 		**  Suspend tagged queuing and start good status counter.
 		**  Announce changes to the generic driver.
 		*/
-		if (tp->usrtags) {
+		if (tp->numtags) {
 			PRINT_ADDR(cmd);
 			printf("QUEUE FULL! suspending tagged command queueing\n");
-			tp->usrtags	= 0;
+			tp->numtags	= 0;
 			tp->num_good	= 0;
 			if (lp) {
 				ncr_settags (tp, lp);
@@ -5278,8 +5779,6 @@
 void ncr_init (ncb_p np, char * msg, u_long code)
 {
 	int	i;
-	u_long	usrsync;
-	u_char	usrwide;
 
 	/*
 	**	Reset chip.
@@ -5292,7 +5791,7 @@
 	**	Message.
 	*/
 
-	if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+	if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
 
 	/*
 	**	Clear Start Queue
@@ -5317,11 +5816,8 @@
 	*/
 
 	OUTB (nc_istat,  0x00   );	/*  Remove Reset, abort */
-	if (driver_setup.scsi_parity)
-		OUTB (nc_scntl0, 0xca);	/*  full arb., ena parity, par->ATN  */
-	else
-		OUTB (nc_scntl0, 0xc0);	/*  full arb., (no parity) */
-
+	OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+					/*  full arb., ena parity, par->ATN  */
 	OUTB (nc_scntl1, 0x00);		/*  odd parity, and remove CRST!! */
 
 	ncr_selectclock(np, np->rv_scntl3);	/* Select SCSI clock */
@@ -5341,30 +5837,6 @@
 	OUTB (nc_stime0, 0x0d	);		/* HTH disabled  STO 0.4 sec. */
 
 	/*
-	**	Reinitialize usrsync.
-	**	Have to renegotiate synch mode.
-	*/
-
-	usrsync = driver_setup.default_sync;
-	if (usrsync != 255) {
-		if (4 * usrsync <= 11 * 50) {
-			if (usrsync < np->ns_sync) {
-				usrsync = np->ns_sync;
-			}
-		}
-		else
-			usrsync = 255;
-	};
-
-	/*
-	**	Reinitialize usrwide.
-	**	Have to renegotiate wide mode.
-	*/
-
-	usrwide = driver_setup.max_wide;
-	if (usrwide > np->maxwide) usrwide=np->maxwide;
-
-	/*
 	**	Disable disconnects.
 	*/
 
@@ -5374,7 +5846,7 @@
 	**    Enable GPIO0 pin for writing if LED support.
 	*/
 
-	if (np->features & _F_LED0) {
+	if (np->features & FE_LED0) {
 		OUTOFFB (nc_gpcntl, 0x01);
 	}
 
@@ -5397,13 +5869,15 @@
 	/*
 	**	For 895/6 enable SBMC interrupt and save current SCSI bus mode.
 	*/
-	if (np->features & _F_ULTRA2) {
+	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.
 	*/
 
@@ -5413,8 +5887,18 @@
 		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);
 	}
@@ -5462,14 +5946,14 @@
 	**	our limit ..
 	*/
 
-	if (minsync < np->ns_sync)
-		minsync = np->ns_sync;
+	if (minsync < np->minsync)
+		minsync = np->minsync;
 
 	/*
 	**	divider limit
 	*/
 
-	if (minsync > (11*50)/4)
+	if (minsync > np->maxsync)
 		minsync = 255;
 
 	tp->minsync = minsync;
@@ -5489,106 +5973,123 @@
 
 /*==========================================================
 **
-**	Get clock factor and sync divisor.
+**	Get clock factor and sync divisor for a given 
+**	synchronous factor period.
+**	Returns the clock factor (in sxfer) and scntl3 
+**	synchronous divisor field.
 **
 **==========================================================
 */
 
-#define SCSI_NCR_USE_ALL_DIVISORS
-
-/*
-**	NCR chip clock divisor table.
-**	Multiplied by 2x2000000 in order to avoid useless operations in 
-**	the code that gets clock factor and sync divisor from sync factor.
-*/
-#define _2M 2000000
-static u_long ncr_div2_2M[] = {2*_2M, 3*_2M, 4*_2M, 6*_2M, 8*_2M, 12*_2M, 16*_2M};
-
-/*
-**	Get clock factor and sync divisor for a given sync factor period.
-**	Returns the clock factor, scntl3 and resulting period.
-*/
-static int ncr_getsync(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p)
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
 {
-	u_long	clk	= np->clock_khz;	/* Clock in kHz		*/
-	int	idiv	= np->clock_divn;	/* # divisors supported	*/
-	u_long	fak, per, per_clk;
+	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 nano-seconds
+	**	Compute the synchronous period in tenths of nano-seconds
 	*/
-	if	(fac <= 10)	per = 25;
-	else if	(fac == 11)	per = 30;
-	else if	(fac == 12)	per = 50;
-	else			per = 4 * fac;
+	if	(sfac <= 10)	per = 250;
+	else if	(sfac == 11)	per = 303;
+	else if	(sfac == 12)	per = 500;
+	else			per = 40 * sfac;
 
 	/*
-	**	Find the greatest divisor that allows an input speed 
-	**	faster than the period.
+	**	Look for the greatest clock divisor that allows an 
+	**	input speed faster than the period.
 	*/
-	per_clk = per * clk;
-	while (--idiv >= 0) {
-#ifndef SCSI_NCR_USE_ALL_DIVISORS
-		if (idiv & 1) continue;
-#endif
-		if (ncr_div2_2M[idiv] <= per_clk) break;
-	}
-	if (idiv < 0) idiv = 0;		/* Should never happen */
+	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 = (4 * per_clk - 1) / ncr_div2_2M[idiv] + 1;
-	per = (fak * ncr_div2_2M[idiv]) / (4 * clk);
+	fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0	/* This optimization does not seem very usefull */
+
+	per = (fak * div_10M[div]) / clk;
 
-#ifdef SCSI_NCR_USE_ALL_DIVISORS
 	/*
-	**	Try the next divisor and choose the one that give 
-	**	the fastest output speed.
+	**	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 (idiv >= 1 && fak < 8) {
+	if (div >= 1 && fak < 8) {
 		u_long fak2, per2;
-		fak2 = (4 * per_clk - 1) / ncr_div2_2M[idiv-1] + 1;
-		per2 = (fak2 * ncr_div2_2M[idiv-1]) / (4 * clk);
+		fak2 = (kpc - 1) / div_10M[div-1] + 1;
+		per2 = (fak2 * div_10M[div-1]) / clk;
 		if (per2 < per && fak2 <= 8) {
 			fak = fak2;
 			per = per2;
-			--idiv;
+			--div;
 		}
 	}
 #endif
-	if (fak < 4) fak = 4;		/* Should never happen */
+
+	if (fak < 4) fak = 4;	/* Should never happen, too bad ... */
 
 	/*
 	**	Compute and return sync parameters for the ncr
 	*/
 	*fakp		= fak - 4;
-	*scntl3p	= ((idiv+1) << 4) + (fac < 25 ? ULTRA : 0);
-
-#ifdef DEBUG_NCR53C8XX
-printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
-#endif
-
-	return per;
+	*scntl3p	= ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
 }
 
+
 /*==========================================================
 **
-**	Switch sync mode for current job and it's target
+**	Set actual values, sync status and patch all ccbs of 
+**	a target according to new sync/wide agreement.
 **
 **==========================================================
 */
 
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
 {
-	Scsi_Cmnd *cmd;
-	tcb_p tp;
-	u_char target = INB (nc_ctest0) & 0x0f;
-	u_char idiv;
-
-	assert (cp);
-	if (!cp) return;
+	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 scntl3, u_char sxfer)
+{
+	Scsi_Cmnd *cmd;
+	tcb_p tp;
+	u_char target = INB (nc_ctest0) & 0x0f;
+	u_char idiv;
+
+	assert (cp);
+	if (!cp) return;
 
 	cmd = cp->cmd;
 	assert (cmd);
@@ -5603,10 +6104,12 @@
 
 	/*
 	**	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)*ncr_div2_2M[idiv-1])/(4*np->clock_khz);
+		tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
 	else
 		tp->period = 0xffff;
 
@@ -5622,66 +6125,53 @@
 	*/
 	PRINT_ADDR(cmd);
 	if (sxfer & 0x01f) {
-		unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0);
+		unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
 		unsigned mb10 = (f10 + tp->period/2) / tp->period;
-		char *msg;
+		char *scsi;
 
 		/*
 		**  Disable extended Sreq/Sack filtering
 		*/
-		if (tp->period <= 200) OUTOFFB (nc_stest2, EXT);
+		if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
 
 		/*
 		**	Bells and whistles   ;-)
 		*/
-		msg = "";
-		if (tp->widedone > 1) {
-			if	(tp->period < 50)  msg = "ULTRA-2 WIDE SCSI ";
-			else if	(tp->period < 100) msg = "ULTRA WIDE SCSI ";
-			else if	(tp->period < 200) msg = "FAST WIDE SCSI-2 ";
-		}
-		else {
-			if	(tp->period < 50)  msg = "ULTRA-2 SCSI ";
-			else if	(tp->period < 100) msg = "ULTRA SCSI ";
-			else if	(tp->period < 200) msg = "FAST SCSI-2 ";
-		}
-
-		printf ("%s%d.%d MB/s (%d ns, offset %d)\n", msg,
-			mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f);
-	} else  printf ("asynchronous.\n");
+		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 %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;
-	OUTB (nc_scntl3, scntl3);
-	np->wide_st = scntl3;
-
-	/*
 	**	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;
-		cp->wide_status = scntl3;
-	};
+	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) & 0x0f;
 	tcb_p tp;
 	u_char	scntl3;
+	u_char	sxfer;
 
 	assert (cp);
 	if (!cp) return;
@@ -5694,7 +6184,14 @@
 	tp = &np->target[target];
 	tp->widedone  =  wide+1;
 	scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
-	if (tp->wval == scntl3) return;
+
+	sxfer = ack ? 0 : tp->sval;
+
+	/*
+	**	 Stop there if sync/wide parameters are unchanged
+	*/
+	if (tp->sval == sxfer && tp->wval == scntl3) return;
+	tp->sval = sxfer;
 	tp->wval = scntl3;
 
 	/*
@@ -5710,18 +6207,9 @@
 
 	/*
 	**	set actual value and sync_status
-	*/
-	OUTB (nc_scntl3, scntl3);
-	np->wide_st = scntl3;
-
-	/*
 	**	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);
 }
 
 /*==========================================================
@@ -5731,11 +6219,13 @@
 **==========================================================
 */
 
-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;
-	tp->maxtags = usrtags;
+	if (numtags > tp->usrtags)
+		numtags = tp->usrtags;
+	tp->numtags = numtags;
+	tp->maxtags = numtags;
 
 	for (l=0; l<MAX_LUN; l++) {
 		lcb_p lp;
@@ -5748,11 +6238,11 @@
 		wastags = lp->usetags;
 		ncr_settags (tp, lp);
 
-		if (usrtags > 1 && lp->reqccbs > 1) {
+		if (numtags > 1 && lp->reqccbs > 1) {
 			PRINT_LUN(np, tp - np->target, l);
-			printf("using tagged command queueing, up to %ld cmds/lun\n", usrtags);
+			printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
 		}
-		else if (usrtags <= 1 && wastags) {
+		else if (numtags <= 1 && wastags) {
 			PRINT_LUN(np, tp - np->target, l);
 			printf("disabling tagged command queueing\n");
 		}
@@ -5773,8 +6263,8 @@
 	*/
 	if ((  tp->inqdata[2] & 0x7) >= 2 &&
 	    (  tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
-		&& tp->usrtags > 1) {
-		reqtags = tp->usrtags;
+		&& tp->numtags > 1) {
+		reqtags = tp->numtags;
 		if (lp->actlink <= 1)
 			lp->usetags=reqtags;
 	} else {
@@ -5830,6 +6320,7 @@
 			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);
 		};
 		break;
@@ -6467,11 +6958,13 @@
 **
 **==========================================================
 **
-**	I'm not quite sure of what is to be done in such a
-**	situation.
-**	For now,
-**	Reset the bus if some devices use too fast sync transfers.
-**	Otherwise, just try to renegotiate sync with targets. 
+**	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.
+**	 
 **
 **----------------------------------------------------------
 */
@@ -6479,32 +6972,14 @@
 static int ncr_int_sbmc (ncb_p np)
 {
 	u_char scsi_mode = INB (nc_stest4) & SMODE;
-	int i;
-	int oversync;
-
-	printf("%s: SCSI bus mode change from %x to %x\n", ncr_name(np),
-		np->scsi_mode, scsi_mode);
 
-	if (scsi_mode == np->scsi_mode)
-		return 0;
+	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;
-	oversync = 0;
-	for (i = 0; i < MAX_TARGET; i++) {
-		tcb_p tp = &np->target[i];
-
-		if (np->ns_sync < 12 && tp->maxoffs && tp->usrsync < 12) {
-			if (scsi_mode != SMODE_SE)
-				ncr_negotiate(np, tp);
-			else
-				++oversync;
-		}
-	}
-
-	if (oversync)
-		ncr_start_reset(np, 2);
+	ncr_start_reset(np, 2);
 
-	return oversync;
+	return 1;
 }
 
 /*==========================================================
@@ -6562,10 +7037,10 @@
 
 	/*
 	**	Take into account dma fifo and various buffers and latches,
-	**	only if the interrupted phase was DATA OUT.
+	**	only if the interrupted phase is an OUTPUT phase.
 	*/
 
-	if ((cmd & 7) == 0) {
+	if ((cmd & 1) == 0) {
 		u_char	ctest5, ss0, ss2;
 		u_short	delta;
 
@@ -7011,7 +7486,7 @@
 			break;
 
 		case NS_WIDE:
-			ncr_setwide (np, cp, 0);
+			ncr_setwide (np, cp, 0, 0);
 			break;
 
 		};
@@ -7054,8 +7529,8 @@
 		**	check values against driver limits.
 		*/
 
-		if (per < np->ns_sync)
-			{chg = 1; per = np->ns_sync;}
+		if (per < np->minsync)
+			{chg = 1; per = np->minsync;}
 		if (per < tp->minsync)
 			{chg = 1; per = tp->minsync;}
 		if (ofs > tp->maxoffs)
@@ -7067,7 +7542,7 @@
 		fak	= 7;
 		scntl3	= 0;
 		if (ofs != 0) {
-			(void) ncr_getsync(np, per, &fak, &scntl3);
+			ncr_getsync(np, per, &fak, &scntl3);
 			if (fak > 7) {
 				chg = 1;
 				ofs = 0;
@@ -7110,7 +7585,7 @@
 				return;
 
 			case NS_WIDE:
-				ncr_setwide (np, cp, 0);
+				ncr_setwide (np, cp, 0, 0);
 				break;
 			};
 		};
@@ -7209,13 +7684,13 @@
 					/*
 					**	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;
@@ -7231,7 +7706,7 @@
 		**      prepare an answer message
 		*/
 
-		ncr_setwide (np, cp, wide);
+		ncr_setwide (np, cp, wide, 1);
 
 		np->msgout[0] = M_EXTENDED;
 		np->msgout[1] = 2;
@@ -7543,10 +8018,12 @@
 		tp->jump_tcb.l_cmd   = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
 		tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
 
-		tp->getscr[0] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(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] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(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);
 
@@ -8206,15 +8683,6 @@
 	*/
 	f1		*= np->multiplier;
 	np->clock_khz	= f1;
-	np->ns_sync	= 25;
-
-	if	(f1 >= 160000) {
-		if	(np->features & _F_ULTRA2)	np->ns_sync = 10;
-		else if	(np->features & _F_ULTRA)	np->ns_sync = 12;
-	}
-	else if	(f1 >= 80000) {
-		if (np->features & _F_ULTRA)		np->ns_sync = 12;
-	}
 }
 
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -8244,13 +8712,14 @@
 {
 #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 	char *cur = str;
-	char *pv;
+	char *pc, *pv;
 	int val;
 	int base;
 	int c;
 
-	while (cur != NULL && (pv = strchr(cur, ':')) != NULL) {
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
 		val = 0;
+		pv = pc;
 		c = *++pv;
 		if	(c == 'n')
 			val = 0;
@@ -8314,9 +8783,15 @@
 			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;
@@ -8324,8 +8799,8 @@
 #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
 }
 
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
-	     uchar bus, uchar device_fn);
+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.
@@ -8336,6 +8811,9 @@
 **   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.
 */
 
@@ -8372,6 +8850,91 @@
 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)
 )
@@ -8381,8 +8944,18 @@
 	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;
 
-	if (bootverbose >= 2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	int nvram_index = 0;
+#endif
+	if (initverbose >= 2)
 		ncr_print_driver_setup();
 
 #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
@@ -8396,32 +8969,99 @@
 # endif
 #endif
 
+	/* 
+	** 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.
+	*/
+
 	if (!pcibios_present())
 		return 0;
 
-	chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[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++) {
-			if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
-						index, &bus, &device_fn))
+			char *msg = "";
+			if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+						index, &bus, &device_fn)) ||
+			    (count == hosts))
 				break;
-			if (!ncr53c8xx_pci_init(tpnt, count, bus, device_fn))
-				++count;
+#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 count;
-}
 
+	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.
 */
 
 __initfunc(
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
-			      uchar bus, uchar device_fn)
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+			      uchar bus, uchar device_fn, ncr_device *device)
 )
 {
 	ushort vendor_id, device_id, command;
@@ -8433,23 +9073,27 @@
 	ulong base, io_port; 
 #endif
 	int i, error;
-	ncr_chip ncrchip, *chip;
 
-	printk("ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram *nvram = device->nvram;
+#endif
+	ncr_chip *chip;
+
+	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
 		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
 	/*
 	 * Read info from the PCI config space
 	 */
 	if (
-(error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id))	||
-(error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id))	||
-(error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command))		||
-(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port))	|| 
-(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base))	||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision))	||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq))	||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
+		(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;
 
@@ -8462,7 +9106,7 @@
 			continue;
 		if (revision > ncr_chip_table[i].revision_id)
 			continue;
-		chip = &ncrchip;
+		chip = &device->chip;
 		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
 		chip->revision_id = revision;
 		break;
@@ -8516,23 +9160,28 @@
 	}
 
 	/*
-	 * Remove not wished features.
+	 * Fix some features according to driver setup.
 	 */
 	if (!driver_setup.special_features)
-		chip->features &= ~_F_SPECIAL_SET;
-	if (driver_setup.ultra_scsi < 2 && chip->features & _F_ULTRA2)
-		chip->features &= ~(_F_ULTRA2 | _F_ULTRA);
+		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 &= ~_F_ULTRA;
+		chip->features &= ~FE_ULTRA;
 	if (!driver_setup.max_wide)
-		chip->features &= ~_F_WIDE;
+		chip->features &= ~FE_WIDE;
+
+
+#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
 
 	/*
 	 * Try to fix up PCI config according to wished features.
 	 */
-#ifdef __i386
+#if defined(__i386) && !defined(MODULE)
 	if ((driver_setup.pci_fix_up & 1) &&
-	    (chip->features & _F_CLSE) && cache_line_size == 0) {
+	    (chip->features & FE_CLSE) && cache_line_size == 0) {
 		extern char x86;
 		switch(x86) {
 		case 4:	cache_line_size = 4; break;
@@ -8542,38 +9191,137 @@
 			error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
 		if (error)
 			goto err_pcibios;
-		if (bootverbose)
+		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 & _F_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+	    (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 (bootverbose)
+		if (initverbose)
 			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
 	}
 #endif
 	/*
-	 * Remove features that will not work
+	 * 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 & _F_CLSE) && cache_line_size == 0) {
-		chip->features &= ~_F_CACHE_SET;
+
+	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 & _F_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		chip->features &= ~_F_WRIE;
+	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;
+
 	/*
-	 * Try to attach the controller
-	 */
-	return ncr_attach (tpnt, unit, chip, base, io_port, (int) irq, 
-			bus, (uchar) device_fn);
+	** Get access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	request_region(io_port, 128, "ncr53c8xx");
+	device->slot.port = ioport;
+#else
+	device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+	if (!device->slot.reg)
+		goto out;
+#endif
+
+	/*
+	** Try to read SYMBIOS nvram.
+	** Data can be used to order booting of boards.
+	**
+	** Data is saved in ncr_device structure if NVRAM found. This
+	** is then used to find drive boot order for ncr_attach().
+	**
+	** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+	** for any device set up.
+	**
+	** Try to read TEKRAM nvram if Symbios nvram not found.
+	*/
+
+	if	(!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+		nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+	else if	(!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+		nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+	else
+		nvram->type = 0;
+out:
+	/*
+	** Release access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	release_region(device->slot.port, 128);
+#else
+	unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
+
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+	return 0;     
+
 err_pcibios:
 	printk("ncr53c8xx: error %s reading configuration space\n",
 		pcibios_strerror(error));
@@ -9229,7 +9977,7 @@
 		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
 		                  (u_long) np->reg);
 #endif
-	copy_info(&info, "  Synchronous period factor %d, ", (int) np->ns_sync);
+	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) {
@@ -9311,6 +10059,460 @@
 **=========================================================================
 */
 #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

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov