patch-2.0.28 linux/drivers/scsi/eata.c

Next file: linux/drivers/scsi/eata.h
Previous file: linux/drivers/scsi/README.qlogicisp
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.27/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
@@ -1,6 +1,13 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *       8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
+ *          Added linked command support.
+ *          Improved detection of PCI boards using ISA base addresses.
+ *
+ *       3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ *          Added support for tagged commands and queue depth adjustment.
+ *
  *      22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
  *          When CONFIG_PCI is defined, BIOS32 is used to include in the
  *          list of i/o ports to be probed all the PCI SCSI controllers.
@@ -85,7 +92,7 @@
  *          This driver is based on the CAM (Common Access Method Committee)
  *          EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
  *
- *  Copyright (C) 1994, 1995, 1996 Dario Ballabio (dario@milano.europe.dg.com)
+ *  Copyright (C) 1994-1997 Dario Ballabio (dario@milano.europe.dg.com)
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that redistributions of source
@@ -121,6 +128,15 @@
  *  PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS 
  *  v003.D0, firmware v07G.0).
  *
+ *  DPT SmartRAID boards support "Hardware Array" - a group of disk drives
+ *  which are all members of the same RAID-1 or RAID-5 array implemented
+ *  in host adapter hardware. Hardware Arrays are fully compatible with this
+ *  driver, since they look to it as a single disk drive.
+ *  By contrast RAID-0 are implemented as "Array Group", which does not
+ *  seem to be supported correctly by the actual SCSI subsystem.
+ *  To get RAID-0 functionality, the linux MD driver must be used instead of
+ *  the DPT "Array Group" feature.
+ *
  *  Multiple ISA, EISA and PCI boards can be configured in the same system.
  *  It is suggested to put all the EISA boards on the same IRQ level, all
  *  the PCI  boards on another IRQ level, while ISA boards cannot share 
@@ -199,6 +215,7 @@
 
 #undef FORCE_CONFIG
 
+#undef  DEBUG_LINKED_COMMANDS
 #undef  DEBUG_DETECT
 #undef  DEBUG_INTERRUPT
 #undef  DEBUG_STATISTICS
@@ -218,6 +235,7 @@
 #define MAX_LARGE_SGLIST 252
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN 16
 
 #define SKIP 1
 #define FALSE 0
@@ -227,6 +245,8 @@
 #define LOCKED   2
 #define IN_RESET 3
 #define IGNORE   4
+#define READY    5
+#define ABORTING 6
 #define NO_DMA  0xff
 #define MAXLOOP 200000
 
@@ -241,8 +261,9 @@
 #define REG_MID         4
 #define REG_MSB         5
 #define REGION_SIZE     9
-#define ISA_RANGE       0x0fff
-#define EISA_RANGE      0xfc88
+#define MAX_ISA_ADDR    0x03ff
+#define MIN_EISA_ADDR   0x1c88
+#define MAX_EISA_ADDR   0xfc88
 #define BSY_ASSERTED      0x80
 #define DRQ_ASSERTED      0x08
 #define ABSY_ASSERTED     0x01
@@ -369,6 +390,7 @@
    ulong  sp_addr;       /* Address where sp is DMA'ed when cp completes */
    ulong  sense_addr;    /* Address where Sense Data is DMA'ed on error */
    unsigned int index;   /* cp index */
+   unsigned int link_id; /* reference cp for linked commands */
    struct sg_list *sglist;
    };
 
@@ -427,6 +449,75 @@
 static int do_trace = FALSE;
 static int setup_done = FALSE;
 
+#if defined (CONFIG_SCSI_EATA_TAGGED_QUEUE)
+static int tagged_commands = TRUE;
+#else
+static int tagged_commands = FALSE;
+#endif
+
+#if defined (CONFIG_SCSI_EATA_LINKED_COMMANDS)
+static int linked_commands = TRUE;
+#else
+static int linked_commands = FALSE;
+#endif
+
+#if defined CONFIG_SCSI_EATA_MAX_TAGS
+static int max_queue_depth = CONFIG_SCSI_EATA_MAX_TAGS;
+#else
+static int max_queue_depth = MAX_CMD_PER_LUN;
+#endif
+
+static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
+   Scsi_Device *dev;
+   int j, ntag = 0, nuntag = 0, tqd, utqd; 
+   unsigned long flags;
+
+   save_flags(flags);
+   cli();
+
+   j = ((struct hostdata *) host->hostdata)->board_number;
+
+   for(dev = devlist; dev; dev = dev->next) {
+
+      if (dev->host != host) continue;
+
+      if (dev->tagged_supported) ntag++;
+      else                       nuntag++;
+      }
+
+   utqd = MAX_CMD_PER_LUN;
+
+   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+
+   if (tqd > max_queue_depth) tqd = max_queue_depth;
+
+   if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
+
+   for(dev = devlist; dev; dev = dev->next) {
+      char *tag_suffix = "";
+
+      if (dev->host != host) continue;
+
+      if (dev->tagged_supported) dev->queue_depth = tqd;
+      else                       dev->queue_depth = utqd;
+
+      if (tagged_commands && dev->tagged_supported) {
+         dev->tagged_queue = 1;
+         dev->current_tag = 1;
+         }
+
+      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
+      else if (dev->tagged_supported) tag_suffix = ", untagged";
+
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+             BN(j), host->host_no, dev->channel, dev->id, dev->lun,
+             dev->queue_depth, tag_suffix);
+      }
+
+   restore_flags(flags);
+   return;
+}
+
 static inline int wait_on_busy(unsigned int iobase) {
    unsigned int loop = MAXLOOP;
 
@@ -472,7 +563,7 @@
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
-   char *bus_type;
+   char *bus_type, dma_name[16];
 
    /* Allowed DMA channels for ISA (0 indicates reserved) */
    unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
@@ -507,34 +598,59 @@
    else
       protocol_rev = 'C';
 
-   irq = info.irq;
-
-   if (port_base > ISA_RANGE) {
-
-      if (!info.haaval || info.ata || info.drqvld) {
-	 printk("%s: unusable EISA/PCI board found (%d%d%d), detaching.\n", 
-		name, info.haaval, info.ata, info.drqvld);
-	 return FALSE;
-	 }
-
+   if (!setup_done && j > 0 && j <= MAX_PCI) {
+      bus_type = "PCI";
+      subversion = ESA;
+      }
+   else if (port_base > MAX_EISA_ADDR || (protocol_rev == 'C' && info.pci)) {
+      bus_type = "PCI";
+      subversion = ESA;
+      }
+   else if (port_base >= MIN_EISA_ADDR || (protocol_rev == 'C' && info.eisa)) {
+      bus_type = "EISA";
+      subversion = ESA;
+      }
+   else if (protocol_rev == 'C' && !info.eisa && !info.pci) {
+      bus_type = "ISA";
+      subversion = ISA;
+      }
+   else if (port_base > MAX_ISA_ADDR) {
+      bus_type = "PCI";
       subversion = ESA;
-      dma_channel = NO_DMA;
       }
    else {
+      bus_type = "ISA";
+      subversion = ISA;
+      }
 
-      if (!info.haaval || info.ata || !info.drqvld) {
-	 printk("%s: unusable ISA board found (%d%d%d), detaching.\n",
-		name, info.haaval, info.ata, info.drqvld);
-	 return FALSE;
-	 }
+   if (!info.haaval || info.ata) {
+      printk("%s: address 0x%03x, unusable %s board (%d%d), detaching.\n",
+             name, port_base, bus_type, info.haaval, info.ata);
+      return FALSE;
+      }
+
+   if (info.drqvld) {
+
+      if (subversion ==  ESA)
+	 printk("%s: warning, weird %s board using DMA.\n", name, bus_type);
 
       subversion = ISA;
       dma_channel = dma_channel_table[3 - info.drqx];
       }
+   else {
+
+      if (subversion ==  ISA)
+	 printk("%s: warning, weird %s board not using DMA.\n", name, bus_type);
+
+      subversion = ESA;
+      dma_channel = NO_DMA;
+      }
 
    if (!info.dmasup)
       printk("%s: warning, DMA protocol support not asserted.\n", name);
 
+   irq = info.irq;
+
    if (subversion == ESA && !info.irq_tr)
       printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
 	     name, irq);
@@ -590,6 +706,7 @@
    sh[j]->this_id = (ushort) info.host_addr[3];
    sh[j]->can_queue = (ushort) ntohs(info.queue_size);
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+   sh[j]->select_queue_depths = select_queue_depths;
 
    /* Register the I/O space that we use */
    request_region(sh[j]->io_port, sh[j]->n_io_port, driver_name);
@@ -645,10 +762,8 @@
          sh[j]->max_lun = info.max_lun + 1;
       }
 
-   if (subversion == ESA && protocol_rev == 'C' && info.pci) bus_type = "PCI";
-   else if (sh[j]->io_port > EISA_RANGE) bus_type = "PCI";
-   else if (subversion == ESA) bus_type = "EISA";
-   else bus_type = "ISA";
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   else                       sprintf(dma_name, "DMA %u", dma_channel);
 
    for (i = 0; i < sh[j]->can_queue; i++)
       if (! ((&HD(j)->cp[i])->sglist = kmalloc(
@@ -659,10 +774,15 @@
          return FALSE;
          }
       
-   printk("%s: rev. 2.0%c, %s, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
-	  "Mbox %d, CmdLun %d.\n", BN(j), HD(j)->protocol_rev, bus_type,
-	   sh[j]->io_port, sh[j]->irq, sh[j]->dma_channel,
-	   sh[j]->sg_tablesize, sh[j]->can_queue, sh[j]->cmd_per_lun);
+   if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN) 
+       max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
+
+   if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
+
+   printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, TC %d, LC %d, "\
+          "MQ %d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
+          sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
+          tagged_commands, linked_commands, max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -766,7 +886,7 @@
       }
 
    if (j > 0) 
-      printk("EATA/DMA 2.0x: Copyright (C) 1994, 1995, 1996 Dario Ballabio.\n");
+      printk("EATA/DMA 2.0x: Copyright (C) 1994-1997 Dario Ballabio.\n");
 
    restore_flags(flags);
    return j;
@@ -874,6 +994,17 @@
    cpp->SCpnt = SCpnt;
    cpp->sense_addr = V2DEV(SCpnt->sense_buffer); 
    cpp->sense_len = sizeof SCpnt->sense_buffer;
+   
+   if (SCpnt->device->tagged_queue) {
+
+      if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] || 
+                            HD(j)->target_to[SCpnt->target][SCpnt->channel])
+         cpp->mess[0] = ORDERED_QUEUE_TAG;
+      else
+         cpp->mess[0] = SIMPLE_QUEUE_TAG;
+
+      cpp->mess[1] = SCpnt->device->current_tag++;
+      }
 
    if (SCpnt->use_sg) {
       cpp->sg = TRUE;
@@ -886,6 +1017,28 @@
 
    memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
 
+   if (linked_commands && SCpnt->device->tagged_supported
+            && !HD(j)->target_to[SCpnt->target][SCpnt->channel]
+	    && !HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+   
+      for (k = 0; k < sh[j]->can_queue; k++) {
+ 
+         if (HD(j)->cp_stat[k] != IN_USE) continue;
+
+         if ((&HD(j)->cp[k])->SCpnt->device != SCpnt->device) continue;
+
+         cpp->link_id = k;
+         HD(j)->cp_stat[i] = READY;
+
+#if defined (DEBUG_LINKED_COMMANDS)
+         printk("%s: qcomm, target %d.%d:%d, pid %ld, Mbox %d ready.\n", 
+                BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun,
+                SCpnt->pid, i);
+#endif
+         restore_flags(flags);
+         return 0;
+         }
+
    /* Send control packet to the board */
    if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
       SCpnt->result = DID_ERROR << 16; 
@@ -962,6 +1115,18 @@
       restore_flags(flags);
       return SCSI_ABORT_NOT_RUNNING;
       }
+
+   if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+      SCarg->result = DID_ABORT << 16;
+      SCarg->host_scribble = NULL;
+      HD(j)->cp_stat[i] = FREE;
+      printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+	     BN(j), i, SCarg->pid);
+      SCarg->scsi_done(SCarg);
+      restore_flags(flags);
+      return SCSI_ABORT_SUCCESS;
+      }
+
    restore_flags(flags);
    panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
 }
@@ -1012,14 +1177,21 @@
 	 continue;
 	 }
 
-      SCpnt = HD(j)->cp[i].SCpnt;
-      HD(j)->cp_stat[i] = IN_RESET;
-      printk("%s: reset, mbox %d in reset, pid %ld.\n",
-	     BN(j), i, SCpnt->pid);
-
-      if (SCpnt == NULL)
+      if (!(SCpnt = HD(j)->cp[i].SCpnt))
 	 panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
 
+      if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+         HD(j)->cp_stat[i] = ABORTING;
+         printk("%s: reset, mbox %d aborting, pid %ld.\n",
+                BN(j), i, SCpnt->pid);
+         }
+
+      else {
+         HD(j)->cp_stat[i] = IN_RESET;
+         printk("%s: reset, mbox %d in reset, pid %ld.\n",
+                BN(j), i, SCpnt->pid);
+         }
+
       if (SCpnt->host_scribble == NULL)
 	 panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
 
@@ -1053,18 +1225,35 @@
 
    for (i = 0; i < sh[j]->can_queue; i++) {
 
-      /* Skip mailboxes already set free by interrupt */
-      if (HD(j)->cp_stat[i] != IN_RESET) continue;
+      if (HD(j)->cp_stat[i] == IN_RESET) {
+         SCpnt = HD(j)->cp[i].SCpnt;
+         SCpnt->result = DID_RESET << 16;
+         SCpnt->host_scribble = NULL;
 
-      SCpnt = HD(j)->cp[i].SCpnt;
-      SCpnt->result = DID_RESET << 16;
-      SCpnt->host_scribble = NULL;
+         /* This mailbox is still waiting for its interrupt */
+         HD(j)->cp_stat[i] = LOCKED;
+
+         printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+	        BN(j), i, SCpnt->pid);
+         }
+
+      else if (HD(j)->cp_stat[i] == ABORTING) {
+         SCpnt = HD(j)->cp[i].SCpnt;
+         SCpnt->result = DID_RESET << 16;
+         SCpnt->host_scribble = NULL;
 
-      /* This mailbox is still waiting for its interrupt */
-      HD(j)->cp_stat[i] = LOCKED;
+         /* This mailbox was never queued to the adapter */
+         HD(j)->cp_stat[i] = FREE;
+
+         printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+	        BN(j), i, SCpnt->pid);
+         }
+
+      else
+
+         /* Any other mailbox has already been set free by interrupt */
+         continue;
 
-      printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
-	     BN(j), i, SCpnt->pid);
       restore_flags(flags);
       SCpnt->scsi_done(SCpnt);
       cli();
@@ -1084,6 +1273,42 @@
       }
 }
 
+static inline void process_ready_list(unsigned int i, unsigned int j) {
+   Scsi_Cmnd *SCpnt;
+   unsigned int k, n_ready = 0;
+   struct mscp *cpp;
+
+   for (k = 0; k < sh[j]->can_queue; k++) {
+
+      if (HD(j)->cp_stat[k] != READY) continue;
+
+      cpp = &HD(j)->cp[k];
+
+      if (cpp->link_id != i) continue;
+
+      SCpnt = cpp->SCpnt;
+      n_ready++;
+
+      if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
+         printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d, link_id %d, "\
+                "n_ready %d, adapter busy, will abort.\n", BN(j),
+                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+                k, i, n_ready);
+         HD(j)->cp_stat[k] = ABORTING;
+         continue;
+         }
+
+      HD(j)->cp_stat[k] = IN_USE;
+
+#if defined (DEBUG_LINKED_COMMANDS)
+      printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d in use, link_id %d,"
+             " n_ready %d.\n", BN(j), SCpnt->channel, SCpnt->target, 
+             SCpnt->lun, SCpnt->pid, k, i, n_ready);
+#endif
+      }
+
+}
+
 static void eata2x_interrupt_handler(int irq, void *dev_id,
                                      struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
@@ -1131,6 +1356,8 @@
    
 	    spp->eoc = FALSE;
    
+            if (linked_commands) process_ready_list(i, j);
+
 	    if (HD(j)->cp_stat[i] == IGNORE) {
 	       HD(j)->cp_stat[i] = FREE;
 	       continue;
@@ -1262,8 +1489,8 @@
 #else
 	    if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
 		(spp->adapter_status != ASOK && 
-		 spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
-		do_trace)
+		spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+		do_trace || msg_byte(spp->target_status))
 #endif
 	       printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
 		      " target %d.%d:%d, pid %ld, count %d.\n",

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