patch-2.0.31 linux/drivers/scsi/ncr53c8xx.c
Next file: linux/drivers/scsi/ncr53c8xx.h
Previous file: linux/drivers/scsi/in2000.h
Back to the patch index
Back to the overall index
- Lines: 8818
- Date:
Thu Aug 21 15:13:05 1997
- Orig file:
v2.0.30/linux/drivers/scsi/ncr53c8xx.c
- Orig date:
Wed Dec 11 07:02:43 1996
diff -u --recursive --new-file v2.0.30/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.
+**
*******************************************************************************
*/
/*
-** 30 August 1996, version 1.12c
+** 21 August 1997, version 2.4a
**
** Supported SCSI-II features:
** Synchronous negotiation
@@ -51,26 +75,22 @@
** Etc...
**
** Supported NCR chips:
-** 53C810 (NCR BIOS in flash-bios required)
-** 53C815 (~53C810 with on board rom BIOS)
-** 53C820 (Wide, NCR BIOS in flash bios required)
-** 53C825 (Wide, ~53C820 with on board rom BIOS)
-** 53C860 (not yet tested)
-** 53C875 (not yet tested)
+** 53C810 (8 bits, Fast SCSI-2, no rom BIOS)
+** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS)
+** 53C820 (Wide, Fast SCSI-2, no rom BIOS)
+** 53C825 (Wide, Fast SCSI-2, on board rom BIOS)
+** 53C860 (8 bits, Fast 20, no rom BIOS)
+** 53C875 (Wide, Fast 20, on board rom BIOS)
+** 53C895 (Wide, Fast 40, on board rom BIOS)
**
** Other features:
-** Memory mapped IO (linux-1.3.X only)
+** Memory mapped IO (linux-1.3.X and above only)
** Module
** Shared IRQ (since linux-1.3.72)
*/
-#define SCSI_NCR_DEBUG
#define SCSI_NCR_DEBUG_FLAGS (0)
-#define NCR_DATE "pl23 95/09/07"
-
-#define NCR_VERSION (2)
-
#define NCR_GETCC_WITHMSG
/*==========================================================
@@ -105,11 +125,22 @@
#include <linux/version.h>
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-#include "linux/blk.h"
+#include <linux/blk.h>
#else
#include "../block/blk.h"
#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
+#include <linux/init.h>
+#else
+#ifndef __initdata
+#define __initdata
+#endif
+#ifndef __initfunc
+#define __initfunc(__arginit) __arginit
+#endif
+#endif
+
#include "scsi.h"
#include "hosts.h"
#include "constants.h"
@@ -132,15 +163,6 @@
*/
/*
-** Proc info and user command support
-*/
-
-#ifdef SCSI_NCR_PROC_INFO_SUPPORT
-#define SCSI_NCR_PROFILE
-#define SCSI_NCR_USER_COMMAND
-#endif
-
-/*
** SCSI address of this device.
** The boot routines should have set it.
** If not, use this.
@@ -151,24 +173,6 @@
#endif
/*
-** The maximal synchronous frequency in kHz.
-** (0=asynchronous)
-*/
-
-#ifndef SCSI_NCR_MAX_SYNC
-#define SCSI_NCR_MAX_SYNC (10000)
-#endif
-
-/*
-** The maximal bus with (in log2 byte)
-** (0=8 bit, 1=16 bit)
-*/
-
-#ifndef SCSI_NCR_MAX_WIDE
-#define SCSI_NCR_MAX_WIDE (1)
-#endif
-
-/*
** The maximum number of tags per logic unit.
** Used only for disk devices that support tags.
*/
@@ -177,13 +181,6 @@
#define SCSI_NCR_MAX_TAGS (4)
#endif
-/*==========================================================
-**
-** Configuration and Debugging
-**
-**==========================================================
-*/
-
/*
** Number of targets supported by the driver.
** n permits target numbers 0..n-1.
@@ -209,7 +206,14 @@
#else
#define MAX_LUN (1)
#endif
+
+/*
+** Asynchronous pre-scaler (ns). Shall be 40
+*/
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
/*
** The maximum number of jobs scheduled for starting.
@@ -231,12 +235,11 @@
#define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
/*
-** The maximum transfer length (should be >= 64k).
-** MUST NOT be greater than (MAX_SCATTER-1) * NBPG.
+** Io mapped or memory mapped.
*/
-#if 0
-#define MAX_SIZE ((MAX_SCATTER-1) * (long) NBPG)
+#if defined(SCSI_NCR_IOMAPPED)
+#define NCR_IOMAPPED
#endif
/*
@@ -245,10 +248,6 @@
#define NCR_SNOOP_TIMEOUT (1000000)
-#if defined(SCSI_NCR_IOMAPPED) || defined(__alpha__)
-#define NCR_IOMAPPED
-#endif
-
/*==========================================================
**
** Defines for Linux.
@@ -287,7 +286,7 @@
** virtual memory addresses of the kernel data segment into
** IO bus adresses.
** On i386 architecture, IO bus addresses match the physical
-** addresses. But on Alpha architecture they are different.
+** addresses. But on other architectures they can be different.
** In the original Bsd driver, vtophys() is called to translate
** data addresses to IO bus addresses. In order to minimize
** change, I decide to define vtophys() as virt_to_bus().
@@ -299,27 +298,42 @@
/*
** Memory mapped IO
**
-** Linux 1.3.X allow to remap physical pages addresses greater than
-** the highest physical memory address to kernel virtual pages.
-** We must use vremap() to map the page and vfree() to unmap it.
-** The memory base of ncr chips is set by the bios at a high physical
-** address. Also we can map it, and MMIO is possible.
+** Since linux-2.1, we must use ioremap() to map the io memory space.
+** iounmap() to unmap it. That allows portability.
+** Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater
+** than the highest physical memory address to kernel virtual pages with
+** vremap() / vfree(). That was not portable but worked with i386
+** architecture.
*/
-static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
+__initfunc(
+static vm_offset_t remap_pci_mem(u_long base, u_long size)
+)
{
u_long page_base = ((u_long) base) & PAGE_MASK;
u_long page_offs = ((u_long) base) - page_base;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+ u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);
+#else
u_long page_remapped = (u_long) vremap(page_base, page_offs+size);
+#endif
return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
}
-static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
-{
- if (vaddr) vfree((void *) (vaddr & PAGE_MASK));
-}
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
+{
+ if (vaddr)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+ iounmap((void *) (vaddr & PAGE_MASK));
#else
+ vfree((void *) (vaddr & PAGE_MASK));
+#endif
+}
+
+#else /* linux-1.2.13 */
/*
** Linux 1.2.X assumes that addresses (virtual, physical, bus)
@@ -332,6 +346,10 @@
#define vtophys(p) ((u_long) (p))
#endif
+/*
+** Insert a delay in micro-seconds.
+*/
+
static void DELAY(long us)
{
for (;us>1000;us-=1000) udelay(1000);
@@ -348,31 +366,81 @@
** I notice that kmalloc() returns NULL during host attach under
** Linux 1.2.13. But this ncr driver is reliable enough to
** accomodate with this joke.
-**/
+**
+** kmalloc() only ensure 8 bytes boundary alignment.
+** The NCR need better alignment for cache line bursting.
+** The global header is moved betewen the NCB and CCBs and need
+** origin and destination addresses to have same lower four bits.
+**
+** We use 32 boundary alignment for NCB and CCBs and offset multiple
+** of 32 for global header fields. That's too much but at least enough.
+*/
+
+#define ALIGN_SIZE(shift) (1UL << shift)
+#define ALIGN_MASK(shift) (~(ALIGN_SIZE(shift)-1))
+
+#define NCB_ALIGN_SHIFT 5
+#define CCB_ALIGN_SHIFT 5
+#define LCB_ALIGN_SHIFT 5
+#define SCR_ALIGN_SHIFT 5
+
+#define NCB_ALIGN_SIZE ALIGN_SIZE(NCB_ALIGN_SHIFT)
+#define NCB_ALIGN_MASK ALIGN_MASK(NCB_ALIGN_SHIFT)
+#define CCB_ALIGN_SIZE ALIGN_SIZE(CCB_ALIGN_SHIFT)
+#define CCB_ALIGN_MASK ALIGN_MASK(CCB_ALIGN_SHIFT)
+#define SCR_ALIGN_SIZE ALIGN_SIZE(SCR_ALIGN_SHIFT)
+#define SCR_ALIGN_MASK ALIGN_MASK(SCR_ALIGN_SHIFT)
+
+static void *m_alloc(int size, int a_shift)
+{
+ u_long addr;
+ void *ptr;
+ u_long a_size, a_mask;
+
+ if (a_shift < 3)
+ a_shift = 3;
+
+ a_size = ALIGN_SIZE(a_shift);
+ a_mask = ALIGN_MASK(a_shift);
+
+ ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
+ if (ptr) {
+ addr = (((u_long) ptr) + a_size) & a_mask;
+ *((void **) (addr - sizeof(void *))) = ptr;
+ ptr = (void *) addr;
+ }
-static inline void *m_alloc(int size)
-{
- void *ptr = (void *) kmalloc(size, GFP_ATOMIC);
- if (((unsigned long) ptr) & 3)
- panic("ncr53c8xx: kmalloc returns misaligned address %lx\n", (unsigned long) ptr);
return ptr;
}
-static inline void m_free(void *ptr, int size)
- { kfree(ptr); }
+#ifdef MODULE
+static void m_free(void *ptr, int size)
+{
+ u_long addr;
+
+ if (ptr) {
+ addr = (u_long) ptr;
+ ptr = *((void **) (addr - sizeof(void *)));
+
+ kfree(ptr);
+ }
+}
+#endif
/*
** Transfer direction
**
-** The middle scsi driver of Linux does not provide the transfer
-** direction in the command structure.
-** FreeBsd ncr driver require this information.
-**
-** I spent some hours to read the scsi2 documentation to see if
-** it was possible to deduce the direction of transfer from the opcode
-** of the command. It seems that it's OK.
-** guess_xfer_direction() seems to work. If it's wrong we will
-** get a phase mismatch on some opcode.
+** Low-level scsi drivers under Linux do not receive the expected
+** data transfer direction from upper scsi drivers.
+** The driver will only check actual data direction for common
+** scsi opcodes. Other ones may cause problem, since they may
+** depend on device type or be vendor specific.
+** I would prefer to never trust the device for data direction,
+** but that is not possible.
+**
+** The original driver requires the expected direction to be known.
+** The Linux version of the driver has been enhanced in order to
+** be able to transfer data in the direction choosen by the target.
*/
#define XferNone 0
@@ -385,6 +453,8 @@
** Head of list of NCR boards
**
** Host is retrieved by its irq level.
+** If interrupts are shared, the internal host control block
+** address (struct ncb) is used as device id.
*/
static struct Scsi_Host *first_host = NULL;
@@ -419,6 +489,41 @@
unsigned char and_map[MAX_TARGET];
} target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES };
+/*
+** Driver setup.
+**
+** This structure is initialized from linux config options.
+** It can be overridden at boot-up by the boot command line.
+*/
+struct ncr_driver_setup {
+ unsigned master_parity : 1;
+ unsigned scsi_parity : 1;
+ unsigned disconnection : 1;
+ unsigned special_features : 1;
+ unsigned ultra_scsi : 2;
+ unsigned force_sync_nego: 1;
+ unsigned reverse_probe: 1;
+ unsigned pci_fix_up: 4;
+ u_char use_nvram;
+ u_char verbose;
+ u_char default_tags;
+ u_short default_sync;
+ u_short debug;
+ u_char burst_max;
+ u_char led_pin;
+ u_char max_wide;
+ u_char settle_delay;
+ u_char diff_support;
+ u_char irqm;
+};
+
+static struct ncr_driver_setup
+ driver_setup = SCSI_NCR_DRIVER_SETUP;
+
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+ driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#endif
/*
** Other Linux definitions
@@ -438,7 +543,149 @@
static void ncr53c8xx_timeout(unsigned long np);
-#define bootverbose 1
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+** Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+ u_short start_marker; /* 0x0000 */
+ u_short byte_count; /* excluding header/trailer */
+ u_short checksum;
+
+/* Controller set up 20 bytes */
+ u_short word0; /* 0x3000 */
+ u_short word2; /* 0x0000 */
+ u_short word4; /* 0x0000 */
+ u_short flags;
+#define SYMBIOS_SCAM_ENABLE (1)
+#define SYMBIOS_PARITY_ENABLE (1<<1)
+#define SYMBIOS_VERBOSE_MSGS (1<<2)
+ u_short flags1;
+#define SYMBIOS_SCAN_HI_LO (1)
+ u_short word10; /* 0x00 */
+ u_short word12; /* 0x00 */
+ u_char host_id;
+ u_char byte15; /* 0x04 */
+ u_short word16; /* 0x0410 */
+ u_short word18; /* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+ struct Symbios_host{
+ u_char word0; /* 0x0004:ok / 0x0000:nok */
+ u_short device_id; /* PCI device id */
+ u_short vendor_id; /* PCI vendor id */
+ u_char byte6; /* 0x00 */
+ u_char device_fn; /* PCI device/function number << 3*/
+ u_short word8;
+ u_short flags;
+#define SYMBIOS_INIT_SCAN_AT_BOOT (1)
+ u_short io_port; /* PCI io_port address */
+ } host[4];
+
+/* Targets 8 bytes * 16 */
+ struct Symbios_target {
+ u_short flags;
+#define SYMBIOS_DISCONNECT_ENABLE (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1)
+#define SYMBIOS_SCAN_LUNS (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3)
+ u_char bus_width; /* 0x08/0x10 */
+ u_char sync_offset;
+ u_char sync_period; /* 4*period factor */
+ u_char byte6; /* 0x00 */
+ u_short timeout;
+ } target[16];
+ u_char spare_devices[19*8];
+ u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram Symbios_nvram;
+typedef struct Symbios_host Symbios_host;
+typedef struct Symbios_target Symbios_target;
+
+/*
+** Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+ struct Tekram_target {
+ u_char flags;
+#define TEKRAM_PARITY_CHECK (1)
+#define TEKRAM_SYNC_NEGO (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE (1<<2)
+#define TEKRAM_START_CMD (1<<3)
+#define TEKRAM_TAGGED_COMMANDS (1<<4)
+#define TEKRAM_WIDE_NEGO (1<<5)
+ u_char sync_index;
+ u_short word2;
+ } target[16];
+ u_char host_id;
+ u_char flags;
+#define TEKRAM_MORE_THAN_2_DRIVES (1)
+#define TEKRAM_DRIVES_SUP_1GB (1<<1)
+#define TEKRAM_RESET_ON_POWER_ON (1<<2)
+#define TEKRAM_ACTIVE_NEGATION (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK (1<<4)
+#define TEKRAM_SCAN_LUNS (1<<5)
+#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */
+ u_char boot_delay_index;
+ u_char max_tags_index;
+ u_short flags1;
+#define TEKRAM_F2_F6_ENABLED (1)
+ u_short spare[29];
+};
+typedef struct Tekram_nvram Tekram_nvram;
+typedef struct Tekram_target Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to
+** transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+ int bus;
+ u_char device_fn;
+ u_int base;
+ u_int io_port;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ u_int port;
+ volatile struct ncr_reg *reg;
+} ncr_slot;
+
+typedef struct {
+ int type;
+#define SCSI_NCR_SYMBIOS_NVRAM (1)
+#define SCSI_NCR_TEKRAM_NVRAM (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ } data;
+#endif
+} ncr_nvram;
+
+/*
+** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+** to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+ ncr_slot slot;
+ ncr_chip chip;
+ ncr_nvram *nvram;
+ int attached;
+} ncr_device;
/*==========================================================
**
@@ -466,7 +713,7 @@
** Can be changed at runtime too.
*/
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
#define DEBUG_FLAGS ncr_debug
#else
#define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
@@ -499,49 +746,45 @@
** Access to the controller chip.
**
** If NCR_IOMAPPED is defined, only IO are used by the driver.
-** Else, we begins initialisations by using MMIO.
-** If cache test fails, we retry using IO mapped.
-** The flag "use_mmio" in the ncb structure is set to 1 if
-** mmio is possible.
**
**==========================================================
*/
/*
-** IO mapped input / ouput
+** IO mapped only input / ouput
*/
-#define IOM_INB(r) inb (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INB_OFF(o) inb (np->port + (o))
-#define IOM_INW(r) inw (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INL(r) inl (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INL_OFF(o) inl (np->port + (o))
-
-#define IOM_OUTB(r, val) outb ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTW(r, val) outw ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTL(r, val) outl ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTL_OFF(o, val) outl ((val), np->port + (o))
+#define IOM_INB(r) inb (np->port + offsetof(struct ncr_reg, r))
+#define IOM_INB_OFF(o) inb (np->port + (o))
+#define IOM_INW(r) inw (np->port + offsetof(struct ncr_reg, r))
+#define IOM_INL(r) inl (np->port + offsetof(struct ncr_reg, r))
+#define IOM_INL_OFF(o) inl (np->port + (o))
+
+#define IOM_OUTB(r, val) outb ((val), np->port+offsetof(struct ncr_reg,r))
+#define IOM_OUTW(r, val) outw ((val), np->port+offsetof(struct ncr_reg,r))
+#define IOM_OUTL(r, val) outl ((val), np->port+offsetof(struct ncr_reg,r))
+#define IOM_OUTL_OFF(o, val) outl ((val), np->port + (o))
/*
** MEMORY mapped IO input / output
*/
-#define MMIO_INB(r) readb(&np->reg_remapped->r)
-#define MMIO_INB_OFF(o) readb((char *)np->reg_remapped + (o))
-#define MMIO_INW(r) readw(&np->reg_remapped->r)
-#define MMIO_INL(r) readl(&np->reg_remapped->r)
-#define MMIO_INL_OFF(o) readl((char *)np->reg_remapped + (o))
-
-#define MMIO_OUTB(r, val) writeb((val), &np->reg_remapped->r)
-#define MMIO_OUTW(r, val) writew((val), &np->reg_remapped->r)
-#define MMIO_OUTL(r, val) writel((val), &np->reg_remapped->r)
-#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg_remapped + (o))
+#define MMIO_INB(r) readb(&np->reg->r)
+#define MMIO_INB_OFF(o) readb((char *)np->reg + (o))
+#define MMIO_INW(r) readw(&np->reg->r)
+#define MMIO_INL(r) readl(&np->reg->r)
+#define MMIO_INL_OFF(o) readl((char *)np->reg + (o))
+
+#define MMIO_OUTB(r, val) writeb((val), &np->reg->r)
+#define MMIO_OUTW(r, val) writew((val), &np->reg->r)
+#define MMIO_OUTL(r, val) writel((val), &np->reg->r)
+#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg + (o))
/*
-** IO mapped only input / output
+** IO mapped input / output
*/
-#ifdef NCR_IOMAPPED
+#if defined(NCR_IOMAPPED)
#define INB(r) IOM_INB(r)
#define INB_OFF(o) IOM_INB_OFF(o)
@@ -555,24 +798,35 @@
#define OUTL_OFF(o, val) IOM_OUTL_OFF(o, val)
/*
-** IO mapped or MEMORY mapped depending on flag "use_mmio"
+** MEMORY mapped only input / output
*/
#else
-#define INB(r) (np->use_mmio ? MMIO_INB(r) : IOM_INB(r))
-#define INB_OFF(o) (np->use_mmio ? MMIO_INB_OFF(o) : IOM_INB_OFF(o))
-#define INW(r) (np->use_mmio ? MMIO_INW(r) : IOM_INW(r))
-#define INL(r) (np->use_mmio ? MMIO_INL(r) : IOM_INL(r))
-#define INL_OFF(o) (np->use_mmio ? MMIO_INL_OFF(o) : IOM_INL_OFF(o))
-
-#define OUTB(r, val) (np->use_mmio ? MMIO_OUTB(r, val) : IOM_OUTB(r, val))
-#define OUTW(r, val) (np->use_mmio ? MMIO_OUTW(r, val) : IOM_OUTW(r, val))
-#define OUTL(r, val) (np->use_mmio ? MMIO_OUTL(r, val) : IOM_OUTL(r, val))
-#define OUTL_OFF(o, val) (np->use_mmio ? MMIO_OUTL_OFF(o, val) : IOM_OUTL_OFF(o, val))
+#define INB(r) MMIO_INB(r)
+#define INB_OFF(o) MMIO_INB_OFF(o)
+#define INW(r) MMIO_INW(r)
+#define INL(r) MMIO_INL(r)
+#define INL_OFF(o) MMIO_INL_OFF(o)
+
+#define OUTB(r, val) MMIO_OUTB(r, val)
+#define OUTW(r, val) MMIO_OUTW(r, val)
+#define OUTL(r, val) MMIO_OUTL(r, val)
+#define OUTL_OFF(o, val) MMIO_OUTL_OFF(o, val)
#endif
+/*
+** Set bit field ON, OFF
+*/
+
+#define OUTONB(r, m) OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m) OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m) OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
/*==========================================================
**
** Command control block states.
@@ -614,7 +868,9 @@
#define SIR_REJECT_SENT (10)
#define SIR_IGN_RESIDUE (11)
#define SIR_MISSING_SAVE (12)
-#define SIR_MAX (12)
+#define SIR_DATA_IO_IS_OUT (13)
+#define SIR_DATA_IO_IS_IN (14)
+#define SIR_MAX (14)
/*==========================================================
**
@@ -673,7 +929,6 @@
*/
#define CCB_MAGIC (0xf2691ad2)
-#define MAX_TAGS (16) /* hard limit */
/*==========================================================
**
@@ -713,7 +968,13 @@
#define UC_SETFLAG 15
#define UC_CLEARPROF 16
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+#define UC_DEBUG_ERROR_RECOVERY 17
+#endif
+
#define UF_TRACE (0x01)
+#define UF_NODISC (0x02)
+#define UF_NOSCAN (0x04)
/*---------------------------------------
**
@@ -727,7 +988,6 @@
u_long end;
u_long select;
u_long command;
- u_long data;
u_long status;
u_long disconnect;
u_long reselect;
@@ -812,6 +1072,14 @@
ccb_p hold_cp;
/*
+ ** pointer to ccb used for negotiating.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ ccb_p nego_cp;
+
+ /*
** statistical data
*/
@@ -821,13 +1089,18 @@
/*
** user settable limits for sync transfer
** and tagged commands.
+ ** These limits are read from the NVRAM if present.
*/
u_char usrsync;
- u_char usrtags;
u_char usrwide;
+ u_char usrtags;
u_char usrflag;
+ u_char numtags;
+ u_char maxtags;
+ u_short num_good;
+
/*
** negotiation of wide and synch transfer.
** device quirks.
@@ -911,6 +1184,15 @@
u_char usetags;
u_char lasttag;
+ /*
+ ** Linux specific fields:
+ ** Number of active commands and current credit.
+ ** Should be managed by the generic scsi driver
+ */
+
+ u_char active;
+ u_char opennings;
+
/*-----------------------------------------------
** Flag to force M_ORDERED_TAG on next command
** in order to avoid spurious timeout when
@@ -960,9 +1242,9 @@
** the last transfer command.
*/
- u_long savep;
- u_long lastp;
- u_long goalp;
+ u_int32 savep;
+ u_int32 lastp;
+ u_int32 goalp;
/*
** The virtual address of the ccb
@@ -1093,6 +1375,14 @@
struct ccb {
/*
+ ** This field forces 32 bytes alignement for phys.header,
+ ** in order to use cache line bursting when copying it
+ ** to the ncb.
+ */
+
+ struct link filler[2];
+
+ /*
** during reselection the ncr jumps to this point.
** If a "SIMPLE_TAG" message was received,
** then SFBR is set to the tag.
@@ -1199,6 +1489,14 @@
*/
u_char tag;
+
+ /*
+ ** Number of segments of the scatter list.
+ ** Used for recalculation of savep/goalp/lastp on
+ ** SIR_DATA_IO_IS_OUT interrupt.
+ */
+
+ u_char segments;
};
#define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl))
@@ -1211,32 +1509,69 @@
*/
struct ncb {
+ /*
+ ** The global header.
+ ** Accessible to both the host and the
+ ** script-processor.
+ ** Is 32 bytes aligned since ncb is, in order to
+ ** allow cache line bursting when copying it from or
+ ** to ccbs.
+ */
+ struct head header;
+
/*-----------------------------------------------
** Specific Linux fields
**-----------------------------------------------
*/
int unit; /* Unit number */
- int chip; /* Chip number */
+ char chip_name[8]; /* Chip name */
+ char inst_name[16]; /* Instance name */
struct timer_list timer; /* Timer link header */
int ncr_cache; /* Cache test variable */
- int release_stage; /* Synchronisation stage on release */
Scsi_Cmnd *waiting_list; /* Waiting list header for commands */
/* that we can't put into the squeue */
-#ifndef NCR_IOMAPPED
- volatile struct ncr_reg*
- reg_remapped; /* Virtual address of the memory */
- /* base of the ncr chip */
- int use_mmio; /* Indicate mmio is OK */
+ u_long settle_time; /* Reset in progess */
+ u_char release_stage; /* Synchronisation stage on release */
+ u_char verbose; /* Boot verbosity for this controller*/
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ u_char debug_error_recovery;
+ u_char stalling;
+ u_char assert_atn;
#endif
+
/*-----------------------------------------------
** Added field to support differences
** between ncr chips.
+ ** sv_xxx are some io register bit value at start-up and
+ ** so assumed to have been set by the sdms bios.
+ ** rv_xxx are the bit fields of io register that will keep
+ ** the features used by the driver.
**-----------------------------------------------
*/
u_short device_id;
u_char revision_id;
-#define ChipDevice ((np)->device_id)
-#define ChipVersion ((np)->revision_id & 0xf0)
+
+ u_char sv_scntl0;
+ u_char sv_scntl3;
+ u_char sv_dmode;
+ u_char sv_dcntl;
+ u_char sv_ctest3;
+ u_char sv_ctest4;
+ u_char sv_ctest5;
+ u_char sv_gpcntl;
+ u_char sv_stest2;
+ u_char sv_stest4;
+
+ u_char rv_scntl0;
+ u_char rv_scntl3;
+ u_char rv_dmode;
+ u_char rv_dcntl;
+ u_char rv_ctest3;
+ u_char rv_ctest4;
+ u_char rv_ctest5;
+ u_char rv_stest2;
+
+ u_char scsi_mode;
/*-----------------------------------------------
** Scripts ..
@@ -1262,6 +1597,9 @@
vm_offset_t vaddr;
vm_offset_t paddr;
+ vm_offset_t vaddr2;
+ vm_offset_t paddr2;
+
/*
** pointer to the chip's registers.
*/
@@ -1269,14 +1607,22 @@
struct ncr_reg* reg;
/*
- ** A copy of the script, relocated for this ncb.
+ ** A copy of the scripts, relocated for this ncb.
+ */
+ struct script *script0;
+ struct scripth *scripth0;
+
+ /*
+ ** Scripts instance virtual address.
*/
struct script *script;
+ struct scripth *scripth;
/*
- ** Physical address of this instance of ncb->script
+ ** Scripts instance physical address.
*/
u_long p_script;
+ u_long p_scripth;
/*
** The SCSI address of the host adapter.
@@ -1284,11 +1630,21 @@
u_char myaddr;
/*
+ ** Max dwords burst supported by the adapter.
+ */
+ u_char maxburst; /* log base 2 of dwords burst */
+
+ /*
** timing parameters
*/
- u_char ns_async;
- u_char ns_sync;
- u_char rv_scntl3;
+ u_char minsync; /* Minimum sync period factor */
+ u_char maxsync; /* Maximum sync period factor */
+ u_char maxoffs; /* Max scsi offset */
+ u_char multiplier; /* Clock multiplier (1,2,4) */
+ u_char clock_divn; /* Number of clock divisors */
+ u_long clock_khz; /* SCSI clock frequency in KHz */
+ u_int features; /* Chip features map */
+
/*-----------------------------------------------
** Link to the generic SCSI driver
@@ -1314,7 +1670,7 @@
/*
** Start queue.
*/
- u_long squeue [MAX_START];
+ u_int32 squeue [MAX_START];
u_short squeueput;
u_short actccbs;
@@ -1343,19 +1699,12 @@
u_long disc_ref;
/*
- ** The global header.
- ** Accessible to both the host and the
- ** script-processor.
- */
- struct head header;
-
- /*
** The global control block.
** It's used only during the configuration phase.
** A target control block will be created
** after the first successful transfer.
*/
- struct ccb ccb;
+ struct ccb *ccb;
/*
** message buffers.
@@ -1365,7 +1714,7 @@
*/
u_char msgout[8];
u_char msgin [8];
- u_long lastmsg;
+ u_int32 lastmsg;
/*
** Buffer for STATUS_IN phase.
@@ -1393,7 +1742,8 @@
u_short irq;
};
-#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl))
+#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl))
+#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth, lbl))
/*==========================================================
**
@@ -1416,12 +1766,15 @@
**----------------------------------------------------------
*/
+/*
+** Script fragments which are loaded into the on-board RAM
+** of 825A, 875 and 895 chips.
+*/
struct script {
ncrcmd start [ 7];
ncrcmd start0 [ 2];
ncrcmd start1 [ 3];
ncrcmd startpos [ 1];
- ncrcmd tryloop [MAX_START*5+2];
ncrcmd trysel [ 8];
ncrcmd skip [ 8];
ncrcmd skip2 [ 3];
@@ -1439,14 +1792,6 @@
ncrcmd status [ 27];
ncrcmd msg_in [ 26];
ncrcmd msg_bad [ 6];
- ncrcmd msg_parity [ 6];
- ncrcmd msg_reject [ 8];
- ncrcmd msg_ign_residue [ 32];
- ncrcmd msg_extended [ 18];
- ncrcmd msg_ext_2 [ 18];
- ncrcmd msg_wdtr [ 27];
- ncrcmd msg_ext_3 [ 18];
- ncrcmd msg_sdtr [ 27];
ncrcmd complete [ 13];
ncrcmd cleanup [ 12];
ncrcmd cleanup0 [ 11];
@@ -1458,6 +1803,30 @@
ncrcmd disconnect1 [ 23];
ncrcmd msg_out [ 9];
ncrcmd msg_out_done [ 7];
+ ncrcmd badgetcc [ 6];
+ ncrcmd reselect [ 8];
+ ncrcmd reselect1 [ 8];
+ ncrcmd reselect2 [ 8];
+ ncrcmd resel_tmp [ 5];
+ ncrcmd resel_lun [ 18];
+ ncrcmd resel_tag [ 24];
+ ncrcmd data_io [ 6];
+ ncrcmd data_in [MAX_SCATTER * 4 + 4];
+};
+
+/*
+** Script fragments which stay in main memory for all chips.
+*/
+struct scripth {
+ ncrcmd tryloop [MAX_START*5+2];
+ ncrcmd msg_parity [ 6];
+ ncrcmd msg_reject [ 8];
+ ncrcmd msg_ign_residue [ 32];
+ ncrcmd msg_extended [ 18];
+ ncrcmd msg_ext_2 [ 18];
+ ncrcmd msg_wdtr [ 27];
+ ncrcmd msg_ext_3 [ 18];
+ ncrcmd msg_sdtr [ 27];
ncrcmd msg_out_abort [ 10];
ncrcmd getcc [ 4];
ncrcmd getcc1 [ 5];
@@ -1467,14 +1836,7 @@
ncrcmd getcc2 [ 14];
#endif
ncrcmd getcc3 [ 10];
- ncrcmd badgetcc [ 6];
- ncrcmd reselect [ 12];
- ncrcmd reselect2 [ 6];
- ncrcmd resel_tmp [ 5];
- ncrcmd resel_lun [ 18];
- ncrcmd resel_tag [ 24];
- ncrcmd data_in [MAX_SCATTER * 4 + 7];
- ncrcmd data_out [MAX_SCATTER * 4 + 7];
+ ncrcmd data_out [MAX_SCATTER * 4 + 4];
ncrcmd aborttag [ 4];
ncrcmd abort [ 22];
ncrcmd snooptest [ 9];
@@ -1493,51 +1855,59 @@
static void ncr_alloc_ccb (ncb_p np, u_long t, u_long l);
static void ncr_complete (ncb_p np, ccb_p cp);
static void ncr_exception (ncb_p np);
-static void ncr_free_ccb (ncb_p np, ccb_p cp);
-static void ncr_getclock (ncb_p np, u_char scntl3);
+static void ncr_free_ccb (ncb_p np, ccb_p cp, u_long t, u_long l);
+static void ncr_getclock (ncb_p np, int mult);
+static void ncr_selectclock (ncb_p np, u_char scntl3);
static ccb_p ncr_get_ccb (ncb_p np, u_long t,u_long l);
static void ncr_init (ncb_p np, char * msg, u_long code);
-static int ncr_intr (ncb_p np);
+static int ncr_int_sbmc (ncb_p np);
+static int ncr_int_par (ncb_p np);
static void ncr_int_ma (ncb_p np);
static void ncr_int_sir (ncb_p np);
static void ncr_int_sto (ncb_p np);
static u_long ncr_lookup (char* id);
static void ncr_negotiate (struct ncb* np, struct tcb* tp);
+static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * xp);
-#ifdef SCSI_NCR_PROFILE
-static int ncr_delta (u_long from, u_long to);
+#ifdef SCSI_NCR_PROFILE_SUPPORT
static void ncb_profile (ncb_p np, ccb_p cp);
#endif
static void ncr_script_copy_and_bind
- (struct script * script, ncb_p np);
-static void ncr_script_fill (struct script * scr);
+ (ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
+static void ncr_script_fill (struct script * scr, struct scripth * scripth);
static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd);
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags);
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer);
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags);
+static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
static void ncr_settags (tcb_p tp, lcb_p lp);
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide);
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack);
static int ncr_show_msg (u_char * msg);
static int ncr_snooptest (ncb_p np);
static void ncr_timeout (ncb_p np);
static void ncr_wakeup (ncb_p np, u_long code);
+static void ncr_start_reset (ncb_p np, int settle_delay);
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
static void ncr_usercmd (ncb_p np);
#endif
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, u_short device_id,
- u_char revision_id, int chip, u_int base, u_int io_port,
- int irq, int bus, u_char device_fn);
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
static void process_waiting_list(ncb_p np, int sts);
+#define remove_from_waiting_list(np, cmd) \
+ retrieve_from_waiting_list(1, (np), (cmd))
#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
-#define abort_waiting_list(np) process_waiting_list((np), DID_ABORT)
#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
/*==========================================================
**
**
@@ -1557,24 +1927,13 @@
+ (u_long) sizeof (struct tcb) * 2;
#endif
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
#endif
-/*==========================================================
-**
-**
-** Global static data: auto configure
-**
-**
-**==========================================================
-*/
-
-static char *ncr_name (ncb_p np)
+static inline char *ncr_name (ncb_p np)
{
- static char name[10];
- sprintf(name, "ncr53c%d-%d", np->chip, np->unit);
- return (name);
+ return np->inst_name;
}
@@ -1601,10 +1960,12 @@
#define RELOC_LABEL 0x50000000
#define RELOC_REGISTER 0x60000000
#define RELOC_KVAR 0x70000000
+#define RELOC_LABELH 0x80000000
#define RELOC_MASK 0xf0000000
#define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label))
#define PADDR(label) (RELOC_LABEL | offsetof(struct script, label))
+#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label))
#define RADDR(label) (RELOC_REGISTER | REG(label))
#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
#define KVAR(which) (RELOC_KVAR | (which))
@@ -1618,10 +1979,10 @@
* Kernel variables referenced in the scripts.
* THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
*/
-static void *script_kvars[] =
+static void *script_kvars[] __initdata =
{ (void *)&jiffies };
-static struct script script0 = {
+static struct script script0 __initdata = {
/*--------------------------< START >-----------------------*/ {
/*
** Claim to be still alive ...
@@ -1661,33 +2022,7 @@
*/
SCR_JUMP,
}/*-------------------------< STARTPOS >--------------------*/,{
- PADDR(tryloop),
-}/*-------------------------< TRYLOOP >---------------------*/,{
-/*
-** Load an entry of the start queue into dsa
-** and try to start it by jumping to TRYSEL.
-**
-** Because the size depends on the
-** #define MAX_START parameter, it is filled
-** in at runtime.
-**
-**-----------------------------------------------------------
-**
-** ##===========< I=0; i<MAX_START >===========
-** || SCR_COPY (4),
-** || NADDR (squeue[i]),
-** || RADDR (dsa),
-** || SCR_CALL,
-** || PADDR (trysel),
-** ##==========================================
-**
-** SCR_JUMP,
-** PADDR(tryloop),
-**
-**-----------------------------------------------------------
-*/
-0
-
+ PADDRH(tryloop),
}/*-------------------------< TRYSEL >----------------------*/,{
/*
** Now:
@@ -1742,7 +2077,7 @@
** patch the launch field.
** should look like an idle process.
*/
- SCR_COPY (4),
+ SCR_COPY_F (4),
RADDR (dsa),
PADDR (skip2),
SCR_COPY (8),
@@ -1848,7 +2183,7 @@
** We patch the address part of a
** COPY command with the DSA-register.
*/
- SCR_COPY (4),
+ SCR_COPY_F (4),
RADDR (dsa),
PADDR (loadpos),
/*
@@ -1925,7 +2260,7 @@
SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
PADDR (msg_in),
SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
- PADDR (msg_reject),
+ PADDRH (msg_reject),
/*
** normal processing
*/
@@ -2142,7 +2477,7 @@
SCR_FROM_REG (socl),
0,
SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
+ PADDRH (msg_parity),
SCR_FROM_REG (scratcha),
0,
/*
@@ -2157,13 +2492,13 @@
SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
PADDR (disconnect),
SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
- PADDR (msg_extended),
+ PADDRH (msg_extended),
SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
PADDR (clrack),
SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
- PADDR (msg_reject),
+ PADDRH (msg_reject),
SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
- PADDR (msg_ign_residue),
+ PADDRH (msg_ign_residue),
/*
** Rest of the messages left as
** an exercise ...
@@ -2182,301 +2517,41 @@
SCR_JUMP,
PADDR (setmsg),
-}/*-------------------------< MSG_PARITY >---------------*/,{
+}/*-------------------------< COMPLETE >-----------------*/,{
/*
- ** count it
+ ** Complete message.
+ **
+ ** If it's not the get condition code,
+ ** copy TEMP register to LASTP in header.
*/
- SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+ SCR_FROM_REG (SS_REG),
0,
- /*
- ** send a "message parity error" message.
+/*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
+ 12,
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.lastp),
+/*>>>*/ /*
+ ** When we terminate the cycle by clearing ACK,
+ ** the target may disconnect immediately.
+ **
+ ** We don't want to be told of an
+ ** "unexpected disconnect",
+ ** so we disable this feature.
*/
- SCR_LOAD_REG (scratcha, M_PARITY),
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
0,
- SCR_JUMP,
- PADDR (setmsg),
-}/*-------------------------< MSG_REJECT >---------------*/,{
/*
- ** If a negotiation was in progress,
- ** negotiation failed.
+ ** Terminate cycle ...
*/
- SCR_FROM_REG (HS_REG),
+ SCR_CLR (SCR_ACK|SCR_ATN),
0,
- SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
- SIR_NEGO_FAILED,
/*
- ** else make host log this message
- */
- SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
- SIR_REJECT_RECEIVED,
- SCR_JUMP,
- PADDR (clrack),
-
-}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
- /*
- ** Terminate cycle
+ ** ... and wait for the disconnect.
*/
- SCR_CLR (SCR_ACK),
+ SCR_WAIT_DISC,
0,
- SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
- PADDR (dispatch),
- /*
- ** get residue size.
- */
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin[1]),
- /*
- ** Check for message parity error.
- */
- SCR_TO_REG (scratcha),
- 0,
- SCR_FROM_REG (socl),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
- SCR_FROM_REG (scratcha),
- 0,
- /*
- ** Size is 0 .. ignore message.
- */
- SCR_JUMP ^ IFTRUE (DATA (0)),
- PADDR (clrack),
- /*
- ** Size is not 1 .. have to interrupt.
- */
-/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)),
- 40,
- /*
- ** Check for residue byte in swide register
- */
- SCR_FROM_REG (scntl2),
- 0,
-/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
- 16,
- /*
- ** There IS data in the swide register.
- ** Discard it.
- */
- SCR_REG_REG (scntl2, SCR_OR, WSR),
- 0,
- SCR_JUMP,
- PADDR (clrack),
- /*
- ** Load again the size to the sfbr register.
- */
-/*>>>*/ SCR_FROM_REG (scratcha),
- 0,
-/*>>>*/ SCR_INT,
- SIR_IGN_RESIDUE,
- SCR_JUMP,
- PADDR (clrack),
-
-}/*-------------------------< MSG_EXTENDED >-------------*/,{
- /*
- ** Terminate cycle
- */
- SCR_CLR (SCR_ACK),
- 0,
- SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
- PADDR (dispatch),
- /*
- ** get length.
- */
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin[1]),
- /*
- ** Check for message parity error.
- */
- SCR_TO_REG (scratcha),
- 0,
- SCR_FROM_REG (socl),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
- SCR_FROM_REG (scratcha),
- 0,
- /*
- */
- SCR_JUMP ^ IFTRUE (DATA (3)),
- PADDR (msg_ext_3),
- SCR_JUMP ^ IFFALSE (DATA (2)),
- PADDR (msg_bad),
-}/*-------------------------< MSG_EXT_2 >----------------*/,{
- SCR_CLR (SCR_ACK),
- 0,
- SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
- PADDR (dispatch),
- /*
- ** get extended message code.
- */
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin[2]),
- /*
- ** Check for message parity error.
- */
- SCR_TO_REG (scratcha),
- 0,
- SCR_FROM_REG (socl),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
- SCR_FROM_REG (scratcha),
- 0,
- SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
- PADDR (msg_wdtr),
- /*
- ** unknown extended message
- */
- SCR_JUMP,
- PADDR (msg_bad)
-}/*-------------------------< MSG_WDTR >-----------------*/,{
- SCR_CLR (SCR_ACK),
- 0,
- SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
- PADDR (dispatch),
- /*
- ** get data bus width
- */
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin[3]),
- SCR_FROM_REG (socl),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
- /*
- ** let the host do the real work.
- */
- SCR_INT,
- SIR_NEGO_WIDE,
- /*
- ** let the target fetch our answer.
- */
- SCR_SET (SCR_ATN),
- 0,
- SCR_CLR (SCR_ACK),
- 0,
-
- SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
- SIR_NEGO_PROTO,
- /*
- ** Send the M_X_WIDE_REQ
- */
- SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
- NADDR (msgout),
- SCR_CLR (SCR_ATN),
- 0,
- SCR_COPY (1),
- RADDR (sfbr),
- NADDR (lastmsg),
- SCR_JUMP,
- PADDR (msg_out_done),
-
-}/*-------------------------< MSG_EXT_3 >----------------*/,{
- SCR_CLR (SCR_ACK),
- 0,
- SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
- PADDR (dispatch),
- /*
- ** get extended message code.
- */
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin[2]),
- /*
- ** Check for message parity error.
- */
- SCR_TO_REG (scratcha),
- 0,
- SCR_FROM_REG (socl),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
- SCR_FROM_REG (scratcha),
- 0,
- SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
- PADDR (msg_sdtr),
- /*
- ** unknown extended message
- */
- SCR_JUMP,
- PADDR (msg_bad)
-
-}/*-------------------------< MSG_SDTR >-----------------*/,{
- SCR_CLR (SCR_ACK),
- 0,
- SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
- PADDR (dispatch),
- /*
- ** get period and offset
- */
- SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
- NADDR (msgin[3]),
- SCR_FROM_REG (socl),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
- PADDR (msg_parity),
- /*
- ** let the host do the real work.
- */
- SCR_INT,
- SIR_NEGO_SYNC,
- /*
- ** let the target fetch our answer.
- */
- SCR_SET (SCR_ATN),
- 0,
- SCR_CLR (SCR_ACK),
- 0,
-
- SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
- SIR_NEGO_PROTO,
- /*
- ** Send the M_X_SYNC_REQ
- */
- SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
- NADDR (msgout),
- SCR_CLR (SCR_ATN),
- 0,
- SCR_COPY (1),
- RADDR (sfbr),
- NADDR (lastmsg),
- SCR_JUMP,
- PADDR (msg_out_done),
-
-}/*-------------------------< COMPLETE >-----------------*/,{
- /*
- ** Complete message.
- **
- ** If it's not the get condition code,
- ** copy TEMP register to LASTP in header.
- */
- SCR_FROM_REG (SS_REG),
- 0,
-/*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
- 12,
- SCR_COPY (4),
- RADDR (temp),
- NADDR (header.lastp),
-/*>>>*/ /*
- ** When we terminate the cycle by clearing ACK,
- ** the target may disconnect immediately.
- **
- ** We don't want to be told of an
- ** "unexpected disconnect",
- ** so we disable this feature.
- */
- SCR_REG_REG (scntl2, SCR_AND, 0x7f),
- 0,
- /*
- ** Terminate cycle ...
- */
- SCR_CLR (SCR_ACK|SCR_ATN),
- 0,
- /*
- ** ... and wait for the disconnect.
- */
- SCR_WAIT_DISC,
- 0,
-}/*-------------------------< CLEANUP >-------------------*/,{
+}/*-------------------------< CLEANUP >-------------------*/,{
/*
** dsa: Pointer to ccb
** or xxxxxxFF (no ccb)
@@ -2497,7 +2572,7 @@
/*
** and copy back the header to the ccb.
*/
- SCR_COPY (4),
+ SCR_COPY_F (4),
RADDR (dsa),
PADDR (cleanup0),
SCR_COPY (sizeof (struct head)),
@@ -2517,7 +2592,7 @@
SCR_FROM_REG (SS_REG),
0,
SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
- PADDR(getcc2),
+ PADDRH(getcc2),
/*
** And make the DSA register invalid.
*/
@@ -2603,7 +2678,7 @@
**
** CAUTION: only little endian architectures supported! XXX
*/
- SCR_COPY (1),
+ SCR_COPY_F (1),
NADDR (header.savep),
PADDR (disconnect0),
}/*-------------------------< DISCONNECT0 >--------------*/,{
@@ -2612,7 +2687,7 @@
/*
** neither this
*/
- SCR_COPY (1),
+ SCR_COPY_F (1),
NADDR (header.goalp),
PADDR (disconnect1),
}/*-------------------------< DISCONNECT1 >--------------*/,{
@@ -2672,7 +2747,7 @@
** If it was no ABORT message ...
*/
SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
- PADDR (msg_out_abort),
+ PADDRH (msg_out_abort),
/*
** ... wait for the next phase
** if it's a message out, send it again, ...
@@ -2693,180 +2768,24 @@
*/
SCR_JUMP,
PADDR (dispatch),
-}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
- /*
- ** After ABORT message,
- **
- ** expect an immediate disconnect, ...
- */
- SCR_REG_REG (scntl2, SCR_AND, 0x7f),
- 0,
- SCR_CLR (SCR_ACK|SCR_ATN),
- 0,
- SCR_WAIT_DISC,
- 0,
- /*
- ** ... and set the status to "ABORTED"
- */
- SCR_LOAD_REG (HS_REG, HS_ABORTED),
- 0,
- SCR_JUMP,
- PADDR (cleanup),
-
-}/*-------------------------< GETCC >-----------------------*/,{
- /*
- ** The ncr doesn't have an indirect load
- ** or store command. So we have to
- ** copy part of the control block to a
- ** fixed place, where we can modify it.
- **
- ** We patch the address part of a COPY command
- ** with the address of the dsa register ...
- */
- SCR_COPY (4),
- RADDR (dsa),
- PADDR (getcc1),
- /*
- ** ... then we do the actual copy.
- */
- SCR_COPY (sizeof (struct head)),
-}/*-------------------------< GETCC1 >----------------------*/,{
- 0,
- NADDR (header),
- /*
- ** Initialize the status registers
- */
- SCR_COPY (4),
- NADDR (header.status),
- RADDR (scr0),
-}/*-------------------------< GETCC2 >----------------------*/,{
- /*
- ** Get the condition code from a target.
- **
- ** DSA points to a data structure.
- ** Set TEMP to the script location
- ** that receives the condition code.
- **
- ** Because there is no script command
- ** to load a longword into a register,
- ** we use a CALL command.
- */
-/*<<<*/ SCR_CALLR,
- 24,
- /*
- ** Get the condition code.
- */
- SCR_MOVE_TBL ^ SCR_DATA_IN,
- offsetof (struct dsb, sense),
- /*
- ** No data phase may follow!
- */
- SCR_CALL,
- PADDR (checkatn),
- SCR_JUMP,
- PADDR (no_data),
-/*>>>*/
-
- /*
- ** The CALL jumps to this point.
- ** Prepare for a RESTORE_POINTER message.
- ** Save the TEMP register into the saved pointer.
- */
- SCR_COPY (4),
- RADDR (temp),
- NADDR (header.savep),
- /*
- ** Load scratcha, because in case of a selection timeout,
- ** the host will expect a new value for startpos in
- ** the scratcha register.
- */
- SCR_COPY (4),
- PADDR (startpos),
- RADDR (scratcha),
-#ifdef NCR_GETCC_WITHMSG
- /*
- ** If QUIRK_NOMSG is set, select without ATN.
- ** and don't send a message.
- */
- SCR_FROM_REG (QU_REG),
- 0,
- SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
- PADDR(getcc3),
- /*
- ** Then try to connect to the target.
- ** If we are reselected, special treatment
- ** of the current job is required before
- ** accepting the reselection.
- */
- SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
- PADDR(badgetcc),
- /*
- ** save target id.
- */
- SCR_FROM_REG (sdid),
- 0,
- SCR_TO_REG (ctest0),
- 0,
- /*
- ** Send the IDENTIFY message.
- ** In case of short transfer, remove ATN.
- */
- SCR_MOVE_TBL ^ SCR_MSG_OUT,
- offsetof (struct dsb, smsg2),
- SCR_CLR (SCR_ATN),
- 0,
- /*
- ** save the first byte of the message.
- */
- SCR_COPY (1),
- RADDR (sfbr),
- NADDR (lastmsg),
- SCR_JUMP,
- PADDR (prepare2),
-
-#endif
-}/*-------------------------< GETCC3 >----------------------*/,{
- /*
- ** Try to connect to the target.
- ** If we are reselected, special treatment
- ** of the current job is required before
- ** accepting the reselection.
- **
- ** Silly target won't accept a message.
- ** Select without ATN.
- */
- SCR_SEL_TBL ^ offsetof (struct dsb, select),
- PADDR(badgetcc),
- /*
- ** save target id.
- */
- SCR_FROM_REG (sdid),
- 0,
- SCR_TO_REG (ctest0),
- 0,
- /*
- ** Force error if selection timeout
- */
- SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
- 0,
- /*
- ** don't negotiate.
- */
- SCR_JUMP,
- PADDR (prepare2),
-
-}/*------------------------< BADGETCC >---------------------*/,{
+}/*------------------------< BADGETCC >---------------------*/,{
/*
** If SIGP was set, clear it and try again.
*/
SCR_FROM_REG (ctest2),
0,
SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
- PADDR (getcc2),
+ PADDRH (getcc2),
SCR_INT,
SIR_SENSE_FAILED,
}/*-------------------------< RESELECT >--------------------*/,{
/*
+ ** This NOP will be patched with LED OFF
+ ** SCR_REG_REG (gpreg, SCR_OR, 0x01)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
** make the DSA invalid.
*/
SCR_LOAD_REG (dsa, 0xff),
@@ -2881,6 +2800,13 @@
*/
SCR_WAIT_RESEL,
PADDR(reselect2),
+}/*-------------------------< RESELECT1 >--------------------*/,{
+ /*
+ ** This NOP will be patched with LED ON
+ ** SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+ */
+ SCR_NO_OP,
+ 0,
/*
** ... zu nichts zu gebrauchen ?
**
@@ -2894,7 +2820,7 @@
** - struct ccb
** to understand what's going on.
*/
- SCR_REG_SFBR (ssid, SCR_AND, 0x87),
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
0,
SCR_TO_REG (ctest0),
0,
@@ -2902,6 +2828,12 @@
NADDR (jump_tcb),
}/*-------------------------< RESELECT2 >-------------------*/,{
/*
+ ** This NOP will be patched with LED ON
+ ** SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
** If it's not connected :(
** -> interrupted by SIGP bit.
** Jump to start.
@@ -2984,80 +2916,530 @@
/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
48,
/*
- ** It WAS a SIMPLE_TAG message.
- ** get it and ack it!
+ ** It WAS a SIMPLE_TAG message.
+ ** get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Wait for the second byte (the tag)
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 24,
+ /*
+ ** Get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_CLR (SCR_ACK|SCR_CARRY),
+ 0,
+ SCR_RETURN,
+ 0,
+ /*
+ ** No message phase or no SIMPLE_TAG message
+ ** or no second byte: return 0.
+ */
+/*>>>*/ SCR_LOAD_SFBR (0),
+ 0,
+ SCR_SET (SCR_CARRY),
+ 0,
+ SCR_RETURN,
+ 0,
+
+}/*-------------------------< DATA_IO >--------------------*/,{
+/*
+** Because Linux does not provide xfer data direction
+** to low-level scsi drivers, we must trust the target
+** for actual data direction when we cannot guess it.
+** The programmed interrupt patches savep, lastp, goalp,
+** etc.., and restarts the scsi script at data_out/in.
+*/
+ SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ SIR_DATA_IO_IS_OUT,
+ SCR_INT ^ IFTRUE (WHEN (SCR_DATA_IN)),
+ SIR_DATA_IO_IS_IN,
+ SCR_JUMP,
+ PADDR (no_data),
+
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTER parameter,
+** it is filled in at runtime.
+**
+** ##===========< i=0; i<MAX_SCATTER >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** || PADDR (checkatn),
+** || SCR_MOVE_TBL ^ SCR_DATA_IN,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+** SCR_CALL,
+** PADDR (checkatn),
+** SCR_JUMP,
+** PADDR (no_data),
+*/
+0
+}/*--------------------------------------------------------*/
+};
+
+static struct scripth scripth0 __initdata = {
+/*-------------------------< TRYLOOP >---------------------*/{
+/*
+** Load an entry of the start queue into dsa
+** and try to start it by jumping to TRYSEL.
+**
+** Because the size depends on the
+** #define MAX_START parameter, it is filled
+** in at runtime.
+**
+**-----------------------------------------------------------
+**
+** ##===========< I=0; i<MAX_START >===========
+** || SCR_COPY (4),
+** || NADDR (squeue[i]),
+** || RADDR (dsa),
+** || SCR_CALL,
+** || PADDR (trysel),
+** ##==========================================
+**
+** SCR_JUMP,
+** PADDRH(tryloop),
+**
+**-----------------------------------------------------------
+*/
+0
+},/*-------------------------< MSG_PARITY >---------------*/{
+ /*
+ ** count it
+ */
+ SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+ 0,
+ /*
+ ** send a "message parity error" message.
+ */
+ SCR_LOAD_REG (scratcha, M_PARITY),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+ /*
+ ** If a negotiation was in progress,
+ ** negotiation failed.
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+ /*
+ ** else make host log this message
+ */
+ SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
+ SIR_REJECT_RECEIVED,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get residue size.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[1]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ ** Size is 0 .. ignore message.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (0)),
+ PADDR (clrack),
+ /*
+ ** Size is not 1 .. have to interrupt.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)),
+ 40,
+ /*
+ ** Check for residue byte in swide register
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+ 16,
+ /*
+ ** There IS data in the swide register.
+ ** Discard it.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ SCR_JUMP,
+ PADDR (clrack),
+ /*
+ ** Load again the size to the sfbr register.
+ */
+/*>>>*/ SCR_FROM_REG (scratcha),
+ 0,
+/*>>>*/ SCR_INT,
+ SIR_IGN_RESIDUE,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_EXTENDED >-------------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get length.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[1]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ */
+ SCR_JUMP ^ IFTRUE (DATA (3)),
+ PADDRH (msg_ext_3),
+ SCR_JUMP ^ IFFALSE (DATA (2)),
+ PADDR (msg_bad),
+}/*-------------------------< MSG_EXT_2 >----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
+ PADDRH (msg_wdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+}/*-------------------------< MSG_WDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get data bus width
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT,
+ SIR_NEGO_WIDE,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_NEGO_PROTO,
+ /*
+ ** Send the M_X_WIDE_REQ
+ */
+ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< MSG_EXT_3 >----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
+ PADDRH (msg_sdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get period and offset
+ */
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT,
+ SIR_NEGO_SYNC,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_NEGO_PROTO,
+ /*
+ ** Send the M_X_SYNC_REQ
+ */
+ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
+ /*
+ ** After ABORT message,
+ **
+ ** expect an immediate disconnect, ...
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** ... and set the status to "ABORTED"
+ */
+ SCR_LOAD_REG (HS_REG, HS_ABORTED),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< GETCC >-----------------------*/,{
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can modify it.
+ **
+ ** We patch the address part of a COPY command
+ ** with the address of the dsa register ...
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDRH (getcc1),
+ /*
+ ** ... then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+}/*-------------------------< GETCC1 >----------------------*/,{
+ 0,
+ NADDR (header),
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ NADDR (header.status),
+ RADDR (scr0),
+}/*-------------------------< GETCC2 >----------------------*/,{
+ /*
+ ** Get the condition code from a target.
+ **
+ ** DSA points to a data structure.
+ ** Set TEMP to the script location
+ ** that receives the condition code.
+ **
+ ** Because there is no script command
+ ** to load a longword into a register,
+ ** we use a CALL command.
+ */
+/*<<<*/ SCR_CALLR,
+ 24,
+ /*
+ ** Get the condition code.
+ */
+ SCR_MOVE_TBL ^ SCR_DATA_IN,
+ offsetof (struct dsb, sense),
+ /*
+ ** No data phase may follow!
+ */
+ SCR_CALL,
+ PADDR (checkatn),
+ SCR_JUMP,
+ PADDR (no_data),
+/*>>>*/
+
+ /*
+ ** The CALL jumps to this point.
+ ** Prepare for a RESTORE_POINTER message.
+ ** Save the TEMP register into the saved pointer.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+ /*
+ ** Load scratcha, because in case of a selection timeout,
+ ** the host will expect a new value for startpos in
+ ** the scratcha register.
+ */
+ SCR_COPY (4),
+ PADDR (startpos),
+ RADDR (scratcha),
+#ifdef NCR_GETCC_WITHMSG
+ /*
+ ** If QUIRK_NOMSG is set, select without ATN.
+ ** and don't send a message.
+ */
+ SCR_FROM_REG (QU_REG),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
+ PADDRH(getcc3),
+ /*
+ ** Then try to connect to the target.
+ ** If we are reselected, special treatment
+ ** of the current job is required before
+ ** accepting the reselection.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
+ PADDR(badgetcc),
+ /*
+ ** save target id.
+ */
+ SCR_FROM_REG (sdid),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ /*
+ ** Send the IDENTIFY message.
+ ** In case of short transfer, remove ATN.
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct dsb, smsg2),
+ SCR_CLR (SCR_ATN),
+ 0,
+ /*
+ ** save the first byte of the message.
*/
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin),
- SCR_CLR (SCR_ACK),
- 0,
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (prepare2),
+
+#endif
+}/*-------------------------< GETCC3 >----------------------*/,{
/*
- ** Wait for the second byte (the tag)
+ ** Try to connect to the target.
+ ** If we are reselected, special treatment
+ ** of the current job is required before
+ ** accepting the reselection.
+ **
+ ** Silly target won't accept a message.
+ ** Select without ATN.
*/
-/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
- 24,
+ SCR_SEL_TBL ^ offsetof (struct dsb, select),
+ PADDR(badgetcc),
/*
- ** Get it and ack it!
+ ** save target id.
*/
- SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
- NADDR (msgin),
- SCR_CLR (SCR_ACK|SCR_CARRY),
+ SCR_FROM_REG (sdid),
0,
- SCR_RETURN,
+ SCR_TO_REG (ctest0),
0,
/*
- ** No message phase or no SIMPLE_TAG message
- ** or no second byte: return 0.
+ ** Force error if selection timeout
*/
-/*>>>*/ SCR_LOAD_SFBR (0),
- 0,
- SCR_SET (SCR_CARRY),
- 0,
- SCR_RETURN,
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
0,
+ /*
+ ** don't negotiate.
+ */
+ SCR_JUMP,
+ PADDR (prepare2),
-}/*-------------------------< DATA_IN >--------------------*/,{
-/*
-** Because the size depends on the
-** #define MAX_SCATTER parameter,
-** it is filled in at runtime.
-**
-** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-** PADDR (no_data),
-** SCR_COPY (sizeof (u_long)),
-** KVAR(SCRIPT_KVAR_JIFFIES),
-** NADDR (header.stamp.data),
-** SCR_MOVE_TBL ^ SCR_DATA_IN,
-** offsetof (struct dsb, data[ 0]),
-**
-** ##===========< i=1; i<MAX_SCATTER >=========
-** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-** || PADDR (checkatn),
-** || SCR_MOVE_TBL ^ SCR_DATA_IN,
-** || offsetof (struct dsb, data[ i]),
-** ##==========================================
-**
-** SCR_CALL,
-** PADDR (checkatn),
-** SCR_JUMP,
-** PADDR (no_data),
-*/
-0
}/*-------------------------< DATA_OUT >-------------------*/,{
/*
** Because the size depends on the
** #define MAX_SCATTER parameter,
** it is filled in at runtime.
**
-** SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-** PADDR (no_data),
-** SCR_COPY (sizeof (u_long)),
-** KVAR(SCRIPT_KVAR_JIFFIES),
-** NADDR (header.stamp.data),
-** SCR_MOVE_TBL ^ SCR_DATA_OUT,
-** offsetof (struct dsb, data[ 0]),
-**
-** ##===========< i=1; i<MAX_SCATTER >=========
+** ##===========< i=0; i<MAX_SCATTER >=========
** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
** || PADDR (dispatch),
** || SCR_MOVE_TBL ^ SCR_DATA_OUT,
@@ -3071,8 +3453,7 @@
**
**---------------------------------------------------------
*/
-0 /* was (u_long)&ident ? */
-
+0
}/*-------------------------< ABORTTAG >-------------------*/,{
/*
** Abort a bad reselection.
@@ -3146,12 +3527,14 @@
**==========================================================
*/
-void ncr_script_fill (struct script * scr)
+__initfunc(
+void ncr_script_fill (struct script * scr, struct scripth * scrh)
+)
{
int i;
ncrcmd *p;
- p = scr->tryloop;
+ p = scrh->tryloop;
for (i=0; i<MAX_START; i++) {
*p++ =SCR_COPY (4);
*p++ =NADDR (squeue[i]);
@@ -3160,21 +3543,13 @@
*p++ =PADDR (trysel);
};
*p++ =SCR_JUMP;
- *p++ =PADDR(tryloop);
+ *p++ =PADDRH(tryloop);
- assert ((u_long)p == (u_long)&scr->tryloop + sizeof (scr->tryloop));
+ assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
p = scr->data_in;
- *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN));
- *p++ =PADDR (no_data);
- *p++ =SCR_COPY (sizeof (u_long));
- *p++ =KVAR(SCRIPT_KVAR_JIFFIES);
- *p++ =NADDR (header.stamp.data);
- *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
- *p++ =offsetof (struct dsb, data[ 0]);
-
- for (i=1; i<MAX_SCATTER; i++) {
+ for (i=0; i<MAX_SCATTER; i++) {
*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
*p++ =PADDR (checkatn);
*p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
@@ -3188,17 +3563,9 @@
assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
- p = scr->data_out;
-
- *p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT));
- *p++ =PADDR (no_data);
- *p++ =SCR_COPY (sizeof (u_long));
- *p++ =KVAR(SCRIPT_KVAR_JIFFIES);
- *p++ =NADDR (header.stamp.data);
- *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
- *p++ =offsetof (struct dsb, data[ 0]);
+ p = scrh->data_out;
- for (i=1; i<MAX_SCATTER; i++) {
+ for (i=0; i<MAX_SCATTER; i++) {
*p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
*p++ =PADDR (dispatch);
*p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
@@ -3210,7 +3577,7 @@
*p++ =SCR_JUMP;
*p++ =PADDR (no_data);
- assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
+ assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
}
/*==========================================================
@@ -3222,19 +3589,17 @@
**==========================================================
*/
-static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
+__initfunc(
+static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
+)
{
ncrcmd opcode, new, old, tmp1, tmp2;
- ncrcmd *src, *dst, *start, *end;
+ ncrcmd *start, *end;
int relocs;
-
- np->p_script = vtophys(np->script);
-
- src = script->start;
- dst = np->script->start;
+ int opchanged = 0;
start = src;
- end = src + (sizeof (struct script) / 4);
+ end = src + len/4;
while (src < end) {
@@ -3278,6 +3643,14 @@
ncr_name(np), (int) (src-start-1));
DELAY (1000000);
}
+ /*
+ ** If PREFETCH feature not enabled, remove
+ ** the NO FLUSH bit if present.
+ */
+ if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
+ dst[-1] = (opcode & ~SCR_NO_FLUSH);
+ ++opchanged;
+ }
break;
case 0x0:
@@ -3321,6 +3694,9 @@
case RELOC_LABEL:
new = (old & ~RELOC_MASK) + np->p_script;
break;
+ case RELOC_LABELH:
+ new = (old & ~RELOC_MASK) + np->p_scripth;
+ break;
case RELOC_SOFTC:
new = (old & ~RELOC_MASK) + vtophys(np);
break;
@@ -3351,6 +3727,9 @@
*dst++ = *src++;
};
+ if (bootverbose > 1 && opchanged)
+ printf("%s: NO FLUSH bit removed from %d script instructions\n",
+ ncr_name(np), opchanged);
}
/*==========================================================
@@ -3362,10 +3741,6 @@
**==========================================================
*/
-#define MIN_ASYNC_PD 40
-#define MIN_SYNC_PD 20
-
-
/*
** Linux host data structure
**
@@ -3375,25 +3750,526 @@
*/
struct host_data {
- struct ncb ncb_data;
+ struct ncb *ncb;
+
+ char ncb_align[NCB_ALIGN_SIZE-1]; /* Filler for alignment */
+ struct ncb _ncb_data;
+
+ char ccb_align[CCB_ALIGN_SIZE-1]; /* Filler for alignment */
+ struct ccb _ccb_data;
+
+ char scr_align[SCR_ALIGN_SIZE-1]; /* Filler for alignment */
struct script script_data;
+
+ struct scripth scripth_data;
};
/*
-** Print something which allow to retreive the controler type, unit,
+** Print something which allow to retrieve the controler type, unit,
** target, lun concerned by a kernel message.
*/
#define PRINT_LUN(np, target, lun) \
-printf("%s-<target %d, lun %d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
-static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
+static void PRINT_ADDR(Scsi_Cmnd *cmd)
{
struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
- ncb_p np = &host_data->ncb_data;
+ ncb_p np = host_data->ncb;
if (np) PRINT_LUN(np, cmd->target, cmd->lun);
}
+/*==========================================================
+**
+** NCR chip clock divisor table.
+** Divisors are multiplied by 10,000,000 in order to make
+** calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+ {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
+/*===============================================================
+**
+** Prepare io register values used by ncr_init() according
+** to selected and supported features.
+**
+** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128
+** transfers. 32,64,128 are only supported by 875 and 895 chips.
+** We use log base 2 (burst length) as internal code, with
+** value 0 meaning "burst disabled".
+**
+**===============================================================
+*/
+
+/*
+ * Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ * Burst code from io register bits.
+ */
+#define burst_code(dmode, ctest4, ctest5) \
+ (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1
+
+/*
+ * Set initial io register bits from burst code.
+ */
+static inline void ncr_init_burst(ncb_p np, u_char bc)
+{
+ np->rv_ctest4 &= ~0x80;
+ np->rv_dmode &= ~(0x3 << 6);
+ np->rv_ctest5 &= ~0x4;
+
+ if (!bc) {
+ np->rv_ctest4 |= 0x80;
+ }
+ else {
+ --bc;
+ np->rv_dmode |= ((bc & 0x3) << 6);
+ np->rv_ctest5 |= (bc & 0x4);
+ }
+}
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+** Get target set-up from Symbios format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ Symbios_target *tn = &nvram->target[target];
+
+ tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+ tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+ tp->usrtags =
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+ if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+ tp->usrflag |= UF_NODISC;
+ if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+ tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+** Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ struct Tekram_target *tn = &nvram->target[target];
+ int i;
+
+ if (tn->flags & TEKRAM_SYNC_NEGO) {
+ i = tn->sync_index & 0xf;
+ tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+ }
+
+ tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+ if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+ tp->usrtags = 2 << nvram->max_tags_index;
+ if (tp->usrtags > SCSI_NCR_MAX_TAGS)
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ }
+
+ if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
+ tp->usrflag = UF_NODISC;
+
+ /* If any device does not support parity, we will not use this option */
+ if (!(tn->flags & TEKRAM_PARITY_CHECK))
+ np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+)
+{
+ u_char burst_max;
+ u_long period;
+ int i;
+
+ /*
+ ** Save assumed BIOS setting
+ */
+
+ np->sv_scntl0 = INB(nc_scntl0) & 0x0a;
+ np->sv_scntl3 = INB(nc_scntl3) & 0x07;
+ np->sv_dmode = INB(nc_dmode) & 0xce;
+ np->sv_dcntl = INB(nc_dcntl) & 0xa8;
+ np->sv_ctest3 = INB(nc_ctest3) & 0x01;
+ np->sv_ctest4 = INB(nc_ctest4) & 0x80;
+ np->sv_ctest5 = INB(nc_ctest5) & 0x24;
+ np->sv_gpcntl = INB(nc_gpcntl);
+ np->sv_stest2 = INB(nc_stest2) & 0x20;
+ np->sv_stest4 = INB(nc_stest4);
+
+ /*
+ ** Wide ?
+ */
+
+ np->maxwide = (np->features & FE_WIDE)? 1 : 0;
+
+ /*
+ ** Get the frequency of the chip's clock.
+ ** Find the right value for scntl3.
+ */
+
+ if (np->features & FE_QUAD)
+ np->multiplier = 4;
+ else if (np->features & FE_DBLR)
+ np->multiplier = 2;
+ else
+ np->multiplier = 1;
+
+ np->clock_khz = (np->features & FE_CLK80)? 80000 : 40000;
+ np->clock_khz *= np->multiplier;
+
+ if (np->clock_khz != 40000)
+ ncr_getclock(np, np->multiplier);
+
+ /*
+ * Divisor to be used for async (timer pre-scaler).
+ */
+ i = np->clock_divn - 1;
+ while (i >= 0) {
+ --i;
+ if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+ ++i;
+ break;
+ }
+ }
+ np->rv_scntl3 = i+1;
+
+ /*
+ * Minimum synchronous period factor supported by the chip.
+ * Btw, 'period' is in tenths of nanoseconds.
+ */
+
+ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+ if (period <= 250) np->minsync = 10;
+ else if (period <= 303) np->minsync = 11;
+ else if (period <= 500) np->minsync = 12;
+ else np->minsync = (period + 40 - 1) / 40;
+
+ /*
+ * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+ */
+
+ if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+ np->minsync = 25;
+ else if (np->minsync < 12 && !(np->features & FE_ULTRA2))
+ np->minsync = 12;
+
+ /*
+ * Maximum synchronous period factor supported by the chip.
+ */
+
+ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+ np->maxsync = period > 2540 ? 254 : period / 10;
+
+ /*
+ ** Get on-board RAM bus address when supported
+ */
+ if (np->features & FE_RAM) {
+ OUTONB(nc_ctest2, 0x8);
+ np->paddr2 = INL(nc_scr0);
+ OUTOFFB(nc_ctest2, 0x8);
+ }
+
+ /*
+ ** Prepare initial value of other IO registers
+ */
+#if defined SCSI_NCR_TRUST_BIOS_SETTING
+ np->rv_scntl0 = np->sv_scntl0;
+ np->rv_dmode = np->sv_dmode;
+ np->rv_dcntl = np->sv_dcntl;
+ np->rv_ctest3 = np->sv_ctest3;
+ np->rv_ctest4 = np->sv_ctest4;
+ np->rv_ctest5 = np->sv_ctest5;
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+#else
+
+ /*
+ ** Select burst length (dwords)
+ */
+ burst_max = driver_setup.burst_max;
+ if (burst_max == 255)
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+ if (burst_max > 7)
+ burst_max = 7;
+ if (burst_max > np->maxburst)
+ burst_max = np->maxburst;
+
+ /*
+ ** Select all supported special features
+ */
+ if (np->features & FE_ERL)
+ np->rv_dmode |= ERL; /* Enable Read Line */
+ if (np->features & FE_BOF)
+ np->rv_dmode |= BOF; /* Burst Opcode Fetch */
+ if (np->features & FE_ERMP)
+ np->rv_dmode |= ERMP; /* Enable Read Multiple */
+ if (np->features & FE_PFEN)
+ np->rv_dcntl |= PFEN; /* Prefetch Enable */
+ if (np->features & FE_CLSE)
+ np->rv_dcntl |= CLSE; /* Cache Line Size Enable */
+ if (np->features & FE_WRIE)
+ np->rv_ctest3 |= WRIE; /* Write and Invalidate */
+ if (np->features & FE_DFS)
+ np->rv_ctest5 |= DFS; /* Dma Fifo Size */
+
+ /*
+ ** Select some other
+ */
+ if (driver_setup.master_parity)
+ np->rv_ctest4 |= MPEE; /* Master parity checking */
+ if (driver_setup.scsi_parity)
+ np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ /*
+ ** Get parity checking, host ID and verbose mode from NVRAM
+ **/
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+ np->rv_scntl0 &= ~0x0a;
+ np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+ if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+ np->verbose += 1;
+ break;
+ }
+ }
+#endif
+ /*
+ ** Get SCSI addr of host adapter (set by bios?).
+ */
+ if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+ if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
+
+#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
+
+ /*
+ * Prepare initial io register bits for burst length
+ */
+ ncr_init_burst(np, burst_max);
+
+ /*
+ ** Set differential mode and LED support.
+ ** Ignore these features for boards known to use a
+ ** specific GPIO wiring (Tekram only for now).
+ ** Probe initial setting of GPREG and GPCNTL for
+ ** other ones.
+ */
+ if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+ switch(driver_setup.diff_support) {
+ case 3:
+ if (INB(nc_gpreg) & 0x08)
+ break;
+ case 2:
+ np->rv_stest2 |= 0x20;
+ break;
+ case 1:
+ np->rv_stest2 |= (np->sv_stest2 & 0x20);
+ break;
+ default:
+ break;
+ }
+ }
+ if ((driver_setup.led_pin ||
+ (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+ !(np->sv_gpcntl & 0x01))
+ np->features |= FE_LED0;
+
+ /*
+ ** Set irq mode.
+ */
+ switch(driver_setup.irqm) {
+ case 2:
+ np->rv_dcntl |= IRQM;
+ break;
+ case 1:
+ np->rv_dcntl |= (np->sv_dcntl & IRQM);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ ** Configure targets according to driver setup.
+ ** If NVRAM present get targets setup from NVRAM.
+ ** Allow to override sync, wide and NOSCAN from
+ ** boot command line.
+ */
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ tcb_p tp = &np->target[i];
+
+ tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+ break;
+ }
+ if (driver_setup.use_nvram & 0x2)
+ tp->usrsync = driver_setup.default_sync;
+ if (driver_setup.use_nvram & 0x4)
+ tp->usrwide = driver_setup.max_wide;
+ if (driver_setup.use_nvram & 0x8)
+ tp->usrflag &= ~UF_NOSCAN;
+ }
+ else {
+#else
+ if (1) {
+#endif
+ tp->usrsync = driver_setup.default_sync;
+ tp->usrwide = driver_setup.max_wide;
+ tp->usrtags = driver_setup.default_tags;
+ if (!driver_setup.disconnection)
+ np->target[i].usrflag = UF_NODISC;
+ }
+ }
+
+ /*
+ ** Announce all that stuff to user.
+ */
+
+ i = nvram ? nvram->type : 0;
+ printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+ i == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
+ (i == SCSI_NCR_TEKRAM_NVRAM ? "Tekram format NVRAM, " : ""),
+ np->myaddr,
+ np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+ (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity",
+ (np->rv_stest2 & 0x20) ? ", Differential" : "");
+
+ if (bootverbose > 1) {
+ printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+ np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+ printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+ np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+ }
+
+ if (bootverbose && np->paddr2)
+ printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+ ncr_name(np), np->paddr2);
+
+ return 0;
+}
+
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printf("%s: HOST ID=%d%s%s%s%s\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERSBOSE" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ ncr_name(np), i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = j < 12 ? Tekram_sync[j] : 255;
+ printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ ncr_name(np), i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
/*
** Host attach and initialisations.
@@ -3401,24 +4277,23 @@
** Allocate host data and ncb structure.
** Request IO region and remap MMIO region.
** Do chip initialization.
-** Try with mmio.
-** If mmio not possible (misconfigured cache),
-** retry with io mapped.
** If all is OK, install interrupt handling and
** start the timer daemon.
*/
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ushort device_id,
- u_char revision_id, int chip, u_int base, u_int io_port,
- int irq, int bus, u_char device_fn)
-
+__initfunc(
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
+)
{
struct host_data *host_data;
ncb_p np;
struct Scsi_Host *instance = 0;
u_long flags = 0;
+ ncr_nvram *nvram = device->nvram;
-printf("ncr_attach: unit=%d chip=%d base=%x, io_port=%x, irq=%d\n", unit, chip, base, io_port, irq);
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+ device->chip.name, unit, device->chip.revision_id, device->slot.base,
+ device->slot.io_port, device->slot.irq);
/*
** Allocate host_data structure
@@ -3429,16 +4304,36 @@
/*
** Initialize structure.
*/
- instance->irq = irq;
host_data = (struct host_data *) instance->hostdata;
- np = &host_data->ncb_data;
+ /*
+ ** Align np and first ccb to 32 boundary for cache line
+ ** bursting when copying the global header.
+ */
+ np = (ncb_p) (((u_long) &host_data->_ncb_data) & NCB_ALIGN_MASK);
+ host_data->ncb = np;
bzero (np, sizeof (*np));
- np->unit = unit;
- np->chip = chip;
- np->device_id = device_id;
- np->revision_id = revision_id;
- np->script = &host_data->script_data;
+
+ np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK);
+ bzero (np->ccb, sizeof (*np->ccb));
+
+ /*
+ ** Store input informations in the host data structure.
+ */
+ strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
+ np->unit = unit;
+ np->verbose = driver_setup.verbose;
+ sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
+ np->device_id = device->chip.device_id;
+ np->revision_id = device->chip.revision_id;
+ np->features = device->chip.features;
+ np->clock_divn = device->chip.nr_divisor;
+ np->maxoffs = device->chip.offset_max;
+ np->maxburst = device->chip.burst_max;
+
+ np->script0 =
+ (struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
+ np->scripth0 = &host_data->scripth_data;
/*
** Initialize timer structure
@@ -3453,38 +4348,74 @@
** virtual and physical memory.
*/
- np->paddr = base;
- np->vaddr = base;
+ np->paddr = device->slot.base;
#ifndef NCR_IOMAPPED
- np->reg_remapped = (struct ncr_reg *) remap_pci_mem((u_long) base, (u_long) 128);
- if (!np->reg_remapped) {
+ np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
+ if (!np->vaddr) {
printf("%s: can't map memory mapped IO region\n", ncr_name(np));
- np->use_mmio = 0;
+ goto attach_error;
}
- printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->reg_remapped);
- np->use_mmio = 1;
-#endif
+ else
+ if (bootverbose > 1)
+ printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+
+ /*
+ ** Make the controller's registers available.
+ ** Now the INB INW INL OUTB OUTW OUTL macros
+ ** can be used safely.
+ */
+
+ np->reg = (struct ncr_reg*) np->vaddr;
+
+#endif /* !defined NCR_IOMAPPED */
+
/*
** Try to map the controller chip into iospace.
*/
- request_region(io_port, 128, "ncr53c8xx");
- np->port = io_port;
+ request_region(device->slot.io_port, 128, "ncr53c8xx");
+ np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+ break;
+ case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+ break;
+ default:
+ nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+ }
+ }
+#endif
/*
** Do chip dependent initialization.
*/
+ (void)ncr_prepare_setting(np, nvram);
- switch (device_id) {
- case PCI_DEVICE_ID_NCR_53C825:
- case PCI_DEVICE_ID_NCR_53C875:
- np->maxwide = 1;
- break;
- default:
- np->maxwide = 0;
- break;
+#ifndef NCR_IOMAPPED
+ if (np->paddr2 && sizeof(struct script) <= 4096) {
+ np->vaddr2 = remap_pci_mem((u_long) np->paddr2, (u_long) 4096);
+ if (!np->vaddr2) {
+ printf("%s: can't map memory mapped IO region\n", ncr_name(np));
+ goto attach_error;
+ }
+ else
+ if (bootverbose > 1)
+ printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2);
}
+#endif /* !defined NCR_IOMAPPED */
/*
** Fill Linux host instance structure
@@ -3495,9 +4426,10 @@
instance->max_lun = SCSI_NCR_MAX_LUN;
#endif
#ifndef NCR_IOMAPPED
- instance->base = (char *) np->reg_remapped;
+ instance->base = (char *) np->reg;
#endif
- instance->io_port = io_port;
+ instance->irq = device->slot.irq;
+ instance->io_port = device->slot.io_port;
instance->n_io_port = 128;
instance->dma_channel = 0;
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -3507,58 +4439,39 @@
/*
** Patch script to physical addresses
*/
- ncr_script_fill (&script0);
- ncr_script_copy_and_bind (&script0, np);
- np->ccb.p_ccb = vtophys (&np->ccb);
+ ncr_script_fill (&script0, &scripth0);
- /*
- ** init data structure
- */
-
- np->jump_tcb.l_cmd = SCR_JUMP;
- np->jump_tcb.l_paddr = NCB_SCRIPT_PHYS (np, abort);
-
- /*
- ** Make the controller's registers available.
- ** Now the INB INW INL OUTB OUTW OUTL macros
- ** can be used safely.
- */
+ np->scripth = np->scripth0;
+ np->p_scripth = vtophys(np->scripth);
- np->reg = (struct ncr_reg*) np->vaddr;
+ np->script = (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0;
+ np->p_script = (np->vaddr2) ? np->paddr2 : vtophys(np->script0);
-#ifndef NCR_IOMAPPED
-retry_chip_init:
-#endif
+ ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
+ ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
+ np->ccb->p_ccb = vtophys (np->ccb);
/*
- ** Get SCSI addr of host adapter (set by bios?).
+ ** Patch the script for LED support.
*/
- np->myaddr = INB(nc_scid) & 0x07;
- if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+ if (np->features & FE_LED0) {
+ np->script0->reselect[0] = SCR_REG_REG(gpreg, SCR_OR, 0x01);
+ np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+ np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+ }
/*
- ** Get the value of the chip's clock.
- ** Find the right value for scntl3.
+ ** init data structure
*/
- ncr_getclock (np, INB(nc_scntl3));
+ np->jump_tcb.l_cmd = SCR_JUMP;
+ np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
/*
** Reset chip.
*/
- OUTW (nc_sien , 0); /* Disable scsi interrupts */
- OUTB (nc_dien , 0); /* Disable dma interrupts */
-
- OUTB (nc_istat, SRST);
- DELAY (1000);
- OUTB (nc_istat, 0 );
-
- /*
- ** Reset chip, once again.
- */
-
OUTB (nc_istat, SRST);
DELAY (1000);
OUTB (nc_istat, 0 );
@@ -3568,14 +4481,6 @@
*/
if (ncr_snooptest (np)) {
-#ifndef NCR_IOMAPPED
- if (np->use_mmio) {
-printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
- ncr_name(np), (u_long) np->port);
- np->use_mmio = 0;
- goto retry_chip_init;
- }
-#endif
printf ("CACHE INCORRECTLY CONFIGURED.\n");
goto attach_error;
};
@@ -3584,55 +4489,50 @@
** Install the interrupt handler.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
- ncr_name(np), irq, (u_long) np);
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
-# else
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
-# endif
+#ifdef SCSI_NCR_SHARE_IRQ
+ if (bootverbose > 1)
+ printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
+ ncr_name(np), device->slot.irq, (u_long) np);
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
+#else
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx", NULL)) {
+#endif
#else
- if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) {
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx")) {
#endif
- printf("%s: request irq %d failure\n", ncr_name(np), irq);
+ printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
goto attach_error;
}
- np->irq = irq;
+ np->irq = device->slot.irq;
/*
** After SCSI devices have been opened, we cannot
** reset the bus safely, so we do it here.
** Interrupt handler does the real work.
- */
-
- OUTB (nc_scntl1, CRST);
- DELAY (1000);
-
- /*
** Process the reset exception,
** if interrupts are not enabled yet.
** Then enable disconnects.
*/
save_flags(flags); cli();
+ ncr_start_reset(np, driver_setup.settle_delay);
ncr_exception (np);
restore_flags(flags);
-#ifndef SCSI_NCR_NO_DISCONNECT
np->disc = 1;
-#endif
/*
** The middle-level SCSI driver does not
** wait devices to settle.
+ ** Wait synchronously if more than 2 seconds.
*/
-#ifdef SCSI_NCR_SETTLE_TIME
-#if SCSI_NCR_SETTLE_TIME > 2
- printf("%s: waiting for scsi devices to settle...\n", ncr_name(np));
-#endif
-#if SCSI_NCR_SETTLE_TIME > 0
- DELAY(SCSI_NCR_SETTLE_TIME*1000000);
-#endif
-#endif
+ if (driver_setup.settle_delay > 2) {
+ printf("%s: waiting %d seconds for scsi devices to settle...\n",
+ ncr_name(np), driver_setup.settle_delay);
+ DELAY(1000000UL * driver_setup.settle_delay);
+ }
/*
** Now let the generic SCSI driver
@@ -3642,8 +4542,8 @@
/*
** start the timeout daemon
*/
- ncr_timeout (np);
np->lasttime=0;
+ ncr_timeout (np);
/*
** use SIMPLE TAG messages by default
@@ -3664,14 +4564,25 @@
attach_error:
if (!instance) return -1;
+ printf("%s: detaching...\n", ncr_name(np));
#ifndef NCR_IOMAPPED
- if (np->reg_remapped) {
- printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128);
- unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128);
+ if (np->vaddr) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+ }
+ if (np->vaddr2) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
}
#endif
if (np->port) {
+#ifdef DEBUG_NCR53C8XX
printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+#endif
release_region(np->port, 128);
}
scsi_unregister(instance);
@@ -3682,47 +4593,6 @@
/*==========================================================
**
**
-** Process pending device interrupts.
-**
-**
-**==========================================================
-*/
-int ncr_intr(np)
- ncb_p np;
-{
- int n = 0;
- u_long flags;
-
- save_flags(flags); cli();
-
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
-
-#ifdef SCSI_NCR_PARANOIA
- if (INB(nc_istat) & (INTF|SIP|DIP)) {
- /*
- ** Repeat until no outstanding ints
- */
- do {
-#endif
- ncr_exception (np);
-#ifdef SCSI_NCR_PARANOIA
- } while (INB(nc_istat) & (INTF|SIP|DIP));
-
- n=1;
- np->ticks = 5 * HZ;
- };
-#endif
-
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
-
- restore_flags(flags);
-
- return (n);
-}
-
-/*==========================================================
-**
-**
** Start execution of a SCSI command.
** This is called from the generic SCSI driver.
**
@@ -3734,7 +4604,7 @@
struct Scsi_Host *host = cmd->host;
/* Scsi_Device *device = cmd->device; */
struct host_data *host_data = (struct host_data *) host->hostdata;
- ncb_p np = &host_data->ncb_data;
+ ncb_p np = host_data->ncb;
tcb_p tp = &np->target[cmd->target];
ccb_p cp;
@@ -3742,7 +4612,7 @@
int segments;
u_char qidx, nego, idmsg, *msgptr;
- u_long msglen, msglen2;
+ u_int msglen, msglen2;
u_long flags;
int xfer_direction;
@@ -3753,22 +4623,6 @@
/*---------------------------------------------
**
- ** Reset SCSI bus
- **
- ** Interrupt handler does the real work.
- **
- **---------------------------------------------
- */
-#if 0
- if (flags & SCSI_RESET) {
- OUTB (nc_scntl1, CRST);
- DELAY (1000);
- return(COMPLETE);
- }
-#endif
-
- /*---------------------------------------------
- **
** Some shortcuts ...
**
**---------------------------------------------
@@ -3779,6 +4633,19 @@
return(DID_BAD_TARGET);
}
+ /*---------------------------------------------
+ **
+ ** Complete the 1st TEST UNIT READY command
+ ** with error condition if the device is
+ ** flagged NOSCAN, in order to speed up
+ ** the boot.
+ **
+ **---------------------------------------------
+ */
+ if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+ tp->usrflag &= ~UF_NOSCAN;
+ return DID_BAD_TARGET;
+ }
if (DEBUG_FLAGS & DEBUG_TINY) {
PRINT_ADDR(cmd);
@@ -3788,13 +4655,14 @@
/*---------------------------------------------------
**
** Assign a ccb / bind cmd
- ** If no free ccb, insert cmd into the waiting list.
+ ** If resetting or no free ccb,
+ ** insert cmd into the waiting list.
**
**----------------------------------------------------
*/
save_flags(flags); cli();
- if (!(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
+ if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
insert_into_waiting_list(np, cmd);
restore_flags(flags);
return(DID_OK);
@@ -3803,16 +4671,14 @@
/*---------------------------------------------------
**
- ** Enable tagged queue if asked by user
+ ** Enable tagged queue if asked by scsi ioctl
**
**----------------------------------------------------
*/
-#ifdef SCSI_NCR_TAGGED_QUEUE_DISABLED
- if (cmd->device && cmd->device->tagged_queue &&
- (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
+ if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
}
-#endif
/*---------------------------------------------------
**
@@ -3820,9 +4686,10 @@
**
**----------------------------------------------------
*/
-
+#ifdef SCSI_NCR_PROFILE_SUPPORT
bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
cp->phys.header.stamp.start = jiffies;
+#endif
/*----------------------------------------------------
**
@@ -3857,7 +4724,8 @@
nego = 0;
- if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+ if (cmd->lun == 0 && !tp->nego_cp &&
+ (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
/*
** negotiate wide transfers ?
*/
@@ -3874,7 +4742,7 @@
*/
if (!nego && !tp->period) {
- if (SCSI_NCR_MAX_SYNC
+ if ( 1
#if defined (CDROM_ASYNC)
&& ((tp->inqdata[0] & 0x1f) != 5)
#endif
@@ -3887,6 +4755,15 @@
printf ("asynchronous.\n");
};
};
+
+ /*
+ ** remember nego is pending for the target.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ if (nego)
+ tp->nego_cp = cp;
};
/*---------------------------------------------------
@@ -3925,7 +4802,7 @@
idmsg = M_IDENTIFY | cmd->lun;
- if ((cp!=&np->ccb) && (np->disc))
+ if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
idmsg |= 0x40;
msgptr = cp->scsi_smsg;
@@ -4013,7 +4890,7 @@
segments = ncr_scatter (cp, cp->cmd);
if (segments < 0) {
- ncr_free_ccb(np, cp);
+ ncr_free_ccb(np, cp, cmd->target, cmd->lun);
restore_flags(flags);
return(DID_ERROR);
}
@@ -4028,10 +4905,12 @@
switch((int) cmd->cmnd[0]) {
case 0x08: /* READ(6) 08 */
case 0x28: /* READ(10) 28 */
+ case 0xA8: /* READ(12) A8 */
xfer_direction = XferIn;
break;
case 0x0A: /* WRITE(6) 0A */
case 0x2A: /* WRITE(10) 2A */
+ case 0xAA: /* WRITE(12) AA */
xfer_direction = XferOut;
break;
default:
@@ -4046,20 +4925,31 @@
**----------------------------------------------------
*/
+ cp->segments = segments;
+ if (!cp->data_len)
+ xfer_direction = XferNone;
+
switch (xfer_direction) {
+ u_long endp;
default:
+ case XferBoth:
+ cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io);
+ cp->phys.header.goalp = cp->phys.header.savep;
+ break;
case XferIn:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_in);
- cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
- break;
+ endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
+ cp->phys.header.goalp = endp + 8;
+ cp->phys.header.savep = endp - segments*16;
+ break;
case XferOut:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out);
- cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
- break;
+ endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
+ cp->phys.header.goalp = endp + 8;
+ cp->phys.header.savep = endp - segments*16;
+ break;
case XferNone:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
- cp->phys.header.goalp = cp->phys.header.savep;
- break;
+ cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
+ cp->phys.header.goalp = cp->phys.header.savep;
+ break;
}
cp->phys.header.lastp = cp->phys.header.savep;
@@ -4161,12 +5051,15 @@
printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
np->squeueput,
(unsigned)(np->script->startpos[0]-
- (NCB_SCRIPT_PHYS (np, tryloop))));
+ (NCB_SCRIPTH_PHYS (np, tryloop))));
/*
** Script processor may be waiting for reselect.
** Wake it up.
*/
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (!np->stalling)
+#endif
OUTB (nc_istat, SIGP);
/*
@@ -4184,28 +5077,112 @@
/*==========================================================
**
**
+** Start reset process.
+** If reset in progress do nothing.
+** The interrupt handler will reinitialize the chip.
+** The timeout handler will wait for settle_time before
+** clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+ u_long flags;
+
+ save_flags(flags); cli();
+
+ if (!np->settle_time) {
+ if (bootverbose > 1)
+ printf("%s: resetting, command processing suspended for %d seconds\n",
+ ncr_name(np), settle_delay);
+ np->settle_time = jiffies + settle_delay * HZ;
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0);
+ OUTW (nc_sien, RST);
+ OUTB (nc_scntl1, CRST);
+ DELAY (100);
+ }
+
+ restore_flags(flags);
+}
+
+/*==========================================================
+**
+**
** Reset the SCSI BUS.
** This is called from the generic SCSI driver.
**
**
**==========================================================
*/
-int ncr_reset_bus (Scsi_Cmnd *cmd)
+int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
{
struct Scsi_Host *host = cmd->host;
/* Scsi_Device *device = cmd->device; */
struct host_data *host_data = (struct host_data *) host->hostdata;
- ncb_p np = &host_data->ncb_data;
+ ncb_p np = host_data->ncb;
+ ccb_p cp;
u_long flags;
+ int found;
- save_flags(flags); cli();
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (np->stalling)
+ np->stalling = 0;
+#endif
+ save_flags(flags); cli();
+/*
+ * Return immediately if reset is in progress.
+ */
+ if (np->settle_time) {
+ restore_flags(flags);
+ return SCSI_RESET_PUNT;
+ }
+/*
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle
+ * delay of 2 seconds will be completed.
+ */
+ ncr_start_reset(np, 2);
+/*
+ * First, look in the wakeup list
+ */
+ for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for the ccb of this command.
+ */
+ if (cp->host_status == HS_IDLE) continue;
+ if (cp->cmd == cmd) {
+ found = 1;
+ break;
+ }
+ }
+/*
+ * Then, look in the waiting list
+ */
+ if (!found && retrieve_from_waiting_list(0, np, cmd))
+ found = 1;
+/*
+ * Wake-up all awaiting commands with DID_RESET.
+ */
reset_waiting_list(np);
- ncr_init(np, "scsi bus reset", HS_RESET);
-
-#ifndef SCSI_NCR_NO_DISCONNECT
- np->disc = 1;
-#endif
+/*
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
+ */
+ ncr_wakeup(np, HS_RESET);
+/*
+ * If the involved command was not in a driver queue, and the
+ * scsi driver told us reset is synchronous, and the command is not
+ * currently in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
+ */
+ if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
+ cmd->result = ScsiResult(DID_RESET, 0);
+ cmd->scsi_done(cmd);
+ }
restore_flags(flags);
@@ -4221,17 +5198,22 @@
**
**==========================================================
*/
-int ncr_abort_command (Scsi_Cmnd *cmd)
+static int ncr_abort_command (Scsi_Cmnd *cmd)
{
struct Scsi_Host *host = cmd->host;
/* Scsi_Device *device = cmd->device; */
struct host_data *host_data = (struct host_data *) host->hostdata;
- ncb_p np = &host_data->ncb_data;
+ ncb_p np = host_data->ncb;
ccb_p cp;
u_long flags;
int found;
int retv;
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (np->stalling == 2)
+ np->stalling = 0;
+#endif
+
save_flags(flags); cli();
/*
* First, look for the scsi command in the waiting list
@@ -4246,7 +5228,7 @@
/*
* Then, look in the wakeup list
*/
- for (found=0, cp=&np->ccb; cp; cp=cp->link_ccb) {
+ for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
/*
** look for the ccb of this command.
*/
@@ -4256,14 +5238,23 @@
break;
}
}
+
if (!found) {
restore_flags(flags);
return SCSI_ABORT_NOT_RUNNING;
}
+ if (np->settle_time) {
+ restore_flags(flags);
+ return SCSI_ABORT_SNOOZE;
+ }
+
/*
** Disable reselect.
** Remove it from startqueue.
+ ** Set cp->tlimit to 0. The ncr_timeout() handler will use
+ ** this condition in order to complete the canceled command
+ ** after the script skipped the ccb, if necessary.
*/
cp->jump_ccb.l_cmd = (SCR_JUMP);
if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) {
@@ -4271,35 +5262,18 @@
cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
}
- switch (cp->host_status) {
- case HS_BUSY:
- case HS_NEGOTIATE:
- /*
- ** still in start queue ?
- */
- if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, skip)) {
- retv = SCSI_ABORT_BUSY;
- break;
- }
- /* fall through */
- case HS_DISCONNECT:
- cp->host_status=HS_ABORTED;
- cp->tag = 0;
- /*
- ** wakeup this ccb.
- */
- ncr_complete (np, cp);
- retv = SCSI_ABORT_SUCCESS;
- break;
- default:
- cp->tag = 0;
- /*
- ** wakeup this ccb.
- */
- ncr_complete (np, cp);
- retv = SCSI_ABORT_SUCCESS;
- break;
- }
+ cp->tlimit = 0;
+ retv = SCSI_ABORT_PENDING;
+
+ /*
+ ** If there are no requests, the script
+ ** processor will sleep on SEL_WAIT_RESEL.
+ ** Let's wake it up, since it may have to work.
+ */
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (!np->stalling)
+#endif
+ OUTB (nc_istat, SIGP);
restore_flags(flags);
@@ -4325,7 +5299,6 @@
lcb_p lp;
int target, lun;
int i;
- u_char scntl3;
printf("%s: releasing host resources\n", ncr_name(np));
@@ -4334,7 +5307,7 @@
** Set release_stage to 1 and wait that ncr_timeout() set it to 2.
*/
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: stopping the timer\n", ncr_name(np));
#endif
np->release_stage = 1;
@@ -4347,7 +5320,7 @@
** Disable chip interrupts
*/
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: disabling chip interrupts\n", ncr_name(np));
#endif
OUTW (nc_sien , 0);
@@ -4357,7 +5330,7 @@
** Free irq
*/
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: freeing irq %d\n", ncr_name(np), irq);
#endif
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
@@ -4372,28 +5345,40 @@
/*
** Reset NCR chip
- ** Preserve scntl3 for automatic clock detection.
+ ** Restore bios setting for automatic clock detection.
*/
printf("%s: resetting chip\n", ncr_name(np));
- scntl3 = INB (nc_scntl3);
OUTB (nc_istat, SRST);
DELAY (1000);
OUTB (nc_istat, 0 );
- OUTB (nc_scntl3, scntl3);
+
+ OUTB(nc_dmode, np->sv_dmode);
+ OUTB(nc_dcntl, np->sv_dcntl);
+ OUTB(nc_ctest3, np->sv_ctest3);
+ OUTB(nc_ctest4, np->sv_ctest4);
+ OUTB(nc_ctest5, np->sv_ctest5);
+ OUTB(nc_gpcntl, np->sv_gpcntl);
+ OUTB(nc_stest2, np->sv_stest2);
+
+ ncr_selectclock(np, np->sv_scntl3);
/*
** Release Memory mapped IO region and IO mapped region
*/
#ifndef NCR_IOMAPPED
-#ifdef DEBUG
- printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg_remapped, 128);
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
#endif
- unmap_pci_mem((vm_offset_t) np->reg_remapped, (u_long) 128);
+ unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
#endif
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
#endif
release_region(np->port, 128);
@@ -4402,13 +5387,13 @@
** Free allocated ccb(s)
*/
- while ((cp=np->ccb.link_ccb) != NULL) {
- np->ccb.link_ccb = cp->link_ccb;
+ while ((cp=np->ccb->link_ccb) != NULL) {
+ np->ccb->link_ccb = cp->link_ccb;
if (cp->host_status) {
printf("%s: shall free an active ccb (host_status=%d)\n",
ncr_name(np), cp->host_status);
}
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
#endif
m_free(cp, sizeof(*cp));
@@ -4423,7 +5408,7 @@
for (lun = 0 ; lun < MAX_LUN ; lun++) {
lp = tp->lp[lun];
if (lp) {
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printf("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
#endif
m_free(lp, sizeof(*lp));
@@ -4476,7 +5461,7 @@
** timestamp
** Optional, spare some CPU time
*/
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
ncb_profile (np, cp);
#endif
@@ -4487,6 +5472,16 @@
cmd = cp->cmd;
cp->cmd = NULL;
tp = &np->target[cmd->target];
+ lp = tp->lp[cmd->lun];
+
+ /*
+ ** We donnot queue more than 1 ccb per target
+ ** with negotiation at any time. If this ccb was
+ ** used for negotiation, clear this info in the tcb.
+ */
+
+ if (cp == tp->nego_cp)
+ tp->nego_cp = 0;
/*
** Check for parity errors.
@@ -4555,11 +5550,11 @@
*/
if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) {
if (np->unit < SCSI_NCR_MAX_HOST) {
-#ifdef SCSI_NCR_FORCE_SYNC_NEGO
- ((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
-#endif
- ((char *) cmd->request_buffer)[7] &=
- (target_capabilities[np->unit].and_map[cmd->target]);
+ if (driver_setup.force_sync_nego)
+ ((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
+ else
+ ((char *) cmd->request_buffer)[7] &=
+ (target_capabilities[np->unit].and_map[cmd->target]);
}
bcopy ( cmd->request_buffer,
&tp->inqdata,
@@ -4568,12 +5563,7 @@
/*
** set number of tags
*/
- lp = tp->lp[cmd->lun];
-#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED
- if (lp && !lp->usetags) {
- ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
- }
-#endif
+ ncr_setmaxtags (np, tp, driver_setup.default_tags);
/*
** prepare negotiation of synch and wide.
*/
@@ -4585,8 +5575,33 @@
tp->quirks |= QUIRK_UPDATE;
}
+ /*
+ ** Announce changes to the generic driver.
+ */
+ if (lp) {
+ ncr_settags (tp, lp);
+ if (lp->reqlink != lp->actlink)
+ ncr_opennings (np, lp, cmd);
+ };
+
tp->bytes += cp->data_len;
tp->transfers ++;
+
+ /*
+ ** If tags was reduced due to queue full,
+ ** increase tags if 100 good status received.
+ */
+ if (tp->numtags < tp->maxtags) {
+ ++tp->num_good;
+ if (tp->num_good >= 100) {
+ tp->num_good = 0;
+ ++tp->numtags;
+ if (tp->numtags == 1) {
+ PRINT_ADDR(cmd);
+ printf("tagged command queueing resumed\n");
+ }
+ }
+ }
} else if ((cp->host_status == HS_COMPLETE)
&& (cp->scsi_status == (S_SENSE|S_GOOD) ||
cp->scsi_status == (S_SENSE|S_CHECK_COND))) {
@@ -4612,6 +5627,29 @@
*/
cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_QUEUE_FULL)) {
+
+ /*
+ ** Target is stuffed.
+ */
+ cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+ /*
+ ** Suspend tagged queuing and start good status counter.
+ ** Announce changes to the generic driver.
+ */
+ if (tp->numtags) {
+ PRINT_ADDR(cmd);
+ printf("QUEUE FULL! suspending tagged command queueing\n");
+ tp->numtags = 0;
+ tp->num_good = 0;
+ if (lp) {
+ ncr_settags (tp, lp);
+ if (lp->reqlink != lp->actlink)
+ ncr_opennings (np, lp, cmd);
+ };
+ }
} else if ((cp->host_status == HS_SEL_TIMEOUT)
|| (cp->host_status == HS_TIMEOUT)) {
@@ -4680,7 +5718,7 @@
/*
** Free this ccb
*/
- ncr_free_ccb (np, cp);
+ ncr_free_ccb (np, cp, cmd->target, cmd->lun);
/*
** requeue awaiting scsi commands
@@ -4713,7 +5751,7 @@
** complete all jobs that are not IDLE.
*/
- ccb_p cp = &np->ccb;
+ ccb_p cp = np->ccb;
while (cp) {
switch (cp->host_status) {
@@ -4751,11 +5789,6 @@
void ncr_init (ncb_p np, char * msg, u_long code)
{
int i;
- u_long usrsync;
- u_char usrwide;
-#if 0
- u_char burstlen;
-#endif
/*
** Reset chip.
@@ -4768,7 +5801,7 @@
** Message.
*/
- if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+ if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
/*
** Clear Start Queue
@@ -4779,10 +5812,9 @@
/*
** Start at first entry.
*/
-
np->squeueput = 0;
- np->script->startpos[0] = NCB_SCRIPT_PHYS (np, tryloop);
- np->script->start0 [0] = SCR_INT ^ IFFALSE (0);
+ np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
+ np->script0->start0 [0] = SCR_INT ^ IFFALSE (0);
/*
** Wakeup all pending jobs.
@@ -4790,122 +5822,73 @@
ncr_wakeup (np, code);
/*
- ** Remove Reset, abort ...
- */
- OUTB (nc_istat, 0 );
-
- /*
** Init chip.
*/
-/** NCR53C810 **/
- if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) {
- OUTB(nc_dmode, 0x80); /* Set 8-transfer burst */
- }
- else
-/** NCR53C815 **/
- if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) {
- OUTB(nc_dmode, 0x80); /* Set 8-transfer burst */
- }
- else
-/** NCR53C825 **/
- if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) {
- OUTB(nc_dmode, 0x80); /* Set 8-transfer burst */
- }
- else
-/** NCR53C810A or NCR53C860 **/
- if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) ||
- ChipDevice == PCI_DEVICE_ID_NCR_53C860) {
- OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */
-#if 0
- OUTB(nc_ctest3, 0x01); /* Set write and invalidate */
- OUTB(nc_dcntl, 0xa1); /* Cache line size enable, */
- /* pre-fetch enable and 700 comp */
-#endif
- }
- else
-/** NCR53C825A or NCR53C875 **/
- if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
- ChipDevice == PCI_DEVICE_ID_NCR_53C875) {
- OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */
-#if 0
- OUTB(nc_ctest5, 0x04); /* Set DMA FIFO to 88 */
- OUTB(nc_ctest5, 0x24); /* Set DMA FIFO to 536 */
- OUTB(nc_dmode, 0x40); /* Set 64-transfer burst */
- OUTB(nc_ctest3, 0x01); /* Set write and invalidate */
- OUTB(nc_dcntl, 0x81); /* Cache line size enable and 700 comp*/
-#endif
- }
-/** OTHERS **/
- else {
- OUTB(nc_dmode, 0xc0); /* Set 16-transfer burst */
- }
-#if 0
- burstlen = 0xc0;
-#endif
-
-#ifdef SCSI_NCR_DISABLE_PARITY_CHECK
- OUTB (nc_scntl0, 0xc0 ); /* full arb., (no parity) */
-#else
- OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */
-#endif
- OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */
- OUTB (nc_scntl3, np->rv_scntl3);/* timing prescaler */
- OUTB (nc_scid , RRE|np->myaddr);/* host adapter SCSI address */
- OUTW (nc_respid, 1ul<<np->myaddr);/* id to respond to */
- OUTB (nc_istat , SIGP ); /* Signal Process */
-#if 0
- OUTB (nc_dmode , burstlen); /* Burst length = 2 .. 16 transfers */
-#endif
- OUTB (nc_dcntl , NOCOM ); /* no single step mode, protect SFBR*/
+ OUTB (nc_istat, 0x00 ); /* Remove Reset, abort */
+ OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+ /* full arb., ena parity, par->ATN */
+ OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */
+
+ ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
+
+ OUTB (nc_scid , RRE|np->myaddr); /* Adapter SCSI address */
+ OUTW (nc_respid, 1ul<<np->myaddr); /* Id to respond to */
+ OUTB (nc_istat , SIGP ); /* Signal Process */
+ OUTB (nc_dmode , np->rv_dmode); /* Burst length, dma mode */
+ OUTB (nc_ctest5, np->rv_ctest5); /* Large fifo + large burst */
+
+ OUTB (nc_dcntl , NOCOM|np->rv_dcntl); /* Protect SFBR */
+ OUTB (nc_ctest3, np->rv_ctest3); /* Write and invalidate */
+ OUTB (nc_ctest4, np->rv_ctest4); /* Master parity checking */
+
+ OUTB (nc_stest2, EXT|np->rv_stest2); /* Extended Sreq/Sack filtering */
+ OUTB (nc_stest3, TE); /* TolerANT enable */
+ OUTB (nc_stime0, 0x0d ); /* HTH disabled STO 0.4 sec. */
-#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK
- OUTB (nc_ctest4, 0x00 ); /* disable master parity checking */
-#else
- OUTB (nc_ctest4, 0x08 ); /* enable master parity checking */
-#endif
+ /*
+ ** Disable disconnects.
+ */
- OUTB (nc_stest2, EXT ); /* Extended Sreq/Sack filtering */
- OUTB (nc_stest3, TE ); /* TolerANT enable */
- OUTB (nc_stime0, 0x0d ); /* HTH = disable STO = 0.4 sec. */
- /* 0.25 sec recommended for scsi 1 */
+ np->disc = 0;
/*
- ** Reinitialize usrsync.
- ** Have to renegotiate synch mode.
+ ** Enable GPIO0 pin for writing if LED support.
*/
- usrsync = 255;
+ if (np->features & FE_LED0) {
+ OUTOFFB (nc_gpcntl, 0x01);
+ }
-#ifndef SCSI_NCR_FORCE_ASYNCHRONOUS
- if (SCSI_NCR_MAX_SYNC) {
- u_long period;
- period =1000000/SCSI_NCR_MAX_SYNC; /* ns = 10e6 / kHz */
- if (period <= 11 * np->ns_sync) {
- if (period < 4 * np->ns_sync)
- usrsync = np->ns_sync;
- else
- usrsync = period / 4;
- };
- };
-#endif
+ /*
+ ** Upload the script into on-board RAM
+ */
+ if (np->vaddr2) {
+ if (bootverbose)
+ printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
+ bcopy(np->script0, np->script, sizeof(struct script));
+ }
/*
- ** Reinitialize usrwide.
- ** Have to renegotiate wide mode.
+ ** enable ints
*/
- usrwide = (SCSI_NCR_MAX_WIDE);
- if (usrwide > np->maxwide) usrwide=np->maxwide;
+ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+ OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
/*
- ** Disable disconnects.
+ ** For 895/6 enable SBMC interrupt and save current SCSI bus mode.
*/
-
- np->disc = 0;
+ if (np->features & FE_ULTRA2) {
+ OUTONW (nc_sien, SBMC);
+ np->scsi_mode = INB (nc_stest4) & SMODE;
+ }
/*
** Fill in target structure.
+ ** Reinitialize usrsync.
+ ** Reinitialize usrwide.
+ ** Prepare sync negotiation according to actual SCSI bus mode.
*/
for (i=0;i<MAX_TARGET;i++) {
@@ -4914,20 +5897,23 @@
tp->sval = 0;
tp->wval = np->rv_scntl3;
- tp->usrsync = usrsync;
- tp->usrwide = usrwide;
+ if (tp->usrsync != 255) {
+ if (tp->usrsync <= np->maxsync) {
+ if (tp->usrsync < np->minsync) {
+ tp->usrsync = np->minsync;
+ }
+ }
+ else
+ tp->usrsync = 255;
+ };
+
+ if (tp->usrwide > np->maxwide)
+ tp->usrwide = np->maxwide;
ncr_negotiate (np, tp);
}
/*
- ** enable ints
- */
-
- OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
- OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
-
- /*
** Start script processor.
*/
@@ -4950,7 +5936,13 @@
u_long minsync = tp->usrsync;
- if (minsync < 25) minsync=25;
+ /*
+ ** SCSI bus mode limit
+ */
+
+ if (np->scsi_mode && np->scsi_mode == SMODE_SE) {
+ if (minsync < 12) minsync = 12;
+ }
/*
** if not scsi 2
@@ -4964,18 +5956,18 @@
** our limit ..
*/
- if (minsync < np->ns_sync)
- minsync = np->ns_sync;
+ if (minsync < np->minsync)
+ minsync = np->minsync;
/*
** divider limit
*/
- if (minsync > (np->ns_sync * 11) / 4)
+ if (minsync > np->maxsync)
minsync = 255;
tp->minsync = minsync;
- tp->maxoffs = (minsync<255 ? 8 : 0);
+ tp->maxoffs = (minsync<255 ? np->maxoffs : 0);
/*
** period=0: has to negotiate sync transfer
@@ -4991,16 +5983,120 @@
/*==========================================================
**
+** Get clock factor and sync divisor for a given
+** synchronous factor period.
+** Returns the clock factor (in sxfer) and scntl3
+** synchronous divisor field.
+**
+**==========================================================
+*/
+
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
+{
+ u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */
+ int div = np->clock_divn; /* Number of divisors supported */
+ u_long fak; /* Sync factor in sxfer */
+ u_long per; /* Period in tenths of ns */
+ u_long kpc; /* (per * clk) */
+
+ /*
+ ** Compute the synchronous period in tenths of nano-seconds
+ */
+ if (sfac <= 10) per = 250;
+ else if (sfac == 11) per = 303;
+ else if (sfac == 12) per = 500;
+ else per = 40 * sfac;
+
+ /*
+ ** Look for the greatest clock divisor that allows an
+ ** input speed faster than the period.
+ */
+ kpc = per * clk;
+ while (--div >= 0)
+ if (kpc >= (div_10M[div] << 2)) break;
+
+ /*
+ ** Calculate the lowest clock factor that allows an output
+ ** speed not faster than the period.
+ */
+ fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0 /* This optimization does not seem very usefull */
+
+ per = (fak * div_10M[div]) / clk;
+
+ /*
+ ** Why not to try the immediate lower divisor and to choose
+ ** the one that allows the fastest output speed ?
+ ** We dont want input speed too much greater than output speed.
+ */
+ if (div >= 1 && fak < 8) {
+ u_long fak2, per2;
+ fak2 = (kpc - 1) / div_10M[div-1] + 1;
+ per2 = (fak2 * div_10M[div-1]) / clk;
+ if (per2 < per && fak2 <= 8) {
+ fak = fak2;
+ per = per2;
+ --div;
+ }
+ }
+#endif
+
+ if (fak < 4) fak = 4; /* Should never happen, too bad ... */
+
+ /*
+ ** Compute and return sync parameters for the ncr
+ */
+ *fakp = fak - 4;
+ *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+}
+
+
+/*==========================================================
+**
+** Set actual values, sync status and patch all ccbs of
+** a target according to new sync/wide agreement.
+**
+**==========================================================
+*/
+
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
+{
+ ccb_p cp;
+ tcb_p tp = &np->target[target];
+
+ /*
+ ** set actual value and sync_status
+ */
+ OUTB (nc_sxfer, tp->sval);
+ np->sync_st = tp->sval;
+ OUTB (nc_scntl3, tp->wval);
+ np->wide_st = tp->wval;
+
+ /*
+ ** patch ALL ccbs of this target.
+ */
+ for (cp = np->ccb; cp; cp = cp->link_ccb) {
+ if (!cp->cmd) continue;
+ if (cp->cmd->target != target) continue;
+ cp->sync_status = tp->sval;
+ cp->wide_status = tp->wval;
+ };
+}
+
+/*==========================================================
+**
** Switch sync mode for current job and it's target
**
**==========================================================
*/
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
{
Scsi_Cmnd *cmd;
tcb_p tp;
- u_char target = INB (nc_ctest0)&7;
+ u_char target = INB (nc_ctest0) & 0x0f;
+ u_char idiv;
assert (cp);
if (!cp) return;
@@ -5011,57 +6107,81 @@
assert (target == (cmd->target & 0xf));
tp = &np->target[target];
- tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff;
- if (tp->sval == sxfer) return;
+ if (!scntl3 || !(sxfer & 0x1f))
+ scntl3 = np->rv_scntl3;
+ scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+
+ /*
+ ** Deduce the value of controller sync period from scntl3.
+ ** period is in tenths of nano-seconds.
+ */
+
+ idiv = ((scntl3 >> 4) & 0x7);
+ if ((sxfer & 0x1f) && idiv)
+ tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+ else
+ tp->period = 0xffff;
+
+ /*
+ ** Stop there if sync parameters are unchanged
+ */
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
tp->sval = sxfer;
+ tp->wval = scntl3;
/*
** Bells and whistles ;-)
*/
PRINT_ADDR(cmd);
- if (sxfer & 0x0f) {
+ if (sxfer & 0x01f) {
+ unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
+ unsigned mb10 = (f10 + tp->period/2) / tp->period;
+ char *scsi;
+
/*
** Disable extended Sreq/Sack filtering
*/
- if (tp->period <= 200) OUTB (nc_stest2, 0);
+ if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
- printf ("%s%dns (%d Mb/sec) offset %d.\n",
- tp->period<200 ? "FAST SCSI-2 ":"",
- tp->period,
- (((tp->wval & EWS)? 2:1)*1000+tp->period/2)/tp->period,
- sxfer & 0x0f);
- } else printf ("asynchronous.\n");
+ /*
+ ** Bells and whistles ;-)
+ */
+ if (tp->period < 500) scsi = "FAST-40";
+ else if (tp->period < 1000) scsi = "FAST-20";
+ else if (tp->period < 2000) scsi = "FAST-10";
+ else scsi = "SLOW";
- /*
- ** set actual value and sync_status
- */
- OUTB (nc_sxfer, sxfer);
- np->sync_st = sxfer;
+ printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+ tp->widedone > 1 ? "WIDE " : "",
+ mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+ } else
+ printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
/*
+ ** set actual value and sync_status
** patch ALL ccbs of this target.
*/
- for (cp = &np->ccb; cp; cp = cp->link_ccb) {
- if (!cp->cmd) continue;
- if (cp->cmd->target != target) continue;
- cp->sync_status = sxfer;
- };
+ ncr_set_sync_wide_status(np, target);
}
/*==========================================================
**
** Switch wide mode for current job and it's target
+** SCSI specs say: a SCSI device that accepts a WDTR
+** message shall reset the synchronous agreement to
+** asynchronous mode.
**
**==========================================================
*/
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
{
Scsi_Cmnd *cmd;
- u_short target = INB (nc_ctest0)&7;
+ u_short target = INB (nc_ctest0) & 0x0f;
tcb_p tp;
- u_char scntl3 = np->rv_scntl3 | (wide ? EWS : 0);
+ u_char scntl3;
+ u_char sxfer;
assert (cp);
if (!cp) return;
@@ -5073,32 +6193,33 @@
tp = &np->target[target];
tp->widedone = wide+1;
- if (tp->wval == scntl3) return;
- tp->wval = scntl3;
+ scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
+
+ sxfer = ack ? 0 : tp->sval;
/*
- ** Bells and whistles ;-)
+ ** Stop there if sync/wide parameters are unchanged
*/
- PRINT_ADDR(cmd);
- if (scntl3 & EWS)
- printf ("WIDE SCSI (16 bit) enabled.\n");
- else
- printf ("WIDE SCSI disabled.\n");
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
+ tp->sval = sxfer;
+ tp->wval = scntl3;
/*
- ** set actual value and sync_status
+ ** Bells and whistles ;-)
*/
- OUTB (nc_scntl3, scntl3);
- np->wide_st = scntl3;
+ if (bootverbose >= 2) {
+ PRINT_ADDR(cmd);
+ if (scntl3 & EWS)
+ printf ("WIDE SCSI (16 bit) enabled.\n");
+ else
+ printf ("WIDE SCSI disabled.\n");
+ }
/*
+ ** set actual value and sync_status
** patch ALL ccbs of this target.
*/
- for (cp = &np->ccb; cp; cp = cp->link_ccb) {
- if (!cp->cmd) continue;
- if (cp->cmd->target != target) continue;
- cp->wide_status = scntl3;
- };
+ ncr_set_sync_wide_status(np, target);
}
/*==========================================================
@@ -5108,19 +6229,32 @@
**==========================================================
*/
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
{
int l;
- tp->usrtags = usrtags;
+ if (numtags > tp->usrtags)
+ numtags = tp->usrtags;
+ tp->numtags = numtags;
+ tp->maxtags = numtags;
+
for (l=0; l<MAX_LUN; l++) {
lcb_p lp;
+ u_char wastags;
+
if (!tp) break;
lp=tp->lp[l];
if (!lp) continue;
+
+ wastags = lp->usetags;
ncr_settags (tp, lp);
- if (lp->usetags > 0) {
+
+ if (numtags > 1 && lp->reqccbs > 1) {
+ PRINT_LUN(np, tp - np->target, l);
+ printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
+ }
+ else if (numtags <= 1 && wastags) {
PRINT_LUN(np, tp - np->target, l);
- printf("using tagged command queueing, up to %d cmds/lun\n", lp->usetags);
+ printf("disabling tagged command queueing\n");
}
};
}
@@ -5139,8 +6273,8 @@
*/
if (( tp->inqdata[2] & 0x7) >= 2 &&
( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
- && tp->usrtags) {
- reqtags = tp->usrtags;
+ && tp->numtags > 1) {
+ reqtags = tp->numtags;
if (lp->actlink <= 1)
lp->usetags=reqtags;
} else {
@@ -5171,7 +6305,7 @@
**----------------------------------------------------
*/
-#ifdef SCSI_NCR_USER_COMMAND
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
static void ncr_usercmd (ncb_p np)
{
@@ -5196,13 +6330,13 @@
np->user.data = SCSI_NCR_MAX_TAGS;
for (t=0; t<MAX_TARGET; t++) {
if (!((np->user.target>>t)&1)) continue;
+ np->target[t].usrtags = np->user.data;
ncr_setmaxtags (np, &np->target[t], np->user.data);
};
- np->disc = 1;
break;
case UC_SETDEBUG:
-#ifdef SCSI_NCR_DEBUG
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
ncr_debug = np->user.data;
#endif
break;
@@ -5234,12 +6368,106 @@
case UC_CLEARPROF:
bzero(&np->profile, sizeof(np->profile));
break;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+ case UC_DEBUG_ERROR_RECOVERY:
+ np->debug_error_recovery = np->user.data;
+ break;
+#endif
}
np->user.cmd=0;
}
#endif
+/*=====================================================================
+**
+** Embedded error recovery debugging code.
+**
+**=====================================================================
+**
+** This code is conditionned by SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT.
+** It only can be enabled after boot-up with a control command.
+**
+** Every 30 seconds the timer handler of the driver decides to
+** change the behaviour of the driver in order to trigger errors.
+**
+** If last command was "debug_error_recovery sge", the driver
+** sets sync offset of all targets that use sync transfers to 2,
+** and so hopes a SCSI gross error at the next read operation.
+**
+** If last command was "debug_error_recovery abort", the driver
+** does not signal new scsi commands to the script processor, until
+** it is asked to abort or reset a command by the mid-level driver.
+**
+** If last command was "debug_error_recovery reset", the driver
+** does not signal new scsi commands to the script processor, until
+** it is asked to reset a command by the mid-level driver.
+**
+** If last command was "debug_error_recovery parity", the driver
+** will assert ATN on the next DATA IN phase mismatch, and so will
+** behave as if a parity error had been detected.
+**
+** The command "debug_error_recovery none" makes the driver behave
+** normaly.
+**
+**=====================================================================
+*/
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+static void ncr_trigger_errors (ncb_p np)
+{
+ /*
+ ** If np->debug_error_recovery is not zero, we want to
+ ** simulate common errors in order to test error recovery.
+ */
+ do {
+ static u_long last = 0l;
+
+ if (!np->debug_error_recovery)
+ break;
+ if (!last)
+ last = jiffies;
+ else if (jiffies < last + 30*HZ)
+ break;
+ last = jiffies;
+ /*
+ * This one triggers SCSI gross errors.
+ */
+ if (np->debug_error_recovery == 1) {
+ int i;
+ printf("%s: testing error recovery from SCSI gross error...\n", ncr_name(np));
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ if (np->target[i].sval & 0x1f) {
+ np->target[i].sval &= ~0x1f;
+ np->target[i].sval += 2;
+ }
+ }
+ }
+ /*
+ * This one triggers abort from the mid-level driver.
+ */
+ else if (np->debug_error_recovery == 2) {
+ printf("%s: testing error recovery from mid-level driver abort()...\n", ncr_name(np));
+ np->stalling = 2;
+ }
+ /*
+ * This one triggers reset from the mid-level driver.
+ */
+ else if (np->debug_error_recovery == 3) {
+ printf("%s: testing error recovery from mid-level driver reset()...\n", ncr_name(np));
+ np->stalling = 3;
+ }
+ /*
+ * This one set ATN on phase mismatch in DATA IN phase and so
+ * will behave as on scsi parity error detected.
+ */
+ else if (np->debug_error_recovery == 4) {
+ printf("%s: testing data in parity error...\n", ncr_name(np));
+ np->assert_atn = 1;
+ }
+ } while (0);
+}
+#endif
/*==========================================================
**
@@ -5267,7 +6495,7 @@
** If release process in progress, let's go
** Set the release stage from 1 to 2 to synchronize
** with the release process.
- **/
+ */
if (np->release_stage) {
if (np->release_stage == 1) np->release_stage = 2;
@@ -5282,7 +6510,33 @@
add_timer(&np->timer);
- if (np->lasttime + HZ < thistime) {
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ ncr_trigger_errors (np);
+#endif
+
+ /*
+ ** If we are resetting the ncr, wait for settle_time before
+ ** clearing it. Then command processing will be resumed.
+ */
+ if (np->settle_time) {
+ if (np->settle_time <= thistime) {
+ if (bootverbose > 1)
+ printf("%s: command processing resumed\n", ncr_name(np));
+ save_flags(flags); cli();
+ np->settle_time = 0;
+ np->disc = 1;
+ requeue_waiting_list(np);
+ restore_flags(flags);
+ }
+ return;
+ }
+
+ /*
+ ** Since the generic scsi driver only allows us 0.5 second
+ ** to perform abort of a command, we must look at ccbs about
+ ** every 0.25 second.
+ */
+ if (np->lasttime + (HZ>>2) <= thistime) {
/*
** block ncr interrupts
*/
@@ -5311,44 +6565,15 @@
t = (thistime - np->heartbeat) / HZ;
if (t<2) np->latetime=0; else np->latetime++;
- if (np->latetime>5) {
- /*
- ** If there are no requests, the script
- ** processor will sleep on SEL_WAIT_RESEL.
- ** But we have to check whether it died.
- ** Let's wake it up.
- */
- OUTB (nc_istat, SIGP);
- }
- if (np->latetime>10) {
- /*
- ** Although we tried to wake it up,
- ** the script processor didn't respond.
- **
- ** May be a target is hanging,
- ** or another initator lets a tape device
- ** rewind with disconnect disabled :-(
- **
- ** We won't accept that.
- */
- if (INB (nc_sbcl) & CBSY)
- OUTB (nc_scntl1, CRST);
- DELAY (1000);
- ncr_init (np, "ncr dead ?", HS_TIMEOUT);
-#ifndef SCSI_NCR_NO_DISCONNECT
- np->disc = 1;
-#endif
- np->heartbeat = thistime;
- }
/*----------------------------------------------------
**
- ** should handle ccb timeouts
- ** Let the middle scsi driver manage timeouts.
+ ** handle ccb timeouts
+ **
**----------------------------------------------------
*/
- for (cp=&np->ccb; cp; cp=cp->link_ccb) {
+ for (cp=np->ccb; cp; cp=cp->link_ccb) {
/*
** look for timed out ccbs.
*/
@@ -5357,7 +6582,7 @@
/*
** Have to force ordered tag to avoid timeouts
*/
- if (cp->cmd && cp->tlimit <=
+ if (cp->cmd && cp->tlimit && cp->tlimit <=
thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) {
lcb_p lp;
lp = np->target[cp->cmd->target].lp[cp->cmd->lun];
@@ -5365,24 +6590,14 @@
lp->force_ordered_tag = 1;
}
}
-/*
-** Let the middle scsi driver manage timeouts
-*/
-#if 0
- if (cp->tlimit > thistime) continue;
-
/*
- ** Disable reselect.
- ** Remove it from startqueue.
+ ** ncr_abort_command() cannot complete canceled
+ ** commands immediately. It sets tlimit to zero
+ ** and ask the script to skip the scsi process if
+ ** necessary. We have to complete this work here.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP);
- if (cp->phys.header.launch.l_paddr ==
- NCB_SCRIPT_PHYS (np, select)) {
- printf ("%s: timeout ccb=%p (skip)\n",
- ncr_name (np), cp);
- cp->phys.header.launch.l_paddr
- = NCB_SCRIPT_PHYS (np, skip);
- };
+
+ if (cp->tlimit) continue;
switch (cp->host_status) {
@@ -5397,7 +6612,7 @@
/* fall through */
case HS_DISCONNECT:
- cp->host_status=HS_TIMEOUT;
+ cp->host_status=HS_ABORTED;
};
cp->tag = 0;
@@ -5405,7 +6620,11 @@
** wakeup this ccb.
*/
ncr_complete (np, cp);
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (!np->stalling)
#endif
+ OUTB (nc_istat, SIGP);
}
restore_flags(flags);
}
@@ -5425,43 +6644,146 @@
#endif /* SCSI_NCR_BROKEN_INTR */
}
-/*==========================================================
-**
+/*==========================================================
+**
+** log message for real hard errors
+**
+** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+** exception register:
+** ds: dstat
+** si: sist
+**
+** SCSI bus lines:
+** so: control lines as driver by NCR.
+** si: control lines as seen by NCR.
+** sd: scsi data lines as seen by NCR.
+**
+** wide/fastmode:
+** sxfer: (see the manual)
+** scntl3: (see the manual)
+**
+** current script command:
+** dsp: script adress (relative to start of script).
+** dbc: first word of script command.
+**
+** First 16 register of the chip:
+** r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+ u_int32 dsp;
+ int script_ofs;
+ int script_size;
+ char *script_name;
+ u_char *script_base;
+ int i;
+
+ dsp = INL (nc_dsp);
+
+ if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+ script_ofs = dsp - np->p_script;
+ script_size = sizeof(struct script);
+ script_base = (u_char *) np->script;
+ script_name = "script";
+ }
+ else {
+ script_ofs = dsp - np->p_scripth;
+ script_size = sizeof(struct scripth);
+ script_base = (u_char *) np->scripth;
+ script_name = "scripth";
+ }
+
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+ ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
+ (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
+ (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
+ (unsigned)INL (nc_dbc));
+
+ if (((script_ofs & 3) == 0) &&
+ (unsigned)script_ofs < script_size) {
+ printf ("%s: script cmd = %08x\n", ncr_name(np),
+ (int) *(ncrcmd *)(script_base + script_ofs));
+ }
+
+ printf ("%s: regdump:", ncr_name(np));
+ for (i=0; i<16;i++)
+ printf (" %02x", (unsigned)INB_OFF(i));
+ printf (".\n");
+}
+
+/*============================================================
**
** ncr chip exception handler.
**
+**============================================================
**
-**==========================================================
+** In normal cases, interrupt conditions occur one at a
+** time. The ncr is able to stack in some extra registers
+** other interrupts that will occurs after the first one.
+** But severall interrupts may occur at the same time.
+**
+** We probably should only try to deal with the normal
+** case, but it seems that multiple interrupts occur in
+** some cases that are not abnormal at all.
+**
+** The most frequent interrupt condition is Phase Mismatch.
+** We should want to service this interrupt quickly.
+** A SCSI parity error may be delivered at the same time.
+** The SIR interrupt is not very frequent in this driver,
+** since the INTFLY is likely used for command completion
+** signaling.
+** The Selection Timeout interrupt may be triggered with
+** IID and/or UDC.
+** The SBMC interrupt (SCSI Bus Mode Change) may probably
+** occur at any time.
+**
+** This handler try to deal as cleverly as possible with all
+** the above.
+**
+**============================================================
*/
void ncr_exception (ncb_p np)
{
u_char istat, dstat;
u_short sist;
- u_int32 dsp, dsa;
- int script_ofs;
int i;
/*
** interrupt on the fly ?
*/
while ((istat = INB (nc_istat)) & INTF) {
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("F");
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (np->stalling)
+ OUTB (nc_istat, INTF);
+ else
+#endif
OUTB (nc_istat, (istat & SIGP) | INTF);
np->profile.num_fly++;
ncr_wakeup (np, 0);
};
- if (!(istat & (SIP|DIP))) return;
+ if (!(istat & (SIP|DIP)))
+ return;
+
+ np->profile.num_int++;
+
+ if (istat & CABRT)
+ OUTB (nc_istat, CABRT);
/*
** Steinbach's Guideline for Systems Programming:
** Never test for an error condition you don't know how to handle.
*/
- dstat = (istat & DIP) ? INB (nc_dstat) : 0;
sist = (istat & SIP) ? INW (nc_sist) : 0;
- np->profile.num_int++;
+ dstat = (istat & DIP) ? INB (nc_dstat) : 0;
if (DEBUG_FLAGS & DEBUG_TINY)
printf ("<%d|%x:%x|%x:%x>",
@@ -5469,297 +6791,121 @@
dstat,sist,
(unsigned)INL(nc_dsp),
(unsigned)INL(nc_dbc));
- if ((dstat==DFE) && (sist==PAR)) return;
-
-/*==========================================================
-**
-** First the normal cases.
-**
-**==========================================================
-*/
- /*-------------------------------------------
- ** SCSI reset
- **-------------------------------------------
- */
-
- if (sist & RST) {
- ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET);
- return;
- };
- /*-------------------------------------------
- ** selection timeout
+ /*========================================================
+ ** First, interrupts we want to service cleanly.
**
- ** IID excluded from dstat mask!
- ** (chip bug)
- **-------------------------------------------
- */
-
- if ((sist & STO) &&
- !(sist & (GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|SIR))) {
- ncr_int_sto (np);
- return;
- };
-
- /*-------------------------------------------
- ** Phase mismatch.
- **-------------------------------------------
- */
-
- if ((sist & MA) &&
- !(sist & (STO|GEN|HTH|SGE|UDC|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
- ncr_int_ma (np);
+ ** Phase mismatch is the most frequent interrupt, and
+ ** so we have to service it as quickly and as cleanly
+ ** as possible.
+ ** Programmed interrupts are rarely used in this driver,
+ ** but we must handle them cleanly anyway.
+ ** We try to deal with PAR and SBMC combined with
+ ** some other interrupt(s).
+ **=========================================================
+ */
+
+ if (!(sist & (STO|GEN|HTH|SGE|UDC|RST)) &&
+ !(dstat & (MDPE|BF|ABRT|IID))) {
+ if ((sist & SBMC) && ncr_int_sbmc (np))
+ return;
+ if ((sist & PAR) && ncr_int_par (np))
+ return;
+ if (sist & MA) {
+ ncr_int_ma (np);
+ return;
+ }
+ if (dstat & SIR) {
+ ncr_int_sir (np);
+ return;
+ }
+ if (!(sist & (SBMC|PAR)) && !(dstat & SSI))
+ printf("%s: unknown interrupt(s) ignored sist=%x dstat=%x\n",
+ ncr_name(np), sist, dstat);
+ OUTONB (nc_dcntl, (STD|NOCOM));
return;
};
- /*----------------------------------------
- ** move command with length 0
- **----------------------------------------
+ /*========================================================
+ ** Now, interrupts that need some fixing up.
+ ** Order and multiple interrupts is so less important.
+ **
+ ** If SRST has been asserted, we just reset the chip.
+ **
+ ** Selection is intirely handled by the chip. If the
+ ** chip says STO, we trust it. Seems some other
+ ** interrupts may occur at the same time (UDC, IID), so
+ ** we ignore them. In any case we do enough fix-up
+ ** in the service routine.
+ ** We just exclude some fatal dma errors.
+ **=========================================================
*/
- if ((dstat & IID) &&
- !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|SIR)) &&
- ((INL(nc_dbc) & 0xf8000000) == SCR_MOVE_TBL)) {
- /*
- ** Target wants more data than available.
- ** The "no_data" script will do it.
- */
- OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, no_data));
+ if (sist & RST) {
+ ncr_init (np, bootverbose ? "scsi reset" : NULL, HS_RESET);
return;
};
- /*-------------------------------------------
- ** Programmed interrupt
- **-------------------------------------------
- */
-
- if ((dstat & SIR) &&
- !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|IID)) &&
- (INB(nc_dsps) <= SIR_MAX)) {
- ncr_int_sir (np);
+ if ((sist & STO) &&
+ !(dstat & (MDPE|BF|ABRT))) {
+ ncr_int_sto (np);
return;
};
- /*========================================
- ** do the register dump
- **========================================
+ /*=========================================================
+ ** Now, interrupts we are not able to recover cleanly.
+ ** (At least for the moment).
+ **
+ ** Do the register dump.
+ ** Log message for real hard errors.
+ ** Clear all fifos.
+ ** For MDPE, BF, ABORT, IID, SGE and HTH we reset the
+ ** BUS and the chip.
+ ** We are more soft for UDC.
+ **=========================================================
*/
if (jiffies - np->regtime > 10*HZ) {
- int i;
np->regtime = jiffies;
- for (i=0; i<sizeof(np->regdump); i++)
+ for (i = 0; i<sizeof(np->regdump); i++)
((char*)&np->regdump)[i] = INB_OFF(i);
np->regdump.nc_dstat = dstat;
np->regdump.nc_sist = sist;
};
- /*=========================================
- ** log message for real hard errors
- **=========================================
-
- "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ (dsp:dbc)."
- " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
-
- exception register:
- ds: dstat
- si: sist
-
- SCSI bus lines:
- so: control lines as driver by NCR.
- si: control lines as seen by NCR.
- sd: scsi data lines as seen by NCR.
-
- wide/fastmode:
- sxfer: (see the manual)
- scntl3: (see the manual)
-
- current script command:
- dsp: script adress (relative to start of script).
- dbc: first word of script command.
-
- First 16 register of the chip:
- r0..rf
-
- =============================================
- */
-
- dsp = (unsigned) INL (nc_dsp);
- dsa = (unsigned) INL (nc_dsa);
-
- script_ofs = dsp - np->p_script;
-
- printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%x:%08x).\n",
- ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
- (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
- (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_ofs,
- (unsigned) INL (nc_dbc));
-
- if (((script_ofs & 3) == 0) &&
- (unsigned)script_ofs < sizeof(struct script)) {
- printf ("\tscript cmd = %08x\n",
- (int) *(ncrcmd *)((char*)np->script +script_ofs));
- }
-
- printf ("\treg:\t");
- for (i=0; i<16;i++)
- printf (" %02x", (unsigned)INB_OFF(i));
- printf (".\n");
-
- /*----------------------------------------
- ** clean up the dma fifo
- **----------------------------------------
- */
-
- if ( (INB(nc_sstat0) & (ILF|ORF|OLF) ) ||
- (INB(nc_sstat1) & (FF3210) ) ||
- (INB(nc_sstat2) & (ILF1|ORF1|OLF1)) || /* wide .. */
- !(dstat & DFE)) {
- printf ("%s: have to clear fifos.\n", ncr_name (np));
- OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
- OUTB (nc_ctest3, CLF); /* clear dma fifo */
- }
+ ncr_log_hard_error(np, sist, dstat);
- /*----------------------------------------
- ** handshake timeout
- **----------------------------------------
- */
+ printf ("%s: have to clear fifos.\n", ncr_name (np));
+ OUTB (nc_stest3, TE|CSF);
+ OUTONB (nc_ctest3, CLF);
+
+ if ((sist & (SGE)) ||
+ (dstat & (MDPE|BF|ABORT|IID))) {
+ ncr_start_reset(np, 2);
+ return;
+ };
if (sist & HTH) {
printf ("%s: handshake timeout\n", ncr_name(np));
- OUTB (nc_scntl1, CRST);
- DELAY (1000);
- OUTB (nc_scntl1, 0x00);
- OUTB (nc_scr0, HS_FAIL);
- OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
- return;
- }
-
- /*----------------------------------------
- ** unexpected disconnect
- **----------------------------------------
- */
-
- if ((sist & UDC) &&
- !(sist & (STO|GEN|HTH|MA|SGE|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
- OUTB (nc_scr0, HS_UNEXPECTED);
- OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
+ ncr_start_reset(np, 2);
return;
};
- /*----------------------------------------
- ** cannot disconnect
- **----------------------------------------
- */
-
- if ((dstat & IID) &&
- !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|SIR)) &&
- ((INL(nc_dbc) & 0xf8000000) == SCR_WAIT_DISC)) {
- /*
- ** Unexpected data cycle while waiting for disconnect.
- */
- if (INB(nc_sstat2) & LDSC) {
- /*
- ** It's an early reconnect.
- ** Let's continue ...
- */
- OUTB (nc_dcntl, (STD|NOCOM));
- /*
- ** info message
- */
- printf ("%s: INFO: LDSC while IID.\n",
- ncr_name (np));
- return;
+ if (sist & UDC) {
+ printf ("%s: unexpected disconnect\n", ncr_name(np));
+ if (INB (nc_scr1) != 0xff) {
+ OUTB (nc_scr1, HS_UNEXPECTED);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
};
- printf ("%s: target %d doesn't release the bus.\n",
- ncr_name (np), (int)INB (nc_ctest0)&0x0f);
- /*
- ** return without restarting the NCR.
- ** timeout will do the real work.
- */
- return;
- };
-
- /*----------------------------------------
- ** single step
- **----------------------------------------
- */
-
- if ((dstat & SSI) &&
- !(sist & (STO|GEN|HTH|MA|SGE|UDC|RST|PAR)) &&
- !(dstat & (MDPE|BF|ABRT|SIR|IID))) {
- OUTB (nc_dcntl, (STD|NOCOM));
- return;
- };
-
-/*
-** @RECOVER@ HTH, SGE, ABRT.
-**
-** We should try to recover from these interrupts.
-** They may occur if there are problems with synch transfers, or
-** if targets are switched on or off while the driver is running.
-*/
-
- if (sist & SGE) {
- OUTB (nc_ctest3, CLF); /* clear scsi offsets */
- }
-
- /*
- ** Freeze controller to be able to read the messages.
- */
-
- if (DEBUG_FLAGS & DEBUG_FREEZE) {
- unsigned char val;
- for (i=0; i<0x60; i++) {
- switch (i%16) {
-
- case 0:
- printf ("%s: reg[%d0]: ",
- ncr_name(np),i/16);
- break;
- case 4:
- case 8:
- case 12:
- printf (" ");
- break;
- };
- val = INB_OFF(i);
- printf (" %x%x", val/16, val%16);
- if (i%16==15) printf (".\n");
- }
-
- del_timer(&np->timer);
-
- printf ("%s: halted!\n", ncr_name(np));
- /*
- ** don't restart controller ...
- */
- OUTB (nc_istat, SRST);
+ ncr_start_reset(np, 2);
return;
};
-#ifdef NCR_FREEZE
- /*
- ** Freeze system to be able to read the messages.
- */
- printf ("ncr: fatal error: system halted - press reset to reboot ...");
- cli();
- for (;;);
-#endif
-
- /*
- ** sorry, have to kill ALL jobs ...
+ /*=========================================================
+ ** We just miss the cause of the interrupt. :(
+ ** Print a message. The timeout will do the real work.
+ **=========================================================
*/
-
- ncr_init (np, "fatal error", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
- np->disc = 1;
-#endif
+ printf ("%s: unknown interrupt\n", ncr_name(np));
}
/*==========================================================
@@ -5789,7 +6935,7 @@
*/
dsa = INL (nc_dsa);
- cp = &np->ccb;
+ cp = np->ccb;
while (cp && (CCB_PHYS (cp, phys) != dsa))
cp = cp->link_ccb;
@@ -5803,7 +6949,7 @@
*/
scratcha = INL (nc_scratcha);
- diff = scratcha - NCB_SCRIPT_PHYS (np, tryloop);
+ diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop);
/* assert ((diff <= MAX_START * 20) && !(diff % 20));*/
@@ -5813,9 +6959,55 @@
return;
};
ncr_init (np, "selection timeout", HS_FAIL);
-#ifndef SCSI_NCR_NO_DISCONNECT
np->disc = 1;
-#endif
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for SCSI bus mode change
+**
+**==========================================================
+**
+** spi2-r12 11.2.3 says a transceiver mode change must
+** generate a reset event and a device that detects a reset
+** event shall initiate a hard reset. It says also that a
+** device that detects a mode change shall set data transfer
+** mode to eight bit asynchronous, etc...
+** So, just resetting should be enough.
+**
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_sbmc (ncb_p np)
+{
+ u_char scsi_mode = INB (nc_stest4) & SMODE;
+
+ printf("%s: SCSI bus mode change from %x to %x, resetting ...\n",
+ ncr_name(np), np->scsi_mode, scsi_mode);
+
+ np->scsi_mode = scsi_mode;
+ ncr_start_reset(np, 2);
+
+ return 1;
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for SCSI parity error.
+**
+**==========================================================
+**
+** SCSI parity errors are handled by the SCSI script.
+** So, we just print some message.
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_par (ncb_p np)
+{
+ printf("%s: SCSI parity error detected\n", ncr_name(np));
+ return 0;
}
/*==========================================================
@@ -5836,49 +7028,76 @@
{
u_int32 dbc;
u_int32 rest;
- u_int32 dsa;
u_int32 dsp;
+ u_int32 dsa;
u_int32 nxtdsp;
u_int32 *vdsp;
u_int32 oadr, olen;
u_int32 *tblp;
ncrcmd *newcmd;
- u_char cmd, sbcl, delta, ss0, ss2;
+ u_char cmd, sbcl;
ccb_p cp;
- dsp = INL (nc_dsp);
- dsa = INL (nc_dsa);
- dbc = INL (nc_dbc);
- ss0 = INB (nc_sstat0);
- ss2 = INB (nc_sstat2);
- sbcl= INB (nc_sbcl);
-
- cmd = dbc >> 24;
- rest= dbc & 0xffffff;
- delta=(INB (nc_dfifo) - rest) & 0x7f;
-
- /*
- ** The data in the dma fifo has not been transfered to
- ** the target -> add the amount to the rest
- ** and clear the data.
- ** Check the sstat2 register in case of wide transfer.
- */
-
- if (! (INB(nc_dstat) & DFE)) rest += delta;
- if (ss0 & OLF) rest++;
- if (ss0 & ORF) rest++;
- if (INB(nc_scntl3) & EWS) {
- if (ss2 & OLF1) rest++;
- if (ss2 & ORF1) rest++;
- };
- OUTB (nc_ctest3, CLF ); /* clear dma fifo */
- OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+ dsp = INL (nc_dsp);
+ dbc = INL (nc_dbc);
+ sbcl = INB (nc_sbcl);
+
+ cmd = dbc >> 24;
+ rest = dbc & 0xffffff;
+
+ /*
+ ** Take into account dma fifo and various buffers and latches,
+ ** only if the interrupted phase is an OUTPUT phase.
+ */
+
+ if ((cmd & 1) == 0) {
+ u_char ctest5, ss0, ss2;
+ u_short delta;
+
+ ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+ if (ctest5 & DFS)
+ delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+ else
+ delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+ /*
+ ** The data in the dma fifo has not been transfered to
+ ** the target -> add the amount to the rest
+ ** and clear the data.
+ ** Check the sstat2 register in case of wide transfer.
+ */
+
+ rest += delta;
+ ss0 = INB (nc_sstat0);
+ if (ss0 & OLF) rest++;
+ if (ss0 & ORF) rest++;
+ if (INB(nc_scntl3) & EWS) {
+ ss2 = INB (nc_sstat2);
+ if (ss2 & OLF1) rest++;
+ if (ss2 & ORF1) rest++;
+ };
+
+ OUTONB (nc_ctest3, CLF ); /* clear dma fifo */
+ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printf ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
+ (unsigned) rest, (unsigned) delta, ss0);
+
+ } else {
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printf ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
+ if ((cmd & 7) != 1) {
+ OUTONB (nc_ctest3, CLF );
+ OUTB (nc_stest3, TE|CSF);
+ }
+ }
/*
** locate matching cp
*/
dsa = INL (nc_dsa);
- cp = &np->ccb;
+ cp = np->ccb;
while (cp && (CCB_PHYS (cp, phys) != dsa))
cp = cp->link_ccb;
@@ -5904,19 +7123,18 @@
} else if (dsp == vtophys (&cp->patch[6])) {
vdsp = &cp->patch[4];
nxtdsp = vdsp[3];
- } else {
+ } else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8);
nxtdsp = dsp;
+ } else {
+ vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8);
+ nxtdsp = dsp;
};
/*
** log the information
*/
- if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) {
- printf ("P%x%x ",cmd&7, sbcl&7);
- printf ("RL=%d D=%d SS0=%x ",
- (unsigned) rest, (unsigned) delta, ss0);
- };
+
if (DEBUG_FLAGS & DEBUG_PHASE) {
printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
cp, np->header.cp,
@@ -5948,7 +7166,7 @@
};
/*
- ** if old phase not dataphase, leave here.
+ ** check cmd against assumed interrupted script command.
*/
if (cmd != (vdsp[0] >> 24)) {
@@ -5958,13 +7176,25 @@
return;
}
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if ((cmd & 7) == 1 && np->assert_atn) {
+ np->assert_atn = 0;
+ OUTONB(nc_socl, CATN);
+ }
+#endif
+
+ /*
+ ** if old phase not dataphase, leave here.
+ */
+
if (cmd & 0x06) {
PRINT_ADDR(cp->cmd);
printf ("phase change %x-%x %d@%08x resid=%d.\n",
cmd&7, sbcl&7, (unsigned)olen,
(unsigned)oadr, (unsigned)rest);
- OUTB (nc_dcntl, (STD|NOCOM));
+ OUTONB (nc_dcntl, (STD|NOCOM));
return;
};
@@ -6000,7 +7230,10 @@
*/
np->profile.num_break++;
OUTL (nc_temp, vtophys (newcmd));
- OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+ if ((cmd & 7) == 0)
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+ else
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn));
}
/*==========================================================
@@ -6031,11 +7264,12 @@
void ncr_int_sir (ncb_p np)
{
+ u_char scntl3;
u_char chg, ofs, per, fak, wide;
u_char num = INB (nc_dsps);
ccb_p cp=0;
u_long dsa;
- u_char target = INB (nc_ctest0) & 7;
+ u_char target = INB (nc_ctest0) & 0x0f;
tcb_p tp = &np->target[target];
int i;
if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
@@ -6044,13 +7278,15 @@
case SIR_SENSE_RESTART:
case SIR_STALL_RESTART:
break;
+ case SIR_STALL_QUEUE: /* Ignore, just restart the script */
+ goto out;
default:
/*
** lookup the ccb
*/
dsa = INL (nc_dsa);
- cp = &np->ccb;
+ cp = np->ccb;
while (cp && (CCB_PHYS (cp, phys) != dsa))
cp = cp->link_ccb;
@@ -6063,6 +7299,32 @@
}
switch (num) {
+ u_long endp;
+ case SIR_DATA_IO_IS_OUT:
+ case SIR_DATA_IO_IS_IN:
+/*
+** We did not guess the direction of transfer. We have to wait for
+** actual data direction driven by the target before setting
+** pointers. We must patch the global header too.
+*/
+ if (num == SIR_DATA_IO_IS_OUT) {
+ endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
+ cp->phys.header.goalp = endp + 8;
+ cp->phys.header.savep = endp - cp->segments*16;
+ } else {
+ endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
+ cp->phys.header.goalp = endp + 8;
+ cp->phys.header.savep = endp - cp->segments*16;
+ }
+
+ cp->phys.header.lastp = cp->phys.header.savep;
+ np->header.savep = cp->phys.header.savep;
+ np->header.goalp = cp->phys.header.goalp;
+ np->header.lastp = cp->phys.header.lastp;
+ OUTL (nc_temp, np->header.savep);
+ OUTL (nc_dsp, np->header.savep);
+ return;
+ /* break; */
/*--------------------------------------------------------------------
**
@@ -6099,7 +7361,7 @@
if (DEBUG_FLAGS & DEBUG_RESTART)
printf ("+ restart job ..\n");
OUTL (nc_dsa, CCB_PHYS (cp, phys));
- OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, getcc));
+ OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc));
return;
};
@@ -6230,11 +7492,11 @@
switch (cp->nego_status) {
case NS_SYNC:
- ncr_setsync (np, cp, 0xe0);
+ ncr_setsync (np, cp, 0, 0xe0);
break;
case NS_WIDE:
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 0);
break;
};
@@ -6277,8 +7539,8 @@
** check values against driver limits.
*/
- if (per < np->ns_sync)
- {chg = 1; per = np->ns_sync;}
+ if (per < np->minsync)
+ {chg = 1; per = np->minsync;}
if (per < tp->minsync)
{chg = 1; per = tp->minsync;}
if (ofs > tp->maxoffs)
@@ -6287,23 +7549,26 @@
/*
** Check against controller limits.
*/
+ fak = 7;
+ scntl3 = 0;
if (ofs != 0) {
- fak = (4ul * per - 1) / np->ns_sync - 3;
- if (fak>7) {
+ ncr_getsync(np, per, &fak, &scntl3);
+ if (fak > 7) {
chg = 1;
ofs = 0;
}
}
if (ofs == 0) {
- fak = 7;
- per = 0;
+ fak = 7;
+ per = 0;
+ scntl3 = 0;
tp->minsync = 0;
}
if (DEBUG_FLAGS & DEBUG_NEGO) {
PRINT_ADDR(cp->cmd);
- printf ("sync: per=%d ofs=%d fak=%d chg=%d.\n",
- per, ofs, fak, chg);
+ printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
+ per, scntl3, ofs, fak, chg);
}
if (INB (HS_PRT) == HS_NEGOTIATE) {
@@ -6318,19 +7583,19 @@
/*
** Answer wasn't acceptable.
*/
- ncr_setsync (np, cp, 0xe0);
+ ncr_setsync (np, cp, 0, 0xe0);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
- ncr_setsync (np, cp, (fak<<5)|ofs);
+ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
case NS_WIDE:
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 0);
break;
};
};
@@ -6354,7 +7619,7 @@
** prepare an answer message
*/
- ncr_setsync (np, cp, (fak<<5)|ofs);
+ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
np->msgout[0] = M_EXTENDED;
np->msgout[1] = 3;
@@ -6367,7 +7632,7 @@
if (DEBUG_FLAGS & DEBUG_NEGO) {
PRINT_ADDR(cp->cmd);
printf ("sync msgout: ");
- (void) ncr_show_msg (np->msgin);
+ (void) ncr_show_msg (np->msgout);
printf (".\n");
}
@@ -6429,19 +7694,19 @@
/*
** Answer wasn't acceptable.
*/
- ncr_setwide (np, cp, 0);
+ ncr_setwide (np, cp, 0, 1);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
} else {
/*
** Answer is ok.
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
};
return;
case NS_SYNC:
- ncr_setsync (np, cp, 0xe0);
+ ncr_setsync (np, cp, 0, 0xe0);
break;
};
};
@@ -6451,7 +7716,7 @@
** prepare an answer message
*/
- ncr_setwide (np, cp, wide);
+ ncr_setwide (np, cp, wide, 1);
np->msgout[0] = M_EXTENDED;
np->msgout[1] = 2;
@@ -6534,13 +7799,14 @@
*/
PRINT_ADDR(cp->cmd);
- printf ("M_DISCONNECT received, but datapointer not saved:\n"
- "\tdata=%x save=%x goal=%x.\n",
+ printf ("M_DISCONNECT received, but datapointer not saved: "
+ "data=%x save=%x goal=%x.\n",
(unsigned) INL (nc_temp),
(unsigned) np->header.savep,
(unsigned) np->header.goalp);
break;
+#if 0 /* This stuff does not work */
/*--------------------------------------------------------------------
**
** Processing of a "S_QUEUE_FULL" status.
@@ -6571,14 +7837,9 @@
np->script->start1[0] = SCR_INT;
/*
- ** For the moment tagged transfers cannot be disabled.
- */
-#if 0
- /*
** Try to disable tagged transfers.
*/
ncr_setmaxtags (np, &np->target[target], 0);
-#endif
/*
** @QUEUE@
@@ -6601,7 +7862,7 @@
/*
** Look for a disconnected job.
*/
- cp = &np->ccb;
+ cp = np->ccb;
while (cp && cp->host_status != HS_DISCONNECT)
cp = cp->link_ccb;
@@ -6623,10 +7884,11 @@
printf ("%s: queue empty.\n", ncr_name (np));
np->script->start1[0] = SCR_INT ^ IFFALSE (0);
break;
+#endif /* This stuff does not work */
};
out:
- OUTB (nc_dcntl, (STD|NOCOM));
+ OUTONB (nc_dcntl, (STD|NOCOM));
}
/*==========================================================
@@ -6649,7 +7911,9 @@
*/
lp = np->target[target].lp[lun];
- if (lp) {
+
+ if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) {
+
cp = lp->next_ccb;
/*
@@ -6657,6 +7921,15 @@
*/
while (cp && cp->magic) cp = cp->next_ccb;
+
+ /*
+ ** Increment active commands and decrement credit.
+ */
+
+ if (cp) {
+ ++lp->active;
+ --lp->opennings;
+ }
}
/*
@@ -6668,7 +7941,7 @@
if ((!cp) && lp && lp->actccbs > 0)
return ((ccb_p) 0);
- if (!cp) cp = &np->ccb;
+ if (!cp) cp = np->ccb;
/*
** Wait until available.
@@ -6697,18 +7970,30 @@
**==========================================================
*/
-void ncr_free_ccb (ncb_p np, ccb_p cp)
+void ncr_free_ccb (ncb_p np, ccb_p cp, u_long target, u_long lun)
{
+ lcb_p lp;
+
/*
** sanity
*/
assert (cp != NULL);
+ /*
+ ** Decrement active commands and increment credit.
+ */
+
+ lp = np->target[target].lp[lun];
+ if (lp) {
+ --lp->active;
+ ++lp->opennings;
+ }
+
cp -> host_status = HS_IDLE;
cp -> magic = 0;
#if 0
- if (cp == &np->ccb)
+ if (cp == np->ccb)
wakeup ((caddr_t) cp);
#endif
}
@@ -6743,10 +8028,12 @@
tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
- tp->getscr[0] = SCR_COPY (1);
+ tp->getscr[0] =
+ (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
tp->getscr[1] = vtophys (&tp->sval);
tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
- tp->getscr[3] = SCR_COPY (1);
+ tp->getscr[3] =
+ (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
tp->getscr[4] = vtophys (&tp->wval);
tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
@@ -6759,7 +8046,7 @@
tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
tp->jump_lcb.l_cmd = (SCR_JUMP);
- tp->jump_lcb.l_paddr = NCB_SCRIPT_PHYS (np, abort);
+ tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
}
@@ -6771,7 +8058,7 @@
/*
** Allocate a lcb
*/
- lp = (lcb_p) m_alloc (sizeof (struct lcb));
+ lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT);
if (!lp) return;
if (DEBUG_FLAGS & DEBUG_ALLOC) {
@@ -6790,31 +8077,25 @@
lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
lp->jump_ccb.l_cmd = (SCR_JUMP);
- lp->jump_ccb.l_paddr = NCB_SCRIPT_PHYS (np, aborttag);
+ lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
lp->actlink = 1;
+ lp->active = 1;
+
/*
** Chain into LUN list
*/
tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
tp->lp[lun] = lp;
-#ifndef SCSI_NCR_TAGGED_QUEUE_DISABLED
- if (!lp->usetags) {
- ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
- }
-#endif
+ ncr_setmaxtags (np, tp, driver_setup.default_tags);
}
/*
** Allocate ccbs up to lp->reqccbs.
- **
- ** This modification will be reworked in a future release.
*/
-loop_alloc_ccb:
-
/*
** Limit possible number of ccbs.
**
@@ -6828,7 +8109,7 @@
/*
** Allocate a ccb
*/
- cp = (ccb_p) m_alloc (sizeof (struct ccb));
+ cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT);
if (!cp)
return;
@@ -6866,16 +8147,68 @@
/*
** Chain into wakeup list
*/
- cp->link_ccb = np->ccb.link_ccb;
- np->ccb.link_ccb = cp;
+ cp->link_ccb = np->ccb->link_ccb;
+ np->ccb->link_ccb = cp;
/*
** Chain into CCB list
*/
- cp->next_ccb = lp->next_ccb;
- lp->next_ccb = cp;
+ cp->next_ccb = lp->next_ccb;
+ lp->next_ccb = cp;
+}
+
+/*==========================================================
+**
+**
+** Announce the number of ccbs/tags to the scsi driver.
+**
+**
+**==========================================================
+*/
+
+static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd)
+{
+ /*
+ ** want to reduce the number ...
+ */
+ if (lp->actlink > lp->reqlink) {
+
+ /*
+ ** Try to reduce the count.
+ ** We assume to run at splbio ..
+ */
+ u_char diff = lp->actlink - lp->reqlink;
+
+ if (!diff) return;
+
+ if (diff > lp->opennings)
+ diff = lp->opennings;
+
+ lp->opennings -= diff;
+
+ lp->actlink -= diff;
+ if (DEBUG_FLAGS & DEBUG_TAGS)
+ printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
+ ncr_name(np), diff, lp->actlink, lp->reqlink);
+ return;
+ };
+
+ /*
+ ** want to increase the number ?
+ */
+ if (lp->reqlink > lp->actlink) {
+ u_char diff = lp->reqlink - lp->actlink;
-goto loop_alloc_ccb;
+ lp->opennings += diff;
+
+ lp->actlink += diff;
+#if 0
+ wakeup ((caddr_t) xp->sc_link);
+#endif
+ if (DEBUG_FLAGS & DEBUG_TAGS)
+ printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
+ ncr_name(np), diff, lp->actlink, lp->reqlink);
+ };
}
/*==========================================================
@@ -6894,8 +8227,7 @@
**----------------------------------------------------------
*/
-/* FreeBSD driver important comments
-** ---------------------------------
+/*
** We try to reduce the number of interrupts caused
** by unexpected phase changes due to disconnects.
** A typical harddisk may disconnect before ANY block.
@@ -6903,158 +8235,49 @@
** we had to use a break point every 512 bytes.
** Of course the number of scatter/gather blocks is
** limited.
+** Under Linux, the scatter/gatter blocks are provided by
+** the generic driver. We just have to copy addresses and
+** sizes to the data segment array.
*/
-/*
-** The scatterlist passed by the linux middle-level scsi drivers
-** may contain blocks of any size (Generaly < 1024 bytes blocks,
-** can be 4096 with a 4K fs).
-*/
-
-#if defined(SCSI_NCR_SEGMENT_SIZE)
static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
{
- struct scatterlist *scatter;
- struct dsb *phys;
- register u_short segment = 0;
- register u_short o_segment = 0;
- u_short chunk, chunk_min;
- u_long segaddr;
- int segsize;
- int datalen;
-
- phys = &cp->phys;
- cp->data_len = 0;
-
- /*
- ** Compute a good value for chunk size
- ** If SCSI_NCR_SEGMENT_SIZE is OK, we will try to use it.
- */
-
- if (!cmd->use_sg)
- cp->data_len = cmd->request_bufflen;
- else {
- scatter = (struct scatterlist *)cmd->buffer;
- for (segment = 0 ; segment < cmd->use_sg ; segment++)
- cp->data_len += scatter[segment].length;
- }
-
-
- if (!cp->data_len) {
- bzero (&phys->data, sizeof (phys->data));
- return 0;
- }
-
- chunk_min = cp->data_len / MAX_SCATTER;
- for (chunk = SCSI_NCR_SEGMENT_SIZE ; chunk < chunk_min ; chunk += chunk);
-
- /*
- ** If the linux scsi command is not a scatterlist,
- ** the computed chunk size is OK.
- */
-
- if (!cmd->use_sg) {
- bzero (&phys->data, sizeof (phys->data));
- datalen = cmd->request_bufflen;
- segaddr = vtophys(cmd->request_buffer);
- segsize = chunk;
- o_segment = 0;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
- printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
- (unsigned) segaddr, (int) datalen, (int) chunk);
-
- while (datalen && (o_segment < MAX_SCATTER)) {
- if (segsize > datalen) segsize = datalen;
- phys->data[o_segment].addr = segaddr;
- phys->data[o_segment].size = segsize;
+ struct scr_tblmove *data;
+ int segment = 0;
+ int use_sg = (int) cmd->use_sg;
- datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
- printf ("ncr53c8xx: seg #%d addr=%lx size=%d (rest=%d).\n",
- o_segment, segaddr, (int) segsize, (int) datalen);
+#if 0
+ bzero (cp->phys.data, sizeof (cp->phys.data));
+#endif
+ data = cp->phys.data;
+ cp->data_len = 0;
- segaddr += segsize;
- o_segment++;
+ if (!use_sg) {
+ if (cmd->request_bufflen) {
+ data = &data[MAX_SCATTER - 1];
+ data[0].addr = vtophys(cmd->request_buffer);
+ data[0].size = cmd->request_bufflen;
+ cp->data_len = data[0].size;
+ segment = 1;
}
-
- return datalen ? -1 : o_segment;
}
+ else if (use_sg <= MAX_SCATTER) {
+ struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
- /*
- ** Else, the computed chunk size is not so good
- ** and we have to iterate.
- ** Rescatter the Linux scatterlist into the data block descriptor.
- ** Loop if necessary, beginning with the not so good chunk size and
- ** doubling it if the scatter process fails.
- */
-
- scatter = (struct scatterlist *)cmd->buffer;
- for (segment = 0; segment < cmd->use_sg; chunk += chunk) {
- o_segment = 0;
- bzero (&phys->data, sizeof (phys->data));
- for (segment = 0 ; segment < cmd->use_sg ; segment++) {
- datalen = scatter[segment].length;
- segaddr = vtophys(scatter[segment].address);
- segsize = chunk;
-
-if (DEBUG_FLAGS & DEBUG_SCATTER)
- printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n",
- (unsigned) segaddr, (int) datalen, (int) chunk);
-
- while (datalen && (o_segment < MAX_SCATTER)) {
- if (segsize > datalen) segsize = datalen;
- phys->data[o_segment].addr = segaddr;
- phys->data[o_segment].size = segsize;
-
- datalen -= segsize;
-
-if(DEBUG_FLAGS & DEBUG_SCATTER)
- printf ("ncr53c8xx: seg #%d addr=%lx size=%d (rest=%d).\n",
- o_segment, segaddr, (int) segsize, (int) datalen);
-
- segaddr += segsize;
- o_segment++;
- }
-
- if (datalen) break;
+ data = &data[MAX_SCATTER - use_sg];
+ while (segment < use_sg) {
+ data[segment].addr = vtophys(scatter[segment].address);
+ data[segment].size = scatter[segment].length;
+ cp->data_len += data[segment].size;
+ ++segment;
}
}
-
- return segment < cmd->use_sg ? -1 : o_segment;
-}
-
-#else /* !defined SCSI_NCR_SEGMENT_SIZE */
-
-static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
-{
- struct dsb *phys = &cp->phys;
- u_short segment = 0;
-
- cp->data_len = 0;
- bzero (&phys->data, sizeof (phys->data));
-
- if (!cmd->use_sg) {
- phys->data[segment].addr = vtophys(cmd->request_buffer);
- phys->data[segment].size = cmd->request_bufflen;
- cp->data_len += phys->data[segment].size;
- segment++;
- return segment;
- }
-
- while (segment < cmd->use_sg && segment < MAX_SCATTER) {
- struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
-
- phys->data[segment].addr = vtophys(scatter[segment].address);
- phys->data[segment].size = scatter[segment].length;
- cp->data_len += phys->data[segment].size;
- ++segment;
+ else {
+ return -1;
}
- return segment < cmd->use_sg ? -1 : segment;
+ return segment;
}
-#endif /* SCSI_NCR_SEGMENT_SIZE */
/*==========================================================
**
@@ -7068,7 +8291,9 @@
*/
#ifndef NCR_IOMAPPED
+__initfunc(
static int ncr_regtest (struct ncb* np)
+)
{
register volatile u_long data;
/*
@@ -7092,12 +8317,14 @@
}
#endif
+__initfunc(
static int ncr_snooptest (struct ncb* np)
+)
{
u_long ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc, err=0;
int i;
#ifndef NCR_IOMAPPED
- if (np->use_mmio) {
+ if (np->reg) {
err |= ncr_regtest (np);
if (err) return (err);
}
@@ -7105,7 +8332,7 @@
/*
** init
*/
- pc = NCB_SCRIPT_PHYS (np, snooptest);
+ pc = NCB_SCRIPTH_PHYS (np, snooptest);
host_wr = 1;
ncr_wr = 2;
/*
@@ -7149,8 +8376,11 @@
/*
** Check termination position.
*/
- if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) {
+ if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) {
printf ("CACHE TEST FAILED: script execution failed.\n");
+ printf ("start=%08lx, pc=%08lx, end=%08lx\n",
+ (u_long) NCB_SCRIPTH_PHYS (np, snooptest), pc,
+ (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
return (0x40);
};
/*
@@ -7183,23 +8413,19 @@
**==========================================================
*/
-/*
-** Compute the difference in milliseconds.
-**/
+#ifdef SCSI_NCR_PROFILE_SUPPORT
-#ifdef SCSI_NCR_PROFILE
+/*
+** Compute the difference in jiffies ticks.
+*/
-static int ncr_delta (u_long from, u_long to)
-{
- if (!from) return (-1);
- if (!to) return (-2);
- return ((to - from) * 1000 / HZ );
-}
+#define ncr_delta(from, to) \
+ ( ((to) && (from))? (to) - (from) : -1 )
#define PROFILE cp->phys.header.stamp
static void ncb_profile (ncb_p np, ccb_p cp)
{
- int co, da, st, en, di, se, post,work,disc;
+ int co, st, en, di, se, post,work,disc;
u_long diff;
PROFILE.end = jiffies;
@@ -7207,9 +8433,6 @@
st = ncr_delta (PROFILE.start,PROFILE.status);
if (st<0) return; /* status not reached */
- da = ncr_delta (PROFILE.start,PROFILE.data);
- if (da<0) return; /* No data transfer phase */
-
co = ncr_delta (PROFILE.start,PROFILE.command);
if (co<0) return; /* command not executed */
@@ -7246,7 +8469,7 @@
}
#undef PROFILE
-#endif /* SCSI_NCR_PROFILE */
+#endif /* SCSI_NCR_PROFILE_SUPPORT */
/*==========================================================
**
@@ -7307,7 +8530,7 @@
/*==========================================================
**
** Determine the ncr's clock frequency.
-** This is important for the negotiation
+** This is essential for the negotiation
** of the synchronous transfer rate.
**
**==========================================================
@@ -7315,84 +8538,161 @@
** Note: we have to return the correct value.
** THERE IS NO SAVE DEFAULT VALUE.
**
-** We assume that all NCR based boards are delivered
-** with a 40Mhz clock. Because we have to divide
-** by an integer value greater than 3, only clock
-** frequencies of 40Mhz (/4) or 50MHz (/5) permit
-** the FAST-SCSI rate of 10MHz.
+** Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+** 53C860 and 53C875 rev. 1 support fast20 transfers but
+** do not have a clock doubler and so are provided with a
+** 80 MHz clock. All other fast20 boards incorporate a doubler
+** and so should be delivered with a 40 MHz clock.
+** The future fast40 chips (895/895) use a 40 Mhz base clock
+** and provide a clock quadrupler (160 Mhz). The code below
+** tries to deal as cleverly as possible with all this stuff.
**
**----------------------------------------------------------
*/
-#ifndef NCR_CLOCK
-# define NCR_CLOCK 40
-#endif /* NCR_CLOCK */
+/*
+ * Select NCR SCSI clock frequency
+ */
+static void ncr_selectclock(ncb_p np, u_char scntl3)
+{
+ if (np->multiplier < 2) {
+ OUTB(nc_scntl3, scntl3);
+ return;
+ }
+
+ if (bootverbose >= 2)
+ printf ("%s: enabling clock multiplier\n", ncr_name(np));
+ OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */
+ if (np->multiplier > 2) { /* Poll bit 5 of stest4 for quadrupler */
+ int i = 20;
+ while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
+ DELAY(20);
+ if (!i)
+ printf("%s: the chip cannot lock the frequency\n", ncr_name(np));
+ } else /* Wait 20 micro-seconds for doubler */
+ DELAY(20);
+ OUTB(nc_stest3, HSC); /* Halt the scsi clock */
+ OUTB(nc_scntl3, scntl3);
+ OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */
+ OUTB(nc_stest3, 0x00); /* Restart scsi clock */
+}
-static void ncr_getclock (ncb_p np, u_char scntl3)
-{
-#if 0
- u_char tbl[5] = {6,2,3,4,6};
- u_char f;
- u_char ns_clock = (1000/NCR_CLOCK);
-
- /*
- ** Compute the best value for scntl3.
- */
-
- f = (2 * MIN_SYNC_PD - 1) / ns_clock;
- if (!f ) f=1;
- if (f>4) f=4;
- np -> ns_sync = (ns_clock * tbl[f]) / 2;
- np -> rv_scntl3 = f<<4;
-
- f = (2 * MIN_ASYNC_PD - 1) / ns_clock;
- if (!f ) f=1;
- if (f>4) f=4;
- np -> ns_async = (ns_clock * tbl[f]) / 2;
- np -> rv_scntl3 |= f;
- if (DEBUG_FLAGS & DEBUG_TIMING)
- printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n",
- ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3);
-#else
- /*
- * If NCR53C875 chip with clock doubler enabled,
- * disable clock doubler and assume 40 MHz clock.
- * If NCR53C860 chip assume 80 MHz clock.
+
+/*
+ * calculate NCR SCSI clock frequency (in KHz)
+ */
+__initfunc(
+static unsigned ncrgetfreq (ncb_p np, int gen)
+)
+{
+ unsigned ms = 0;
+
+ /*
+ * Measure GEN timer delay in order
+ * to calculate SCSI clock frequency
+ *
+ * This code will never execute too
+ * many loop iterations (if DELAY is
+ * reasonably correct). It could get
+ * too low a delay (too high a freq.)
+ * if the CPU is slow executing the
+ * loop for some reason (an NMI, for
+ * example). For this reason we will
+ * if multiple measurements are to be
+ * performed trust the higher delay
+ * (lower frequency returned).
*/
+ OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */
+ OUTW (nc_sien , 0); /* mask all scsi interrupts */
+ (void) INW (nc_sist); /* clear pending scsi interrupt */
+ OUTB (nc_dien , 0); /* mask all dma interrupts */
+ (void) INW (nc_sist); /* another one, just to be sure :) */
+ OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ OUTB (nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */
+ while (!(INW(nc_sist) & GEN) && ms++ < 100000)
+ DELAY(1000); /* count ms */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ /*
+ * set prescaler to divide by whatever 0 means
+ * 0 ought to choose divide by 2, but appears
+ * to set divide by 3.5 mode in my 53c810 ...
+ */
+ OUTB (nc_scntl3, 0);
+
+ if (bootverbose >= 2)
+ printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
+ /*
+ * adjust for prescaler, and convert into KHz
+ */
+ return ms ? ((1 << gen) * 4340) / ms : 0;
+}
- switch(np->device_id) {
- case PCI_DEVICE_ID_NCR_53C875:
- if ((INB(nc_stest1) & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
- if (bootverbose)
- printf ("%s: disabling clock doubler\n", ncr_name(np));
- OUTB(nc_stest1, 0);
- scntl3 = 3;
- }
- break;
- case PCI_DEVICE_ID_NCR_53C860:
- scntl3 = 5;
- break;
+/*
+ * Get/probe NCR SCSI clock frequency
+ */
+__initfunc(
+static void ncr_getclock (ncb_p np, int mult)
+)
+{
+ unsigned char scntl3 = INB(nc_scntl3);
+ unsigned char stest1 = INB(nc_stest1);
+ unsigned f1;
+
+ np->multiplier = 1;
+ f1 = 40000;
+
+ /*
+ ** True with 875 or 895 with clock multiplier selected
+ */
+ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+ if (bootverbose >= 2)
+ printf ("%s: clock multiplier found\n", ncr_name(np));
+ np->multiplier = mult;
}
/*
- * For now just preserve the BIOS setting ...
- */
+ ** If multiplier not found or scntl3 not 7,5,3,
+ ** reset chip and get frequency from general purpose timer.
+ ** Otherwise trust scntl3 BIOS setting.
+ */
+ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+ unsigned f2;
- if ((scntl3 & 7) < 3) {
- printf ("%s: assuming 40MHz clock\n", ncr_name(np));
- scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */
- }
+ OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0);
- np->ns_sync = 25;
- np->ns_async = 50;
- np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
-
- if (bootverbose) {
- printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
- ncr_name(np), scntl3, np->rv_scntl3);
+ (void) ncrgetfreq (np, 11); /* throw away first result */
+ f1 = ncrgetfreq (np, 11);
+ f2 = ncrgetfreq (np, 11);
+
+ if (bootverbose)
+ printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+
+ if (f1 > f2) f1 = f2; /* trust lower result */
+
+ if (f1 < 45000) f1 = 40000;
+ else if (f1 < 55000) f1 = 50000;
+ else f1 = 80000;
+
+ if (f1 < 80000 && mult > 1) {
+ if (bootverbose >= 2)
+ printf ("%s: clock multiplier assumed\n", ncr_name(np));
+ np->multiplier = mult;
+ }
+ } else {
+ if ((scntl3 & 7) == 3) f1 = 40000;
+ else if ((scntl3 & 7) == 5) f1 = 80000;
+ else f1 = 160000;
+
+ f1 /= np->multiplier;
}
-#endif
+
+ /*
+ ** Compute controller synchronous parameters.
+ */
+ f1 *= np->multiplier;
+ np->clock_khz = f1;
}
/*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -7409,29 +8709,108 @@
#define ulong unsigned long
#endif
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
- uchar bus, uchar device_fn, int options);
-
-/*
-** NCR53C8XX devices description table
+/* ---------------------------------------------------------------------
+**
+** Driver setup from the boot command line
+**
+** ---------------------------------------------------------------------
*/
-static struct {
- ushort pci_device_id;
- int chip;
- int max_revision;
- int min_revision;
-} pci_chip_ids[] = {
- {PCI_DEVICE_ID_NCR_53C810, 810, -1, -1},
-/* {PCI_DEVICE_ID_NCR_53C810AP, 810, -1, -1}, */
- {PCI_DEVICE_ID_NCR_53C815, 815, -1, -1},
- {PCI_DEVICE_ID_NCR_53C820, 820, -1, -1},
- {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1},
- {PCI_DEVICE_ID_NCR_53C860, 860, -1, -1},
- {PCI_DEVICE_ID_NCR_53C875, 875, -1, -1}
-};
+__initfunc(
+void ncr53c8xx_setup(char *str, int *ints)
+)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+ char *cur = str;
+ char *pc, *pv;
+ int val;
+ int base;
+ int c;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ val = 0;
+ pv = pc;
+ c = *++pv;
+ if (c == 'n')
+ val = 0;
+ else if (c == 'y')
+ val = 1;
+ else {
+ base = 0;
+#if 0
+ if (c == '0') {
+ c = *pv++;
+ base = 8;
+ }
+ if (c == 'x') {
+ ++pv;
+ base = 16;
+ }
+ else if (c >= '0' && c <= '9')
+ base = 10;
+ else
+ break;
+#endif
+ val = (int) simple_strtoul(pv, NULL, base);
+ }
-#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+ if (!strncmp(cur, "mpar:", 5))
+ driver_setup.master_parity = val;
+ else if (!strncmp(cur, "spar:", 5))
+ driver_setup.scsi_parity = val;
+ else if (!strncmp(cur, "disc:", 5))
+ driver_setup.disconnection = val;
+ else if (!strncmp(cur, "specf:", 6))
+ driver_setup.special_features = val;
+ else if (!strncmp(cur, "ultra:", 6))
+ driver_setup.ultra_scsi = val;
+ else if (!strncmp(cur, "fsn:", 4))
+ driver_setup.force_sync_nego = val;
+ else if (!strncmp(cur, "revprob:", 8))
+ driver_setup.reverse_probe = val;
+ else if (!strncmp(cur, "tags:", 5)) {
+ if (val > SCSI_NCR_MAX_TAGS)
+ val = SCSI_NCR_MAX_TAGS;
+ driver_setup.default_tags = val;
+ }
+ else if (!strncmp(cur, "sync:", 5))
+ driver_setup.default_sync = val;
+ else if (!strncmp(cur, "verb:", 5))
+ driver_setup.verbose = val;
+ else if (!strncmp(cur, "debug:", 6))
+ driver_setup.debug = val;
+ else if (!strncmp(cur, "burst:", 6))
+ driver_setup.burst_max = val;
+ else if (!strncmp(cur, "led:", 4))
+ driver_setup.led_pin = val;
+ else if (!strncmp(cur, "wide:", 5))
+ driver_setup.max_wide = val? 1:0;
+ else if (!strncmp(cur, "settle:", 7))
+ driver_setup.settle_delay= val;
+ else if (!strncmp(cur, "diff:", 5))
+ driver_setup.diff_support= val;
+ else if (!strncmp(cur, "irqm:", 5))
+ driver_setup.irqm = val;
+ else if (!strncmp(cur, "pcifix:", 7))
+ driver_setup.pci_fix_up = val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ else if (!strncmp(cur, "nvram:", 6))
+ driver_setup.use_nvram = val;
+#endif
+
+ else if (!strncmp(cur, "safe:", 5) && val)
+ memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
+ else
+ printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+
+ if ((cur = strchr(cur, ',')) != NULL)
+ ++cur;
+ }
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+}
+
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device);
/*
** Linux entry point for NCR53C8XX devices detection routine.
@@ -7442,15 +8821,156 @@
** Read the PCI configuration and try to attach each
** detected NCR board.
**
+** If NVRAM is present, try to attach boards according to
+** the used defined boot order.
+**
** Returns the number of boards successfully attached.
*/
+__initfunc(
+static void ncr_print_driver_setup(void)
+)
+{
+#define YesNo(y) y ? 'y' : 'n'
+ printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+ YesNo(driver_setup.disconnection),
+ YesNo(driver_setup.special_features),
+ YesNo(driver_setup.ultra_scsi),
+ driver_setup.default_tags,
+ driver_setup.default_sync,
+ driver_setup.burst_max,
+ YesNo(driver_setup.max_wide),
+ driver_setup.diff_support);
+ printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,led:%c,settle:%d,irqm:%d\n",
+ YesNo(driver_setup.master_parity),
+ YesNo(driver_setup.scsi_parity),
+ YesNo(driver_setup.force_sync_nego),
+ driver_setup.verbose,
+ driver_setup.debug,
+ YesNo(driver_setup.led_pin),
+ driver_setup.settle_delay,
+ driver_setup.irqm);
+#undef YesNo
+}
+
+/*
+** NCR53C8XX devices description table and chip ids list.
+*/
+
+static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
+static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+ int i, j;
+ int attach_count = 0;
+ ncr_nvram *nvram;
+ ncr_device *devp;
+
+ if (!nvram_index)
+ return 0;
+
+ /* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+ for (i = 0, nvram_index = -1; i < count; i++) {
+ devp = &device[i];
+ nvram = devp->nvram;
+ if (!nvram)
+ continue;
+ if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+ if (nvram_index == -1)
+ nvram_index = i;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+ for (j = 0 ; j < 4 ; j++) {
+ Symbios_host *h = &nvram->data.Symbios.host[j];
+ printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+ j, h->device_id, h->vendor_id,
+ h->device_fn, h->io_port,
+ (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+ }
+ }
+ else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
+ /* display Tekram nvram data */
+ printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+#endif
+ }
+ }
+
+ if (nvram_index >= 0 && nvram_index < count)
+ nvram = device[nvram_index].nvram;
+ else
+ nvram = 0;
+
+ if (!nvram)
+ goto out;
+
+ /*
+ ** check devices in the boot record against devices detected.
+ ** attach devices if we find a match. boot table records that
+ ** do not match any detected devices will be ignored.
+ ** devices that do not match any boot table will not be attached
+ ** here but will attempt to be attached during the device table
+ ** rescan.
+ */
+ for (i = 0; i < 4; i++) {
+ Symbios_host *h = &nvram->data.Symbios.host[i];
+ for (j = 0 ; j < count ; j++) {
+ devp = &device[j];
+ if (h->device_fn == devp->slot.device_fn &&
+#if 0 /* bus number location in nvram ? */
+ h->bus == devp->slot.bus &&
+#endif
+ h->device_id == devp->chip.device_id)
+ break;
+ }
+ if (j < count && !devp->attached &&
+ !ncr_attach (tpnt, attach_count, devp)) {
+ attach_count++;
+ devp->attached = 1;
+ }
+ }
+
+out:
+ return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
+)
{
- int i;
- int count = 0; /* Number of boards detected */
- uchar pci_bus, pci_device_fn;
- short pci_index; /* Device index to PCI BIOS calls */
+ int i, j;
+ int chips;
+ int count = 0;
+ uchar bus, device_fn;
+ short index;
+ int attach_count = 0;
+ ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram nvram[4];
+ int k, nvrams;
+#endif
+ int hosts;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ int nvram_index = 0;
+#endif
+ if (initverbose >= 2)
+ ncr_print_driver_setup();
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+ ncr_debug = driver_setup.debug;
+#endif
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
tpnt->proc_dir = &proc_scsi_ncr53c8xx;
@@ -7459,131 +8979,363 @@
# endif
#endif
- if (pcibios_present()) {
- for (i = 0; i < NPCI_CHIP_IDS; ++i)
- for (pci_index = 0;
- !pcibios_find_device(PCI_VENDOR_ID_NCR,
- pci_chip_ids[i].pci_device_id, pci_index, &pci_bus,
- &pci_device_fn);
- ++pci_index)
- if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip,
- pci_bus, pci_device_fn, /* no options */ 0))
- ++count;
- }
+ /*
+ ** Detect all 53c8xx hosts and then attach them.
+ **
+ ** If we are using NVRAM, once all hosts are detected, we need to check
+ ** any NVRAM for boot order in case detect and boot order differ and
+ ** attach them using the order in the NVRAM.
+ **
+ ** If no NVRAM is found or data appears invalid attach boards in the
+ ** the order they are detected.
+ */
- return count;
-}
+ if (!pcibios_present())
+ return 0;
+
+ chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+ hosts = sizeof(device) / sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ k = 0;
+ if (driver_setup.use_nvram & 0x1)
+ nvrams = sizeof(nvram) / sizeof(nvram[0]);
+ else
+ nvrams = 0;
+#endif
+
+ for (j = 0; j < chips ; ++j) {
+ i = driver_setup.reverse_probe ? chips-1 - j : j;
+ for (index = 0; ; index++) {
+ char *msg = "";
+ if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+ index, &bus, &device_fn)) ||
+ (count == hosts))
+ break;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+ device[count].nvram = 0;
+#endif
+ if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+ device[count].nvram = 0;
+ continue;
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (device[count].nvram) {
+ ++k;
+ nvram_index |= device[count].nvram->type;
+ switch (device[count].nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ msg = "with Tekram NVRAM";
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ msg = "with Symbios NVRAM";
+ break;
+ default:
+ msg = "";
+ device[count].nvram = 0;
+ --k;
+ }
+ }
+#endif
+ printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+ device[count].chip.name, msg);
+
+ device[count].attached = 0;
+ ++count;
+ }
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+ /*
+ ** rescan device list to make sure all boards attached.
+ ** devices without boot records will not be attached yet
+ ** so try to attach them here.
+ */
+ for (i= 0; i < count; i++) {
+ if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+ attach_count++;
+ device[i].attached = 1;
+ }
+ }
+ return attach_count;
+}
/*
-** Read the PCI configuration of a found NCR board and
-** try yo attach it.
+** Read and check the PCI configuration for any detected NCR
+** boards and save data for attaching after all boards have
+** been detected.
*/
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip,
- uchar bus, uchar device_fn, int options)
-{
- ushort vendor_id, device_id, command;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
- uint base, io_port;
+__initfunc(
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device)
+)
+{
+ ushort vendor_id, device_id, command;
+ uchar cache_line_size, latency_timer;
+ uchar irq, revision;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+ uint base, io_port;
+#else
+ ulong base, io_port;
+#endif
+ int i, error;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram *nvram = device->nvram;
+#endif
+ ncr_chip *chip;
+
+ printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+ bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+ /*
+ * Read info from the PCI config space
+ */
+ if (
+ (error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id)) ||
+ (error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id)) ||
+ (error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) ||
+ (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port)) ||
+ (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
+ (error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
+ )
+ goto err_pcibios;
+
+ /*
+ * Check if the chip is supported
+ */
+ chip = 0;
+ for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+ if (device_id != ncr_chip_table[i].device_id)
+ continue;
+ if (revision > ncr_chip_table[i].revision_id)
+ continue;
+ chip = &device->chip;
+ memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+ chip->revision_id = revision;
+ break;
+ }
+ if (!chip) {
+ printk("ncr53c8xx: not initializing, device not supported\n");
+ return -1;
+ }
+
+ /*
+ * Check availability of IO space, memory space and master capability.
+ */
+ if (command & PCI_COMMAND_IO) {
+ if ((io_port & 3) != 1) {
+ printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n"
+ " bits 0..1 indicate a non-IO mapping\n", (int) io_port);
+ io_port = 0;
+ }
+ else
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ }
+ else
+ io_port = 0;
+
+ if (command & PCI_COMMAND_MEMORY) {
+ if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ printk("ncr53c8xx: disabling memory mapping since base address 1\n"
+ " contains a non-memory mapping\n");
+ base = 0;
+ }
+ else
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ else
+ base = 0;
+
+ if (!io_port && !base) {
+ printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
+ return -1;
+ }
+
+ if (io_port && check_region (io_port, 128)) {
+ printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
+ (int) io_port, (int) (io_port + 127));
+ return -1;
+ }
+
+ if (!(command & PCI_COMMAND_MASTER)) {
+ printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
+ return -1;
+ }
+
+ /*
+ * Fix some features according to driver setup.
+ */
+ if (!driver_setup.special_features)
+ chip->features &= ~FE_SPECIAL_SET;
+ if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+ chip->features |= FE_ULTRA;
+ chip->features &= ~FE_ULTRA2;
+ }
+ if (driver_setup.ultra_scsi < 1)
+ chip->features &= ~FE_ULTRA;
+ if (!driver_setup.max_wide)
+ chip->features &= ~FE_WIDE;
+
+
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
+
+ /*
+ * Try to fix up PCI config according to wished features.
+ */
+#if defined(__i386) && !defined(MODULE)
+ if ((driver_setup.pci_fix_up & 1) &&
+ (chip->features & FE_CLSE) && cache_line_size == 0) {
+ extern char x86;
+ switch(x86) {
+ case 4: cache_line_size = 4; break;
+ case 5: cache_line_size = 8; break;
+ }
+ if (cache_line_size)
+ error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
+ if (error)
+ goto err_pcibios;
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
+ }
+
+ if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
+ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ command |= PCI_COMMAND_INVALIDATE;
+ error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+ if (error)
+ goto err_pcibios;
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
+ }
+#endif
+ /*
+ * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+ * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+ * and donnot enable READ LINE.
+ * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed).
+ */
+
+ if (!(chip->features & FE_CLSE)) {
+ int burst_max = chip->burst_max;
+ if (cache_line_size == 0) {
+ chip->features &= ~FE_ERL;
+ if (burst_max > 3)
+ burst_max = 3;
+ }
+ else {
+ while (cache_line_size < (1 << burst_max))
+ --burst_max;
+ }
+ chip->burst_max = burst_max;
+ }
+
+ /*
+ * Tune PCI LATENCY TIMER according to burst max length transfer.
+ * (latency timer >= burst length + 6, we add 10 to be quite sure)
+ * If current value is zero, the device has probably been configured
+ * for no bursting due to some broken hardware.
+ */
+
+ if (latency_timer == 0 && chip->burst_max)
+ printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+ if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+ uchar lt = (1 << chip->burst_max) + 6 + 10;
+ if (latency_timer < lt) {
+ latency_timer = lt;
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
+ error = pcibios_write_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, latency_timer);
+ if (error)
+ goto err_pcibios;
+ }
+ }
+
+ /*
+ * Fix up for recent chips that support CACHE LINE SIZE.
+ * If PCI config space is not OK, remove features that shall not be
+ * used by the chip. No need to trigger possible chip bugs.
+ */
+
+ if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+ chip->features &= ~FE_CACHE_SET;
+ printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
+ }
+
+ if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ chip->features &= ~FE_WRIE;
+ printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
+ }
+
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ /* initialise ncr_device structure with items required by ncr_attach */
+ device->slot.bus = bus;
+ device->slot.device_fn = device_fn;
+ device->slot.base = base;
+ device->slot.io_port = io_port;
+ device->slot.irq = irq;
+ device->attached = 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (!nvram)
+ goto out;
+
+ /*
+ ** Get access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ request_region(io_port, 128, "ncr53c8xx");
+ device->slot.port = io_port;
+#else
+ device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+ if (!device->slot.reg)
+ goto out;
+#endif
+
+ /*
+ ** Try to read SYMBIOS nvram.
+ ** Data can be used to order booting of boards.
+ **
+ ** Data is saved in ncr_device structure if NVRAM found. This
+ ** is then used to find drive boot order for ncr_attach().
+ **
+ ** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+ ** for any device set up.
+ **
+ ** Try to read TEKRAM nvram if Symbios nvram not found.
+ */
+
+ if (!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+ nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+ else if (!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+ nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+ else
+ nvram->type = 0;
+out:
+ /*
+ ** Release access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ release_region(device->slot.port, 128);
#else
- ulong base, io_port;
+ unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
#endif
- uchar irq, revision;
- int error, expected_chip;
- int expected_id = -1, max_revision = -1, min_revision = -1;
- int i;
-
- printk("ncr53c8xx : at PCI bus %d, device %d, function %d\n",
- bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
-
- if (!pcibios_present()) {
- printk("ncr53c8xx : not initializing due to lack of PCI BIOS,\n");
- return -1;
- }
-
- if ((error = pcibios_read_config_word( bus, device_fn, PCI_VENDOR_ID, &vendor_id)) ||
- (error = pcibios_read_config_word( bus, device_fn, PCI_DEVICE_ID, &device_id)) ||
- (error = pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) ||
- (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, &io_port)) ||
- (error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) ||
- (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION, &revision)) ||
- (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE, &irq))) {
- printk("ncr53c8xx : error %s not initializing due to error reading configuration space\n",
- pcibios_strerror(error));
- return -1;
- }
-
- if (vendor_id != PCI_VENDOR_ID_NCR) {
- printk("ncr53c8xx : not initializing, 0x%04x is not NCR vendor ID\n", (int) vendor_id);
- return -1;
- }
-
-
- if (command & PCI_COMMAND_IO) {
- if ((io_port & 3) != 1) {
- printk("ncr53c8xx : disabling I/O mapping since base address 0 (0x%x)\n"
- " bits 0..1 indicate a non-IO mapping\n", (int) io_port);
- io_port = 0;
- }
- else
- io_port &= PCI_BASE_ADDRESS_IO_MASK;
- }
- else
- io_port = 0;
- if (command & PCI_COMMAND_MEMORY) {
- if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
- printk("ncr53c8xx : disabling memory mapping since base address 1\n"
- " contains a non-memory mapping\n");
- base = 0;
- }
- else
- base &= PCI_BASE_ADDRESS_MEM_MASK;
- }
- else
- base = 0;
-
- if (!io_port && !base) {
- printk("ncr53c8xx : not initializing, both I/O and memory mappings disabled\n");
- return -1;
- }
-
- if (!(command & PCI_COMMAND_MASTER)) {
- printk ("ncr53c8xx : not initializing, BUS MASTERING was disabled\n");
- return -1;
- }
-
- for (i = 0; i < NPCI_CHIP_IDS; ++i) {
- if (device_id == pci_chip_ids[i].pci_device_id) {
- max_revision = pci_chip_ids[i].max_revision;
- min_revision = pci_chip_ids[i].min_revision;
- expected_chip = pci_chip_ids[i].chip;
- }
- if (chip == pci_chip_ids[i].chip)
- expected_id = pci_chip_ids[i].pci_device_id;
- }
-
- if (chip && device_id != expected_id)
- printk("ncr53c8xx : warning : device id of 0x%04x doesn't\n"
- " match expected 0x%04x\n",
- (unsigned int) device_id, (unsigned int) expected_id );
-
- if (max_revision != -1 && revision > max_revision)
- printk("ncr53c8xx : warning : revision %d is greater than expected.\n",
- (int) revision);
- else if (min_revision != -1 && revision < min_revision)
- printk("ncr53c8xx : warning : revision %d is lower than expected.\n",
- (int) revision);
-
- if (io_port && check_region (io_port, 128)) {
- printk("ncr53c8xx : IO region 0x%x to 0x%x is in use\n",
- (int) io_port, (int) (io_port + 127));
- return -1;
- }
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+ return 0;
- return ncr_attach (tpnt, unit, device_id, revision, chip, base, io_port,
- (int) irq, bus, (uchar) device_fn);
+err_pcibios:
+ printk("ncr53c8xx: error %s reading configuration space\n",
+ pcibios_strerror(error));
+ return -1;
}
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -7596,13 +9348,18 @@
for (device = devlist; device; device = device->next) {
if (device->host == host) {
+#if SCSI_NCR_MAX_TAGS > 1
if (device->tagged_supported) {
device->queue_depth = SCSI_NCR_MAX_TAGS;
}
else {
- device->queue_depth = 1;
+ device->queue_depth = 2;
}
-#ifdef DEBUG
+#else
+ device->queue_depth = 1;
+#endif
+
+#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
device->id, device->lun, device->queue_depth);
#endif
@@ -7618,19 +9375,19 @@
int ncr53c8xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
{
int sts;
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx_queue_command\n");
#endif
if ((sts = ncr_queue_command(cmd, done)) != DID_OK) {
cmd->result = ScsiResult(sts, 0);
done(cmd);
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : command not queued - result=%d\n", sts);
#endif
return sts;
}
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : command successfully queued\n");
#endif
return sts;
@@ -7648,8 +9405,13 @@
{
struct Scsi_Host *host;
struct host_data *host_data;
+#if 0
+ u_long flags;
+
+ save_flags(flags); cli();
+#endif
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : interrupt received\n");
#endif
@@ -7658,12 +9420,20 @@
host_data = (struct host_data *) host->hostdata;
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
# ifdef SCSI_NCR_SHARE_IRQ
- if (dev_id == &host_data->ncb_data)
+ if (dev_id == host_data->ncb) {
+#else
+ if (1) {
# endif
#endif
- ncr_intr(&host_data->ncb_data);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception(host_data->ncb);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+ }
}
}
+#if 0
+ restore_flags(flags);
+#endif
}
/*
@@ -7679,40 +9449,106 @@
** Linux entry point of reset() function
*/
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+ int sts;
+ unsigned long flags;
+
+ printk("ncr53c8xx_reset: pid=%lu reset_flags=%x serial_number=%ld serial_number_at_timeout=%ld\n",
+ cmd->pid, reset_flags, cmd->serial_number, cmd->serial_number_at_timeout);
+
+ save_flags(flags); cli();
+
+ /*
+ * We have to just ignore reset requests in some situations.
+ */
+#if defined SCSI_RESET_NOT_RUNNING
+ if (cmd->serial_number != cmd->serial_number_at_timeout) {
+ sts = SCSI_RESET_NOT_RUNNING;
+ goto out;
+ }
+#endif
+ /*
+ * If the mid-level driver told us reset is synchronous, it seems
+ * that we must call the done() callback for the involved command,
+ * even if this command was not queued to the low-level driver,
+ * before returning SCSI_RESET_SUCCESS.
+ */
+
+ sts = ncr_reset_bus(cmd,
+ (reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS);
+ /*
+ * Since we always reset the controller, when we return success,
+ * we add this information to the return code.
+ */
+#if defined SCSI_RESET_HOST_RESET
+ if (sts == SCSI_RESET_SUCCESS)
+ sts |= SCSI_RESET_HOST_RESET;
+#endif
+
+out:
+ restore_flags(flags);
+ return sts;
+}
#else
int ncr53c8xx_reset(Scsi_Cmnd *cmd)
-#endif
{
-#ifdef DEBUG
-printk("ncr53c8xx_reset : reset call\n");
-#endif
- return ncr_reset_bus(cmd);
+ printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid);
+ return ncr_reset_bus(cmd, 1);
}
+#endif
/*
** Linux entry point of abort() function
*/
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
+int ncr53c8xx_abort(Scsi_Cmnd *cmd)
+{
+ int sts;
+ unsigned long flags;
+
+ printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n",
+ cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout);
+
+ save_flags(flags); cli();
+
+ /*
+ * We have to just ignore abort requests in some situations.
+ */
+ if (cmd->serial_number != cmd->serial_number_at_timeout) {
+ sts = SCSI_ABORT_NOT_RUNNING;
+ goto out;
+ }
+
+ sts = ncr_abort_command(cmd);
+out:
+ restore_flags(flags);
+ return sts;
+}
+#else
int ncr53c8xx_abort(Scsi_Cmnd *cmd)
{
-printk("ncr53c8xx_abort : abort call\n");
+ printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
return ncr_abort_command(cmd);
}
+#endif
#ifdef MODULE
int ncr53c8xx_release(struct Scsi_Host *host)
{
struct host_data *host_data;
-#ifdef DEBUG
+#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : release\n");
#endif
for (host = first_host; host; host = host->next) {
if (host->hostt == the_template) {
host_data = (struct host_data *) host->hostdata;
- ncr_detach(&host_data->ncb_data, host->irq);
+ ncr_detach(host_data->ncb, host->irq);
}
}
@@ -7752,17 +9588,19 @@
}
}
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd)
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd)
{
Scsi_Cmnd *wcmd;
if (!(wcmd = np->waiting_list)) return 0;
while (wcmd->next_wcmd) {
if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) {
- wcmd->next_wcmd = cmd->next_wcmd;
- cmd->next_wcmd = 0;
+ if (to_remove) {
+ wcmd->next_wcmd = cmd->next_wcmd;
+ cmd->next_wcmd = 0;
+ }
#ifdef DEBUG_WAITING_LIST
- printf("%s: cmd %lx removed from waiting list\n", ncr_name(np), (u_long) cmd);
+ printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
#endif
return cmd;
}
@@ -7802,13 +9640,7 @@
#undef next_wcmd
/*
-** In order to patch the SCSI script for SAVE/RESTORE DATA POINTER,
-** we need the direction of transfer.
-** Linux middle-level scsi driver does not provide this information.
-** So we have to guess it.
-** My documentation about SCSI-II standard is old. Probably some opcode
-** are missing.
-** If I do'nt know the command code, I assume input transfer direction.
+** Returns data transfer direction for common op-codes.
*/
static int guess_xfer_direction(int opcode)
@@ -7816,111 +9648,31 @@
int d;
switch(opcode) {
- case 0x00: /* TEST UNIT READY 00 */
- case 0x08: /* READ(6) 08 */
case 0x12: /* INQUIRY 12 */
case 0x4D: /* LOG SENSE 4D */
case 0x5A: /* MODE SENSE(10) 5A */
case 0x1A: /* MODE SENSE(6) 1A */
- case 0x28: /* READ(10) 28 */
- case 0xA8: /* READ(12) A8 */
case 0x3C: /* READ BUFFER 3C */
case 0x1C: /* RECEIVE DIAGNOSTIC RESULTS 1C */
- case 0xB7: /* READ DEFECT DATA(12) B7 */
- case 0xB8: /* READ ELEMENT STATUS B8 */
- /* GET WINDOW 25 */
- case 0x25: /* READ CAPACITY 25 */
- case 0x29: /* READ GENERATION 29 */
- case 0x3E: /* READ LONG 3E */
- /* GET DATA BUFFER STATUS 34 */
- /* PRE-FETCH 34 */
- case 0x34: /* READ POSITION 34 */
case 0x03: /* REQUEST SENSE 03 */
- case 0x05: /* READ BLOCK LIMITS 05 */
- case 0x0F: /* READ REVERSE 0F */
- case 0x14: /* RECOVER BUFFERED DATA 14 */
- case 0x2D: /* READ UPDATED BLOCK 2D */
- case 0x37: /* READ DEFECT DATA(10) 37 */
- case 0x42: /* READ SUB-CHANNEL 42 */
- case 0x43: /* READ TOC 43 */
- case 0x44: /* READ HEADER 44 */
- case 0xC7: /* ??? ??? C7 */
d = XferIn;
break;
case 0x39: /* COMPARE 39 */
case 0x3A: /* COPY AND VERIFY 3A */
- /* PRINT 0A */
- /* SEND MESSAGE(6) 0A */
- case 0x0A: /* WRITE(6) 0A */
case 0x18: /* COPY 18 */
case 0x4C: /* LOG SELECT 4C */
case 0x55: /* MODE SELECT(10) 55 */
case 0x3B: /* WRITE BUFFER 3B */
case 0x1D: /* SEND DIAGNOSTIC 1D */
case 0x40: /* CHANGE DEFINITION 40 */
- /* SEND MESSAGE(12) AA */
- case 0xAA: /* WRITE(12) AA */
- case 0xB6: /* SEND VOLUME TAG B6 */
- case 0x3F: /* WRITE LONG 3F */
- case 0x04: /* FORMAT UNIT 04 */
- /* INITIALIZE ELEMENT STATUS 07 */
- case 0x07: /* REASSIGN BLOCKS 07 */
case 0x15: /* MODE SELECT(6) 15 */
- case 0x24: /* SET WINDOW 24 */
- case 0x2A: /* WRITE(10) 2A */
- case 0x2E: /* WRITE AND VERIFY(10) 2E */
- case 0xAE: /* WRITE AND VERIFY(12) AE */
- case 0xB0: /* SEARCH DATA HIGH(12) B0 */
- case 0xB1: /* SEARCH DATA EQUAL(12) B1 */
- case 0xB2: /* SEARCH DATA LOW(12) B2 */
- /* OBJECT POSITION 31 */
- case 0x30: /* SEARCH DATA HIGH(10) 30 */
- case 0x31: /* SEARCH DATA EQUAL(10) 31 */
- case 0x32: /* SEARCH DATA LOW(10) 32 */
- case 0x38: /* MEDIUM SCAN 38 */
- case 0x3D: /* UPDATE BLOCK 3D */
- case 0x41: /* WRITE SAME 41 */
- /* LOAD UNLOAD 1B */
- /* SCAN 1B */
- case 0x1B: /* START STOP UNIT 1B */
d = XferOut;
break;
- case 0x01: /* REZERO UNIT 01 */
- /* SEEK(6) 0B */
- case 0x0B: /* SLEW AND PRINT 0B */
- /* SYNCHRONIZE BUFFER 10 */
- case 0x10: /* WRITE FILEMARKS 10 */
- case 0x11: /* SPACE 11 */
- case 0x13: /* VERIFY 13 */
- case 0x16: /* RESERVE UNIT 16 */
- case 0x17: /* RELEASE UNIT 17 */
- case 0x19: /* ERASE 19 */
- /* LOCATE 2B */
- /* POSITION TO ELEMENT 2B */
- case 0x2B: /* SEEK(10) 2B */
- case 0x1E: /* PREVENT ALLOW MEDIUM REMOVAL 1E */
- case 0x2C: /* ERASE(10) 2C */
- case 0xAC: /* ERASE(12) AC */
- case 0x2F: /* VERIFY(10) 2F */
- case 0xAF: /* VERIFY(12) AF */
- case 0x33: /* SET LIMITS(10) 33 */
- case 0xB3: /* SET LIMITS(12) B3 */
- case 0x35: /* SYNCHRONIZE CACHE 35 */
- case 0x36: /* LOCK UNLOCK CACHE 36 */
- case 0x45: /* PLAY AUDIO(10) 45 */
- case 0x47: /* PLAY AUDIO MSF 47 */
- case 0x48: /* PLAY AUDIO TRACK/INDEX 48 */
- case 0x49: /* PLAY TRACK RELATIVE(10) 49 */
- case 0xA9: /* PLAY TRACK RELATIVE(12) A9 */
- case 0x4B: /* PAUSE/RESUME 4B */
- /* MOVE MEDIUM A5 */
- case 0xA5: /* PLAY AUDIO(12) A5 */
- case 0xA6: /* EXCHANGE MEDIUM A6 */
- case 0xB5: /* REQUEST VOLUME ELEMENT ADDRESS B5 */
+ case 0x00: /* TEST UNIT READY 00 */
d = XferNone;
break;
default:
- d = XferIn;
+ d = XferBoth;
break;
}
@@ -7940,6 +9692,8 @@
**=========================================================================
*/
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+
#define is_digit(c) ((c) >= '0' && (c) <= '9')
#define digit_to_bin(c) ((c) - '0')
#define is_space(c) ((c) == ' ' || (c) == '\t')
@@ -8021,6 +9775,10 @@
uc->cmd = UC_SETFLAG;
else if ((arg_len = is_keyword(ptr, len, "clearprof")) != 0)
uc->cmd = UC_CLEARPROF;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+ else if ((arg_len = is_keyword(ptr, len, "debug_error_recovery")) != 0)
+ uc->cmd = UC_DEBUG_ERROR_RECOVERY;
+#endif
else
arg_len = 0;
@@ -8038,13 +9796,16 @@
case UC_SETWIDE:
case UC_SETFLAG:
SKIP_SPACES(1);
- GET_INT_ARG(target);
+ if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+ ptr += arg_len; len -= arg_len;
+ uc->target = ~0;
+ } else {
+ GET_INT_ARG(target);
+ uc->target = (1<<target);
#ifdef DEBUG_PROC_INFO
printf("ncr_user_command: target=%ld\n", target);
#endif
- if (target > MAX_TARGET)
- return -EINVAL;
- uc->target = (1<<target);
+ }
break;
}
@@ -8074,6 +9835,8 @@
SKIP_SPACES(1);
if ((arg_len = is_keyword(ptr, len, "alloc")))
uc->data |= DEBUG_ALLOC;
+ else if ((arg_len = is_keyword(ptr, len, "phase")))
+ uc->data |= DEBUG_PHASE;
else if ((arg_len = is_keyword(ptr, len, "poll")))
uc->data |= DEBUG_POLL;
else if ((arg_len = is_keyword(ptr, len, "queue")))
@@ -8086,6 +9849,8 @@
uc->data |= DEBUG_SCRIPT;
else if ((arg_len = is_keyword(ptr, len, "tiny")))
uc->data |= DEBUG_TINY;
+ else if ((arg_len = is_keyword(ptr, len, "timing")))
+ uc->data |= DEBUG_TIMING;
else if ((arg_len = is_keyword(ptr, len, "nego")))
uc->data |= DEBUG_NEGO;
else if ((arg_len = is_keyword(ptr, len, "tags")))
@@ -8107,24 +9872,37 @@
SKIP_SPACES(1);
if ((arg_len = is_keyword(ptr, len, "trace")))
uc->data |= UF_TRACE;
+ else if ((arg_len = is_keyword(ptr, len, "no_disc")))
+ uc->data |= UF_NODISC;
else
return -EINVAL;
ptr += arg_len; len -= arg_len;
}
break;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+ case UC_DEBUG_ERROR_RECOVERY:
+ SKIP_SPACES(1);
+ if ((arg_len = is_keyword(ptr, len, "sge")))
+ uc->data = 1;
+ else if ((arg_len = is_keyword(ptr, len, "abort")))
+ uc->data = 2;
+ else if ((arg_len = is_keyword(ptr, len, "reset")))
+ uc->data = 3;
+ else if ((arg_len = is_keyword(ptr, len, "parity")))
+ uc->data = 4;
+ else if ((arg_len = is_keyword(ptr, len, "none")))
+ uc->data = 0;
+ else
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+ break;
+#endif
default:
break;
}
- /*
- ** Not allow to disable tagged queue
- */
- if (uc->cmd == UC_SETTAGS && uc->data < 1)
- return -EINVAL;
-
if (len)
return -EINVAL;
-#ifdef SCSI_NCR_USER_COMMAND
else {
long flags;
@@ -8132,10 +9910,13 @@
ncr_usercmd (np);
restore_flags(flags);
}
-#endif
return length;
}
+#endif /* SCSI_NCR_USER_COMMAND_SUPPORT */
+
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
struct info_str
{
char *buffer;
@@ -8182,6 +9963,8 @@
** Copy formatted profile information into the input buffer.
*/
+#define to_ms(t) ((t) * 1000 / HZ)
+
static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
{
struct info_str info;
@@ -8192,7 +9975,7 @@
info.pos = 0;
copy_info(&info, "General information:\n");
- copy_info(&info, " Chip NCR53C%03d, ", np->chip);
+ copy_info(&info, " Chip NCR53C%s, ", np->chip_name);
copy_info(&info, "device id 0x%x, ", np->device_id);
copy_info(&info, "revision id 0x%x\n", np->revision_id);
@@ -8200,12 +9983,19 @@
copy_info(&info, "IRQ number %d\n", (int) np->irq);
#ifndef NCR_IOMAPPED
- if (np->use_mmio)
+ if (np->reg)
copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n",
- (u_long) np->reg_remapped);
+ (u_long) np->reg);
#endif
+ copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync);
+ copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+
+ if (driver_setup.debug || driver_setup.verbose > 1) {
+ copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug);
+ copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+ }
-#ifdef SCSI_NCR_PROFILE
+#ifdef SCSI_NCR_PROFILE_SUPPORT
copy_info(&info, "Profiling information:\n");
copy_info(&info, " %-12s = %lu\n", "num_trans",np->profile.num_trans);
copy_info(&info, " %-12s = %lu\n", "num_kbytes",np->profile.num_kbytes);
@@ -8213,15 +10003,17 @@
copy_info(&info, " %-12s = %lu\n", "num_break",np->profile.num_break);
copy_info(&info, " %-12s = %lu\n", "num_int", np->profile.num_int);
copy_info(&info, " %-12s = %lu\n", "num_fly", np->profile.num_fly);
- copy_info(&info, " %-12s = %lu\n", "ms_setup", np->profile.ms_setup);
- copy_info(&info, " %-12s = %lu\n", "ms_data", np->profile.ms_data);
- copy_info(&info, " %-12s = %lu\n", "ms_disc", np->profile.ms_disc);
- copy_info(&info, " %-12s = %lu\n", "ms_post", np->profile.ms_post);
+ copy_info(&info, " %-12s = %lu\n", "ms_setup", to_ms(np->profile.ms_setup));
+ copy_info(&info, " %-12s = %lu\n", "ms_data", to_ms(np->profile.ms_data));
+ copy_info(&info, " %-12s = %lu\n", "ms_disc", to_ms(np->profile.ms_disc));
+ copy_info(&info, " %-12s = %lu\n", "ms_post", to_ms(np->profile.ms_post));
#endif
return info.pos > info.offset? info.pos - info.offset : 0;
}
+#endif /* SCSI_NCR_USER_INFO_SUPPORT */
+
/*
** Entry point of the scsi proc fs of the driver.
** - func = 0 means read (returns profile data)
@@ -8243,7 +10035,7 @@
for (host = first_host; host; host = host->next) {
if (host->hostt == the_template && host->host_no == hostno) {
host_data = (struct host_data *) host->hostdata;
- ncb = &host_data->ncb_data;
+ ncb = host_data->ncb;
break;
}
}
@@ -8252,25 +10044,485 @@
return -EINVAL;
if (func) {
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
retv = ncr_user_command(ncb, buffer, length);
-#ifdef DEBUG_PROC_INFO
-printf("ncr_user_command: retv=%d\n", retv);
+#else
+ retv = -EINVAL;
#endif
}
else {
if (start)
*start = buffer;
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
retv = ncr_host_info(ncb, buffer, offset, length);
+#else
+ retv = -EINVAL;
+#endif
}
return retv;
}
+
/*=========================================================================
** End of proc file system stuff
**=========================================================================
*/
#endif
+
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+ u_char ack_data;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+ gpcntl = old_gpcntl & 0xfc;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB (nc_gpreg, old_gpreg);
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ nvram_setBit(np, 0, &gpreg, CLR_CLK);
+ nvram_setBit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ nvram_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ nvram_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ nvram_write_byte(np, &ack_data,
+ 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ nvram_write_byte(np, &ack_data,
+ (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ nvram_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ nvram_write_byte(np, &ack_data,
+ 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all active data - only part of total NVRAM */
+ csum = nvram_read_data(np,
+ (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB (nc_gpcntl, gpcntl);
+ nvram_stop(np, &gpreg);
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+ nvram->start_marker,
+ nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+ nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+ nvram->byte_count, sizeof(*nvram) - 12,
+ nvram->checksum, csum);
+#endif
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->start_marker == 0 &&
+ !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+ nvram->byte_count == sizeof(*nvram) - 12 &&
+ csum == nvram->checksum)
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_short csum;
+
+ for (x = 0; x < len; x++)
+ nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+
+ return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ nvram_doBit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl & 0xfe);
+ nvram_doBit(np, 0, write_bit, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl | 0x01);
+ nvram_doBit(np, read_bit, 1, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+ nvram_setBit(np, write_bit, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB (nc_gpreg);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+ DELAY(5);
+ switch (bit_mode){
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB (nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ csum = Tnvram_read_data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ /* check data valid */
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+ u_char read_bit;
+ u_short csum;
+ int x;
+
+ for (x = 0, csum = 0; x < len; x++) {
+
+ /* output read command and address */
+ Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 0; /* Force bad checksum */
+
+ Tnvram_Read_Word(np, &data[x], gpreg);
+ csum += data[x];
+
+ Tnvram_Stop(np, gpreg);
+ }
+
+ return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+ DELAY(2);
+ Tnvram_Clk(np, gpreg);
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ *gpreg &= 0xef;
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+ OUTB (nc_gpreg, *gpreg | 0x04);
+ DELAY(2);
+ OUTB (nc_gpreg, *gpreg);
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
/*
** Module stuff
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov