Fix for drivers/scsi/fdomain.c

Brian Beaudoin (baddog@darkknight.net)
Fri, 17 Apr 1998 15:51:06 -0400


This is a multi-part message in MIME format.
--------------DDB20666A3F5F0CAA0D649B5
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Guys,

Hate to say this, but the Future Domain autodetection was broken in
2.1.96 (don't ask me why, I don't know a @#$! thing about C
programming).

Apparently, the PCI autodetect code in drivers/scsi/fdomain.c was
re-written and doesn't work with all Future Domain based chipsets. The
original code works fine (as implemented in 2.1.92) and I've restored
it.

Someone please reverse the changes until we can get working autodetect
code in that driver. I've included a patch that fixes the problem in my
case (Adaptec 2920 PCI based on Future Domain TMC-3260).

Brian Beaudoin
baddog@darkknight.net

With the "new" PCI detection routines that snuck their way into the
kernel recently without being completely tested:

TMC-3260 36C70 PCI scsi chip detection failed.
Send mail to mckinley@msupa.pa.msu.edu.
scsi : 0 hosts.
scsi : detected total.

With the old detection routines by James T. McKinley completely restored
to it's prior glory:

scsi0 <fdomain>: BIOS version 3.?. at 0xc8000 using scsi id 7
scsi0 <fdomain>: TMC-36C70 (PCI bus) chip at 0x6100 irq 11
scsi0 : Future Domain TMC-16x0 SCSI driver, version 5.45
scsi : 1 host.
scsi : detected 2 SCSI disks total.

-Brian
--------------DDB20666A3F5F0CAA0D649B5
Content-Type: text/plain; charset=us-ascii; name="fdomain.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="fdomain.diff"

--- fdomain.c.old Sat Apr 11 14:13:25 1998
+++ fdomain.c Fri Apr 17 15:37:15 1998
@@ -21,6 +21,8 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.

* PCI detection rewritten by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * Old PCI detection restored by Brian Beaudoin <baddog@darkknight.net>
+ Because the new ones weren't working. Yikes!! (17 Apr 1998)

**************************************************************************

@@ -403,6 +405,9 @@
extern void fdomain_16x0_intr( int irq, void *dev_id,
struct pt_regs * regs );

+extern void do_fdomain_16x0_intr( int irq, void *dev_id,
+ struct pt_regs * regs );
+
static unsigned long addresses[] = {
0xc8000,
0xca000,
@@ -751,63 +756,172 @@
return 1; /* success */
}

+static int fdomain_pci_nobios_detect( int *irq, int *iobase )
+{
+ int i;
+ int flag = 0;
+
+ /* 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. */
+
+ for (i = 0xfff8; i > 0xe000; i -= 8) {
+ if (check_region( i, 0x10 )) {
+#if DEBUG_DETECT
+ printk( " (%x inuse)," , i );
+#endif
+ 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_pci_bios_detect(int* irq, int*
iobase) This function gets the Interrupt Level and I/O base address from
the PCI configuration registers. */

+/* PCI detection function: int fdomain_pci_bios_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 CONFIG_PCI
static int fdomain_pci_bios_detect( int *irq, int *iobase )
{
- unsigned int pci_irq; /* PCI interrupt line */
- unsigned long pci_base; /* PCI I/O base address */
- struct pci_dev *pdev = NULL;
-
- if (!pci_present()) return 0;
+ int error;
+ unsigned char pci_bus, pci_dev_fn; /* PCI bus & device function */
+ unsigned char pci_irq; /* PCI interrupt line */
+ unsigned int 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_nobios_detect( irq, iobase );

#if DEBUG_DETECT
/* 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: use lspci -v to see list of PCI devices\n" );
+ 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

- if ((pdev = pci_find_device(PCI_VENDOR_ID, PCI_DEVICE_ID, pdev)) == NULL)
- return 0;
+ /* 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;
+ }
+ }
+ }

#if DEBUG_DETECT
printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
+ 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. */

- pci_base = pdev->base_address[0];
- pci_irq = pdev->irq;
+ 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. */
+ /* 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 & PCI_BASE_ADDRESS_IO_MASK);
+ *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 );
+ 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

- if (!fdomain_is_valid_port( *iobase )) return 0;
- return 1;
+ if (!fdomain_is_valid_port( *iobase )) return 0;
+ return 1;
+ }
+ return 0;
}
#endif

@@ -875,8 +989,7 @@
#ifdef CONFIG_PCI
flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
#else
- printk(KERN_ERR "No PCI support in this kernel, giving up.\n");
- flag = 0;
+ flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );
#endif
}

--------------DDB20666A3F5F0CAA0D649B5--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu