patch-2.3.34 linux/drivers/scsi/sym53c8xx.c

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

diff -u --recursive --new-file v2.3.33/linux/drivers/scsi/sym53c8xx.c linux/drivers/scsi/sym53c8xx.c
@@ -55,7 +55,7 @@
 */
 
 /*
-**	October 3 1999, sym53c8xx 1.5f
+**	December 6 1999, sym53c8xx 1.5g
 **
 **	Supported SCSI features:
 **	    Synchronous data transfers
@@ -73,6 +73,7 @@
 **		53C895	  (Wide,   Fast 40,	 on-board rom BIOS)
 **		53C895A	  (Wide,   Fast 40,	 on-board rom BIOS)
 **		53C896	  (Wide,   Fast 40 Dual, on-board rom BIOS)
+**		53C1510D  (Wide,   Fast 40 Dual, on-board rom BIOS)
 **
 **	Other features:
 **		Memory mapped IO
@@ -83,7 +84,7 @@
 /*
 **	Name and version of the driver
 */
-#define SCSI_NCR_DRIVER_NAME	"sym53c8xx - version 1.5f"
+#define SCSI_NCR_DRIVER_NAME	"sym53c8xx - version 1.5g"
 
 /* #define DEBUG_896R1 */
 #define SCSI_NCR_OPTIMIZE_896
@@ -449,6 +450,133 @@
 #define offsetof(t, m)	((size_t) (&((t *)0)->m))
 #endif
 
+/*
+**	Simple Wrapper to kernel PCI bus interface.
+**
+**	This wrapper allows to get rid of old kernel PCI interface 
+**	and still allows to preserve linux-2.0 compatibilty.
+**	In fact, it is mostly an incomplete emulation of the new 
+**	PCI code for pre-2.2 kernels. When kernel-2.0 support 
+**	will be dropped, we will just have to remove most of this 
+**	code.
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0)
+
+typedef struct pci_dev *pcidev_t;
+#define PCIDEV_NULL		(0)
+#define PciBusNumber(d)		(d)->bus->number
+#define PciDeviceFn(d)		(d)->devfn
+#define PciVendorId(d)		(d)->vendor
+#define PciDeviceId(d)		(d)->device
+#define PciIrqLine(d)		(d)->irq
+
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,3,12)
+
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->resource[index].start;
+	if ((pdev->resource[index].flags & 0x7) == 0x4)
+		++index;
+	return ++index;
+}
+#else
+static int __init 
+pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
+{
+	*base = pdev->base_address[index++];
+	if ((*base & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		*base |= (((u_long)pdev->base_address[index]) << 32);
+#endif
+		++index;
+	}
+	return index;
+}
+#endif
+
+#else	/* Incomplete emulation of current PCI code for pre-2.2 kernels */
+
+typedef unsigned int pcidev_t;
+#define PCIDEV_NULL		(~0u)
+#define PciBusNumber(d)		((d)>>8)
+#define PciDeviceFn(n)		((d)&0xff)
+#define __PciDev(busn, devfn)	(((busn)<<8)+(devfn))
+
+#define pci_present pcibios_present
+
+#define pci_read_config_byte(d, w, v) \
+	pcibios_read_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_word(d, w, v) \
+	pcibios_read_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_read_config_dword(d, w, v) \
+	pcibios_read_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+#define pci_write_config_byte(d, w, v) \
+	pcibios_write_config_byte(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_word(d, w, v) \
+	pcibios_write_config_word(PciBusNumber(d), PciDeviceFn(d), w, v)
+#define pci_write_config_dword(d, w, v) \
+	pcibios_write_config_dword(PciBusNumber(d), PciDeviceFn(d), w, v)
+
+static pcidev_t __init
+pci_find_device(unsigned int vendor, unsigned int device, pcidev_t prev)
+{
+	static unsigned short pci_index;
+	int retv;
+	unsigned char bus_number, device_fn;
+
+	if (prev == PCIDEV_NULL)
+		pci_index = 0;
+	else
+		++pci_index;
+	retv = pcibios_find_device (vendor, device, pci_index,
+				    &bus_number, &device_fn);
+	return retv ? PCIDEV_NULL : __PciDev(bus_number, device_fn);
+}
+
+static u_short __init PciVendorId(pcidev_t dev)
+{
+	u_short vendor_id;
+	pcibios_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
+	return vendor_id;
+}
+
+static u_short __init PciDeviceId(pcidev_t dev)
+{
+	u_short device_id;
+	pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
+	return device_id;
+}
+
+static u_int __init PciIrqLine(pcidev_t dev)
+{
+	u_short irq;
+	pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+	return irq;
+}
+
+static int __init 
+pci_get_base_address(pcidev_t dev, int offset, u_long *base)
+{
+	u_int32 tmp;
+	
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+	*base = tmp;
+	offset += sizeof(u_int32);
+	if ((tmp & 0x7) == 0x4) {
+#if BITS_PER_LONG > 32
+		pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + offset, &tmp);
+		*base |= (((u_long)tmp) << 32);
+#endif
+		offset += sizeof(u_int32);
+	}
+	return offset;
+}
+
+#endif	/* LINUX_VERSION_CODE >= LinuxVersionCode(2,2,0) */
+
 /*==========================================================
 **
 **	Debugging tags
@@ -504,20 +632,11 @@
 #define	NCR_LOCK_NCB(np, flags)    spin_lock_irqsave(&np->smp_lock, flags)
 #define	NCR_UNLOCK_NCB(np, flags)  spin_unlock_irqrestore(&np->smp_lock, flags)
 
-#	if LINUX_VERSION_CODE < LinuxVersionCode(2,3,99)
-
-#	define	NCR_LOCK_SCSI_DONE(np, flags) \
+#define	NCR_LOCK_SCSI_DONE(np, flags) \
 		spin_lock_irqsave(&io_request_lock, flags)
-#	define	NCR_UNLOCK_SCSI_DONE(np, flags) \
+#define	NCR_UNLOCK_SCSI_DONE(np, flags) \
 		spin_unlock_irqrestore(&io_request_lock, flags)
 
-#	else
-
-#	define	NCR_LOCK_SCSI_DONE(np, flags)    do {;} while (0)
-#	define	NCR_UNLOCK_SCSI_DONE(np, flags)  do {;} while (0)
-
-#	endif
-
 #else
 
 #define	NCR_LOCK_DRIVER(flags)     do { save_flags(flags); cli(); } while (0)
@@ -560,10 +679,11 @@
 #endif
 
 #ifdef __sparc__
+#include <asm/irq.h>
 #  define ioremap(base, size)	((u_long) __va(base))
 #  define iounmap(vaddr)
 #  define pcivtobus(p)			((p) & pci_dvma_mask)
-#  define memcpy_to_pci(a, b, c)	memcpy_toio((u_long) (a), (b), (c))
+#  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
 #elif defined(__alpha__)
 #  define pcivtobus(p)			((p) & 0xfffffffful)
 #  define memcpy_to_pci(a, b, c)	memcpy_toio((a), (b), (c))
@@ -635,9 +755,9 @@
 #endif
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
-#define sym53c8xx_get_pages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order)
+#define GetPages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order)
 #else
-#define sym53c8xx_get_pages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order, 0)
+#define GetPages(order) __get_free_pages(GFP_ATOMIC | GFP_DMA_32BIT, order, 0)
 #endif
 
 /*
@@ -668,7 +788,7 @@
 	j = i;
 	while (!h[j].next) {
 		if (s == (PAGE_SIZE << MEMO_PAGE_ORDER)) {
-			h[j].next = (struct m_link *)sym53c8xx_get_pages(MEMO_PAGE_ORDER);
+			h[j].next = (struct m_link *) GetPages(MEMO_PAGE_ORDER);
 			if (h[j].next)
 				h[j].next->next = 0;
 			break;
@@ -827,7 +947,12 @@
 /*
 **	/proc directory entry and proc_info function
 */
-
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_sym53c8xx = {
+    PROC_SCSI_SYM53C8XX, 9, NAME53C8XX,
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
 #ifdef SCSI_NCR_PROC_INFO_SUPPORT
 static int sym53c8xx_proc_info(char *buffer, char **start, off_t offset,
 			int length, int hostno, int func);
@@ -859,6 +984,17 @@
 #define SetScsiResult(cmd, h_sts, s_sts) \
 	cmd->result = (((h_sts) << 16) + ((s_sts) & 0x7f))
 
+/* We may have to remind our amnesiac SCSI layer of the reason of the abort */
+#if 0
+#define SetScsiAbortResult(cmd)	\
+	  SetScsiResult(	\
+	    cmd, 		\
+	    (cmd)->abort_reason == DID_TIME_OUT ? DID_TIME_OUT : DID_ABORT, \
+	    0xff)
+#else
+#define SetScsiAbortResult(cmd) SetScsiResult(cmd, DID_ABORT, 0xff)
+#endif
+
 static void sym53c8xx_select_queue_depths(
 	struct Scsi_Host *host, struct scsi_device *devlist);
 static void sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
@@ -868,7 +1004,8 @@
 #define bootverbose (np->verbose)
 
 #ifdef SCSI_NCR_NVRAM_SUPPORT
-static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+static u_char Tekram_sync[16] __initdata =
+	{25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10};
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
 
 /*
@@ -1775,7 +1912,7 @@
 	**----------------------------------------------------------------
 	*/
 	u_char	sv_scntl0, sv_scntl3, sv_dmode, sv_dcntl, sv_ctest3, sv_ctest4,
-		sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4;
+		sv_ctest5, sv_gpcntl, sv_stest2, sv_stest4, sv_stest1;
 
 	/*----------------------------------------------------------------
 	**	Actual initial value of IO register bits used by the 
@@ -1831,7 +1968,8 @@
 	*/
 	u_short		device_id;	/* PCI device id		*/
 	u_char		revision_id;	/* PCI device revision id	*/
-	u_int		features;	/* Chip features map		*/
+	u_char		bus;		/* PCI BUS number		*/
+	u_char		device_fn;	/* PCI BUS device and function	*/
 	u_char		myaddr;		/* SCSI id of the adapter	*/
 	u_char		maxburst;	/* log base 2 of dwords burst	*/
 	u_char		maxwide;	/* Maximum transfer width	*/
@@ -1841,6 +1979,7 @@
 	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		*/
 
 	/*----------------------------------------------------------------
 	**	Range for the PCI clock frequency measurement result
@@ -2213,6 +2352,7 @@
 static  int     ncr_wakeup_done (ncb_p np);
 static	void	ncr_start_next_ccb (ncb_p np, lcb_p lp, int maxn);
 static	void	ncr_put_start_queue(ncb_p np, ccb_p cp);
+static	void	ncr_chip_reset	(ncb_p np);
 static	void	ncr_soft_reset	(ncb_p np);
 static	void	ncr_start_reset	(ncb_p np);
 static	int	ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
@@ -2235,9 +2375,10 @@
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
 #ifdef SCSI_NCR_NVRAM_SUPPORT
-static  void	ncr_get_nvram		(ncr_device *devp, ncr_nvram *nvp);
-static	int	ncr_get_Symbios_nvram	(ncr_slot *np, Symbios_nvram *nvram);
-static	int	ncr_get_Tekram_nvram	(ncr_slot *np, Tekram_nvram *nvram);
+static  void	ncr_get_nvram	       (ncr_device *devp, ncr_nvram *nvp);
+static  int	sym_read_Tekram_nvram  (ncr_slot *np, u_short device_id,
+				        Tekram_nvram *nvram);
+static  int	sym_read_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
 #endif
 
 /*==========================================================
@@ -4558,7 +4699,7 @@
 
 	if (tn->flags & TEKRAM_SYNC_NEGO) {
 		i = tn->sync_index & 0xf;
-		tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+		tp->usrsync = Tekram_sync[i];
 	}
 
 	tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
@@ -4576,16 +4717,12 @@
 }
 #endif /* SCSI_NCR_NVRAM_SUPPORT */
 
-static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+/*
+**	Save initial settings of some IO registers.
+**	Assumed to have been set by BIOS.
+*/
+static void __init ncr_save_initial_setting(ncb_p np)
 {
-	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;
@@ -4596,6 +4733,18 @@
 	np->sv_gpcntl	= INB(nc_gpcntl);
 	np->sv_stest2	= INB(nc_stest2) & 0x20;
 	np->sv_stest4	= INB(nc_stest4);
+	np->sv_stest1	= INB(nc_stest1);
+}
+
+/*
+**	Prepare io register values used by ncr_init() 
+**	according to selected and supported features.
+*/
+static int __init ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+{
+	u_char	burst_max;
+	u_long	period;
+	int i;
 
 	/*
 	**	Wide ?
@@ -4985,7 +5134,7 @@
 		int sync, j;
 		struct Tekram_target *tn = &nvram->target[i];
 		j = tn->sync_index & 0xf;
-		sync = j < 12 ? Tekram_sync[j] : 255;
+		sync = Tekram_sync[j];
 		printk(KERN_DEBUG "%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
 		ncr_name(np), i,
 		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
@@ -5019,16 +5168,21 @@
 	ncr_nvram *nvram = device->nvram;
 	int i;
 
+	printk(KERN_INFO NAME53C "%s-%d: rev 0x%x on pci bus %d device %d function %d "
 #ifdef __sparc__
-printk(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=0x%x\n",
-	device->chip.name, unit, device->chip.revision_id, device->slot.base,
-	device->slot.io_port, device->slot.irq);
-#else
-printk(KERN_INFO NAME53C "%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n",
-	device->chip.name, unit, device->chip.revision_id, device->slot.base,
-	device->slot.io_port, device->slot.irq);
+		"irq %s\n",
+#else
+		"irq %d\n",
 #endif
- 
+		device->chip.name, unit, device->chip.revision_id,
+		device->slot.bus, (device->slot.device_fn & 0xf8) >> 3,
+		device->slot.device_fn & 7,
+#ifdef __sparc__
+		__irq_itoa(device->slot.irq));
+#else
+		device->slot.irq);
+#endif
+
 	/*
 	**	Allocate host_data structure
 	*/
@@ -5054,6 +5208,8 @@
 	sprintf(np->inst_name, NAME53C "%s-%d", np->chip_name, np->unit);
 	np->device_id	= device->chip.device_id;
 	np->revision_id	= device->chip.revision_id;
+	np->bus		= device->slot.bus;
+	np->device_fn	= device->slot.device_fn;
 	np->features	= device->chip.features;
 	np->clock_divn	= device->chip.nr_divisor;
 	np->maxoffs	= device->chip.offset_max;
@@ -5141,6 +5297,14 @@
 #endif /* !defined NCR_IOMAPPED */
 
 	/*
+	**	If on-chip RAM is used, make sure SCRIPTS isn't too large.
+	*/
+	if (np->base2_ba && sizeof(struct script) > 4096) {
+		printk(KERN_ERR "%s: script too large.\n", ncr_name(np));
+		goto attach_error;
+	}
+
+	/*
 	**	Try to map the controller chip into iospace.
 	*/
 
@@ -5171,13 +5335,22 @@
 	}
 #endif
 
+ 	/*
+	**	Save setting of some IO registers, so we will 
+	**	be able to probe specific implementations.
+	*/
+	ncr_save_initial_setting (np);
+
+	/*
+	**	Reset the chip now, since it has been reported 
+	**	that SCSI clock calibration may not work properly 
+	**	if the chip is currently active.
+	*/
+	ncr_chip_reset (np);
+
 	/*
 	**	Do chip dependent initialization.
 	*/
-	if (np->base2_ba && sizeof(struct script) > 4096) {
-		printk(KERN_ERR "%s: script too large.\n", ncr_name(np));
-		goto attach_error;
-	}
 	(void) ncr_prepare_setting(np, nvram);
 
 	/*
@@ -5382,9 +5555,7 @@
 	**	so, since we may not be safe if ABRT interrupt occurs due 
 	**	to the BIOS or previous O/S having enable this interrupt.
 	*/
-	OUTB (nc_istat, SRST);
-	UDELAY(10);
-	OUTB (nc_istat, 0);
+	ncr_chip_reset(np);
 
 	/*
 	**	Now check the cache handling of the pci chipset.
@@ -5474,7 +5645,11 @@
 	instance->max_id	= np->maxwide ? 16 : 8;
 	instance->max_lun	= MAX_LUN;
 #ifndef NCR_IOMAPPED
-	instance->base		= (unsigned long)np->reg;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,29)
+	instance->base		= (unsigned long) np->reg;
+#else
+	instance->base		= (char *) np->reg;
+#endif
 #endif
 	instance->irq		= np->irq;
 	instance->unique_id	= np->base_io;
@@ -6114,6 +6289,13 @@
 **==========================================================
 */
 
+static void ncr_chip_reset (ncb_p np)
+{
+	OUTB (nc_istat, SRST);
+	UDELAY (10);
+	OUTB (nc_istat, 0);
+}
+
 static void ncr_soft_reset(ncb_p np)
 {
 	u_char istat;
@@ -6135,9 +6317,7 @@
 	if (!i)
 		printk("%s: unable to abort current chip operation.\n",
 			ncr_name(np));
-	OUTB (nc_istat, SRST);
-	UDELAY(10);
-	OUTB (nc_istat, 0);
+	ncr_chip_reset(np);
 }
 
 /*==========================================================
@@ -6300,7 +6480,7 @@
  * First, look for the scsi command in the waiting list
  */
 	if (remove_from_waiting_list(np, cmd)) {
-		SetScsiResult(cmd, DID_ABORT, 0);
+		SetScsiAbortResult(cmd);
 		ncr_queue_done_cmd(np, cmd);
 		return SCSI_ABORT_SUCCESS;
 	}
@@ -6381,9 +6561,7 @@
 */
 
 	printk("%s: resetting chip\n", ncr_name(np));
-	OUTB (nc_istat,  SRST);
-	UDELAY (100);
-	OUTB (nc_istat,  0   );
+	ncr_chip_reset(np);
 
 /*
 **	Restore bios setting for automatic clock detection.
@@ -6486,15 +6664,15 @@
 
 	if (cp->xerr_status) {
 		if (cp->xerr_status & XE_PARITY_ERR) {
-			PRINT_ADDR(cp->cmd);
+			PRINT_ADDR(cmd);
 			printk ("unrecovered SCSI parity error.\n");
 		}
 		if (cp->xerr_status & XE_EXTRA_DATA) {
-			PRINT_ADDR(cp->cmd);
+			PRINT_ADDR(cmd);
 			printk ("extraneous data discarded.\n");
 		}
 		if (cp->xerr_status & XE_BAD_PHASE) {
-			PRINT_ADDR(cp->cmd);
+			PRINT_ADDR(cmd);
 			printk ("illegal scsi phase (4/5).\n");
 		}
 
@@ -6602,7 +6780,7 @@
 		/*
 		**   Transfer aborted
 		*/
-		SetScsiResult(cmd, DID_ABORT, cp->scsi_status);
+		SetScsiAbortResult(cmd);
 
 	} else {
 		int did_status;
@@ -6829,22 +7007,23 @@
 
 	/*
 	**	DEL 441 - 53C876 Rev 5 - Part Number 609-0392787/2788 - ITEM 2.
-	**	Disable overlapped arbitration.
-	**	The 896 Rev 1 needs also this work-around to be applied.
+	**	Disable overlapped arbitration for all dual-function 
+	**	devices, regardless revision id.
+	**	We may consider it is a post-chip-design feature. ;-)
 	*/
-	if (np->device_id == PCI_DEVICE_ID_NCR_53C875 &&
-	    np->revision_id >= 0x10 && np->revision_id <= 0x15)
+	if (np->device_id == PCI_DEVICE_ID_NCR_53C875)
 		OUTB (nc_ctest0, (1<<5));
-	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896 &&
-		 np->revision_id <= 0x1)
+	else if (np->device_id == PCI_DEVICE_ID_NCR_53C896)
 		np->rv_ccntl0 |= DPR;
 
 	/*
-	**	If 64 bit (53C895A or 53C896) enable 40 bit address table 
-	**	indirect addressing for MOVE.
+	**	If 64 bit (895A/896/1010) write the CCNTL1 register to 
+	**	enable 40 bit address table indirect addressing for MOVE.
+	**	Also write CCNTL0 if 64 bit chip, since this register seems 
+	**	to only be used by 64 bit cores.
 	*/
-
 	if (np->features & FE_64BIT) {
+		OUTB (nc_ccntl0, np->rv_ccntl0);
 		OUTB (nc_ccntl1, np->rv_ccntl1);
 	}
 
@@ -6856,7 +7035,6 @@
 	if (np->features & FE_NOPM) {
 		printk(KERN_INFO "%s: handling phase mismatch from SCRIPTS.\n", 
 		       ncr_name(np));
-		OUTB (nc_ccntl0, np->rv_ccntl0);
 		OUTL (nc_pmjad1, NCB_SCRIPTH_PHYS (np, pm_handle));
 		OUTL (nc_pmjad2, NCB_SCRIPTH_PHYS (np, pm_handle));
 	}
@@ -9216,18 +9394,20 @@
 		pm = 0;
 
 	if (pm) {
-		dp_scr  = pm->ret;
-		dp_ofs -= pm->sg.size;
+		dp_scr  = scr_to_cpu(pm->ret);
+		dp_ofs -= scr_to_cpu(pm->sg.size);
 	}
 
 	/*
 	**	Deduce the index of the sg entry.
 	**	Keep track of the index of the first valid entry.
 	**	If result is dp_sg = MAX_SCATTER, then we are at the 
-	**	end of the data.
+	**	end of the data and vice-versa.
 	*/
 	tmp = scr_to_cpu(cp->phys.header.goalp);
-	dp_sg = MAX_SCATTER - (tmp - 8 - (int)dp_scr) / (SCR_SG_SIZE*4);
+	dp_sg = MAX_SCATTER;
+	if (dp_scr != tmp)
+		dp_sg -= (tmp - 8 - (int)dp_scr) / (SCR_SG_SIZE*4);
 	dp_sgmin = MAX_SCATTER - cp->segments;
 
 	/*
@@ -9257,9 +9437,9 @@
 	}
 	else if (dp_ofs > 0) {
 		while (dp_sg < MAX_SCATTER) {
-			++dp_sg;
 			tmp = scr_to_cpu(cp->phys.data[dp_sg].size);
 			dp_ofs -= (tmp & 0xffffff);
+			++dp_sg;
 			if (dp_ofs <= 0)
 				break;
 		}
@@ -9278,7 +9458,7 @@
 	**	Save the extreme pointer if needed.
 	*/
 	if (dp_sg > cp->ext_sg ||
-            (dp_sg == cp->ext_sg && dp_ofs < cp->ext_ofs)) {
+            (dp_sg == cp->ext_sg && dp_ofs > cp->ext_ofs)) {
 		cp->ext_sg  = dp_sg;
 		cp->ext_ofs = dp_ofs;
 	}
@@ -9312,6 +9492,7 @@
 	int dp_ofs	= ofs;
 	u_int32 dp_scr	= INL (nc_temp);
 	u_int32	dp_ret;
+	u_int32	tmp;
 	u_char	hflags;
 	int	dp_sg;
 	struct pm_ctx *pm;
@@ -9375,8 +9556,10 @@
 	**	to the main data script.
 	*/
 	pm->ret = cpu_to_scr(dp_ret);
-	pm->sg.addr = cp->phys.data[dp_sg-1].addr + dp_ofs;
-	pm->sg.size = cp->phys.data[dp_sg-1].size - dp_ofs;
+	tmp  = scr_to_cpu(cp->phys.data[dp_sg-1].addr);
+	tmp += scr_to_cpu(cp->phys.data[dp_sg-1].size) + dp_ofs;
+	pm->sg.addr = cpu_to_scr(tmp);
+	pm->sg.size = cpu_to_scr(-dp_ofs);
 
 out_ok:
 	OUTL (nc_temp, dp_scr);
@@ -10935,8 +11118,8 @@
  */
 static void __init ncr_getclock (ncb_p np, int mult)
 {
-	unsigned char scntl3 = INB(nc_scntl3);
-	unsigned char stest1 = INB(nc_stest1);
+	unsigned char scntl3 = np->sv_scntl3;
+	unsigned char stest1 = np->sv_stest1;
 	unsigned f1;
 
 	np->multiplier = 1;
@@ -11238,8 +11421,8 @@
 #endif
 #endif
 
-static int sym53c8xx_pci_init(Scsi_Host_Template *tpnt,
-	     uchar bus, uchar device_fn, ncr_device *device);
+static int 
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device);
 
 /*
 **   Linux entry point for SYM53C8XX devices detection routine.
@@ -11318,26 +11501,27 @@
 static void __init ncr_detect_pqs_pds(void)
 {
 	short index;
+	pcidev_t dev = PCIDEV_NULL;
 
-	for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index ++) {
-		u_char tmp, bus, device_fn;
+	for(index=0; index < SCSI_NCR_MAX_PQS_BUS; index++) {
+		u_char tmp;
 
-		if (pcibios_find_device(0x101a, 0x0009, index, &bus, 
-					&device_fn) != PCIBIOS_SUCCESSFUL) {
+		dev = pci_find_device(0x101a, 0x0009, dev);
+		if (dev == PCIDEV_NULL) {
 			pqs_bus[index] = -1;
 			break;
 		}
-		printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", bus);
-		pcibios_read_config_byte(bus, device_fn, 0x44, &tmp);
+		printk(KERN_INFO NAME53C8XX ": NCR PQS/PDS memory controller detected on bus %d\n", PciBusNumber(dev));
+		pci_read_config_byte(dev, 0x44, &tmp);
 		/* bit 1: allow individual 875 configuration */
 		tmp |= 0x2;
-		pcibios_write_config_byte(bus, device_fn, 0x44, tmp);
-		pcibios_read_config_byte(bus, device_fn, 0x45, &tmp);
+		pci_write_config_byte(dev, 0x44, tmp);
+		pci_read_config_byte(dev, 0x45, &tmp);
 		/* bit 2: drive individual 875 interrupts to the bus */
 		tmp |= 0x4;
-		pcibios_write_config_byte(bus, device_fn, 0x45, tmp);
+		pci_write_config_byte(dev, 0x45, tmp);
 
-		pqs_bus[index] = bus;
+		pqs_bus[index] = PciBusNumber(dev);
 	}
 }
 #endif /* SCSI_NCR_PQS_PDS_SUPPORT */
@@ -11355,9 +11539,8 @@
 */
 int __init sym53c8xx_detect(Scsi_Host_Template *tpnt)
 {
+	pcidev_t pcidev;
 	int i, j, chips, hosts, count;
-	u_char bus, device_fn;
-	short index;
 	int attach_count = 0;
 	ncr_device *devtbl, *devp;
 #ifdef SCSI_NCR_NVRAM_SUPPORT
@@ -11367,18 +11550,18 @@
 	/*
 	**    PCI is required.
 	*/
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,92)
 	if (!pci_present())
-#else
-	if (!pcibios_present())
-#endif
 		return 0;
 
 	/*
 	**    Initialize driver general stuff.
 	*/
 #ifdef SCSI_NCR_PROC_INFO_SUPPORT
-     tpnt->proc_name  = NAME53C8XX;
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+     tpnt->proc_dir  = &proc_scsi_sym53c8xx;
+#else
+     tpnt->proc_name = NAME53C8XX;
+#endif
      tpnt->proc_info = sym53c8xx_proc_info;
 #endif
 
@@ -11420,8 +11603,8 @@
 	nvp = (driver_setup.use_nvram & 0x1) ? &nvram0 : 0;
 #endif
 	j = 0;
-	index = 0;
 	count = 0;
+	pcidev = PCIDEV_NULL;
 	while (1) {
 		char *msg = "";
 		if (count >= hosts)
@@ -11429,17 +11612,16 @@
 		if (j >= chips)
 			break;
 		i = driver_setup.reverse_probe ? chips - 1 - j : j;
-		if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
-					index, &bus, &device_fn)) {
+		pcidev = pci_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+					 pcidev);
+		if (pcidev == PCIDEV_NULL) {
 			++j;
-			index = 0;
 			continue;
 		}
-		++index;
 		/* Some HW as the HP LH4 may report twice PCI devices */
 		for (i = 0; i < count ; i++) {
-			if (devtbl[i].slot.bus	     == bus && 
-			    devtbl[i].slot.device_fn == device_fn)
+			if (devtbl[i].slot.bus	     == PciBusNumber(pcidev) && 
+			    devtbl[i].slot.device_fn == PciDeviceFn(pcidev))
 				break;
 		}
 		if (i != count)	/* Ignore this device if we already have it */
@@ -11447,7 +11629,7 @@
 		devp = &devtbl[count];
 		devp->host_id = driver_setup.host_id;
 		devp->attach_done = 0;
-		if (sym53c8xx_pci_init(tpnt, bus, device_fn, devp)) {
+		if (sym53c8xx_pci_init(tpnt, pcidev, devp)) {
 			continue;
 		}
 		++count;
@@ -11542,109 +11724,45 @@
 }
 
 /*===================================================================
-**   Generically read a base address from the PCI configuration space.
-**   Return the offset immediately after the base address that has 
-**   been read. Btw, we blindly assume that the high 32 bits of 64 bit 
-**   base addresses are set to zero on 32 bit architectures.
-**===================================================================
-*/
-#if LINUX_VERSION_CODE <= LinuxVersionCode(2,1,92)
-static int __init 
-pci_read_base_address(u_char bus, u_char device_fn, int offset, u_long *base)
-{
-	u_int32 tmp;
-
-	pcibios_read_config_dword(bus, device_fn, offset, &tmp);
-	*base = tmp;
-	offset += sizeof(u_int32);
-	if ((tmp & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		pcibios_read_config_dword(bus, device_fn, offset, &tmp);
-		*base |= (((u_long)tmp) << 32);
-#endif
-		offset += sizeof(u_int32);
-	}
-	return offset;
-}
-#elif	LINUX_VERSION_CODE <= LinuxVersionCode(2,3,12)
-static int __init 
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
-	*base = pdev->base_address[index++];
-	if ((*base & 0x7) == 0x4) {
-#if BITS_PER_LONG > 32
-		*base |= (((u_long)pdev->base_address[index]) << 32);
-#endif
-		++index;
-	}
-	return index;
-}
-#else	/* LINUX_VERSION_CODE > LinuxVersionCode(2,3,12) */
-static int __init 
-pci_get_base_address(struct pci_dev *pdev, int index, u_long *base)
-{
-	*base = pdev->resource[index].start;
-	if ((pdev->resource[index].flags & 0x7) == 0x4)
-		++index;
-	return ++index;
-}
-#endif
-
-/*===================================================================
 **   Read and check the PCI configuration for any detected NCR 
 **   boards and save data for attaching after all boards have 
 **   been detected.
 **===================================================================
 */
-static int __init sym53c8xx_pci_init(Scsi_Host_Template *tpnt,
-			      uchar bus, uchar device_fn, ncr_device *device)
+static int __init
+sym53c8xx_pci_init(Scsi_Host_Template *tpnt, pcidev_t pdev, ncr_device *device)
 {
 	u_short vendor_id, device_id, command;
 	u_char cache_line_size, latency_timer;
 	u_char suggested_cache_line_size = 0;
-	u_char pci_fix_up;
+	u_char pci_fix_up = driver_setup.pci_fix_up;
 	u_char revision;
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
-	struct pci_dev *pdev;
 	u_int irq;
-#else
-	u_char irq;
-#endif
 	u_long base, base_2, io_port; 
 	int i;
 	ncr_chip *chip;
 
 	printk(KERN_INFO NAME53C8XX ": at PCI bus %d, device %d, function %d\n",
-		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+		PciBusNumber(pdev),
+		(int) (PciDeviceFn(pdev) & 0xf8) >> 3,
+		(int) (PciDeviceFn(pdev) & 7));
 	/*
 	**    Read info from the PCI config space.
-	**    pcibios_read_config_xxx() functions are assumed to be used for 
+	**    pci_read_config_xxx() functions are assumed to be used for 
 	**    successfully detected PCI devices.
 	*/
-#if LINUX_VERSION_CODE > LinuxVersionCode(2,1,92)
-	pdev = pci_find_slot(bus, device_fn);
-	vendor_id = pdev->vendor;
-	device_id = pdev->device;
-	irq = pdev->irq;
+	vendor_id = PciVendorId(pdev);
+	device_id = PciDeviceId(pdev);
+	irq	  = PciIrqLine(pdev);
 	i =	0;
 	i =	pci_get_base_address(pdev, i, &io_port);
 	i =	pci_get_base_address(pdev, i, &base);
 	(void)	pci_get_base_address(pdev, i, &base_2);
-#else
-	pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id);
-	pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id);
-	pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq);
-	i =	PCI_BASE_ADDRESS_0;
-	i =	pci_read_base_address(bus, device_fn, i, &io_port);
-	i =	pci_read_base_address(bus, device_fn, i, &base);
-	(void)	pci_read_base_address(bus, device_fn, i, &base_2);
-#endif
-	pcibios_read_config_word(bus, device_fn, PCI_COMMAND, &command);
-	pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION, &revision);
-	pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE,
-				 &cache_line_size);
-	pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER,
-				 &latency_timer);
+
+	pci_read_config_word(pdev, PCI_COMMAND,		&command);
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION,	&revision);
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE,	&cache_line_size);
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER,	&latency_timer);
 
 #ifdef SCSI_NCR_PQS_PDS_SUPPORT
 	/*
@@ -11655,8 +11773,8 @@
 	*/
 	for(i = 0; i < SCSI_NCR_MAX_PQS_BUS && pqs_bus[i] != -1; i++) {
 		u_char tmp;
-		if (pqs_bus[i] == bus) {
-			pcibios_read_config_byte(bus, device_fn, 0x84, &tmp);
+		if (pqs_bus[i] == PciBusNumber(pdev)) {
+			pci_read_config_byte(pdev, 0x84, &tmp);
 			device->pqs_pds = 1;
 			device->host_id = tmp;
 			break;
@@ -11733,7 +11851,7 @@
 		(command & PCI_COMMAND_IO)     ? "" : " PCI_COMMAND_IO",
 		(command & PCI_COMMAND_MEMORY) ? "" : " PCI_COMMAND_MEMORY");
 		command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
 	}
 
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0)
@@ -11741,20 +11859,20 @@
 		if (io_port >= 0x10000000) {
 			printk(NAME53C8XX ": reallocating io_port (Wacky IBM)");
 			io_port = (io_port & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn,
-						   PCI_BASE_ADDRESS_0, io_port);
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_0, io_port);
 		}
 		if (base >= 0x10000000) {
 			printk(NAME53C8XX ": reallocating base (Wacky IBM)");
 			base = (base & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn,
-						   PCI_BASE_ADDRESS_1, base);
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_1, base);
 		}
 		if (base_2 >= 0x10000000) {
 			printk(NAME53C8XX ": reallocating base2 (Wacky IBM)");
 			base_2 = (base_2 & 0x00FFFFFF) | 0x01000000;
-			pcibios_write_config_dword(bus, device_fn,
-						   PCI_BASE_ADDRESS_2, base_2);
+			pci_write_config_dword(pdev,
+					       PCI_BASE_ADDRESS_2, base_2);
 		}
 	}
 #endif
@@ -11763,25 +11881,12 @@
 #ifdef __sparc__
 	/*
 	**    Fix-ups for sparc.
-	**
-	**    I wrote:	   Should not be performed by the driver,
-	**    Guy wrote:   but how can OBP know each and every PCI card,
-	** 		   if they don't use Fcode?
-	**    I replied:   no need to know each and every PCI card, just 
-	**	           be skilled enough to understand the PCI specs.
-	*/
-
-	/*
-	**    PCI configuration is based on configuration registers being
-	**    coherent with hardware and software resource identifications.
-	**    This is fairly simple, but seems still too complex for Sparc.
 	*/
 	base = __pa(base);
 	base_2 = __pa(base_2);
 
 	if (!cache_line_size)
 		suggested_cache_line_size = 16;
-
 #endif	/* __sparc__ */
 
 #if defined(__i386__) && !defined(MODULE)
@@ -11852,7 +11957,7 @@
 		(command & PCI_COMMAND_MASTER) ? "" : " PCI_COMMAND_MASTER",
 		(command & PCI_COMMAND_PARITY) ? "" : " PCI_COMMAND_PARITY");
 		command |= (PCI_COMMAND_MASTER | PCI_COMMAND_PARITY);
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
 	}
 
 	/*
@@ -11882,7 +11987,6 @@
 	**	We must ensure the chip will use WRITE AND INVALIDATE.
 	**	The revision number limit is for now arbitrary.
 	*/
-	pci_fix_up = driver_setup.pci_fix_up;
 	if (device_id == PCI_DEVICE_ID_NCR_53C896 && revision <= 0x10) {
 		chip->features	|= (FE_WRIE | FE_CLSE);
 		pci_fix_up	|=  3;	/* Force appropriate PCI fix-up */
@@ -11895,8 +11999,8 @@
 	if ((pci_fix_up & 1) && (chip->features & FE_CLSE) && 
 	    !cache_line_size && suggested_cache_line_size) {
 		cache_line_size = suggested_cache_line_size;
-		pcibios_write_config_byte(bus, device_fn,
-				PCI_CACHE_LINE_SIZE, cache_line_size);
+		pci_write_config_byte(pdev,
+				      PCI_CACHE_LINE_SIZE, cache_line_size);
 		printk(NAME53C8XX ": PCI_CACHE_LINE_SIZE set to %d (fix-up).\n",
 			cache_line_size);
 	}
@@ -11905,7 +12009,7 @@
 	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
 		printk(NAME53C8XX": setting PCI_COMMAND_INVALIDATE (fix-up)\n");
 		command |= PCI_COMMAND_INVALIDATE;
-		pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+		pci_write_config_word(pdev, PCI_COMMAND, command);
 	}
 
 	/*
@@ -11913,15 +12017,15 @@
 	**    (latency timer >= burst length + 6, we add 10 to be quite sure)
 	*/
 
-	if ((pci_fix_up & 4) && chip->burst_max) {
+	if (chip->burst_max && (latency_timer == 0 || (pci_fix_up & 4))) {
 		uchar lt = (1 << chip->burst_max) + 6 + 10;
 		if (latency_timer < lt) {
-			latency_timer = lt;
 			printk(NAME53C8XX 
-			       ": setting PCI_LATENCY_TIMER to %d (fix-up).\n",
-			       latency_timer);
-			pcibios_write_config_byte(bus, device_fn,
-					PCI_LATENCY_TIMER, latency_timer);
+			       ": changing PCI_LATENCY_TIMER from %d to %d.\n",
+			       (int) latency_timer, (int) lt);
+			latency_timer = lt;
+			pci_write_config_byte(pdev,
+					      PCI_LATENCY_TIMER, latency_timer);
 		}
 	}
 
@@ -11930,8 +12034,8 @@
  	/*
 	**    Initialise ncr_device structure with items required by ncr_attach.
 	*/
-	device->slot.bus	= bus;
-	device->slot.device_fn	= device_fn;
+	device->slot.bus	= PciBusNumber(pdev);
+	device->slot.device_fn	= PciDeviceFn(pdev);
 	device->slot.base	= base;
 	device->slot.base_2	= base_2;
 	device->slot.io_port	= io_port;
@@ -11976,9 +12080,10 @@
 	**    Try to read SYMBIOS nvram.
 	**    Try to read TEKRAM nvram if Symbios nvram not found.
 	*/
-	if	(!ncr_get_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
+	if	(!sym_read_Symbios_nvram(&devp->slot, &nvp->data.Symbios))
 		nvp->type = SCSI_NCR_SYMBIOS_NVRAM;
-	else if	(!ncr_get_Tekram_nvram(&devp->slot, &nvp->data.Tekram))
+	else if	(!sym_read_Tekram_nvram(&devp->slot, devp->chip.device_id,
+					&nvp->data.Tekram))
 		nvp->type = SCSI_NCR_TEKRAM_NVRAM;
 	else {
 		nvp->type = 0;
@@ -12690,24 +12795,28 @@
 	info.pos	= 0;
 
 	copy_info(&info, "General information:\n");
-	copy_info(&info, "  Chip " NAME53C "%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, "  IO port address 0x%lx, ", (u_long) np->base_io);
-	copy_info(&info, "IRQ number %d\n", (int) np->irq);
-
-#ifndef NCR_IOMAPPED
-	if (np->reg)
-		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
-		                  (u_long) np->reg);
+	copy_info(&info, "  Chip " NAME53C "%s, device id 0x%x, "
+			 "revision id 0x%x\n",
+			 np->chip_name, np->device_id,	np->revision_id);
+	copy_info(&info, "  On PCI bus %d, device %d, function %d, "
+#ifdef __sparc__
+		"IRQ %s\n",
+#else
+		"IRQ %d\n",
+#endif
+		np->bus, (np->device_fn & 0xf8) >> 3, np->device_fn & 7,
+#ifdef __sparc__
+		__irq_itoa(np->irq));
+#else
+		(int) np->irq);
 #endif
-	copy_info(&info, "  Synchronous period factor %d, ", (int) np->minsync);
-	copy_info(&info, "max commands per lun %d\n", MAX_TAGS);
+	copy_info(&info, "  Synchronous period factor %d, "
+			 "max commands per lun %d\n",
+			 (int) np->minsync, 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);
+		copy_info(&info, "  Debug flags 0x%x, verbosity level %d\n",
+			  driver_setup.debug, driver_setup.verbose);
 	}
 
 #ifdef SCSI_NCR_PROFILE_SUPPORT
@@ -12794,255 +12903,212 @@
 
 #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
-** ---------------------------------------------------------------------
-*/
+/*
+ *  24C16 EEPROM reading.
+ *
+ *  GPOI0 - data in/data out
+ *  GPIO1 - clock
+ *  Symbios NVRAM wiring now also used by Tekram.
+ */
 
 #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);
-
-static int __init ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+/*
+ *  Set/clear data/clock bit in GPIO0
+ */
+static void __init
+S24C16_set_bit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
 {
-	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;
+	UDELAY (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;
 
-	/* save current state of GPCNTL and GPREG */
-	old_gpreg	= INB (nc_gpreg);
-	old_gpcntl	= INB (nc_gpcntl);
-	gpcntl		= old_gpcntl & 0xfc;
+	}
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (5);
+}
 
-	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
-	OUTB (nc_gpreg,  old_gpreg);
-	OUTB (nc_gpcntl, gpcntl);
+/*
+ *  Send START condition to NVRAM to wake it up.
+ */
+static void __init S24C16_start(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+}
 
-	/* 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
-printk("sym53c8xx: NvRAM type=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
-	nvram->type,
-	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->type == 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;
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+static void __init S24C16_stop(ncr_slot *np, u_char *gpreg)
+{
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	S24C16_set_bit(np, 1, gpreg, SET_BIT);
 }
 
 /*
- * Read Symbios NvRAM data and compute checksum.
+ *  Read or write a bit to the NVRAM,
+ *  read if GPIO0 input else write if GPIO0 output
  */
-static u_short __init 
-nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+static void __init 
+S24C16_do_bit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
 {
-	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];
+	S24C16_set_bit(np, write_bit, gpreg, SET_BIT);
+	S24C16_set_bit(np, 0, gpreg, SET_CLK);
+	if (read_bit)
+		*read_bit = INB (nc_gpreg);
+	S24C16_set_bit(np, 0, gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, gpreg, CLR_BIT);
+}
 
-	return csum;
+/*
+ *  Output an ACK to the NVRAM after reading,
+ *  change GPIO0 to output and when done back to an input
+ */
+static void __init
+S24C16_write_ack(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+{
+	OUTB (nc_gpcntl, *gpcntl & 0xfe);
+	S24C16_do_bit(np, 0, write_bit, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
 }
 
 /*
- * Send START condition to NVRAM to wake it up.
+ *  Input an ACK from NVRAM after writing,
+ *  change GPIO0 to input and when done back to an output
  */
-static void __init nvram_start(ncr_slot *np, u_char *gpreg)
+static void __init 
+S24C16_read_ack(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
 {
-	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);
+	OUTB (nc_gpcntl, *gpcntl | 0x01);
+	S24C16_do_bit(np, read_bit, 1, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
 }
 
 /*
- * 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
+ *  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
  */
 static void __init 
-nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+S24C16_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);
+		S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
 		
-	nvram_readAck(np, ack_data, gpreg, gpcntl);
+	S24C16_read_ack(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
+ *  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
  */
 static void __init 
-nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+S24C16_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);
+		S24C16_do_bit(np, &read_bit, 1, gpreg);
 		*read_data |= ((read_bit & 0x01) << (7 - x));
 	}
 
-	nvram_writeAck(np, ack_data, gpreg, gpcntl);
+	S24C16_write_ack(np, ack_data, gpreg, gpcntl);
 }
 
 /*
- * Output an ACK to the NVRAM after reading,
- * change GPIO0 to output and when done back to an input
+ *  Read 'len' bytes starting at 'offset'.
  */
-static void __init 
-nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+static int __init 
+sym_read_S24C16_nvram (ncr_slot *np, int offset, u_char *data, int len)
 {
-	OUTB (nc_gpcntl, *gpcntl & 0xfe);
-	nvram_doBit(np, 0, write_bit, gpreg);
-	OUTB (nc_gpcntl, *gpcntl);
-}
+	u_char	gpcntl, gpreg;
+	u_char	old_gpcntl, old_gpreg;
+	u_char	ack_data;
+	int	retv = 1;
+	int	x;
 
-/*
- * Input an ACK from NVRAM after writing,
- * change GPIO0 to input and when done back to an output
- */
-static void __init 
-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);
-}
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+	gpcntl		= old_gpcntl & 0xfc;
 
-/*
- * Read or write a bit to the NVRAM,
- * read if GPIO0 input else write if GPIO0 output
- */
-static void __init 
-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);
-}
+	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+	OUTB (nc_gpreg,  old_gpreg);
+	OUTB (nc_gpcntl, gpcntl);
 
-/*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
- */
-static void __init nvram_stop(ncr_slot *np, u_char *gpreg)
-{
-	nvram_setBit(np, 0, gpreg, SET_CLK);
-	nvram_setBit(np, 1, gpreg, SET_BIT);
-}
+	/* this is to set NVRAM into a known state with GPIO0/1 both low */
+	gpreg = old_gpreg;
+	S24C16_set_bit(np, 0, &gpreg, CLR_CLK);
+	S24C16_set_bit(np, 0, &gpreg, CLR_BIT);
+		
+	/* now set NVRAM inactive with GPIO0/1 both high */
+	S24C16_stop(np, &gpreg);
+	
+	/* activate NVRAM */
+	S24C16_start(np, &gpreg);
 
-/*
- * Set/clear data/clock bit in GPIO0
- */
-static void __init 
-nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
-{
-	UDELAY (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;
+	/* write device code and random address MSB */
+	S24C16_write_byte(np, &ack_data,
+		0xa0 | ((offset >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
 
-	}
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (5);
+	/* write random address LSB */
+	S24C16_write_byte(np, &ack_data,
+		offset & 0xff, &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* regenerate START state to set up for reading */
+	S24C16_start(np, &gpreg);
+	
+	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+	S24C16_write_byte(np, &ack_data,
+		0xa1 | ((offset >> 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 requested data - only part of total NVRAM */
+	for (x = 0; x < len; x++) 
+		S24C16_read_byte(np, &data[x], (x == (len-1)), &gpreg, &gpcntl);
+
+	/* finally put NVRAM back in inactive mode */
+	gpcntl &= 0xfe;
+	OUTB (nc_gpcntl, gpcntl);
+	S24C16_stop(np, &gpreg);
+	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;
 }
 
 #undef SET_BIT 0
@@ -13050,115 +13116,126 @@
 #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);
-
-static int __init ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+/*
+ *  Try reading Symbios NVRAM.
+ *  Return 0 if OK.
+ */
+static int __init sym_read_Symbios_nvram (ncr_slot *np, Symbios_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);
+	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+	u_char *data = (u_char *) nvram;
+	int len  = sizeof(*nvram);
+	u_short	csum;
+	int x;
 
-	/* 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);
+	/* probe the 24c16 and read the SYMBIOS 24c16 area */
+	if (sym_read_S24C16_nvram (np, SYMBIOS_NVRAM_ADDRESS, data, len))
+		return 1;
 
-	/* 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 valid NVRAM signature, verify byte count and checksum */
+	if (nvram->type != 0 ||
+	    memcmp(nvram->trailer, Symbios_trailer, 6) ||
+	    nvram->byte_count != len - 12)
+		return 1;
 
-	/* check data valid */
-	if (csum != 0x1234)
+	/* verify checksum */
+	for (x = 6, csum = 0; x < len - 6; x++)
+		csum += data[x];
+	if (csum != nvram->checksum)
 		return 1;
 
 	return 0;
 }
 
 /*
- * Read Tekram NvRAM data and compute checksum.
+ *  93C46 EEPROM reading.
+ *
+ *  GPOI0 - data in
+ *  GPIO1 - data out
+ *  GPIO2 - clock
+ *  GPIO4 - chip select
+ *
+ *  Used by Tekram.
+ */
+
+/*
+ *  Pulse clock bit in GPIO0
  */
-static u_short __init 
-Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+static void __init T93C46_Clk(ncr_slot *np, u_char *gpreg)
 {
-	u_char	read_bit;
-	u_short	csum;
-	int	x;
+	OUTB (nc_gpreg, *gpreg | 0x04);
+	UDELAY (2);
+	OUTB (nc_gpreg, *gpreg);
+}
 
-	for (x = 0, csum = 0; x < len; x++)  {
+/* 
+ *  Read bit from NVRAM
+ */
+static void __init T93C46_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+{
+	UDELAY (2);
+	T93C46_Clk(np, gpreg);
+	*read_bit = INB (nc_gpreg);
+}
 
-		/* output read command and address */
-		Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
-		if (read_bit & 0x01)
-			return 0; /* Force bad checksum */
+/*
+ *  Write bit to GPIO0
+ */
+static void __init T93C46_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);
+	UDELAY (2);
 
-		Tnvram_Read_Word(np, &data[x], gpreg);
-		csum += data[x];
+	T93C46_Clk(np, gpreg);
+}
 
-		Tnvram_Stop(np, gpreg);
-	}
+/*
+ *  Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+static void __init T93C46_Stop(ncr_slot *np, u_char *gpreg)
+{
+	*gpreg &= 0xef;
+	OUTB (nc_gpreg, *gpreg);
+	UDELAY (2);
 
-	return csum;
+	T93C46_Clk(np, gpreg);
 }
 
 /*
- * Send read command and address to NVRAM
+ *  Send read command and address to NVRAM
  */
 static void __init 
-Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+T93C46_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);
+		T93C46_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
 
 	*read_bit = INB (nc_gpreg);
 }
 
 /*
- * READ a byte from the NVRAM
+ *  READ 2 bytes from the NVRAM
  */
 static void __init 
-Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+T93C46_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);
+		T93C46_Read_Bit(np, &read_bit, gpreg);
 
 		if (read_bit & 0x01)
 			*nvram_data |=  (0x01 << (15 - x));
@@ -13167,56 +13244,98 @@
 	}
 }
 
-/* 
- * Read bit from NVRAM
- */
-static void __init 
-Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
-{
-	UDELAY (2);
-	Tnvram_Clk(np, gpreg);
-	*read_bit = INB (nc_gpreg);
-}
-
 /*
- * Write bit to GPIO0
+ *  Read Tekram NvRAM data.
  */
-static void __init 
-Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+static int __init 
+T93C46_Read_Data(ncr_slot *np, u_short *data,int len,u_char *gpreg)
 {
-	if (write_bit & 0x01)
-		*gpreg |= 0x02;
-	else
-		*gpreg &= 0xfd;
-		
-	*gpreg |= 0x10;
-		
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
+	u_char	read_bit;
+	int	x;
+
+	for (x = 0; x < len; x++)  {
+
+		/* output read command and address */
+		T93C46_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+		if (read_bit & 0x01)
+			return 1; /* Bad */
+		T93C46_Read_Word(np, &data[x], gpreg);
+		T93C46_Stop(np, gpreg);
+	}
 
-	Tnvram_Clk(np, gpreg);
+	return 0;
 }
 
 /*
- * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ *  Try reading 93C46 Tekram NVRAM.
  */
-static void __init Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+static int __init 
+sym_read_T93C46_nvram (ncr_slot *np, Tekram_nvram *nvram)
 {
-	*gpreg &= 0xef;
-	OUTB (nc_gpreg, *gpreg);
-	UDELAY (2);
+	u_char gpcntl, gpreg;
+	u_char old_gpcntl, old_gpreg;
+	int retv = 1;
 
-	Tnvram_Clk(np, gpreg);
+	/* 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 */
+	retv = T93C46_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);
+
+	return retv;
 }
 
 /*
- * Pulse clock bit in GPIO0
+ *  Try reading Tekram NVRAM.
+ *  Return 0 if OK.
  */
-static void __init Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+static int __init 
+sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
 {
-	OUTB (nc_gpreg, *gpreg | 0x04);
-	UDELAY (2);
-	OUTB (nc_gpreg, *gpreg);
+	u_char *data = (u_char *) nvram;
+	int len = sizeof(*nvram);
+	u_short	csum;
+	int x;
+
+	switch (device_id) {
+	case PCI_DEVICE_ID_NCR_53C885:
+	case PCI_DEVICE_ID_NCR_53C895:
+	case PCI_DEVICE_ID_NCR_53C896:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		break;
+	case PCI_DEVICE_ID_NCR_53C875:
+		x = sym_read_S24C16_nvram(np, TEKRAM_24C16_NVRAM_ADDRESS,
+					  data, len);
+		if (!x)
+			break;
+	default:
+		x = sym_read_T93C46_nvram(np, nvram);
+		break;
+	}
+	if (x)
+		return 1;
+
+	/* verify checksum */
+	for (x = 0, csum = 0; x < len - 1; x += 2)
+		csum += data[x] + (data[x+1] << 8);
+	if (csum != 0x1234)
+		return 1;
+
+	return 0;
 }
 
 #endif	/* SCSI_NCR_NVRAM_SUPPORT */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)