patch-1.3.4 linux/drivers/scsi/fdomain.c

Next file: linux/drivers/scsi/hosts.c
Previous file: linux/drivers/scsi/aic7xxx.seq
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.3/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
@@ -1,10 +1,10 @@
 /* fdomain.c -- Future Domain TMC-16x0 SCSI driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Mon Jun  5 09:21:54 1995 by faith@cs.unc.edu
+ * Revised: Fri Jun 23 17:07:09 1995 by r.faith@ieee.org
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
  *
- * $Id: fdomain.c,v 5.28 1995/06/05 13:21:57 faith Exp $
+ * $Id: fdomain.c,v 5.31 1995/06/23 21:07:16 faith Exp $
 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -145,6 +145,11 @@
  (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
  work on the Quantum RAM layout.
 
+ Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
+ providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
+ card (a PCI bus card with the 36C70 chip).  Please send James PCI-related
+ bug reports.
+ 
  All of the alpha testers deserve much thanks.
 
 
@@ -193,8 +198,10 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
 
-#define VERSION          "$Revision: 5.28 $"
+#define VERSION          "$Revision: 5.31 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
@@ -411,17 +418,22 @@
    printk( " at 0x%x using scsi id %d\n",
 	   (unsigned)bios_base, shpnt->this_id );
 
+				/* If this driver works for later FD PCI
+                                   boards, we will have to modify banner
+                                   for additional PCI cards, but for now if
+                                   it's PCI it's a TMC-3260 - JTM */
    printk( "scsi%d <fdomain>: %s chip at 0x%x irq ",
 	   shpnt->host_no,
 	   chip == tmc1800 ? "TMC-1800"
 	   : (chip == tmc18c50 ? "TMC-18C50"
-	      : (chip == tmc18c30 ? "TMC-18C30" : "Unknown")),
+	      : (chip == tmc18c30 ?
+		 (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30")
+		 : "Unknown")),
 	   port_base );
 
    if (interrupt_level) printk( "%d", interrupt_level );
    else                 printk( "<none>" );
 
-   if (PCI_bus)         printk( " (PCI bus)" );
    printk( "\n" );
 }
 
@@ -444,8 +456,6 @@
 
 static int fdomain_is_valid_port( int port )
 {
-   int options;
-
 #if DEBUG_DETECT 
    printk( " (%x%x),",
 	   inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
@@ -473,9 +483,9 @@
 				   we'll use the other method.) */
 
       outb( 0x80, port + IO_Control );
-      if (inb( port + Configuration2 ) & 0x80 == 0x80) {
+      if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
 	 outb( 0x00, port + IO_Control );
-	 if (inb( port + Configuration2 ) & 0x80 == 0x00) {
+	 if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
 	    chip = tmc18c30;
 	    FIFO_Size = 0x800;	/* 2k FIFO */
 	 }
@@ -494,31 +504,6 @@
 				/* If that failed, we are an 18c50. */
    }
 
-   /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board.
-      Now, check to be sure the bios_base matches these ports.  If someone
-      was unlucky enough to have purchased more than one Future Domain
-      board, then they will have to modify this code, as we only detect one
-      board here.  [The one with the lowest bios_base.]  */
-
-   options = inb( port + Configuration1 );
-
-#if DEBUG_DETECT
-   printk( " Options = %x\n", options );
-#endif
-
-				/* Check for board with lowest bios_base --
-				   this isn't valid for the 18c30 or for
-				   boards on the PCI bus, so just assume we
-				   have the right board. */
-
-   if (chip != tmc18c30
-       && !PCI_bus
-       && addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
-
-				/* Get the IRQ from the options. */
-
-   interrupt_level = ints[ (options & 0x0e) >> 1 ];
-
    return 1;
 }
 
@@ -536,48 +521,45 @@
    return 0;
 }
 
-int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
+/* fdomain_get_irq assumes that we have a valid MCA ID for a
+   TMC-1660/TMC-1680 Future Domain board.  Now, check to be sure the
+   bios_base matches these ports.  If someone was unlucky enough to have
+   purchased more than one Future Domain board, then they will have to
+   modify this code, as we only detect one board here.  [The one with the
+   lowest bios_base.]
+
+   Note that this routine is only used for systems without a PCI BIOS32
+   (e.g., ISA bus).  For PCI bus systems, this routine will likely fail
+   unless one of the IRQs listed in the ints array is used by the board.
+   Sometimes it is possible to use the computer's BIOS setup screen to
+   configure a PCI system so that one of these IRQs will be used by the
+   Future Domain card. */
+
+static int fdomain_get_irq( int base )
 {
-   int              i, j;
-   int              flag = 0;
-   int              retcode;
-   struct Scsi_Host *shpnt;
-#if DO_DETECT
-   const int        buflen = 255;
-   Scsi_Cmnd        SCinit;
-   unsigned char    do_inquiry[] =       { INQUIRY, 0, 0, 0, buflen, 0 };
-   unsigned char    do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
-   unsigned char    do_read_capacity[] = { READ_CAPACITY,
-					   0, 0, 0, 0, 0, 0, 0, 0, 0 };
-   unsigned char    buf[buflen];
-#endif
+   int options = inb( base + Configuration1 );
 
 #if DEBUG_DETECT
-   printk( "fdomain_16x0_detect()," );
+   printk( " Options = %x\n", options );
 #endif
+   
+				/* Check for board with lowest bios_base --
+				   this isn't valid for the 18c30 or for
+				   boards on the PCI bus, so just assume we
+				   have the right board. */
 
-   for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
-#if DEBUG_DETECT
-      printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
-#endif
-      for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
-	 if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
-		      signatures[j].signature, signatures[j].sig_length )) {
-	    bios_major = signatures[j].major_bios_version;
-	    bios_minor = signatures[j].minor_bios_version;
-	    PCI_bus    = (signatures[j].flag == 1);
-	    Quantum    = (signatures[j].flag > 1) ? signatures[j].flag : 0;
-	    bios_base  = addresses[i];
-	 }
-      }
-   }
+   if (chip != tmc18c30
+       && !PCI_bus
+       && addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
 
-   if (!bios_base) {
-#if DEBUG_DETECT
-      printk( " FAILED: NO BIOS\n" );
-#endif
-      return 0;
-   }
+   return ints[ (options & 0x0e) >> 1 ];
+}
+
+static int fdomain_isa_detect( int *irq, int *iobase )
+{
+   int i;
+   int base;
+   int flag = 0;
 
    if (bios_major == 2) {
       /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
@@ -591,95 +573,299 @@
       switch (Quantum) {
       case 2:			/* ISA_200S */
       case 3:			/* ISA_250MG */
-	 port_base = *((char *)bios_base + 0x1fa2)
+	 base = *((char *)bios_base + 0x1fa2)
 	       + (*((char *)bios_base + 0x1fa3) << 8);
 	 break;
       case 4:			/* ISA_200S (another one) */
-	 port_base = *((char *)bios_base + 0x1fa3)
+	 base = *((char *)bios_base + 0x1fa3)
 	       + (*((char *)bios_base + 0x1fa4) << 8);
 	 break;
       default:
-	 port_base = *((char *)bios_base + 0x1fcc)
+	 base = *((char *)bios_base + 0x1fcc)
 	       + (*((char *)bios_base + 0x1fcd) << 8);
 	 break;
       }
    
 #if DEBUG_DETECT
-      printk( " %x,", port_base );
+      printk( " %x,", base );
 #endif
 
       for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
-	 if (port_base == ports[i])
+	 if (base == ports[i])
 	       ++flag;
       }
 
-      if (flag)
-	    flag = fdomain_is_valid_port( port_base );
-   }
-
-   if (!flag) {			/* Cannot get port base from BIOS RAM */
+      if (flag && fdomain_is_valid_port( base )) {
+	 *irq    = fdomain_get_irq( base );
+	 *iobase = base;
+	 return 1;
+      }
       
       /* This is a bad sign.  It usually means that someone patched the
 	 BIOS signature list (the signatures variable) to contain a BIOS
-	 signature for a board *OTHER THAN* the TMC-1660/TMC-1680.  It
-	 also means that we don't have a Version 2.0 BIOS :-)
-       */
+	 signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
       
 #if DEBUG_DETECT
-      if (bios_major != 2) printk( " RAM FAILED, " );
+      printk( " RAM FAILED, " );
+#endif
+   }
+
+   /* Anyway, the alternative to finding the address in the RAM is to just
+      search through every possible port address for one that is attached
+      to the Future Domain card.  Don't panic, though, about reading all
+      these random port addresses -- there are rumors that the Future
+      Domain BIOS does something very similar.
+
+      Do not, however, check ports which the kernel knows are being used by
+      another driver. */
+
+   for (i = 0; i < PORT_COUNT; i++) {
+      base = ports[i];
+      if (check_region( base, 0x10 )) {
+#if DEBUG_DETECT
+	 printk( " (%x inuse),", base );
+#endif
+	 continue;
+      }
+#if DEBUG_DETECT
+      printk( " %x,", base );
 #endif
+      if ((flag = fdomain_is_valid_port( base ))) break;
+   }
+
+   if (!flag) return 0;		/* iobase not found */
+
+   *irq    = fdomain_get_irq( base );
+   *iobase = base;
+
+   return 1;			/* success */
+}
 
-      /* Anyway, the alternative to finding the address in the RAM is to
-	 just search through every possible port address for one that is
-	 attached to the Future Domain card.  Don't panic, though, about
-	 reading all these random port addresses -- there are rumors that
-	 the Future Domain BIOS does something very similar.
+static int fdomain_pci_nobios_detect( int *irq, int *iobase )
+{
+   int i;
+   int flag = 0;
 
-	 Do not, however, check ports which the kernel knows are being used
-	 by another driver. */
+   /* The proper way of doing this is to use ask the PCI bus for the device
+      IRQ and interrupt level.  But we can't do that if PCI BIOS32 support
+      isn't compiled into the kernel, or if a PCI BIOS32 isn't present.
+
+      Instead, we scan down a bunch of addresses (Future Domain tech
+      support says we will probably find the address before we get to
+      0xf800).  This works fine on some systems -- other systems may have
+      to scan more addresses.  If you have to modify this section for your
+      installation, please send mail to faith@cs.unc.edu. */
 
-      if (!PCI_bus) {
-	 for (i = 0; !flag && i < PORT_COUNT; i++) {
-	    port_base = ports[i];
-	    if (check_region( port_base, 0x10 )) {
+   for (i = 0xfff8; i > 0xe000; i -= 8) {
+      if (check_region( i, 0x10 )) {
 #if DEBUG_DETECT
-	       printk( " (%x inuse),", port_base );
+	 printk( " (%x inuse)," , i );
 #endif
-	       continue;
-	    }
+	 continue;
+      }
+      if ((flag = fdomain_is_valid_port( i ))) break;
+   }
+
+   if (!flag) return 0;		/* iobase not found */
+
+   *irq    = fdomain_get_irq( i );
+   *iobase = i;
+
+   return 1;			/* success */
+}
+
+/* PCI detection function: int fdomain_36c70_detect(int* irq, int* iobase)
+   This function gets the Interrupt Level and I/O base address from the PCI
+   configuration registers.  The I/O base address is masked with 0xfff8
+   since on my card the address read from the PCI config registers is off
+   by one from the actual I/O base address necessary for accessing the
+   status and control registers on the card (PCI config register gives
+   0xf801, actual address is 0xf800).  This is likely a bug in the FD
+   config code that writes to the PCI registers, however using a mask
+   should be safe since I think the scan done by the card to determine the
+   I/O base is done in increments of 8 (i.e., 0xf800, 0xf808, ...), at
+   least the old scan code we used to use to get the I/O base did...  Also,
+   the device ID from the PCI config registers is 0x0 and should be 0x60e9
+   as it is in the status registers (offset 5 from I/O base).  If this is
+   changed in future hardware/BIOS changes it will need to be fixed in this
+   detection function.  Comments, bug reports, etc... on this function
+   should be sent to mckinley@msupa.pa.msu.edu - James T. McKinley.  */
+
+#ifdef PCI_CONFIG
+static int fdomain_36c70_detect( int *irq, int *iobase )
+{
+   int              error;
+   unsigned char    pci_bus, pci_dev_fn;    /* PCI bus & device function */
+   unsigned char    pci_irq;                /* PCI interrupt line */
+   unsigned long    pci_base;               /* PCI I/O base address */
+   unsigned short   pci_vendor, pci_device; /* PCI vendor & device IDs */
+
+   /* If the PCI BIOS doesn't exist, use the old-style detection routines.
+      Otherwise, get the I/O base address and interrupt from the PCI config
+      registers. */
+   
+   if (!pcibios_present()) return fdomain_pci_detect( irq, iobase );
+
 #if DEBUG_DETECT
-	    printk( " %x,", port_base );
-#endif
-	    flag = fdomain_is_valid_port( port_base );
+   /* Tell how to print a list of the known PCI devices from bios32 and
+      list vendor and device IDs being used if in debug mode.  */
+      
+   printk( "\nINFO: cat /proc/pci to see list of PCI devices from bios32\n" );
+   printk( "\nTMC-3260 detect:"
+	   " Using PCI Vendor ID: 0x%x, PCI Device ID: 0x%x\n",
+	   PCI_VENDOR_ID_FD, 
+	   PCI_DEVICE_ID_FD_36C70 );
+#endif 
+
+   /* We will have to change this if more than 1 PCI bus is present and the
+      FD scsi host is not on the first bus (i.e., a PCI to PCI bridge,
+      which is not supported by bios32 right now anyway).  This should
+      probably be done by a call to pcibios_find_device but I can't get it
+      to work...  Also the device ID reported from the PCI config registers
+      does not match the device ID quoted in the tech manual or available
+      from offset 5 from the I/O base address.  It should be 0x60E9, but it
+      is 0x0 if read from the PCI config registers.  I guess the FD folks
+      neglected to write it to the PCI registers...  This loop is necessary
+      to get the device function (at least until someone can get
+      pcibios_find_device to work, I cannot but 53c7,8xx.c uses it...). */
+    
+   pci_bus = 0;
+
+   for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++) {
+      pcibios_read_config_word( pci_bus,
+				pci_dev_fn,
+				PCI_VENDOR_ID,
+				&pci_vendor );
+
+      if (pci_vendor == PCI_VENDOR_ID_FD) {
+	 pcibios_read_config_word( pci_bus,
+				   pci_dev_fn,
+				   PCI_DEVICE_ID,
+				   &pci_device );
+
+	 if (pci_device == PCI_DEVICE_ID_FD_36C70) {
+	    /* Break out once we have the correct device.  If other FD
+	       PCI devices are added to this driver we will need to add
+	       an or of the other PCI_DEVICE_ID_FD_XXXXX's here. */
+	    break;
+	 } else {
+	    /* If we can't find an FD scsi card we give up. */
+	    return 0;
 	 }
-      } else {
+      }
+   }
+       
+#if DEBUG_DETECT
+   printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n",
+	   pci_bus,
+	   (pci_dev_fn & 0xf8) >> 3, 
+	   pci_dev_fn & 7 );
+#endif
+
+   /* We now have the appropriate device function for the FD board so we
+      just read the PCI config info from the registers.  */
+
+   if ((error = pcibios_read_config_dword( pci_bus,
+					   pci_dev_fn, 
+					   PCI_BASE_ADDRESS_0,
+					   &pci_base ))
+       || (error = pcibios_read_config_byte( pci_bus,
+					     pci_dev_fn, 
+					     PCI_INTERRUPT_LINE,
+					     &pci_irq ))) {
+      printk ( "PCI ERROR: Future Domain 36C70 not initializing"
+	       " due to error reading configuration space\n" );
+      return 0;
+   } else {
+#if DEBUG_DETECT
+      printk( "TMC-3260 PCI: IRQ = %u, I/O base = 0x%lx\n", 
+	      pci_irq, pci_base );
+#endif
+
+      /* Now we have the I/O base address and interrupt from the PCI
+	 configuration registers.  Unfortunately it seems that the I/O base
+	 address is off by one on my card so I mask it with 0xfff8.  This
+	 must be some kind of goof in the FD code that does the autoconfig
+	 and writes to the PCI registers (or maybe I just don't understand
+	 something).  If they fix it in later versions of the card or BIOS
+	 we may have to adjust the address based on the signature or
+	 something...  */
+
+      *irq    = pci_irq;
+      *iobase = (pci_base & 0xfff8);
+
+#if DEBUG_DETECT
+      printk( "TMC-3260 fix: Masking I/O base address with 0xff00.\n" ); 
+      printk( "TMC-3260: IRQ = %d, I/O base = 0x%x\n", *irq, *iobase );
+#endif
 
-	 /* The proper way of doing this is to use ask the PCI bus for the
-            device IRQ and interrupt level.
+      if (!fdomain_is_valid_port( *iobase )) return 0;
+      return 1;
+   }
+   return 0;
+}
+#endif
 
-	    Until the Linux kernel supports this sort of PCI bus query, we
-	    scan down a bunch of addresses (Future Domain tech support says
-	    we will probably find the address before we get to 0xf800).
-	    This works fine on some systems -- other systems may have to
-	    scan more addresses.  If you have to modify this section for
-	    your installation, please send mail to faith@cs.unc.edu. */
-
-	 for (i = 0xfff8; !flag && i > 0xe000; i -= 8) {
-	    port_base = i;
-	    if (check_region( port_base, 0x10 )) {
-#if DEBUG_DETECT
-	       printk( " (%x inuse)," , port_base );
-#endif
-	       continue;
-	    }
-	    flag = fdomain_is_valid_port( port_base );
+int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
+{
+   int              i, j;
+   int              flag = 0;
+   int              retcode;
+   struct Scsi_Host *shpnt;
+#if DO_DETECT
+   const int        buflen = 255;
+   Scsi_Cmnd        SCinit;
+   unsigned char    do_inquiry[] =       { INQUIRY, 0, 0, 0, buflen, 0 };
+   unsigned char    do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
+   unsigned char    do_read_capacity[] = { READ_CAPACITY,
+					   0, 0, 0, 0, 0, 0, 0, 0, 0 };
+   unsigned char    buf[buflen];
+#endif
+
+#if DEBUG_DETECT
+   printk( "fdomain_16x0_detect()," );
+#endif
+
+   for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
+#if DEBUG_DETECT
+      printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
+#endif
+      for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
+	 if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
+		      signatures[j].signature, signatures[j].sig_length )) {
+	    bios_major = signatures[j].major_bios_version;
+	    bios_minor = signatures[j].minor_bios_version;
+	    PCI_bus    = (signatures[j].flag == 1);
+	    Quantum    = (signatures[j].flag > 1) ? signatures[j].flag : 0;
+	    bios_base  = addresses[i];
 	 }
       }
    }
 
+   if (!bios_base) {
+#if DEBUG_DETECT
+      printk( " FAILED: NO BIOS\n" );
+#endif
+      return 0;
+   }
+
+   if (!PCI_bus) {
+      flag = fdomain_isa_detect( &interrupt_level, &port_base );
+   } else {
+#ifdef PCI_CONFIG
+      flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
+#else
+      flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );
+#endif
+   }
+	 
    if (!flag) {
 #if DEBUG_DETECT
       printk( " FAILED: NO PORT\n" );
+#endif
+#ifdef PCI_CONFIG
+      printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" );
+      printk( "Send mail to mckinley@msupa.pa.msu.edu.\n" );
 #endif
       return 0;		/* Cannot find valid set of ports */
    }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this