summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/BusLogic.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/BusLogic.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi/BusLogic.c')
-rw-r--r--drivers/scsi/BusLogic.c3574
1 files changed, 3574 insertions, 0 deletions
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
new file mode 100644
index 000000000000..41b5197ce4e6
--- /dev/null
+++ b/drivers/scsi/BusLogic.c
@@ -0,0 +1,3574 @@
+
+/*
+
+ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
+
+ Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for complete details.
+
+ The author respectfully requests that any modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+ advice has been invaluable, to David Gentzel, for writing the original Linux
+ BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+ Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+ Manager available as freely redistributable source code.
+
+*/
+
+#define BusLogic_DriverVersion "2.1.16"
+#define BusLogic_DriverDate "18 July 2002"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <scsi/scsicam.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include "BusLogic.h"
+#include "FlashPoint.c"
+
+#ifndef FAILURE
+#define FAILURE (-1)
+#endif
+
+static struct scsi_host_template Bus_Logic_template;
+
+/*
+ BusLogic_DriverOptionsCount is a count of the number of BusLogic Driver
+ Options specifications provided via the Linux Kernel Command Line or via
+ the Loadable Kernel Module Installation Facility.
+*/
+
+static int BusLogic_DriverOptionsCount;
+
+
+/*
+ BusLogic_DriverOptions is an array of Driver Options structures representing
+ BusLogic Driver Options specifications provided via the Linux Kernel Command
+ Line or via the Loadable Kernel Module Installation Facility.
+*/
+
+static struct BusLogic_DriverOptions BusLogic_DriverOptions[BusLogic_MaxHostAdapters];
+
+
+/*
+ BusLogic can be assigned a string by insmod.
+*/
+
+MODULE_LICENSE("GPL");
+#ifdef MODULE
+static char *BusLogic;
+module_param(BusLogic, charp, 0);
+#endif
+
+
+/*
+ BusLogic_ProbeOptions is a set of Probe Options to be applied across
+ all BusLogic Host Adapters.
+*/
+
+static struct BusLogic_ProbeOptions BusLogic_ProbeOptions;
+
+
+/*
+ BusLogic_GlobalOptions is a set of Global Options to be applied across
+ all BusLogic Host Adapters.
+*/
+
+static struct BusLogic_GlobalOptions BusLogic_GlobalOptions;
+
+static LIST_HEAD(BusLogic_host_list);
+
+/*
+ BusLogic_ProbeInfoCount is the number of entries in BusLogic_ProbeInfoList.
+*/
+
+static int BusLogic_ProbeInfoCount;
+
+
+/*
+ BusLogic_ProbeInfoList is the list of I/O Addresses and Bus Probe Information
+ to be checked for potential BusLogic Host Adapters. It is initialized by
+ interrogating the PCI Configuration Space on PCI machines as well as from the
+ list of standard BusLogic I/O Addresses.
+*/
+
+static struct BusLogic_ProbeInfo *BusLogic_ProbeInfoList;
+
+
+/*
+ BusLogic_CommandFailureReason holds a string identifying the reason why a
+ call to BusLogic_Command failed. It is only non-NULL when BusLogic_Command
+ returns a failure code.
+*/
+
+static char *BusLogic_CommandFailureReason;
+
+/*
+ BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+ Name, Copyright Notice, and Electronic Mail Address.
+*/
+
+static void BusLogic_AnnounceDriver(struct BusLogic_HostAdapter *HostAdapter)
+{
+ BusLogic_Announce("***** BusLogic SCSI Driver Version " BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n", HostAdapter);
+ BusLogic_Announce("Copyright 1995-1998 by Leonard N. Zubkoff " "<lnz@dandelion.com>\n", HostAdapter);
+}
+
+
+/*
+ BusLogic_DriverInfo returns the Host Adapter Name to identify this SCSI
+ Driver and Host Adapter.
+*/
+
+static const char *BusLogic_DriverInfo(struct Scsi_Host *Host)
+{
+ struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Host->hostdata;
+ return HostAdapter->FullModelName;
+}
+
+/*
+ BusLogic_InitializeCCBs initializes a group of Command Control Blocks (CCBs)
+ for Host Adapter from the BlockSize bytes located at BlockPointer. The newly
+ created CCBs are added to Host Adapter's free list.
+*/
+
+static void BusLogic_InitializeCCBs(struct BusLogic_HostAdapter *HostAdapter, void *BlockPointer, int BlockSize, dma_addr_t BlockPointerHandle)
+{
+ struct BusLogic_CCB *CCB = (struct BusLogic_CCB *) BlockPointer;
+ unsigned int offset = 0;
+ memset(BlockPointer, 0, BlockSize);
+ CCB->AllocationGroupHead = BlockPointerHandle;
+ CCB->AllocationGroupSize = BlockSize;
+ while ((BlockSize -= sizeof(struct BusLogic_CCB)) >= 0) {
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->HostAdapter = HostAdapter;
+ CCB->DMA_Handle = (u32) BlockPointerHandle + offset;
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) {
+ CCB->CallbackFunction = BusLogic_QueueCompletedCCB;
+ CCB->BaseAddress = HostAdapter->FlashPointInfo.BaseAddress;
+ }
+ CCB->Next = HostAdapter->Free_CCBs;
+ CCB->NextAll = HostAdapter->All_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+ HostAdapter->All_CCBs = CCB;
+ HostAdapter->AllocatedCCBs++;
+ CCB++;
+ offset += sizeof(struct BusLogic_CCB);
+ }
+}
+
+
+/*
+ BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
+*/
+
+static boolean __init BusLogic_CreateInitialCCBs(struct BusLogic_HostAdapter *HostAdapter)
+{
+ int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(struct BusLogic_CCB);
+ void *BlockPointer;
+ dma_addr_t BlockPointerHandle;
+ while (HostAdapter->AllocatedCCBs < HostAdapter->InitialCCBs) {
+ BlockPointer = pci_alloc_consistent(HostAdapter->PCI_Device, BlockSize, &BlockPointerHandle);
+ if (BlockPointer == NULL) {
+ BusLogic_Error("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n", HostAdapter);
+ return false;
+ }
+ BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize, BlockPointerHandle);
+ }
+ return true;
+}
+
+
+/*
+ BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+*/
+
+static void BusLogic_DestroyCCBs(struct BusLogic_HostAdapter *HostAdapter)
+{
+ struct BusLogic_CCB *NextCCB = HostAdapter->All_CCBs, *CCB, *Last_CCB = NULL;
+ HostAdapter->All_CCBs = NULL;
+ HostAdapter->Free_CCBs = NULL;
+ while ((CCB = NextCCB) != NULL) {
+ NextCCB = CCB->NextAll;
+ if (CCB->AllocationGroupHead) {
+ if (Last_CCB)
+ pci_free_consistent(HostAdapter->PCI_Device, Last_CCB->AllocationGroupSize, Last_CCB, Last_CCB->AllocationGroupHead);
+ Last_CCB = CCB;
+ }
+ }
+ if (Last_CCB)
+ pci_free_consistent(HostAdapter->PCI_Device, Last_CCB->AllocationGroupSize, Last_CCB, Last_CCB->AllocationGroupHead);
+}
+
+
+/*
+ BusLogic_CreateAdditionalCCBs allocates Additional CCBs for Host Adapter. If
+ allocation fails and there are no remaining CCBs available, the Driver Queue
+ Depth is decreased to a known safe value to avoid potential deadlocks when
+ multiple host adapters share the same IRQ Channel.
+*/
+
+static void BusLogic_CreateAdditionalCCBs(struct BusLogic_HostAdapter *HostAdapter, int AdditionalCCBs, boolean SuccessMessageP)
+{
+ int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(struct BusLogic_CCB);
+ int PreviouslyAllocated = HostAdapter->AllocatedCCBs;
+ void *BlockPointer;
+ dma_addr_t BlockPointerHandle;
+ if (AdditionalCCBs <= 0)
+ return;
+ while (HostAdapter->AllocatedCCBs - PreviouslyAllocated < AdditionalCCBs) {
+ BlockPointer = pci_alloc_consistent(HostAdapter->PCI_Device, BlockSize, &BlockPointerHandle);
+ if (BlockPointer == NULL)
+ break;
+ BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize, BlockPointerHandle);
+ }
+ if (HostAdapter->AllocatedCCBs > PreviouslyAllocated) {
+ if (SuccessMessageP)
+ BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n", HostAdapter, HostAdapter->AllocatedCCBs - PreviouslyAllocated, HostAdapter->AllocatedCCBs);
+ return;
+ }
+ BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter);
+ if (HostAdapter->DriverQueueDepth > HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount) {
+ HostAdapter->DriverQueueDepth = HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount;
+ HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth;
+ }
+}
+
+/*
+ BusLogic_AllocateCCB allocates a CCB from Host Adapter's free list,
+ allocating more memory from the Kernel if necessary. The Host Adapter's
+ Lock should already have been acquired by the caller.
+*/
+
+static struct BusLogic_CCB *BusLogic_AllocateCCB(struct BusLogic_HostAdapter
+ *HostAdapter)
+{
+ static unsigned long SerialNumber = 0;
+ struct BusLogic_CCB *CCB;
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB != NULL) {
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ if (HostAdapter->Free_CCBs == NULL)
+ BusLogic_CreateAdditionalCCBs(HostAdapter, HostAdapter->IncrementalCCBs, true);
+ return CCB;
+ }
+ BusLogic_CreateAdditionalCCBs(HostAdapter, HostAdapter->IncrementalCCBs, true);
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB == NULL)
+ return NULL;
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ return CCB;
+}
+
+
+/*
+ BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+ free list. The Host Adapter's Lock should already have been acquired by the
+ caller.
+*/
+
+static void BusLogic_DeallocateCCB(struct BusLogic_CCB *CCB)
+{
+ struct BusLogic_HostAdapter *HostAdapter = CCB->HostAdapter;
+ struct scsi_cmnd *cmd = CCB->Command;
+
+ if (cmd->use_sg != 0) {
+ pci_unmap_sg(HostAdapter->PCI_Device,
+ (struct scatterlist *)cmd->request_buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ } else if (cmd->request_bufflen != 0) {
+ pci_unmap_single(HostAdapter->PCI_Device, CCB->DataPointer,
+ CCB->DataLength, cmd->sc_data_direction);
+ }
+ pci_unmap_single(HostAdapter->PCI_Device, CCB->SenseDataPointer,
+ CCB->SenseDataLength, PCI_DMA_FROMDEVICE);
+
+ CCB->Command = NULL;
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->Next = HostAdapter->Free_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+}
+
+
+/*
+ BusLogic_Command sends the command OperationCode to HostAdapter, optionally
+ providing ParameterLength bytes of ParameterData and receiving at most
+ ReplyLength bytes of ReplyData; any excess reply data is received but
+ discarded.
+
+ On success, this function returns the number of reply bytes read from
+ the Host Adapter (including any discarded data); on failure, it returns
+ -1 if the command was invalid, or -2 if a timeout occurred.
+
+ BusLogic_Command is called exclusively during host adapter detection and
+ initialization, so performance and latency are not critical, and exclusive
+ access to the Host Adapter hardware is assumed. Once the host adapter and
+ driver are initialized, the only Host Adapter command that is issued is the
+ single byte Execute Mailbox Command operation code, which does not require
+ waiting for the Host Adapter Ready bit to be set in the Status Register.
+*/
+
+static int BusLogic_Command(struct BusLogic_HostAdapter *HostAdapter, enum BusLogic_OperationCode OperationCode, void *ParameterData, int ParameterLength, void *ReplyData, int ReplyLength)
+{
+ unsigned char *ParameterPointer = (unsigned char *) ParameterData;
+ unsigned char *ReplyPointer = (unsigned char *) ReplyData;
+ union BusLogic_StatusRegister StatusRegister;
+ union BusLogic_InterruptRegister InterruptRegister;
+ unsigned long ProcessorFlags = 0;
+ int ReplyBytes = 0, Result;
+ long TimeoutCounter;
+ /*
+ Clear out the Reply Data if provided.
+ */
+ if (ReplyLength > 0)
+ memset(ReplyData, 0, ReplyLength);
+ /*
+ If the IRQ Channel has not yet been acquired, then interrupts must be
+ disabled while issuing host adapter commands since a Command Complete
+ interrupt could occur if the IRQ Channel was previously enabled by another
+ BusLogic Host Adapter or another driver sharing the same IRQ Channel.
+ */
+ if (!HostAdapter->IRQ_ChannelAcquired) {
+ local_irq_save(ProcessorFlags);
+ local_irq_disable();
+ }
+ /*
+ Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+ Register Busy bit to be reset in the Status Register.
+ */
+ TimeoutCounter = 10000;
+ while (--TimeoutCounter >= 0) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.HostAdapterReady && !StatusRegister.sr.CommandParameterRegisterBusy)
+ break;
+ udelay(100);
+ }
+ if (TimeoutCounter < 0) {
+ BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ Write the OperationCode to the Command/Parameter Register.
+ */
+ HostAdapter->HostAdapterCommandCompleted = false;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+ /*
+ Write any additional Parameter Bytes.
+ */
+ TimeoutCounter = 10000;
+ while (ParameterLength > 0 && --TimeoutCounter >= 0) {
+ /*
+ Wait 100 microseconds to give the Host Adapter enough time to determine
+ whether the last value written to the Command/Parameter Register was
+ valid or not. If the Command Complete bit is set in the Interrupt
+ Register, then the Command Invalid bit in the Status Register will be
+ reset if the Operation Code or Parameter was valid and the command
+ has completed, or set if the Operation Code or Parameter was invalid.
+ If the Data In Register Ready bit is set in the Status Register, then
+ the Operation Code was valid, and data is waiting to be read back
+ from the Host Adapter. Otherwise, wait for the Command/Parameter
+ Register Busy bit in the Status Register to be reset.
+ */
+ udelay(100);
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister.ir.CommandComplete)
+ break;
+ if (HostAdapter->HostAdapterCommandCompleted)
+ break;
+ if (StatusRegister.sr.DataInRegisterReady)
+ break;
+ if (StatusRegister.sr.CommandParameterRegisterBusy)
+ continue;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+ ParameterLength--;
+ }
+ if (TimeoutCounter < 0) {
+ BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ The Modify I/O Address command does not cause a Command Complete Interrupt.
+ */
+ if (OperationCode == BusLogic_ModifyIOAddress) {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.CommandInvalid) {
+ BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+ Result = -1;
+ goto Done;
+ }
+ if (BusLogic_GlobalOptions.TraceConfiguration)
+ BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: " "(Modify I/O Address)\n", HostAdapter, OperationCode, StatusRegister.All);
+ Result = 0;
+ goto Done;
+ }
+ /*
+ Select an appropriate timeout value for awaiting command completion.
+ */
+ switch (OperationCode) {
+ case BusLogic_InquireInstalledDevicesID0to7:
+ case BusLogic_InquireInstalledDevicesID8to15:
+ case BusLogic_InquireTargetDevices:
+ /* Approximately 60 seconds. */
+ TimeoutCounter = 60 * 10000;
+ break;
+ default:
+ /* Approximately 1 second. */
+ TimeoutCounter = 10000;
+ break;
+ }
+ /*
+ Receive any Reply Bytes, waiting for either the Command Complete bit to
+ be set in the Interrupt Register, or for the Interrupt Handler to set the
+ Host Adapter Command Completed bit in the Host Adapter structure.
+ */
+ while (--TimeoutCounter >= 0) {
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister.ir.CommandComplete)
+ break;
+ if (HostAdapter->HostAdapterCommandCompleted)
+ break;
+ if (StatusRegister.sr.DataInRegisterReady) {
+ if (++ReplyBytes <= ReplyLength)
+ *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+ else
+ BusLogic_ReadDataInRegister(HostAdapter);
+ }
+ if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && StatusRegister.sr.HostAdapterReady)
+ break;
+ udelay(100);
+ }
+ if (TimeoutCounter < 0) {
+ BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ Clear any pending Command Complete Interrupt.
+ */
+ BusLogic_InterruptReset(HostAdapter);
+ /*
+ Provide tracing information if requested.
+ */
+ if (BusLogic_GlobalOptions.TraceConfiguration) {
+ int i;
+ BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", HostAdapter, OperationCode, StatusRegister.All, ReplyLength, ReplyBytes);
+ if (ReplyLength > ReplyBytes)
+ ReplyLength = ReplyBytes;
+ for (i = 0; i < ReplyLength; i++)
+ BusLogic_Notice(" %02X", HostAdapter, ((unsigned char *) ReplyData)[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ }
+ /*
+ Process Command Invalid conditions.
+ */
+ if (StatusRegister.sr.CommandInvalid) {
+ /*
+ Some early BusLogic Host Adapters may not recover properly from
+ a Command Invalid condition, so if this appears to be the case,
+ a Soft Reset is issued to the Host Adapter. Potentially invalid
+ commands are never attempted after Mailbox Initialization is
+ performed, so there should be no Host Adapter state lost by a
+ Soft Reset in response to a Command Invalid condition.
+ */
+ udelay(1000);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.sr.CommandInvalid ||
+ StatusRegister.sr.Reserved ||
+ StatusRegister.sr.DataInRegisterReady ||
+ StatusRegister.sr.CommandParameterRegisterBusy || !StatusRegister.sr.HostAdapterReady || !StatusRegister.sr.InitializationRequired || StatusRegister.sr.DiagnosticActive || StatusRegister.sr.DiagnosticFailure) {
+ BusLogic_SoftReset(HostAdapter);
+ udelay(1000);
+ }
+ BusLogic_CommandFailureReason = "Command Invalid";
+ Result = -1;
+ goto Done;
+ }
+ /*
+ Handle Excess Parameters Supplied conditions.
+ */
+ if (ParameterLength > 0) {
+ BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+ Result = -1;
+ goto Done;
+ }
+ /*
+ Indicate the command completed successfully.
+ */
+ BusLogic_CommandFailureReason = NULL;
+ Result = ReplyBytes;
+ /*
+ Restore the interrupt status if necessary and return.
+ */
+ Done:
+ if (!HostAdapter->IRQ_ChannelAcquired)
+ local_irq_restore(ProcessorFlags);
+ return Result;
+}
+
+
+/*
+ BusLogic_AppendProbeAddressISA appends a single ISA I/O Address to the list
+ of I/O Address and Bus Probe Information to be checked for potential BusLogic
+ Host Adapters.
+*/
+
+static void __init BusLogic_AppendProbeAddressISA(unsigned long IO_Address)
+{
+ struct BusLogic_ProbeInfo *ProbeInfo;
+ if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
+ return;
+ ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Device = NULL;
+}
+
+
+/*
+ BusLogic_InitializeProbeInfoListISA initializes the list of I/O Address and
+ Bus Probe Information to be checked for potential BusLogic SCSI Host Adapters
+ only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
+*/
+
+static void __init BusLogic_InitializeProbeInfoListISA(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ /*
+ If BusLogic Driver Options specifications requested that ISA Bus Probes
+ be inhibited, do not proceed further.
+ */
+ if (BusLogic_ProbeOptions.NoProbeISA)
+ return;
+ /*
+ Append the list of standard BusLogic MultiMaster ISA I/O Addresses.
+ */
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe330 : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x330);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe334 : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x334);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe230 : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x230);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe234 : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x234);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe130 : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x130);
+ if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe134 : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x134);
+}
+
+
+#ifdef CONFIG_PCI
+
+
+/*
+ BusLogic_SortProbeInfo sorts a section of BusLogic_ProbeInfoList in order
+ of increasing PCI Bus and Device Number.
+*/
+
+static void __init BusLogic_SortProbeInfo(struct BusLogic_ProbeInfo *ProbeInfoList, int ProbeInfoCount)
+{
+ int LastInterchange = ProbeInfoCount - 1, Bound, j;
+ while (LastInterchange > 0) {
+ Bound = LastInterchange;
+ LastInterchange = 0;
+ for (j = 0; j < Bound; j++) {
+ struct BusLogic_ProbeInfo *ProbeInfo1 = &ProbeInfoList[j];
+ struct BusLogic_ProbeInfo *ProbeInfo2 = &ProbeInfoList[j + 1];
+ if (ProbeInfo1->Bus > ProbeInfo2->Bus || (ProbeInfo1->Bus == ProbeInfo2->Bus && (ProbeInfo1->Device > ProbeInfo2->Device))) {
+ struct BusLogic_ProbeInfo TempProbeInfo;
+ memcpy(&TempProbeInfo, ProbeInfo1, sizeof(struct BusLogic_ProbeInfo));
+ memcpy(ProbeInfo1, ProbeInfo2, sizeof(struct BusLogic_ProbeInfo));
+ memcpy(ProbeInfo2, &TempProbeInfo, sizeof(struct BusLogic_ProbeInfo));
+ LastInterchange = j;
+ }
+ }
+ }
+}
+
+
+/*
+ BusLogic_InitializeMultiMasterProbeInfo initializes the list of I/O Address
+ and Bus Probe Information to be checked for potential BusLogic MultiMaster
+ SCSI Host Adapters by interrogating the PCI Configuration Space on PCI
+ machines as well as from the list of standard BusLogic MultiMaster ISA
+ I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found.
+*/
+
+static int __init BusLogic_InitializeMultiMasterProbeInfo(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ struct BusLogic_ProbeInfo *PrimaryProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
+ int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
+ int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
+ boolean ForceBusDeviceScanningOrder = false;
+ boolean ForceBusDeviceScanningOrderChecked = false;
+ boolean StandardAddressSeen[6];
+ struct pci_dev *PCI_Device = NULL;
+ int i;
+ if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
+ return 0;
+ BusLogic_ProbeInfoCount++;
+ for (i = 0; i < 6; i++)
+ StandardAddressSeen[i] = false;
+ /*
+ Iterate over the MultiMaster PCI Host Adapters. For each enumerated host
+ adapter, determine whether its ISA Compatible I/O Port is enabled and if
+ so, whether it is assigned the Primary I/O Address. A host adapter that is
+ assigned the Primary I/O Address will always be the preferred boot device.
+ The MultiMaster BIOS will first recognize a host adapter at the Primary I/O
+ Address, then any other PCI host adapters, and finally any host adapters
+ located at the remaining standard ISA I/O Addresses. When a PCI host
+ adapter is found with its ISA Compatible I/O Port enabled, a command is
+ issued to disable the ISA Compatible I/O Port, and it is noted that the
+ particular standard ISA I/O Address need not be probed.
+ */
+ PrimaryProbeInfo->IO_Address = 0;
+ while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER, PCI_Device)) != NULL) {
+ struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter;
+ struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation;
+ enum BusLogic_ISACompatibleIOPort ModifyIOAddressRequest;
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned int IRQ_Channel;
+ unsigned long BaseAddress0;
+ unsigned long BaseAddress1;
+ unsigned long IO_Address;
+ unsigned long PCI_Address;
+
+ if (pci_enable_device(PCI_Device))
+ continue;
+
+ if (pci_set_dma_mask(PCI_Device, (u64) 0xffffffff))
+ continue;
+
+ Bus = PCI_Device->bus->number;
+ Device = PCI_Device->devfn >> 3;
+ IRQ_Channel = PCI_Device->irq;
+ IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0);
+ PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1);
+
+ if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) {
+ BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "MultiMaster Host Adapter\n", NULL, BaseAddress0);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (pci_resource_flags(PCI_Device, 1) & IORESOURCE_IO) {
+ BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "MultiMaster Host Adapter\n", NULL, BaseAddress1);
+ BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n", NULL, Bus, Device, PCI_Address);
+ continue;
+ }
+ if (IRQ_Channel == 0) {
+ BusLogic_Error("BusLogic: IRQ Channel %d invalid for " "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe) {
+ BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter " "detected at\n", NULL);
+ BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address " "0x%X PCI Address 0x%X\n", NULL, Bus, Device, IO_Address, PCI_Address);
+ }
+ /*
+ Issue the Inquire PCI Host Adapter Information command to determine
+ the ISA Compatible I/O Port. If the ISA Compatible I/O Port is
+ known and enabled, note that the particular Standard ISA I/O
+ Address should not be probed.
+ */
+ HostAdapter->IO_Address = IO_Address;
+ BusLogic_InterruptReset(HostAdapter);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation))
+ == sizeof(PCIHostAdapterInformation)) {
+ if (PCIHostAdapterInformation.ISACompatibleIOPort < 6)
+ StandardAddressSeen[PCIHostAdapterInformation.ISACompatibleIOPort] = true;
+ } else
+ PCIHostAdapterInformation.ISACompatibleIOPort = BusLogic_IO_Disable;
+ /*
+ * Issue the Modify I/O Address command to disable the ISA Compatible
+ * I/O Port. On PCI Host Adapters, the Modify I/O Address command
+ * allows modification of the ISA compatible I/O Address that the Host
+ * Adapter responds to; it does not affect the PCI compliant I/O Address
+ * assigned at system initialization.
+ */
+ ModifyIOAddressRequest = BusLogic_IO_Disable;
+ BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress, &ModifyIOAddressRequest, sizeof(ModifyIOAddressRequest), NULL, 0);
+ /*
+ For the first MultiMaster Host Adapter enumerated, issue the Fetch
+ Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
+ for the setting of the "Use Bus And Device # For PCI Scanning Seq."
+ option. Issue the Inquire Board ID command since this option is
+ only valid for the BT-948/958/958D.
+ */
+ if (!ForceBusDeviceScanningOrderChecked) {
+ struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest;
+ struct BusLogic_AutoSCSIByte45 AutoSCSIByte45;
+ struct BusLogic_BoardID BoardID;
+ FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_AutoSCSI_BaseOffset + 45;
+ FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte45);
+ BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &AutoSCSIByte45, sizeof(AutoSCSIByte45));
+ BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, &BoardID, sizeof(BoardID));
+ if (BoardID.FirmwareVersion1stDigit == '5')
+ ForceBusDeviceScanningOrder = AutoSCSIByte45.ForceBusDeviceScanningOrder;
+ ForceBusDeviceScanningOrderChecked = true;
+ }
+ /*
+ Determine whether this MultiMaster Host Adapter has its ISA
+ Compatible I/O Port enabled and is assigned the Primary I/O Address.
+ If it does, then it is the Primary MultiMaster Host Adapter and must
+ be recognized first. If it does not, then it is added to the list
+ for probing after any Primary MultiMaster Host Adapter is probed.
+ */
+ if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330) {
+ PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ PrimaryProbeInfo->IO_Address = IO_Address;
+ PrimaryProbeInfo->PCI_Address = PCI_Address;
+ PrimaryProbeInfo->Bus = Bus;
+ PrimaryProbeInfo->Device = Device;
+ PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
+ PrimaryProbeInfo->PCI_Device = PCI_Device;
+ PCIMultiMasterCount++;
+ } else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Address = PCI_Address;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ ProbeInfo->PCI_Device = PCI_Device;
+ NonPrimaryPCIMultiMasterCount++;
+ PCIMultiMasterCount++;
+ } else
+ BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL);
+ }
+ /*
+ If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option is ON
+ for the first enumerated MultiMaster Host Adapter, and if that host adapter
+ is a BT-948/958/958D, then the MultiMaster BIOS will recognize MultiMaster
+ Host Adapters in the order of increasing PCI Bus and Device Number. In
+ that case, sort the probe information into the same order the BIOS uses.
+ If this option is OFF, then the MultiMaster BIOS will recognize MultiMaster
+ Host Adapters in the order they are enumerated by the PCI BIOS, and hence
+ no sorting is necessary.
+ */
+ if (ForceBusDeviceScanningOrder)
+ BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[NonPrimaryPCIMultiMasterIndex], NonPrimaryPCIMultiMasterCount);
+ /*
+ If no PCI MultiMaster Host Adapter is assigned the Primary I/O Address,
+ then the Primary I/O Address must be probed explicitly before any PCI
+ host adapters are probed.
+ */
+ if (!BusLogic_ProbeOptions.NoProbeISA)
+ if (PrimaryProbeInfo->IO_Address == 0 && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe330 : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)) {
+ PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+ PrimaryProbeInfo->IO_Address = 0x330;
+ }
+ /*
+ Append the list of standard BusLogic MultiMaster ISA I/O Addresses,
+ omitting the Primary I/O Address which has already been handled.
+ */
+ if (!BusLogic_ProbeOptions.NoProbeISA) {
+ if (!StandardAddressSeen[1] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe334 : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x334);
+ if (!StandardAddressSeen[2] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe230 : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x230);
+ if (!StandardAddressSeen[3] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe234 : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x234);
+ if (!StandardAddressSeen[4] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe130 : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x130);
+ if (!StandardAddressSeen[5] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe134 : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x134);
+ }
+ /*
+ Iterate over the older non-compliant MultiMaster PCI Host Adapters,
+ noting the PCI bus location and assigned IRQ Channel.
+ */
+ PCI_Device = NULL;
+ while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, PCI_Device)) != NULL) {
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned int IRQ_Channel;
+ unsigned long IO_Address;
+
+ if (pci_enable_device(PCI_Device))
+ continue;
+
+ if (pci_set_dma_mask(PCI_Device, (u64) 0xffffffff))
+ continue;
+
+ Bus = PCI_Device->bus->number;
+ Device = PCI_Device->devfn >> 3;
+ IRQ_Channel = PCI_Device->irq;
+ IO_Address = pci_resource_start(PCI_Device, 0);
+
+ if (IO_Address == 0 || IRQ_Channel == 0)
+ continue;
+ for (i = 0; i < BusLogic_ProbeInfoCount; i++) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[i];
+ if (ProbeInfo->IO_Address == IO_Address && ProbeInfo->HostAdapterType == BusLogic_MultiMaster) {
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->PCI_Address = 0;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ ProbeInfo->PCI_Device = PCI_Device;
+ break;
+ }
+ }
+ }
+ return PCIMultiMasterCount;
+}
+
+
+/*
+ BusLogic_InitializeFlashPointProbeInfo initializes the list of I/O Address
+ and Bus Probe Information to be checked for potential BusLogic FlashPoint
+ Host Adapters by interrogating the PCI Configuration Space. It returns the
+ number of FlashPoint Host Adapters found.
+*/
+
+static int __init BusLogic_InitializeFlashPointProbeInfo(struct BusLogic_HostAdapter
+ *PrototypeHostAdapter)
+{
+ int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
+ struct pci_dev *PCI_Device = NULL;
+ /*
+ Interrogate PCI Configuration Space for any FlashPoint Host Adapters.
+ */
+ while ((PCI_Device = pci_find_device(PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT, PCI_Device)) != NULL) {
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned int IRQ_Channel;
+ unsigned long BaseAddress0;
+ unsigned long BaseAddress1;
+ unsigned long IO_Address;
+ unsigned long PCI_Address;
+
+ if (pci_enable_device(PCI_Device))
+ continue;
+
+ if (pci_set_dma_mask(PCI_Device, (u64) 0xffffffff))
+ continue;
+
+ Bus = PCI_Device->bus->number;
+ Device = PCI_Device->devfn >> 3;
+ IRQ_Channel = PCI_Device->irq;
+ IO_Address = BaseAddress0 = pci_resource_start(PCI_Device, 0);
+ PCI_Address = BaseAddress1 = pci_resource_start(PCI_Device, 1);
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+ if (pci_resource_flags(PCI_Device, 0) & IORESOURCE_MEM) {
+ BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for " "FlashPoint Host Adapter\n", NULL, BaseAddress0);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (pci_resource_flags(PCI_Device, 1) & IORESOURCE_IO) {
+ BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for " "FlashPoint Host Adapter\n", NULL, BaseAddress1);
+ BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n", NULL, Bus, Device, PCI_Address);
+ continue;
+ }
+ if (IRQ_Channel == 0) {
+ BusLogic_Error("BusLogic: IRQ Channel %d invalid for " "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n", NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe) {
+ BusLogic_Notice("BusLogic: FlashPoint Host Adapter " "detected at\n", NULL);
+ BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address " "0x%X PCI Address 0x%X\n", NULL, Bus, Device, IO_Address, PCI_Address);
+ }
+ if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) {
+ struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Address = PCI_Address;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ P