diff options
Diffstat (limited to 'drivers/scsi/BusLogic.c')
-rw-r--r-- | drivers/scsi/BusLogic.c | 3574 |
1 files changed, 3574 insertions, 0 deletions
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c new file mode 100644 index 0000000..41b5197 --- /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; + ProbeInfo->IRQ_Channel = IRQ_Channel; + ProbeInfo->PCI_Device = PCI_Device; + FlashPointCount++; + } else + BusLogic_Warning("BusLogic: Too many Host Adapters " "detected\n", NULL); +#else + BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", NULL, Bus, Device); + BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, irq %d, " "but FlashPoint\n", NULL, IO_Address, PCI_Address, IRQ_Channel); + BusLogic_Error("BusLogic: support was omitted in this kernel " "configuration.\n", NULL); +#endif + } + /* + The FlashPoint BIOS will scan for FlashPoint Host Adapters in the order of + increasing PCI Bus and Device Number, so sort the probe information into + the same order the BIOS uses. + */ + BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[FlashPointIndex], FlashPointCount); + return FlashPointCount; +} + + +/* + BusLogic_InitializeProbeInfoList initializes the list of I/O Address and Bus + Probe Information to be checked for potential BusLogic 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. By default, if both + FlashPoint and PCI MultiMaster Host Adapters are present, this driver will + probe for FlashPoint Host Adapters first unless the BIOS primary disk is + controlled by the first PCI MultiMaster Host Adapter, in which case + MultiMaster Host Adapters will be probed first. The BusLogic Driver Options + specifications "MultiMasterFirst" and "FlashPointFirst" can be used to force + a particular probe order. +*/ + +static void __init BusLogic_InitializeProbeInfoList(struct BusLogic_HostAdapter + *PrototypeHostAdapter) +{ + /* + If a PCI BIOS is present, interrogate it for MultiMaster and FlashPoint + Host Adapters; otherwise, default to the standard ISA MultiMaster probe. + */ + if (!BusLogic_ProbeOptions.NoProbePCI) { + if (BusLogic_ProbeOptions.MultiMasterFirst) { + BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter); + BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter); + } else if (BusLogic_ProbeOptions.FlashPointFirst) { + BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter); + BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter); + } else { + int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter); + int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter); + if (FlashPointCount > 0 && PCIMultiMasterCount > 0) { + struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[FlashPointCount]; + struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter; + struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest; + struct BusLogic_BIOSDriveMapByte Drive0MapByte; + while (ProbeInfo->HostAdapterBusType != BusLogic_PCI_Bus) + ProbeInfo++; + HostAdapter->IO_Address = ProbeInfo->IO_Address; + FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_BIOS_BaseOffset + BusLogic_BIOS_DriveMapOffset + 0; + FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(Drive0MapByte); + BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &Drive0MapByte, sizeof(Drive0MapByte)); + /* + If the Map Byte for BIOS Drive 0 indicates that BIOS Drive 0 + is controlled by this PCI MultiMaster Host Adapter, then + reverse the probe order so that MultiMaster Host Adapters are + probed before FlashPoint Host Adapters. + */ + if (Drive0MapByte.DiskGeometry != BusLogic_BIOS_Disk_Not_Installed) { + struct BusLogic_ProbeInfo SavedProbeInfo[BusLogic_MaxHostAdapters]; + int MultiMasterCount = BusLogic_ProbeInfoCount - FlashPointCount; + memcpy(SavedProbeInfo, BusLogic_ProbeInfoList, BusLogic_ProbeInfoCount * sizeof(struct BusLogic_ProbeInfo)); + memcpy(&BusLogic_ProbeInfoList[0], &SavedProbeInfo[FlashPointCount], MultiMasterCount * sizeof(struct BusLogic_ProbeInfo)); + memcpy(&BusLogic_ProbeInfoList[MultiMasterCount], &SavedProbeInfo[0], FlashPointCount * sizeof(struct BusLogic_ProbeInfo)); + } + } + } + } else + BusLogic_InitializeProbeInfoListISA(PrototypeHostAdapter); +} + + +#endif /* CONFIG_PCI */ + + +/* + BusLogic_Failure prints a standardized error message, and then returns false. +*/ + +static boolean BusLogic_Failure(struct BusLogic_HostAdapter *HostAdapter, char *ErrorMessage) +{ + BusLogic_AnnounceDriver(HostAdapter); + if (HostAdapter->HostAdapterBusType == BusLogic_PCI_Bus) { + BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n", HostAdapter); + BusLogic_Error("Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device, HostAdapter->IO_Address, HostAdapter->PCI_Address); + } else + BusLogic_Error("While configuring BusLogic Host Adapter at " "I/O Address 0x%X:\n", HostAdapter, HostAdapter->IO_Address); + BusLogic_Error("%s FAILED - DETACHING\n", HostAdapter, ErrorMessage); + if (BusLogic_CommandFailureReason != NULL) + BusLogic_Error("ADDITIONAL FAILURE INFO - %s\n", HostAdapter, BusLogic_CommandFailureReason); + return false; +} + + +/* + BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter. +*/ + +static boolean __init BusLogic_ProbeHostAdapter(struct BusLogic_HostAdapter *HostAdapter) +{ + union BusLogic_StatusRegister StatusRegister; + union BusLogic_InterruptRegister InterruptRegister; + union BusLogic_GeometryRegister GeometryRegister; + /* + FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { + struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo; + FlashPointInfo->BaseAddress = (u32) HostAdapter->IO_Address; + FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel; + FlashPointInfo->Present = false; + if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && FlashPointInfo->Present)) { + BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device); + BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, " "but FlashPoint\n", HostAdapter, HostAdapter->IO_Address, HostAdapter->PCI_Address); + BusLogic_Error("BusLogic: Probe Function failed to validate it.\n", HostAdapter); + return false; + } + if (BusLogic_GlobalOptions.TraceProbe) + BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Found\n", HostAdapter, HostAdapter->IO_Address); + /* + Indicate the Host Adapter Probe completed successfully. + */ + return true; + } + /* + Read the Status, Interrupt, and Geometry Registers to test if there are I/O + ports that respond, and to check the values to determine if they are from a + BusLogic Host Adapter. A nonexistent I/O port will return 0xFF, in which + case there is definitely no BusLogic Host Adapter at this base I/O Address. + The test here is a subset of that used by the BusLogic Host Adapter BIOS. + */ + StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); + InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter); + GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter); + if (BusLogic_GlobalOptions.TraceProbe) + BusLogic_Notice("BusLogic_Probe(0x%X): Status 0x%02X, Interrupt 0x%02X, " "Geometry 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All, InterruptRegister.All, GeometryRegister.All); + if (StatusRegister.All == 0 || StatusRegister.sr.DiagnosticActive || StatusRegister.sr.CommandParameterRegisterBusy || StatusRegister.sr.Reserved || StatusRegister.sr.CommandInvalid || InterruptRegister.ir.Reserved != 0) + return false; + /* + Check the undocumented Geometry Register to test if there is an I/O port + that responded. Adaptec Host Adapters do not implement the Geometry + Register, so this test helps serve to avoid incorrectly recognizing an + Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C + series does respond to the Geometry Register I/O port, but it will be + rejected later when the Inquire Extended Setup Information command is + issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a + BusLogic clone that implements the same interface as earlier BusLogic + Host Adapters, including the undocumented commands, and is therefore + supported by this driver. However, the AMI FastDisk always returns 0x00 + upon reading the Geometry Register, so the extended translation option + should always be left disabled on the AMI FastDisk. + */ + if (GeometryRegister.All == 0xFF) + return false; + /* + Indicate the Host Adapter Probe completed successfully. + */ + return true; +} + + +/* + BusLogic_HardwareResetHostAdapter issues a Hardware Reset to the Host Adapter + and waits for Host Adapter Diagnostics to complete. If HardReset is true, a + Hard Reset is performed which also initiates a SCSI Bus Reset. Otherwise, a + Soft Reset is performed which only resets the Host Adapter without forcing a + SCSI Bus Reset. +*/ + +static boolean BusLogic_HardwareResetHostAdapter(struct BusLogic_HostAdapter + *HostAdapter, boolean HardReset) +{ + union BusLogic_StatusRegister StatusRegister; + int TimeoutCounter; + /* + FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { + struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo; + FlashPointInfo->HostSoftReset = !HardReset; + FlashPointInfo->ReportDataUnderrun = true; + HostAdapter->CardHandle = FlashPoint_HardwareResetHostAdapter(FlashPointInfo); + if (HostAdapter->CardHandle == FlashPoint_BadCardHandle) + return false; + /* + Indicate the Host Adapter Hard Reset completed successfully. + */ + return true; + } + /* + Issue a Hard Reset or Soft Reset Command to the Host Adapter. The Host + Adapter should respond by setting Diagnostic Active in the Status Register. + */ + if (HardReset) + BusLogic_HardReset(HostAdapter); + else + BusLogic_SoftReset(HostAdapter); + /* + Wait until Diagnostic Active is set in the Status Register. + */ + TimeoutCounter = 5 * 10000; + while (--TimeoutCounter >= 0) { + StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); + if (StatusRegister.sr.DiagnosticActive) + break; + udelay(100); + } + if (BusLogic_GlobalOptions.TraceHardwareReset) + BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Active, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All); + if (TimeoutCounter < 0) + return false; + /* + Wait 100 microseconds to allow completion of any initial diagnostic + activity which might leave the contents of the Status Register + unpredictable. + */ + udelay(100); + /* + Wait until Diagnostic Active is reset in the Status Register. + */ + TimeoutCounter = 10 * 10000; + while (--TimeoutCounter >= 0) { + StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); + if (!StatusRegister.sr.DiagnosticActive) + break; + udelay(100); + } + if (BusLogic_GlobalOptions.TraceHardwareReset) + BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Completed, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All); + if (TimeoutCounter < 0) + return false; + /* + Wait until at least one of the Diagnostic Failure, Host Adapter Ready, + or Data In Register Ready bits is set in the Status Register. + */ + TimeoutCounter = 10000; + while (--TimeoutCounter >= 0) { + StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); + if (StatusRegister.sr.DiagnosticFailure || StatusRegister.sr.HostAdapterReady || StatusRegister.sr.DataInRegisterReady) + break; + udelay(100); + } + if (BusLogic_GlobalOptions.TraceHardwareReset) + BusLogic_Notice("BusLogic_HardwareReset(0x%X): Host Adapter Ready, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All); + if (TimeoutCounter < 0) + return false; + /* + If Diagnostic Failure is set or Host Adapter Ready is reset, then an + error occurred during the Host Adapter diagnostics. If Data In Register + Ready is set, then there is an Error Code available. + */ + if (StatusRegister.sr.DiagnosticFailure || !StatusRegister.sr.HostAdapterReady) { + BusLogic_CommandFailureReason = NULL; + BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS"); + BusLogic_Error("HOST ADAPTER STATUS REGISTER = %02X\n", HostAdapter, StatusRegister.All); + if (StatusRegister.sr.DataInRegisterReady) { + unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter); + BusLogic_Error("HOST ADAPTER ERROR CODE = %d\n", HostAdapter, ErrorCode); + } + return false; + } + /* + Indicate the Host Adapter Hard Reset completed successfully. + */ + return true; +} + + +/* + BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic + Host Adapter. +*/ + +static boolean __init BusLogic_CheckHostAdapter(struct BusLogic_HostAdapter *HostAdapter) +{ + struct BusLogic_ExtendedSetupInformation ExtendedSetupInformation; + unsigned char RequestedReplyLength; + boolean Result = true; + /* + FlashPoint Host Adapters do not require this protection. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) + return true; + /* + Issue the Inquire Extended Setup Information command. Only genuine + BusLogic Host Adapters and true clones support this command. Adaptec 1542C + series Host Adapters that respond to the Geometry Register I/O port will + fail this command. + */ + RequestedReplyLength = sizeof(ExtendedSetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &ExtendedSetupInformation, sizeof(ExtendedSetupInformation)) + != sizeof(ExtendedSetupInformation)) + Result = false; + /* + Provide tracing information if requested and return. + */ + if (BusLogic_GlobalOptions.TraceProbe) + BusLogic_Notice("BusLogic_Check(0x%X): MultiMaster %s\n", HostAdapter, HostAdapter->IO_Address, (Result ? "Found" : "Not Found")); + return Result; +} + + +/* + BusLogic_ReadHostAdapterConfiguration reads the Configuration Information + from Host Adapter and initializes the Host Adapter structure. +*/ + +static boolean __init BusLogic_ReadHostAdapterConfiguration(struct BusLogic_HostAdapter + *HostAdapter) +{ + struct BusLogic_BoardID BoardID; + struct BusLogic_Configuration Configuration; + struct BusLogic_SetupInformation SetupInformation; + struct BusLogic_ExtendedSetupInformation ExtendedSetupInformation; + unsigned char HostAdapterModelNumber[5]; + unsigned char FirmwareVersion3rdDigit; + unsigned char FirmwareVersionLetter; + struct BusLogic_PCIHostAdapterInformation PCIHostAdapterInformation; + struct BusLogic_FetchHostAdapterLocalRAMRequest FetchHostAdapterLocalRAMRequest; + struct BusLogic_AutoSCSIData AutoSCSIData; + union BusLogic_GeometryRegister GeometryRegister; + unsigned char RequestedReplyLength; + unsigned char *TargetPointer, Character; + int TargetID, i; + /* + Configuration Information for FlashPoint Host Adapters is provided in the + FlashPoint_Info structure by the FlashPoint SCCB Manager's Probe Function. + Initialize fields in the Host Adapter structure from the FlashPoint_Info + structure. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { + struct FlashPoint_Info *FlashPointInfo = &HostAdapter->FlashPointInfo; + TargetPointer = HostAdapter->ModelName; + *TargetPointer++ = 'B'; + *TargetPointer++ = 'T'; + *TargetPointer++ = '-'; + for (i = 0; i < sizeof(FlashPointInfo->ModelNumber); i++) + *TargetPointer++ = FlashPointInfo->ModelNumber[i]; + *TargetPointer++ = '\0'; + strcpy(HostAdapter->FirmwareVersion, FlashPoint_FirmwareVersion); + HostAdapter->SCSI_ID = FlashPointInfo->SCSI_ID; + HostAdapter->ExtendedTranslationEnabled = FlashPointInfo->ExtendedTranslationEnabled; + HostAdapter->ParityCheckingEnabled = FlashPointInfo->ParityCheckingEnabled; + HostAdapter->BusResetEnabled = !FlashPointInfo->HostSoftReset; + HostAdapter->LevelSensitiveInterrupt = true; + HostAdapter->HostWideSCSI = FlashPointInfo->HostWideSCSI; + HostAdapter->HostDifferentialSCSI = false; + HostAdapter->HostSupportsSCAM = true; + HostAdapter->HostUltraSCSI = true; + HostAdapter->ExtendedLUNSupport = true; + HostAdapter->TerminationInfoValid = true; + HostAdapter->LowByteTerminated = FlashPointInfo->LowByteTerminated; + HostAdapter->HighByteTerminated = FlashPointInfo->HighByteTerminated; + HostAdapter->SCAM_Enabled = FlashPointInfo->SCAM_Enabled; + HostAdapter->SCAM_Level2 = FlashPointInfo->SCAM_Level2; + HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit; + HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8); + HostAdapter->MaxLogicalUnits = 32; + HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize; + HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize; + HostAdapter->DriverQueueDepth = 255; + HostAdapter->HostAdapterQueueDepth = HostAdapter->DriverQueueDepth; + HostAdapter->SynchronousPermitted = FlashPointInfo->SynchronousPermitted; + HostAdapter->FastPermitted = FlashPointInfo->FastPermitted; + HostAdapter->UltraPermitted = FlashPointInfo->UltraPermitted; + HostAdapter->WidePermitted = FlashPointInfo->WidePermitted; + HostAdapter->DisconnectPermitted = FlashPointInfo->DisconnectPermitted; + HostAdapter->TaggedQueuingPermitted = 0xFFFF; + goto Common; + } + /* + Issue the Inquire Board ID command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, &BoardID, sizeof(BoardID)) != sizeof(BoardID)) + return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID"); + /* + Issue the Inquire Configuration command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0, &Configuration, sizeof(Configuration)) + != sizeof(Configuration)) + return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION"); + /* + Issue the Inquire Setup Information command. + */ + RequestedReplyLength = sizeof(SetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &SetupInformation, sizeof(SetupInformation)) + != sizeof(SetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + /* + Issue the Inquire Extended Setup Information command. + */ + RequestedReplyLength = sizeof(ExtendedSetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &ExtendedSetupInformation, sizeof(ExtendedSetupInformation)) + != sizeof(ExtendedSetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION"); + /* + Issue the Inquire Firmware Version 3rd Digit command. + */ + FirmwareVersion3rdDigit = '\0'; + if (BoardID.FirmwareVersion1stDigit > '0') + if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit, NULL, 0, &FirmwareVersion3rdDigit, sizeof(FirmwareVersion3rdDigit)) + != sizeof(FirmwareVersion3rdDigit)) + return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT"); + /* + Issue the Inquire Host Adapter Model Number command. + */ + if (ExtendedSetupInformation.BusType == 'A' && BoardID.FirmwareVersion1stDigit == '2') + /* BusLogic BT-542B ISA 2.xx */ + strcpy(HostAdapterModelNumber, "542B"); + else if (ExtendedSetupInformation.BusType == 'E' && BoardID.FirmwareVersion1stDigit == '2' && (BoardID.FirmwareVersion2ndDigit <= '1' || (BoardID.FirmwareVersion2ndDigit == '2' && FirmwareVersion3rdDigit == '0'))) + /* BusLogic BT-742A EISA 2.1x or 2.20 */ + strcpy(HostAdapterModelNumber, "742A"); + else if (ExtendedSetupInformation.BusType == 'E' && BoardID.FirmwareVersion1stDigit == '0') + /* AMI FastDisk EISA Series 441 0.x */ + strcpy(HostAdapterModelNumber, "747A"); + else { + RequestedReplyLength = sizeof(HostAdapterModelNumber); + if (BusLogic_Command(HostAdapter, BusLogic_InquireHostAdapterModelNumber, &RequestedReplyLength, sizeof(RequestedReplyLength), &HostAdapterModelNumber, sizeof(HostAdapterModelNumber)) + != sizeof(HostAdapterModelNumber)) + return BusLogic_Failure(HostAdapter, "INQUIRE HOST ADAPTER MODEL NUMBER"); + } + /* + BusLogic MultiMaster Host Adapters can be identified by their model number + and the major version number of their firmware as follows: + + 5.xx BusLogic "W" Series Host Adapters: + BT-948/958/958D + 4.xx BusLogic "C" Series Host Adapters: + BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF + 3.xx BusLogic "S" Series Host Adapters: + BT-747S/747D/757S/757D/445S/545S/542D + BT-542B/742A (revision H) + 2.xx BusLogic "A" Series Host Adapters: + BT-542B/742A (revision G and below) + 0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter + */ + /* + Save the Model Name and Host Adapter Name in the Host Adapter structure. + */ + TargetPointer = HostAdapter->ModelName; + *TargetPointer++ = 'B'; + *TargetPointer++ = 'T'; + *TargetPointer++ = '-'; + for (i = 0; i < sizeof(HostAdapterModelNumber); i++) { + Character = HostAdapterModelNumber[i]; + if (Character == ' ' || Character == '\0') + break; + *TargetPointer++ = Character; + } + *TargetPointer++ = '\0'; + /* + Save the Firmware Version in the Host Adapter structure. + */ + TargetPointer = HostAdapter->FirmwareVersion; + *TargetPointer++ = BoardID.FirmwareVersion1stDigit; + *TargetPointer++ = '.'; + *TargetPointer++ = BoardID.FirmwareVersion2ndDigit; + if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0') + *TargetPointer++ = FirmwareVersion3rdDigit; + *TargetPointer = '\0'; + /* + Issue the Inquire Firmware Version Letter command. + */ + if (strcmp(HostAdapter->FirmwareVersion, "3.3") >= 0) { + if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter, NULL, 0, &FirmwareVersionLetter, sizeof(FirmwareVersionLetter)) + != sizeof(FirmwareVersionLetter)) + return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER"); + if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0') + *TargetPointer++ = FirmwareVersionLetter; + *TargetPointer = '\0'; + } + /* + Save the Host Adapter SCSI ID in the Host Adapter structure. + */ + HostAdapter->SCSI_ID = Configuration.HostAdapterID; + /* + Determine the Bus Type and save it in the Host Adapter structure, determine + and save the IRQ Channel if necessary, and determine and save the DMA + Channel for ISA Host Adapters. + */ + HostAdapter->HostAdapterBusType = BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4']; + if (HostAdapter->IRQ_Channel == 0) { + if (Configuration.IRQ_Channel9) + HostAdapter->IRQ_Channel = 9; + else if (Configuration.IRQ_Channel10) + HostAdapter->IRQ_Channel = 10; + else if (Configuration.IRQ_Channel11) + HostAdapter->IRQ_Channel = 11; + else if (Configuration.IRQ_Channel12) + HostAdapter->IRQ_Channel = 12; + else if (Configuration.IRQ_Channel14) + HostAdapter->IRQ_Channel = 14; + else if (Configuration.IRQ_Channel15) + HostAdapter->IRQ_Channel = 15; + } + if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus) { + if (Configuration.DMA_Channel5) + HostAdapter->DMA_Channel = 5; + else if (Configuration.DMA_Channel6) + HostAdapter->DMA_Channel = 6; + else if (Configuration.DMA_Channel7) + HostAdapter->DMA_Channel = 7; + } + /* + Determine whether Extended Translation is enabled and save it in + the Host Adapter structure. + */ + GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter); + HostAdapter->ExtendedTranslationEnabled = GeometryRegister.gr.ExtendedTranslationEnabled; + /* + Save the Scatter Gather Limits, Level Sensitive Interrupt flag, Wide + SCSI flag, Differential SCSI flag, SCAM Supported flag, and + Ultra SCSI flag in the Host Adapter structure. + */ + HostAdapter->HostAdapterScatterGatherLimit = ExtendedSetupInformation.ScatterGatherLimit; + HostAdapter->DriverScatterGatherLimit = HostAdapter->HostAdapterScatterGatherLimit; + if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit) + HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit; + if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupt) + HostAdapter->LevelSensitiveInterrupt = true; + HostAdapter->HostWideSCSI = ExtendedSetupInformation.HostWideSCSI; + HostAdapter->HostDifferentialSCSI = ExtendedSetupInformation.HostDifferentialSCSI; + HostAdapter->HostSupportsSCAM = ExtendedSetupInformation.HostSupportsSCAM; + HostAdapter->HostUltraSCSI = ExtendedSetupInformation.HostUltraSCSI; + /* + Determine whether Extended LUN Format CCBs are supported and save the + information in the Host Adapter structure. + */ + if (HostAdapter->FirmwareVersion[0] == '5' || (HostAdapter->FirmwareVersion[0] == '4' && HostAdapter->HostWideSCSI)) + HostAdapter->ExtendedLUNSupport = true; + /* + Issue the Inquire PCI Host Adapter Information command to read the + Termination Information from "W" series MultiMaster Host Adapters. + */ + if (HostAdapter->FirmwareVersion[0] == '5') { + if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation)) + != sizeof(PCIHostAdapterInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE PCI HOST ADAPTER INFORMATION"); + /* + Save the Termination Information in the Host Adapter structure. + */ + if (PCIHostAdapterInformation.GenericInfoValid) { + HostAdapter->TerminationInfoValid = true; + HostAdapter->LowByteTerminated = PCIHostAdapterInformation.LowByteTerminated; + HostAdapter->HighByteTerminated = PCIHostAdapterInformation.HighByteTerminated; + } + } + /* + Issue the Fetch Host Adapter Local RAM command to read the AutoSCSI data + from "W" and "C" series MultiMaster Host Adapters. + */ + if (HostAdapter->FirmwareVersion[0] >= '4') { + FetchHostAdapterLocalRAMRequest.ByteOffset = BusLogic_AutoSCSI_BaseOffset; + FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIData); + if (BusLogic_Command(HostAdapter, BusLogic_FetchHostAdapterLocalRAM, &FetchHostAdapterLocalRAMRequest, sizeof(FetchHostAdapterLocalRAMRequest), &AutoSCSIData, sizeof(AutoSCSIData)) + != sizeof(AutoSCSIData)) + return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM"); + /* + Save the Parity Checking Enabled, Bus Reset Enabled, and Termination + Information in the Host Adapter structure. + */ + HostAdapter->ParityCheckingEnabled = AutoSCSIData.ParityCheckingEnabled; + HostAdapter->BusResetEnabled = AutoSCSIData.BusResetEnabled; + if (HostAdapter->FirmwareVersion[0] == '4') { + HostAdapter->TerminationInfoValid = true; + HostAdapter->LowByteTerminated = AutoSCSIData.LowByteTerminated; + HostAdapter->HighByteTerminated = AutoSCSIData.HighByteTerminated; + } + /* + Save the Wide Permitted, Fast Permitted, Synchronous Permitted, + Disconnect Permitted, Ultra Permitted, and SCAM Information in the + Host Adapter structure. + */ + HostAdapter->WidePermitted = AutoSCSIData.WidePermitted; + HostAdapter->FastPermitted = AutoSCSIData.FastPermitted; + HostAdapter->SynchronousPermitted = AutoSCSIData.SynchronousPermitted; + HostAdapter->DisconnectPermitted = AutoSCSIData.DisconnectPermitted; + if (HostAdapter->HostUltraSCSI) + HostAdapter->UltraPermitted = AutoSCSIData.UltraPermitted; + if (HostAdapter->HostSupportsSCAM) { + HostAdapter->SCAM_Enabled = AutoSCSIData.SCAM_Enabled; + HostAdapter->SCAM_Level2 = AutoSCSIData.SCAM_Level2; + } + } + /* + Initialize fields in the Host Adapter structure for "S" and "A" series + MultiMaster Host Adapters. + */ + if (HostAdapter->FirmwareVersion[0] < '4') { + if (SetupInformation.SynchronousInitiationEnabled) { + HostAdapter->SynchronousPermitted = 0xFF; + if (HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus) { + if (ExtendedSetupInformation.Misc.FastOnEISA) + HostAdapter->FastPermitted = 0xFF; + if (strcmp(HostAdapter->ModelName, "BT-757") == 0) + HostAdapter->WidePermitted = 0xFF; + } + } + HostAdapter->DisconnectPermitted = 0xFF; + HostAdapter->ParityCheckingEnabled = SetupInformation.ParityCheckingEnabled; + HostAdapter->BusResetEnabled = true; + } + /* + Determine the maximum number of Target IDs and Logical Units supported by + this driver for Wide and Narrow Host Adapters. + */ + HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8); + HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8); + /* + Select appropriate values for the Mailbox Count, Driver Queue Depth, + Initial CCBs, and Incremental CCBs variables based on whether or not Strict + Round Robin Mode is supported. If Strict Round Robin Mode is supported, + then there is no performance degradation in using the maximum possible + number of Outgoing and Incoming Mailboxes and allowing the Tagged and + Untagged Queue Depths to determine the actual utilization. If Strict Round + Robin Mode is not supported, then the Host Adapter must scan all the + Outgoing Mailboxes whenever an Outgoing Mailbox entry is made, which can + cause a substantial performance penalty. The host adapters actually have + room to store the following number of CCBs internally; that is, they can + internally queue and manage this many active commands on the SCSI bus + simultaneously. Performance measurements demonstrate that the Driver Queue + Depth should be set to the Mailbox Count, rather than the Host Adapter + Queue Depth (internal CCB capacity), as it is more efficient to have the + queued commands waiting in Outgoing Mailboxes if necessary than to block + the process in the higher levels of the SCSI Subsystem. + + 192 BT-948/958/958D + 100 BT-946C/956C/956CD/747C/757C/757CD/445C + 50 BT-545C/540CF + 30 BT-747S/747D/757S/757D/445S/545S/542D/542B/742A + */ + if (HostAdapter->FirmwareVersion[0] == '5') + HostAdapter->HostAdapterQueueDepth = 192; + else if (HostAdapter->FirmwareVersion[0] == '4') + HostAdapter->HostAdapterQueueDepth = (HostAdapter->HostAdapterBusType != BusLogic_ISA_Bus ? 100 : 50); + else + HostAdapter->HostAdapterQueueDepth = 30; + if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0) { + HostAdapter->StrictRoundRobinModeSupport = true; + HostAdapter->MailboxCount = BusLogic_MaxMailboxes; + } else { + HostAdapter->StrictRoundRobinModeSupport = false; + HostAdapter->MailboxCount = 32; + } + HostAdapter->DriverQueueDepth = HostAdapter->MailboxCount; + HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize; + HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize; + /* + Tagged Queuing support is available and operates properly on all "W" series + MultiMaster Host Adapters, on "C" series MultiMaster Host Adapters with + firmware version 4.22 and above, and on "S" series MultiMaster Host + Adapters with firmware version 3.35 and above. + */ + HostAdapter->TaggedQueuingPermitted = 0; + switch (HostAdapter->FirmwareVersion[0]) { + case '5': + HostAdapter->TaggedQueuingPermitted = 0xFFFF; + break; + case '4': + if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0) + HostAdapter->TaggedQueuingPermitted = 0xFFFF; + break; + case '3': + if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0) + HostAdapter->TaggedQueuingPermitted = 0xFFFF; + break; + } + /* + Determine the Host Adapter BIOS Address if the BIOS is enabled and + save it in the Host Adapter structure. The BIOS is disabled if the + BIOS_Address is 0. + */ + HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12; + /* + ISA Host Adapters require Bounce Buffers if there is more than 16MB memory. + */ + if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus && (void *) high_memory > (void *) MAX_DMA_ADDRESS) + HostAdapter->BounceBuffersRequired = true; + /* + BusLogic BT-445S Host Adapters prior to board revision E have a hardware + bug whereby when the BIOS is enabled, transfers to/from the same address + range the BIOS occupies modulo 16MB are handled incorrectly. Only properly + functioning BT-445S Host Adapters have firmware version 3.37, so require + that ISA Bounce Buffers be used for the buggy BT-445S models if there is + more than 16MB memory. + */ + if (HostAdapter->BIOS_Address > 0 && strcmp(HostAdapter->ModelName, "BT-445S") == 0 && strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 && (void *) high_memory > (void *) MAX_DMA_ADDRESS) + HostAdapter->BounceBuffersRequired = true; + /* + Initialize parameters common to MultiMaster and FlashPoint Host Adapters. + */ + Common: + /* + Initialize the Host Adapter Full Model Name from the Model Name. + */ + strcpy(HostAdapter->FullModelName, "BusLogic "); + strcat(HostAdapter->FullModelName, HostAdapter->ModelName); + /* + Select an appropriate value for the Tagged Queue Depth either from a + BusLogic Driver Options specification, or based on whether this Host + Adapter requires that ISA Bounce Buffers be used. The Tagged Queue Depth + is left at 0 for automatic determination in BusLogic_SelectQueueDepths. + Initialize the Untagged Queue Depth. + */ + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) { + unsigned char QueueDepth = 0; + if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->QueueDepth[TargetID] > 0) + QueueDepth = HostAdapter->DriverOptions->QueueDepth[TargetID]; + else if (HostAdapter->BounceBuffersRequired) + QueueDepth = BusLogic_TaggedQueueDepthBB; + HostAdapter->QueueDepth[TargetID] = QueueDepth; + } + if (HostAdapter->BounceBuffersRequired) + HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepthBB; + else + HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth; + if (HostAdapter->DriverOptions != NULL) + HostAdapter->CommonQueueDepth = HostAdapter->DriverOptions->CommonQueueDepth; + if (HostAdapter->CommonQueueDepth > 0 && HostAdapter->CommonQueueDepth < HostAdapter->UntaggedQueueDepth) + HostAdapter->UntaggedQueueDepth = HostAdapter->CommonQueueDepth; + /* + Tagged Queuing is only allowed if Disconnect/Reconnect is permitted. + Therefore, mask the Tagged Queuing Permitted Default bits with the + Disconnect/Reconnect Permitted bits. + */ + HostAdapter->TaggedQueuingPermitted &= HostAdapter->DisconnectPermitted; + /* + Combine the default Tagged Queuing Permitted bits with any BusLogic Driver + Options Tagged Queuing specification. + */ + if (HostAdapter->DriverOptions != NULL) + HostAdapter->TaggedQueuingPermitted = + (HostAdapter->DriverOptions->TaggedQueuingPermitted & HostAdapter->DriverOptions->TaggedQueuingPermittedMask) | (HostAdapter->TaggedQueuingPermitted & ~HostAdapter->DriverOptions->TaggedQueuingPermittedMask); + + /* + Select an appropriate value for Bus Settle Time either from a BusLogic + Driver Options specification, or from BusLogic_DefaultBusSettleTime. + */ + if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->BusSettleTime > 0) + HostAdapter->BusSettleTime = HostAdapter->DriverOptions->BusSettleTime; + else + HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime; + /* + Indicate reading the Host Adapter Configuration completed successfully. + */ + return true; +} + + +/* + BusLogic_ReportHostAdapterConfiguration reports the configuration of + Host Adapter. +*/ + +static boolean __init BusLogic_ReportHostAdapterConfiguration(struct BusLogic_HostAdapter + *HostAdapter) +{ + unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1; + unsigned short SynchronousPermitted, FastPermitted; + unsigned short UltraPermitted, WidePermitted; + unsigned short DisconnectPermitted, TaggedQueuingPermitted; + boolean CommonSynchronousNegotiation, CommonTaggedQueueDepth; + char SynchronousString[BusLogic_MaxTargetDevices + 1]; + char WideString[BusLogic_MaxTargetDevices + 1]; + char DisconnectString[BusLogic_MaxTargetDevices + 1]; + char TaggedQueuingString[BusLogic_MaxTargetDevices + 1]; + char *SynchronousMessage = SynchronousString; + char *WideMessage = WideString; + char *DisconnectMessage = DisconnectString; + char *TaggedQueuingMessage = TaggedQueuingString; + int TargetID; + BusLogic_Info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n", + HostAdapter, HostAdapter->ModelName, + BusLogic_HostAdapterBusNames[HostAdapter->HostAdapterBusType], (HostAdapter->HostWideSCSI ? " Wide" : ""), (HostAdapter->HostDifferentialSCSI ? " Differential" : ""), (HostAdapter->HostUltraSCSI ? " Ultra" : "")); + BusLogic_Info(" Firmware Version: %s, I/O Address: 0x%X, " "IRQ Channel: %d/%s\n", HostAdapter, HostAdapter->FirmwareVersion, HostAdapter->IO_Address, HostAdapter->IRQ_Channel, (HostAdapter->LevelSensitiveInterrupt ? "Level" : "Edge")); + if (HostAdapter->HostAdapterBusType != BusLogic_PCI_Bus) { + BusLogic_Info(" DMA Channel: ", HostAdapter); + if (HostAdapter->DMA_Channel > 0) + BusLogic_Info("%d, ", HostAdapter, HostAdapter->DMA_Channel); + else + BusLogic_Info("None, ", HostAdapter); + if (HostAdapter->BIOS_Address > 0) + BusLogic_Info("BIOS Address: 0x%X, ", HostAdapter, HostAdapter->BIOS_Address); + else + BusLogic_Info("BIOS Address: None, ", HostAdapter); + } else { + BusLogic_Info(" PCI Bus: %d, Device: %d, Address: ", HostAdapter, HostAdapter->Bus, HostAdapter->Device); + if (HostAdapter->PCI_Address > 0) + BusLogic_Info("0x%X, ", HostAdapter, HostAdapter->PCI_Address); + else + BusLogic_Info("Unassigned, ", HostAdapter); + } + BusLogic_Info("Host Adapter SCSI ID: %d\n", HostAdapter, HostAdapter->SCSI_ID); + BusLogic_Info(" Parity Checking: %s, Extended Translation: %s\n", HostAdapter, (HostAdapter->ParityCheckingEnabled ? "Enabled" : "Disabled"), (HostAdapter->ExtendedTranslationEnabled ? "Enabled" : "Disabled")); + AllTargetsMask &= ~(1 << HostAdapter->SCSI_ID); + SynchronousPermitted = HostAdapter->SynchronousPermitted & AllTargetsMask; + FastPermitted = HostAdapter->FastPermitted & AllTargetsMask; + UltraPermitted = HostAdapter->UltraPermitted & AllTargetsMask; + if ((BusLogic_MultiMasterHostAdapterP(HostAdapter) && (HostAdapter->FirmwareVersion[0] >= '4' || HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)) || BusLogic_FlashPointHostAdapterP(HostAdapter)) { + CommonSynchronousNegotiation = false; + if (SynchronousPermitted == 0) { + SynchronousMessage = "Disabled"; + CommonSynchronousNegotiation = true; + } else if (SynchronousPermitted == AllTargetsMask) { + if (FastPermitted == 0) { + SynchronousMessage = "Slow"; + CommonSynchronousNegotiation = true; + } else if (FastPermitted == AllTargetsMask) { + if (UltraPermitted == 0) { + SynchronousMessage = "Fast"; + CommonSynchronousNegotiation = true; + } else if (UltraPermitted == AllTargetsMask) { + SynchronousMessage = "Ultra"; + CommonSynchronousNegotiation = true; + } + } + } + if (!CommonSynchronousNegotiation) { + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + SynchronousString[TargetID] = ((!(SynchronousPermitted & (1 << TargetID))) ? 'N' : (!(FastPermitted & (1 << TargetID)) ? 'S' : (!(UltraPermitted & (1 << TargetID)) ? 'F' : 'U'))); + SynchronousString[HostAdapter->SCSI_ID] = '#'; + SynchronousString[HostAdapter->MaxTargetDevices] = '\0'; + } + } else + SynchronousMessage = (SynchronousPermitted == 0 ? "Disabled" : "Enabled"); + WidePermitted = HostAdapter->WidePermitted & AllTargetsMask; + if (WidePermitted == 0) + WideMessage = "Disabled"; + else if (WidePermitted == AllTargetsMask) + WideMessage = "Enabled"; + else { + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + WideString[TargetID] = ((WidePermitted & (1 << TargetID)) ? 'Y' : 'N'); + WideString[HostAdapter->SCSI_ID] = '#'; + WideString[HostAdapter->MaxTargetDevices] = '\0'; + } + DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask; + if (DisconnectPermitted == 0) + DisconnectMessage = "Disabled"; + else if (DisconnectPermitted == AllTargetsMask) + DisconnectMessage = "Enabled"; + else { + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + DisconnectString[TargetID] = ((DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N'); + DisconnectString[HostAdapter->SCSI_ID] = '#'; + DisconnectString[HostAdapter->MaxTargetDevices] = '\0'; + } + TaggedQueuingPermitted = HostAdapter->TaggedQueuingPermitted & AllTargetsMask; + if (TaggedQueuingPermitted == 0) + TaggedQueuingMessage = "Disabled"; + else if (TaggedQueuingPermitted == AllTargetsMask) + TaggedQueuingMessage = "Enabled"; + else { + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + TaggedQueuingString[TargetID] = ((TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N'); + TaggedQueuingString[HostAdapter->SCSI_ID] = '#'; + TaggedQueuingString[HostAdapter->MaxTargetDevices] = '\0'; + } + BusLogic_Info(" Synchronous Negotiation: %s, Wide Negotiation: %s\n", HostAdapter, SynchronousMessage, WideMessage); + BusLogic_Info(" Disconnect/Reconnect: %s, Tagged Queuing: %s\n", HostAdapter, DisconnectMessage, TaggedQueuingMessage); + if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) { + BusLogic_Info(" Scatter/Gather Limit: %d of %d segments, " "Mailboxes: %d\n", HostAdapter, HostAdapter->DriverScatterGatherLimit, HostAdapter->HostAdapterScatterGatherLimit, HostAdapter->MailboxCount); + BusLogic_Info(" Driver Queue Depth: %d, " "Host Adapter Queue Depth: %d\n", HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->HostAdapterQueueDepth); + } else + BusLogic_Info(" Driver Queue Depth: %d, " "Scatter/Gather Limit: %d segments\n", HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->DriverScatterGatherLimit); + BusLogic_Info(" Tagged Queue Depth: ", HostAdapter); + CommonTaggedQueueDepth = true; + for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->QueueDepth[TargetID] != HostAdapter->QueueDepth[0]) { + CommonTaggedQueueDepth = false; + break; + } + if (CommonTaggedQueueDepth) { + if (HostAdapter->QueueDepth[0] > 0) + BusLogic_Info("%d", HostAdapter, HostAdapter->QueueDepth[0]); + else + BusLogic_Info("Automatic", HostAdapter); + } else + BusLogic_Info("Individual", HostAdapter); + BusLogic_Info(", Untagged Queue Depth: %d\n", HostAdapter, HostAdapter->UntaggedQueueDepth); + if (HostAdapter->TerminationInfoValid) { + if (HostAdapter->HostWideSCSI) + BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter, (HostAdapter->LowByteTerminated ? (HostAdapter->HighByteTerminated ? "Both Enabled" : "Low Enabled") + : (HostAdapter->HighByteTerminated ? "High Enabled" : "Both Disabled"))); + else + BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter, (HostAdapter->LowByteTerminated ? "Enabled" : "Disabled")); + if (HostAdapter->HostSupportsSCAM) + BusLogic_Info(", SCAM: %s", HostAdapter, (HostAdapter->SCAM_Enabled ? (HostAdapter->SCAM_Level2 ? "Enabled, Level 2" : "Enabled, Level 1") + : "Disabled")); + BusLogic_Info("\n", HostAdapter); + } + /* + Indicate reporting the Host Adapter configuration completed successfully. + */ + return true; +} + + +/* + BusLogic_AcquireResources acquires the system resources necessary to use + Host Adapter. +*/ + +static boolean __init BusLogic_AcquireResources(struct BusLogic_HostAdapter *HostAdapter) +{ + if (HostAdapter->IRQ_Channel == 0) { + BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n", HostAdapter); + return false; + } + /* + Acquire shared access to the IRQ Channel. + */ + if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, SA_SHIRQ, HostAdapter->FullModelName, HostAdapter) < 0) { + BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", HostAdapter, HostAdapter->IRQ_Channel); + return false; + } + HostAdapter->IRQ_ChannelAcquired = true; + /* + Acquire exclusive access to the DMA Channel. + */ + if (HostAdapter->DMA_Channel > 0) { + if (request_dma(HostAdapter->DMA_Channel, HostAdapter->FullModelName) < 0) { + BusLogic_Error("UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n", HostAdapter, HostAdapter->DMA_Channel); + return false; + } + set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE); + enable_dma(HostAdapter->DMA_Channel); + HostAdapter->DMA_ChannelAcquired = true; + } + /* + Indicate the System Resource Acquisition completed successfully, + */ + return true; +} + + +/* + BusLogic_ReleaseResources releases any system resources previously acquired + by BusLogic_AcquireResources. +*/ + +static void BusLogic_ReleaseResources(struct BusLogic_HostAdapter *HostAdapter) +{ + /* + Release shared access to the IRQ Channel. + */ + if (HostAdapter->IRQ_ChannelAcquired) + free_irq(HostAdapter->IRQ_Channel, HostAdapter); + /* + Release exclusive access to the DMA Channel. + */ + if (HostAdapter->DMA_ChannelAcquired) + free_dma(HostAdapter->DMA_Channel); + /* + Release any allocated memory structs not released elsewhere + */ + if (HostAdapter->MailboxSpace) + pci_free_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, HostAdapter->MailboxSpace, HostAdapter->MailboxSpaceHandle); + HostAdapter->MailboxSpace = NULL; + HostAdapter->MailboxSpaceHandle = 0; + HostAdapter->MailboxSize = 0; +} + + +/* + BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only + function called during SCSI Host Adapter detection which modifies the state + of the Host Adapter from its initial power on or hard reset state. +*/ + +static boolean BusLogic_InitializeHostAdapter(struct BusLogic_HostAdapter + *HostAdapter) +{ + struct BusLogic_ExtendedMailboxRequest ExtendedMailboxRequest; + enum BusLogic_RoundRobinModeRequest RoundRobinModeRequest; + enum BusLogic_SetCCBFormatRequest SetCCBFormatRequest; + int TargetID; + /* + Initialize the pointers to the first and last CCBs that are queued for + completion processing. + */ + HostAdapter->FirstCompletedCCB = NULL; + HostAdapter->LastCompletedCCB = NULL; + /* + Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active, + Command Successful Flag, Active Commands, and Commands Since Reset + for each Target Device. + */ + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL; + HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false; + HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false; + HostAdapter->ActiveCommands[TargetID] = 0; + HostAdapter->CommandsSinceReset[TargetID] = 0; + } + /* + FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) + goto Done; + /* + Initialize the Outgoing and Incoming Mailbox pointers. + */ + HostAdapter->MailboxSize = HostAdapter->MailboxCount * (sizeof(struct BusLogic_OutgoingMailbox) + sizeof(struct BusLogic_IncomingMailbox)); + HostAdapter->MailboxSpace = pci_alloc_consistent(HostAdapter->PCI_Device, HostAdapter->MailboxSize, &HostAdapter->MailboxSpaceHandle); + if (HostAdapter->MailboxSpace == NULL) + return BusLogic_Failure(HostAdapter, "MAILBOX ALLOCATION"); + HostAdapter->FirstOutgoingMailbox = (struct BusLogic_OutgoingMailbox *) HostAdapter->MailboxSpace; + HostAdapter->LastOutgoingMailbox = HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1; + HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->FirstIncomingMailbox = (struct BusLogic_IncomingMailbox *) (HostAdapter->LastOutgoingMailbox + 1); + HostAdapter->LastIncomingMailbox = HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1; + HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + + /* + Initialize the Outgoing and Incoming Mailbox structures. + */ + memset(HostAdapter->FirstOutgoingMailbox, 0, HostAdapter->MailboxCount * sizeof(struct BusLogic_OutgoingMailbox)); + memset(HostAdapter->FirstIncomingMailbox, 0, HostAdapter->MailboxCount * sizeof(struct BusLogic_IncomingMailbox)); + /* + Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes. + */ + ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount; + ExtendedMailboxRequest.BaseMailboxAddress = (u32) HostAdapter->MailboxSpaceHandle; + if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox, &ExtendedMailboxRequest, sizeof(ExtendedMailboxRequest), NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION"); + /* + Enable Strict Round Robin Mode if supported by the Host Adapter. In + Strict Round Robin Mode, the Host Adapter only looks at the next Outgoing + Mailbox for each new command, rather than scanning through all the + Outgoing Mailboxes to find any that have new commands in them. Strict + Round Robin Mode is significantly more efficient. + */ + if (HostAdapter->StrictRoundRobinModeSupport) { + RoundRobinModeRequest = BusLogic_StrictRoundRobinMode; + if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode, &RoundRobinModeRequest, sizeof(RoundRobinModeRequest), NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE"); + } + /* + For Host Adapters that support Extended LUN Format CCBs, issue the Set CCB + Format command to allow 32 Logical Units per Target Device. + */ + if (HostAdapter->ExtendedLUNSupport) { + SetCCBFormatRequest = BusLogic_ExtendedLUNFormatCCB; + if (BusLogic_Command(HostAdapter, BusLogic_SetCCBFormat, &SetCCBFormatRequest, sizeof(SetCCBFormatRequest), NULL, 0) < 0) + return BusLogic_Failure(HostAdapter, "SET CCB FORMAT"); + } + /* + Announce Successful Initialization. + */ + Done: + if (!HostAdapter->HostAdapterInitialized) { + BusLogic_Info("*** %s Initialized Successfully ***\n", HostAdapter, HostAdapter->FullModelName); + BusLogic_Info("\n", HostAdapter); + } else + BusLogic_Warning("*** %s Initialized Successfully ***\n", HostAdapter, HostAdapter->FullModelName); + HostAdapter->HostAdapterInitialized = true; + /* + Indicate the Host Adapter Initialization completed successfully. + */ + return true; +} + + +/* + BusLogic_TargetDeviceInquiry inquires about the Target Devices accessible + through Host Adapter. +*/ + +static boolean __init BusLogic_TargetDeviceInquiry(struct BusLogic_HostAdapter + *HostAdapter) +{ + u16 InstalledDevices; + u8 InstalledDevicesID0to7[8]; + struct BusLogic_SetupInformation SetupInformation; + u8 SynchronousPeriod[BusLogic_MaxTargetDevices]; + unsigned char RequestedReplyLength; + int TargetID; + /* + Wait a few seconds between the Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get + confused if they receive SCSI Commands too soon after a SCSI Bus Reset. + */ + BusLogic_Delay(HostAdapter->BusSettleTime); + /* + FlashPoint Host Adapters do not provide for Target Device Inquiry. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) + return true; + /* + Inhibit the Target Device Inquiry if requested. + */ + if (HostAdapter->DriverOptions != NULL && HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry) + return true; + /* + Issue the Inquire Target Devices command for host adapters with firmware + version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command + for older host adapters. This is necessary to force Synchronous Transfer + Negotiation so that the Inquire Setup Information and Inquire Synchronous + Period commands will return valid data. The Inquire Target Devices command + is preferable to Inquire Installed Devices ID 0 to 7 since it only probes + Logical Unit 0 of each Target Device. + */ + if (strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0) { + + /* + * Issue a Inquire Target Devices command. Inquire Target Devices only + * tests Logical Unit 0 of each Target Device unlike the Inquire Installed + * Devices commands which test Logical Units 0 - 7. Two bytes are + * returned, where byte 0 bit 0 set indicates that Target Device 0 exists, + * and so on. + */ + + if (BusLogic_Command(HostAdapter, BusLogic_InquireTargetDevices, NULL, 0, &InstalledDevices, sizeof(InstalledDevices)) + != sizeof(InstalledDevices)) + return BusLogic_Failure(HostAdapter, "INQUIRE TARGET DEVICES"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->TargetFlags[TargetID].TargetExists = (InstalledDevices & (1 << TargetID) ? true : false); + } else { + + /* + * Issue an Inquire Installed Devices command. For each Target Device, + * a byte is returned where bit 0 set indicates that Logical Unit 0 + * exists, bit 1 set indicates that Logical Unit 1 exists, and so on. + */ + + if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7, NULL, 0, &InstalledDevicesID0to7, sizeof(InstalledDevicesID0to7)) + != sizeof(InstalledDevicesID0to7)) + return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7"); + for (TargetID = 0; TargetID < 8; TargetID++) + HostAdapter->TargetFlags[TargetID].TargetExists = (InstalledDevicesID0to7[TargetID] != 0 ? true : false); + } + /* + Issue the Inquire Setup Information command. + */ + RequestedReplyLength = sizeof(SetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, &RequestedReplyLength, sizeof(RequestedReplyLength), &SetupInformation, sizeof(SetupInformation)) + != sizeof(SetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->SynchronousOffset[TargetID] = (TargetID < 8 ? SetupInformation.SynchronousValuesID0to7[TargetID].Offset : SetupInformation.SynchronousValuesID8to15[TargetID - 8].Offset); + if (strcmp(HostAdapter->FirmwareVersion, "5.06L") >= 0) + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->TargetFlags[TargetID].WideTransfersActive = (TargetID < 8 ? (SetupInformation.WideTransfersActiveID0to7 & (1 << TargetID) + ? true : false) + : (SetupInformation.WideTransfersActiveID8to15 & (1 << (TargetID - 8)) + ? true : false)); + /* + Issue the Inquire Synchronous Period command. + */ + if (HostAdapter->FirmwareVersion[0] >= '3') { + + /* Issue a Inquire Synchronous Period command. For each Target Device, + * a byte is returned which represents the Synchronous Transfer Period + * in units of 10 nanoseconds. + */ + + RequestedReplyLength = sizeof(SynchronousPeriod); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod, &RequestedReplyLength, sizeof(RequestedReplyLength), &SynchronousPeriod, sizeof(SynchronousPeriod)) + != sizeof(SynchronousPeriod)) + return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->SynchronousPeriod[TargetID] = SynchronousPeriod[TargetID]; + } else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0) + HostAdapter->SynchronousPeriod[TargetID] = 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID] + .TransferPeriod; + /* + Indicate the Target Device Inquiry completed successfully. + */ + return true; +} + +/* + BusLogic_InitializeHostStructure initializes the fields in the SCSI Host + structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the + SCSI Host structure are intentionally left uninitialized, as this driver + handles acquisition and release of these resources explicitly, as well as + ensuring exclusive access to the Host Adapter hardware and data structures + through explicit acquisition and release of the Host Adapter's Lock. +*/ + +static void __init BusLogic_InitializeHostStructure(struct BusLogic_HostAdapter + *HostAdapter, struct Scsi_Host *Host) +{ + Host->max_id = HostAdapter->MaxTargetDevices; + Host->max_lun = HostAdapter->MaxLogicalUnits; + Host->max_channel = 0; + Host->unique_id = HostAdapter->IO_Address; + Host->this_id = HostAdapter->SCSI_ID; + Host->can_queue = HostAdapter->DriverQueueDepth; + Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit; + Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired; + Host->cmd_per_lun = HostAdapter->UntaggedQueueDepth; +} + +/* + BusLogic_SlaveConfigure will actually set the queue depth on individual + scsi devices as they are permanently added to the device chain. We + shamelessly rip off the SelectQueueDepths code to make this work mostly + like it used to. Since we don't get called once at the end of the scan + but instead get called for each device, we have to do things a bit + differently. +*/ +static int BusLogic_SlaveConfigure(struct scsi_device *Device) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Device->host->hostdata; + int TargetID = Device->id; + int QueueDepth = HostAdapter->QueueDepth[TargetID]; + + if (HostAdapter->TargetFlags[TargetID].TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) { + if (QueueDepth == 0) + QueueDepth = BusLogic_MaxAutomaticTaggedQueueDepth; + HostAdapter->QueueDepth[TargetID] = QueueDepth; + scsi_adjust_queue_depth(Device, MSG_SIMPLE_TAG, QueueDepth); + } else { + HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID); + QueueDepth = HostAdapter->UntaggedQueueDepth; + HostAdapter->QueueDepth[TargetID] = QueueDepth; + scsi_adjust_queue_depth(Device, 0, QueueDepth); + } + QueueDepth = 0; + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->TargetFlags[TargetID].TargetExists) { + QueueDepth += HostAdapter->QueueDepth[TargetID]; + } + if (QueueDepth > HostAdapter->AllocatedCCBs) + BusLogic_CreateAdditionalCCBs(HostAdapter, QueueDepth - HostAdapter->AllocatedCCBs, false); + return 0; +} + +/* + BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard + I/O Addresses where they may be located, initializing, registering, and + reporting the configuration of each BusLogic Host Adapter it finds. It + returns the number of BusLogic Host Adapters successfully initialized and + registered. +*/ + +static int __init BusLogic_init(void) +{ + int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex; + struct BusLogic_HostAdapter *PrototypeHostAdapter; + +#ifdef MODULE + if (BusLogic) + BusLogic_Setup(BusLogic); +#endif + + if (BusLogic_ProbeOptions.NoProbe) + return -ENODEV; + BusLogic_ProbeInfoList = (struct BusLogic_ProbeInfo *) + kmalloc(BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo), GFP_ATOMIC); + if (BusLogic_ProbeInfoList == NULL) { + BusLogic_Error("BusLogic: Unable to allocate Probe Info List\n", NULL); + return -ENOMEM; + } + memset(BusLogic_ProbeInfoList, 0, BusLogic_MaxHostAdapters * sizeof(struct BusLogic_ProbeInfo)); + PrototypeHostAdapter = (struct BusLogic_HostAdapter *) + kmalloc(sizeof(struct BusLogic_HostAdapter), GFP_ATOMIC); + if (PrototypeHostAdapter == NULL) { + kfree(BusLogic_ProbeInfoList); + BusLogic_Error("BusLogic: Unable to allocate Prototype " "Host Adapter\n", NULL); + return -ENOMEM; + } + memset(PrototypeHostAdapter, 0, sizeof(struct BusLogic_HostAdapter)); +#ifdef MODULE + if (BusLogic != NULL) + BusLogic_Setup(BusLogic); +#endif + BusLogic_InitializeProbeInfoList(PrototypeHostAdapter); + for (ProbeIndex = 0; ProbeIndex < BusLogic_ProbeInfoCount; ProbeIndex++) { + struct BusLogic_ProbeInfo *ProbeInfo = &BusLogic_ProbeInfoList[ProbeIndex]; + struct BusLogic_HostAdapter *HostAdapter = PrototypeHostAdapter; + struct Scsi_Host *Host; + if (ProbeInfo->IO_Address == 0) + continue; + memset(HostAdapter, 0, sizeof(struct BusLogic_HostAdapter)); + HostAdapter->HostAdapterType = ProbeInfo->HostAdapterType; + HostAdapter->HostAdapterBusType = ProbeInfo->HostAdapterBusType; + HostAdapter->IO_Address = ProbeInfo->IO_Address; + HostAdapter->PCI_Address = ProbeInfo->PCI_Address; + HostAdapter->Bus = ProbeInfo->Bus; + HostAdapter->Device = ProbeInfo->Device; + HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel; + HostAdapter->AddressCount = BusLogic_HostAdapterAddressCount[HostAdapter->HostAdapterType]; + /* + Probe the Host Adapter. If unsuccessful, abort further initialization. + */ + if (!BusLogic_ProbeHostAdapter(HostAdapter)) + continue; + /* + Hard Reset the Host Adapter. If unsuccessful, abort further + initialization. + */ + if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true)) + continue; + /* + Check the Host Adapter. If unsuccessful, abort further initialization. + */ + if (!BusLogic_CheckHostAdapter(HostAdapter)) + continue; + /* + Initialize the Driver Options field if provided. + */ + if (DriverOptionsIndex < BusLogic_DriverOptionsCount) + HostAdapter->DriverOptions = &BusLogic_DriverOptions[DriverOptionsIndex++]; + /* + Announce the Driver Version and Date, Author's Name, Copyright Notice, + and Electronic Mail Address. + */ + BusLogic_AnnounceDriver(HostAdapter); + /* + Register usage of the I/O Address range. From this point onward, any + failure will be assumed to be due to a problem with the Host Adapter, + rather than due to having mistakenly identified this port as belonging + to a BusLogic Host Adapter. The I/O Address range will not be + released, thereby preventing it from being incorrectly identified as + any other type of Host Adapter. + */ + if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, "BusLogic")) + continue; + /* + Register the SCSI Host structure. + */ + + Host = scsi_host_alloc(&Bus_Logic_template, sizeof(struct BusLogic_HostAdapter)); + if (Host == NULL) { + release_region(HostAdapter->IO_Address, HostAdapter->AddressCount); + continue; + } + HostAdapter = (struct BusLogic_HostAdapter *) Host->hostdata; + memcpy(HostAdapter, PrototypeHostAdapter, sizeof(struct BusLogic_HostAdapter)); + HostAdapter->SCSI_Host = Host; + HostAdapter->HostNumber = Host->host_no; + /* + Add Host Adapter to the end of the list of registered BusLogic + Host Adapters. + */ + list_add_tail(&HostAdapter->host_list, &BusLogic_host_list); + + /* + Read the Host Adapter Configuration, Configure the Host Adapter, + Acquire the System Resources necessary to use the Host Adapter, then + Create the Initial CCBs, Initialize the Host Adapter, and finally + perform Target Device Inquiry. + */ + if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) && + BusLogic_ReportHostAdapterConfiguration(HostAdapter) && BusLogic_AcquireResources(HostAdapter) && BusLogic_CreateInitialCCBs(HostAdapter) && BusLogic_InitializeHostAdapter(HostAdapter) && BusLogic_TargetDeviceInquiry(HostAdapter)) { + /* + Initialization has been completed successfully. Release and + re-register usage of the I/O Address range so that the Model + Name of the Host Adapter will appear, and initialize the SCSI + Host structure. + */ + release_region(HostAdapter->IO_Address, HostAdapter->AddressCount); + if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, HostAdapter->FullModelName)) { + printk(KERN_WARNING "BusLogic: Release and re-register of " "port 0x%04lx failed \n", (unsigned long) HostAdapter->IO_Address); + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + list_del(&HostAdapter->host_list); + scsi_host_put(Host); + } else { + BusLogic_InitializeHostStructure(HostAdapter, Host); + scsi_add_host(Host, NULL); + scsi_scan_host(Host); + BusLogicHostAdapterCount++; + } + } else { + /* + An error occurred during Host Adapter Configuration Querying, Host + Adapter Configuration, Resource Acquisition, CCB Creation, Host + Adapter Initialization, or Target Device Inquiry, so remove Host + Adapter from the list of registered BusLogic Host Adapters, destroy + the CCBs, Release the System Resources, and Unregister the SCSI + Host. + */ + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + list_del(&HostAdapter->host_list); + scsi_host_put(Host); + } + } + kfree(PrototypeHostAdapter); + kfree(BusLogic_ProbeInfoList); + BusLogic_ProbeInfoList = NULL; + return 0; +} + + +/* + BusLogic_ReleaseHostAdapter releases all resources previously acquired to + support a specific Host Adapter, including the I/O Address range, and + unregisters the BusLogic Host Adapter. +*/ + +static int __exit BusLogic_ReleaseHostAdapter(struct BusLogic_HostAdapter *HostAdapter) +{ + struct Scsi_Host *Host = HostAdapter->SCSI_Host; + + scsi_remove_host(Host); + + /* + FlashPoint Host Adapters must first be released by the FlashPoint + SCCB Manager. + */ + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) + FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle); + /* + Destroy the CCBs and release any system resources acquired to + support Host Adapter. + */ + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + /* + Release usage of the I/O Address range. + */ + release_region(HostAdapter->IO_Address, HostAdapter->AddressCount); + /* + Remove Host Adapter from the list of registered BusLogic Host Adapters. + */ + list_del(&HostAdapter->host_list); + + scsi_host_put(Host); + return 0; +} + + +/* + BusLogic_QueueCompletedCCB queues CCB for completion processing. +*/ + +static void BusLogic_QueueCompletedCCB(struct BusLogic_CCB *CCB) +{ + struct BusLogic_HostAdapter *HostAdapter = CCB->HostAdapter; + CCB->Status = BusLogic_CCB_Completed; + CCB->Next = NULL; + if (HostAdapter->FirstCompletedCCB == NULL) { + HostAdapter->FirstCompletedCCB = CCB; + HostAdapter->LastCompletedCCB = CCB; + } else { + HostAdapter->LastCompletedCCB->Next = CCB; + HostAdapter->LastCompletedCCB = CCB; + } + HostAdapter->ActiveCommands[CCB->TargetID]--; +} + + +/* + BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from + the Host Adapter Status and Target Device Status. +*/ + +static int BusLogic_ComputeResultCode(struct BusLogic_HostAdapter *HostAdapter, enum BusLogic_HostAdapterStatus HostAdapterStatus, enum BusLogic_TargetDeviceStatus TargetDeviceStatus) +{ + int HostStatus; + switch (HostAdapterStatus) { + case BusLogic_CommandCompletedNormally: + case BusLogic_LinkedCommandCompleted: + case BusLogic_LinkedCommandCompletedWithFlag: + HostStatus = DID_OK; + break; + case BusLogic_SCSISelectionTimeout: + HostStatus = DID_TIME_OUT; + break; + case BusLogic_InvalidOutgoingMailboxActionCode: + case BusLogic_InvalidCommandOperationCode: + case BusLogic_InvalidCommandParameter: + BusLogic_Warning("BusLogic Driver Protocol Error 0x%02X\n", HostAdapter, HostAdapterStatus); + case BusLogic_DataUnderRun: + case BusLogic_DataOverRun: + case BusLogic_UnexpectedBusFree: + case BusLogic_LinkedCCBhasInvalidLUN: + case BusLogic_AutoRequestSenseFailed: + case BusLogic_TaggedQueuingMessageRejected: + case BusLogic_UnsupportedMessageReceived: + case BusLogic_HostAdapterHardwareFailed: + case BusLogic_TargetDeviceReconnectedImproperly: + case BusLogic_AbortQueueGenerated: + case BusLogic_HostAdapterSoftwareError: + case BusLogic_HostAdapterHardwareTimeoutError: + case BusLogic_SCSIParityErrorDetected: + HostStatus = DID_ERROR; + break; + case BusLogic_InvalidBusPhaseRequested: + case BusLogic_TargetFailedResponseToATN: + case BusLogic_HostAdapterAssertedRST: + case BusLogic_OtherDeviceAssertedRST: + case BusLogic_HostAdapterAssertedBusDeviceReset: + HostStatus = DID_RESET; + break; + default: + BusLogic_Warning("Unknown Host Adapter Status 0x%02X\n", HostAdapter, HostAdapterStatus); + HostStatus = DID_ERROR; + break; + } + return (HostStatus << 16) | TargetDeviceStatus; +} + + +/* + BusLogic_ScanIncomingMailboxes scans the Incoming Mailboxes saving any + Incoming Mailbox entries for completion processing. +*/ + +static void BusLogic_ScanIncomingMailboxes(struct BusLogic_HostAdapter *HostAdapter) +{ + /* + Scan through the Incoming Mailboxes in Strict Round Robin fashion, saving + any completed CCBs for further processing. It is essential that for each + CCB and SCSI Command issued, command completion processing is performed + exactly once. Therefore, only Incoming Mailboxes with completion code + Command Completed Without Error, Command Completed With Error, or Command + Aborted At Host Request are saved for completion processing. When an + Incoming Mailbox has a completion code of Aborted Command Not Found, the + CCB had already completed or been aborted before the current Abort request + was processed, and so completion processing has already occurred and no + further action should be taken. + */ + struct BusLogic_IncomingMailbox *NextIncomingMailbox = HostAdapter->NextIncomingMailbox; + enum BusLogic_CompletionCode CompletionCode; + while ((CompletionCode = NextIncomingMailbox->CompletionCode) != BusLogic_IncomingMailboxFree) { + /* + We are only allowed to do this because we limit our architectures we + run on to machines where bus_to_virt() actually works. There *needs* + to be a dma_addr_to_virt() in the new PCI DMA mapping interface to + replace bus_to_virt() or else this code is going to become very + innefficient. + */ + struct BusLogic_CCB *CCB = (struct BusLogic_CCB *) Bus_to_Virtual(NextIncomingMailbox->CCB); + if (CompletionCode != BusLogic_AbortedCommandNotFound) { + if (CCB->Status == BusLogic_CCB_Active || CCB->Status == BusLogic_CCB_Reset) { + /* + Save the Completion Code for this CCB and queue the CCB + for completion processing. + */ + CCB->CompletionCode = CompletionCode; + BusLogic_QueueCompletedCCB(CCB); + } else { + /* + If a CCB ever appears in an Incoming Mailbox and is not marked + as status Active or Reset, then there is most likely a bug in + the Host Adapter firmware. + */ + BusLogic_Warning("Illegal CCB #%ld status %d in " "Incoming Mailbox\n", HostAdapter, CCB->SerialNumber, CCB->Status); + } + } + NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree; + if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox) + NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + } + HostAdapter->NextIncomingMailbox = NextIncomingMailbox; +} + + +/* + BusLogic_ProcessCompletedCCBs iterates over the completed CCBs for Host + Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and + calling the SCSI Subsystem Completion Routines. The Host Adapter's Lock + should already have been acquired by the caller. +*/ + +static void BusLogic_ProcessCompletedCCBs(struct BusLogic_HostAdapter *HostAdapter) +{ + if (HostAdapter->ProcessCompletedCCBsActive) + return; + HostAdapter->ProcessCompletedCCBsActive = true; + while (HostAdapter->FirstCompletedCCB != NULL) { + struct BusLogic_CCB *CCB = HostAdapter->FirstCompletedCCB; + struct scsi_cmnd *Command = CCB->Command; + HostAdapter->FirstCompletedCCB = CCB->Next; + if (HostAdapter->FirstCompletedCCB == NULL) + HostAdapter->LastCompletedCCB = NULL; + /* + Process the Completed CCB. + */ + if (CCB->Opcode == BusLogic_BusDeviceReset) { + int TargetID = CCB->TargetID; + BusLogic_Warning("Bus Device Reset CCB #%ld to Target " "%d Completed\n", HostAdapter, CCB->SerialNumber, TargetID); + BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].BusDeviceResetsCompleted); + HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false; + HostAdapter->CommandsSinceReset[TargetID] = 0; + HostAdapter->LastResetCompleted[TargetID] = jiffies; + /* + Place CCB back on the Host Adapter's free list. + */ + BusLogic_DeallocateCCB(CCB); +#if 0 /* this needs to be redone different for new EH */ + /* + Bus Device Reset CCBs have the Command field non-NULL only when a + Bus Device Reset was requested for a Command that did not have a + currently active CCB in the Host Adapter (i.e., a Synchronous + Bus Device Reset), and hence would not have its Completion Routine + called otherwise. + */ + while (Command != NULL) { + struct scsi_cmnd *NextCommand = Command->reset_chain; + Command->reset_chain = NULL; + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + Command = NextCommand; + } +#endif + /* + Iterate over the CCBs for this Host Adapter performing completion + processing for any CCBs marked as Reset for this Target. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Reset && CCB->TargetID == TargetID) { + Command = CCB->Command; + BusLogic_DeallocateCCB(CCB); + HostAdapter->ActiveCommands[TargetID]--; + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + } + HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL; + } else { + /* + Translate the Completion Code, Host Adapter Status, and Target + Device Status into a SCSI Subsystem Result Code. + */ + switch (CCB->CompletionCode) { + case BusLogic_IncomingMailboxFree: + case BusLogic_AbortedCommandNotFound: + case BusLogic_InvalidCCB: + BusLogic_Warning("CCB #%ld to Target %d Impossible State\n", HostAdapter, CCB->SerialNumber, CCB->TargetID); + break; + case BusLogic_CommandCompletedWithoutError: + HostAdapter->TargetStatistics[CCB->TargetID] + .CommandsCompleted++; + HostAdapter->TargetFlags[CCB->TargetID] + .CommandSuccessfulFlag = true; + Command->result = DID_OK << 16; + break; + case BusLogic_CommandAbortedAtHostRequest: + BusLogic_Warning("CCB #%ld to Target %d Aborted\n", HostAdapter, CCB->SerialNumber, CCB->TargetID); + BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[CCB->TargetID] + .CommandAbortsCompleted); + Command->result = DID_ABORT << 16; + break; + case BusLogic_CommandCompletedWithError: + Command->result = BusLogic_ComputeResultCode(HostAdapter, CCB->HostAdapterStatus, CCB->TargetDeviceStatus); + if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout) { + HostAdapter->TargetStatistics[CCB->TargetID] + .CommandsCompleted++; + if (BusLogic_GlobalOptions.TraceErrors) { + int i; + BusLogic_Notice("CCB #%ld Target %d: Result %X Host " + "Adapter Status %02X " "Target Status %02X\n", HostAdapter, CCB->SerialNumber, CCB->TargetID, Command->result, CCB->HostAdapterStatus, CCB->TargetDeviceStatus); + BusLogic_Notice("CDB ", HostAdapter); + for (i = 0; i < CCB->CDB_Length; i++) + BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]); + BusLogic_Notice("\n", HostAdapter); + BusLogic_Notice("Sense ", HostAdapter); + for (i = 0; i < CCB->SenseDataLength; i++) + BusLogic_Notice(" %02X", HostAdapter, Command->sense_buffer[i]); + BusLogic_Notice("\n", HostAdapter); + } + } + break; + } + /* + When an INQUIRY command completes normally, save the + CmdQue (Tagged Queuing Supported) and WBus16 (16 Bit + Wide Data Transfers Supported) bits. + */ + if (CCB->CDB[0] == INQUIRY && CCB->CDB[1] == 0 && CCB->HostAdapterStatus == BusLogic_CommandCompletedNormally) { + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[CCB->TargetID]; + struct SCSI_Inquiry *InquiryResult = (struct SCSI_Inquiry *) Command->request_buffer; + TargetFlags->TargetExists = true; + TargetFlags->TaggedQueuingSupported = InquiryResult->CmdQue; + TargetFlags->WideTransfersSupported = InquiryResult->WBus16; + } + /* + Place CCB back on the Host Adapter's free list. + */ + BusLogic_DeallocateCCB(CCB); + /* + Call the SCSI Command Completion Routine. + */ + Command->scsi_done(Command); + } + } + HostAdapter->ProcessCompletedCCBsActive = false; +} + + +/* + BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host + Adapters. +*/ + +static irqreturn_t BusLogic_InterruptHandler(int IRQ_Channel, void *DeviceIdentifier, struct pt_regs *InterruptRegisters) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) DeviceIdentifier; + unsigned long ProcessorFlags; + /* + Acquire exclusive access to Host Adapter. + */ + spin_lock_irqsave(HostAdapter->SCSI_Host->host_lock, ProcessorFlags); + /* + Handle Interrupts appropriately for each Host Adapter type. + */ + if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) { + union BusLogic_InterruptRegister InterruptRegister; + /* + Read the Host Adapter Interrupt Register. + */ + InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter); + if (InterruptRegister.ir.InterruptValid) { + /* + Acknowledge the interrupt and reset the Host Adapter + Interrupt Register. + */ + BusLogic_InterruptReset(HostAdapter); + /* + Process valid External SCSI Bus Reset and Incoming Mailbox + Loaded Interrupts. Command Complete Interrupts are noted, + and Outgoing Mailbox Available Interrupts are ignored, as + they are never enabled. + */ + if (InterruptRegister.ir.ExternalBusReset) + HostAdapter->HostAdapterExternalReset = true; + else if (InterruptRegister.ir.IncomingMailboxLoaded) + BusLogic_ScanIncomingMailboxes(HostAdapter); + else if (InterruptRegister.ir.CommandComplete) + HostAdapter->HostAdapterCommandCompleted = true; + } + } else { + /* + Check if there is a pending interrupt for this Host Adapter. + */ + if (FlashPoint_InterruptPending(HostAdapter->CardHandle)) + switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle)) { + case FlashPoint_NormalInterrupt: + break; + case FlashPoint_ExternalBusReset: + HostAdapter->HostAdapterExternalReset = true; + break; + case FlashPoint_InternalError: + BusLogic_Warning("Internal FlashPoint Error detected" " - Resetting Host Adapter\n", HostAdapter); + HostAdapter->HostAdapterInternalError = true; + break; + } + } + /* + Process any completed CCBs. + */ + if (HostAdapter->FirstCompletedCCB != NULL) + BusLogic_ProcessCompletedCCBs(HostAdapter); + /* + Reset the Host Adapter if requested. + */ + if (HostAdapter->HostAdapterExternalReset) { + BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n", HostAdapter, HostAdapter->FullModelName); + BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets); + BusLogic_ResetHostAdapter(HostAdapter, false); + HostAdapter->HostAdapterExternalReset = false; + } else if (HostAdapter->HostAdapterInternalError) { + BusLogic_Warning("Resetting %s due to Host Adapter Internal Error\n", HostAdapter, HostAdapter->FullModelName); + BusLogic_IncrementErrorCounter(&HostAdapter->HostAdapterInternalErrors); + BusLogic_ResetHostAdapter(HostAdapter, true); + HostAdapter->HostAdapterInternalError = false; + } + /* + Release exclusive access to Host Adapter. + */ + spin_unlock_irqrestore(HostAdapter->SCSI_Host->host_lock, ProcessorFlags); + return IRQ_HANDLED; +} + + +/* + BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing + Mailbox for execution by Host Adapter. The Host Adapter's Lock should + already have been acquired by the caller. +*/ + +static boolean BusLogic_WriteOutgoingMailbox(struct BusLogic_HostAdapter + *HostAdapter, enum BusLogic_ActionCode ActionCode, struct BusLogic_CCB *CCB) +{ + struct BusLogic_OutgoingMailbox *NextOutgoingMailbox; + NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox; + if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree) { + CCB->Status = BusLogic_CCB_Active; + /* + The CCB field must be written before the Action Code field since + the Host Adapter is operating asynchronously and the locking code + does not protect against simultaneous access by the Host Adapter. + */ + NextOutgoingMailbox->CCB = CCB->DMA_Handle; + NextOutgoingMailbox->ActionCode = ActionCode; + BusLogic_StartMailboxCommand(HostAdapter); + if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox) + NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox; + if (ActionCode == BusLogic_MailboxStartCommand) { + HostAdapter->ActiveCommands[CCB->TargetID]++; + if (CCB->Opcode != BusLogic_BusDeviceReset) + HostAdapter->TargetStatistics[CCB->TargetID].CommandsAttempted++; + } + return true; + } + return false; +} + +/* Error Handling (EH) support */ + +static int BusLogic_host_reset(struct scsi_cmnd * SCpnt) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) SCpnt->device->host->hostdata; + + unsigned int id = SCpnt->device->id; + struct BusLogic_TargetStatistics *stats = &HostAdapter->TargetStatistics[id]; + BusLogic_IncrementErrorCounter(&stats->HostAdapterResetsRequested); + + return BusLogic_ResetHostAdapter(HostAdapter, false); +} + +/* + BusLogic_QueueCommand creates a CCB for Command and places it into an + Outgoing Mailbox for execution by the associated Host Adapter. +*/ + +static int BusLogic_QueueCommand(struct scsi_cmnd *Command, void (*CompletionRoutine) (struct scsi_cmnd *)) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Command->device->host->hostdata; + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[Command->device->id]; + struct BusLogic_TargetStatistics *TargetStatistics = HostAdapter->TargetStatistics; + unsigned char *CDB = Command->cmnd; + int CDB_Length = Command->cmd_len; + int TargetID = Command->device->id; + int LogicalUnit = Command->device->lun; + void *BufferPointer = Command->request_buffer; + int BufferLength = Command->request_bufflen; + int SegmentCount = Command->use_sg; + struct BusLogic_CCB *CCB; + /* + SCSI REQUEST_SENSE commands will be executed automatically by the Host + Adapter for any errors, so they should not be executed explicitly unless + the Sense Data is zero indicating that no error occurred. + */ + if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0) { + Command->result = DID_OK << 16; + CompletionRoutine(Command); + return 0; + } + /* + Allocate a CCB from the Host Adapter's free list. In the unlikely event + that there are none available and memory allocation fails, wait 1 second + and try again. If that fails, the Host Adapter is probably hung so signal + an error as a Host Adapter Hard Reset should be initiated soon. + */ + CCB = BusLogic_AllocateCCB(HostAdapter); + if (CCB == NULL) { + spin_unlock_irq(HostAdapter->SCSI_Host->host_lock); + BusLogic_Delay(1); + spin_lock_irq(HostAdapter->SCSI_Host->host_lock); + CCB = BusLogic_AllocateCCB(HostAdapter); + if (CCB == NULL) { + Command->result = DID_ERROR << 16; + CompletionRoutine(Command); + return 0; + } + } + /* + Initialize the fields in the BusLogic Command Control Block (CCB). + */ + if (SegmentCount == 0 && BufferLength != 0) { + CCB->Opcode = BusLogic_InitiatorCCB; + CCB->DataLength = BufferLength; + CCB->DataPointer = pci_map_single(HostAdapter->PCI_Device, + BufferPointer, BufferLength, + Command->sc_data_direction); + } else if (SegmentCount != 0) { + struct scatterlist *ScatterList = (struct scatterlist *) BufferPointer; + int Segment, Count; + + Count = pci_map_sg(HostAdapter->PCI_Device, ScatterList, SegmentCount, + Command->sc_data_direction); + CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather; + CCB->DataLength = Count * sizeof(struct BusLogic_ScatterGatherSegment); + if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) + CCB->DataPointer = (unsigned int) CCB->DMA_Handle + ((unsigned long) &CCB->ScatterGatherList - (unsigned long) CCB); + else + CCB->DataPointer = Virtual_to_32Bit_Virtual(CCB->ScatterGatherList); + for (Segment = 0; Segment < Count; Segment++) { + CCB->ScatterGatherList[Segment].SegmentByteCount = sg_dma_len(ScatterList + Segment); + CCB->ScatterGatherList[Segment].SegmentDataPointer = sg_dma_address(ScatterList + Segment); + } + } else { + CCB->Opcode = BusLogic_InitiatorCCB; + CCB->DataLength = BufferLength; + CCB->DataPointer = 0; + } + switch (CDB[0]) { + case READ_6: + case READ_10: + CCB->DataDirection = BusLogic_DataInLengthChecked; + TargetStatistics[TargetID].ReadCommands++; + BusLogic_IncrementByteCounter(&TargetStatistics[TargetID].TotalBytesRead, BufferLength); + BusLogic_IncrementSizeBucket(TargetStatistics[TargetID].ReadCommandSizeBuckets, BufferLength); + break; + case WRITE_6: + case WRITE_10: + CCB->DataDirection = BusLogic_DataOutLengthChecked; + TargetStatistics[TargetID].WriteCommands++; + BusLogic_IncrementByteCounter(&TargetStatistics[TargetID].TotalBytesWritten, BufferLength); + BusLogic_IncrementSizeBucket(TargetStatistics[TargetID].WriteCommandSizeBuckets, BufferLength); + break; + default: + CCB->DataDirection = BusLogic_UncheckedDataTransfer; + break; + } + CCB->CDB_Length = CDB_Length; + CCB->HostAdapterStatus = 0; + CCB->TargetDeviceStatus = 0; + CCB->TargetID = TargetID; + CCB->LogicalUnit = LogicalUnit; + CCB->TagEnable = false; + CCB->LegacyTagEnable = false; + /* + BusLogic recommends that after a Reset the first couple of commands that + are sent to a Target Device be sent in a non Tagged Queue fashion so that + the Host Adapter and Target Device can establish Synchronous and Wide + Transfer before Queue Tag messages can interfere with the Synchronous and + Wide Negotiation messages. By waiting to enable Tagged Queuing until after + the first BusLogic_MaxTaggedQueueDepth commands have been queued, it is + assured that after a Reset any pending commands are requeued before Tagged + Queuing is enabled and that the Tagged Queuing message will not occur while + the partition table is being printed. In addition, some devices do not + properly handle the transition from non-tagged to tagged commands, so it is + necessary to wait until there are no pending commands for a target device + before queuing tagged commands. + */ + if (HostAdapter->CommandsSinceReset[TargetID]++ >= + BusLogic_MaxTaggedQueueDepth && !TargetFlags->TaggedQueuingActive && HostAdapter->ActiveCommands[TargetID] == 0 && TargetFlags->TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) { + TargetFlags->TaggedQueuingActive = true; + BusLogic_Notice("Tagged Queuing now active for Target %d\n", HostAdapter, TargetID); + } + if (TargetFlags->TaggedQueuingActive) { + enum BusLogic_QueueTag QueueTag = BusLogic_SimpleQueueTag; + /* + When using Tagged Queuing with Simple Queue Tags, it appears that disk + drive controllers do not guarantee that a queued command will not + remain in a disconnected state indefinitely if commands that read or + write nearer the head position continue to arrive without interruption. + Therefore, for each Target Device this driver keeps track of the last + time either the queue was empty or an Ordered Queue Tag was issued. If + more than 4 seconds (one fifth of the 20 second disk timeout) have + elapsed since this last sequence point, this command will be issued + with an Ordered Queue Tag rather than a Simple Queue Tag, which forces + the Target Device to complete all previously queued commands before + this command may be executed. + */ + if (HostAdapter->ActiveCommands[TargetID] == 0) + HostAdapter->LastSequencePoint[TargetID] = jiffies; + else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 4 * HZ) { + HostAdapter->LastSequencePoint[TargetID] = jiffies; + QueueTag = BusLogic_OrderedQueueTag; + } + if (HostAdapter->ExtendedLUNSupport) { + CCB->TagEnable = true; + CCB->QueueTag = QueueTag; + } else { + CCB->LegacyTagEnable = true; + CCB->LegacyQueueTag = QueueTag; + } + } + memcpy(CCB->CDB, CDB, CDB_Length); + CCB->SenseDataLength = sizeof(Command->sense_buffer); + CCB->SenseDataPointer = pci_map_single(HostAdapter->PCI_Device, Command->sense_buffer, CCB->SenseDataLength, PCI_DMA_FROMDEVICE); + CCB->Command = Command; + Command->scsi_done = CompletionRoutine; + if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) { + /* + Place the CCB in an Outgoing Mailbox. The higher levels of the SCSI + Subsystem should not attempt to queue more commands than can be placed + in Outgoing Mailboxes, so there should always be one free. In the + unlikely event that there are none available, wait 1 second and try + again. If that fails, the Host Adapter is probably hung so signal an + error as a Host Adapter Hard Reset should be initiated soon. + */ + if (!BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxStartCommand, CCB)) { + spin_unlock_irq(HostAdapter->SCSI_Host->host_lock); + BusLogic_Warning("Unable to write Outgoing Mailbox - " "Pausing for 1 second\n", HostAdapter); + BusLogic_Delay(1); + spin_lock_irq(HostAdapter->SCSI_Host->host_lock); + if (!BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxStartCommand, CCB)) { + BusLogic_Warning("Still unable to write Outgoing Mailbox - " "Host Adapter Dead?\n", HostAdapter); + BusLogic_DeallocateCCB(CCB); + Command->result = DID_ERROR << 16; + Command->scsi_done(Command); + } + } + } else { + /* + Call the FlashPoint SCCB Manager to start execution of the CCB. + */ + CCB->Status = BusLogic_CCB_Active; + HostAdapter->ActiveCommands[TargetID]++; + TargetStatistics[TargetID].CommandsAttempted++; + FlashPoint_StartCCB(HostAdapter->CardHandle, CCB); + /* + The Command may have already completed and BusLogic_QueueCompletedCCB + been called, or it may still be pending. + */ + if (CCB->Status == BusLogic_CCB_Completed) + BusLogic_ProcessCompletedCCBs(HostAdapter); + } + return 0; +} + + +/* + BusLogic_AbortCommand aborts Command if possible. +*/ + +static int BusLogic_AbortCommand(struct scsi_cmnd *Command) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) Command->device->host->hostdata; + + int TargetID = Command->device->id; + struct BusLogic_CCB *CCB; + BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsRequested); + /* + If this Command has already completed, then no Abort is necessary. + */ + if (Command->serial_number != Command->serial_number_at_timeout) { + BusLogic_Warning("Unable to Abort Command to Target %d - " "Already Completed\n", HostAdapter, TargetID); + return SUCCESS; + } + /* + Attempt to find an Active CCB for this Command. If no Active CCB for this + Command is found, then no Abort is necessary. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Command == Command) + break; + if (CCB == NULL) { + BusLogic_Warning("Unable to Abort Command to Target %d - " "No CCB Found\n", HostAdapter, TargetID); + return SUCCESS; + } else if (CCB->Status == BusLogic_CCB_Completed) { + BusLogic_Warning("Unable to Abort Command to Target %d - " "CCB Completed\n", HostAdapter, TargetID); + return SUCCESS; + } else if (CCB->Status == BusLogic_CCB_Reset) { + BusLogic_Warning("Unable to Abort Command to Target %d - " "CCB Reset\n", HostAdapter, TargetID); + return SUCCESS; + } + if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) { + /* + Attempt to Abort this CCB. MultiMaster Firmware versions prior to 5.xx + do not generate Abort Tag messages, but only generate the non-tagged + Abort message. Since non-tagged commands are not sent by the Host + Adapter until the queue of outstanding tagged commands has completed, + and the Abort message is treated as a non-tagged command, it is + effectively impossible to abort commands when Tagged Queuing is active. + Firmware version 5.xx does generate Abort Tag messages, so it is + possible to abort commands when Tagged Queuing is active. + */ + if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive && HostAdapter->FirmwareVersion[0] < '5') { + BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " "Abort Tag Not Supported\n", HostAdapter, CCB->SerialNumber, TargetID); + return FAILURE; + } else if (BusLogic_WriteOutgoingMailbox(HostAdapter, BusLogic_MailboxAbortCommand, CCB)) { + BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID); + BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted); + return SUCCESS; + } else { + BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " "No Outgoing Mailboxes\n", HostAdapter, CCB->SerialNumber, TargetID); + return FAILURE; + } + } else { + /* + Call the FlashPoint SCCB Manager to abort execution of the CCB. + */ + BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID); + BusLogic_IncrementErrorCounter(&HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted); + FlashPoint_AbortCCB(HostAdapter->CardHandle, CCB); + /* + The Abort may have already been completed and + BusLogic_QueueCompletedCCB been called, or it + may still be pending. + */ + if (CCB->Status == BusLogic_CCB_Completed) { + BusLogic_ProcessCompletedCCBs(HostAdapter); + } + return SUCCESS; + } + return SUCCESS; +} + +/* + BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all + currently executing SCSI Commands as having been Reset. +*/ + +static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *HostAdapter, boolean HardReset) +{ + struct BusLogic_CCB *CCB; + int TargetID; + + /* + * Attempt to Reset and Reinitialize the Host Adapter. + */ + + if (!(BusLogic_HardwareResetHostAdapter(HostAdapter, HardReset) && BusLogic_InitializeHostAdapter(HostAdapter))) { + BusLogic_Error("Resetting %s Failed\n", HostAdapter, HostAdapter->FullModelName); + return FAILURE; + } + + /* + * Deallocate all currently executing CCBs. + */ + + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Active) + BusLogic_DeallocateCCB(CCB); + /* + * Wait a few seconds between the Host Adapter Hard Reset which + * initiates a SCSI Bus Reset and issuing any SCSI Commands. Some + * SCSI devices get confused if they receive SCSI Commands too soon + * after a SCSI Bus Reset. + */ + + if (HardReset) { + spin_unlock_irq(HostAdapter->SCSI_Host->host_lock); + BusLogic_Delay(HostAdapter->BusSettleTime); + spin_lock_irq(HostAdapter->SCSI_Host->host_lock); + } + + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + HostAdapter->LastResetAttempted[TargetID] = jiffies; + HostAdapter->LastResetCompleted[TargetID] = jiffies; + } + return SUCCESS; +} + +/* + BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk + Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and + the appropriate number of cylinders so as not to exceed drive capacity. In + order for disks equal to or larger than 1 GB to be addressable by the BIOS + without exceeding the BIOS limitation of 1024 cylinders, Extended Translation + may be enabled in AutoSCSI on FlashPoint Host Adapters and on "W" and "C" + series MultiMaster Host Adapters, or by a dip switch setting on "S" and "A" + series MultiMaster Host Adapters. With Extended Translation enabled, drives + between 1 GB inclusive and 2 GB exclusive are given a disk geometry of 128 + heads and 32 sectors, and drives above 2 GB inclusive are given a disk + geometry of 255 heads and 63 sectors. However, if the BIOS detects that the + Extended Translation setting does not match the geometry in the partition + table, then the translation inferred from the partition table will be used by + the BIOS, and a warning may be displayed. +*/ + +static int BusLogic_BIOSDiskParameters(struct scsi_device *sdev, struct block_device *Device, sector_t capacity, int *Parameters) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) sdev->host->hostdata; + struct BIOS_DiskParameters *DiskParameters = (struct BIOS_DiskParameters *) Parameters; + unsigned char *buf; + if (HostAdapter->ExtendedTranslationEnabled && capacity >= 2 * 1024 * 1024 /* 1 GB in 512 byte sectors */ ) { + if (capacity >= 4 * 1024 * 1024 /* 2 GB in 512 byte sectors */ ) { + DiskParameters->Heads = 255; + DiskParameters->Sectors = 63; + } else { + DiskParameters->Heads = 128; + DiskParameters->Sectors = 32; + } + } else { + DiskParameters->Heads = 64; + DiskParameters->Sectors = 32; + } + DiskParameters->Cylinders = (unsigned long) capacity / (DiskParameters->Heads * DiskParameters->Sectors); + buf = scsi_bios_ptable(Device); + if (buf == NULL) + return 0; + /* + If the boot sector partition table flag is valid, search for a partition + table entry whose end_head matches one of the standard BusLogic geometry + translations (64/32, 128/32, or 255/63). + */ + if (*(unsigned short *) (buf + 64) == 0xAA55) { + struct partition *FirstPartitionEntry = (struct partition *) buf; + struct partition *PartitionEntry = FirstPartitionEntry; + int SavedCylinders = DiskParameters->Cylinders, PartitionNumber; + unsigned char PartitionEntryEndHead = 0, PartitionEntryEndSector = 0; + for (PartitionNumber = 0; PartitionNumber < 4; PartitionNumber++) { + PartitionEntryEndHead = PartitionEntry->end_head; + PartitionEntryEndSector = PartitionEntry->end_sector & 0x3F; + if (PartitionEntryEndHead == 64 - 1) { + DiskParameters->Heads = 64; + DiskParameters->Sectors = 32; + break; + } else if (PartitionEntryEndHead == 128 - 1) { + DiskParameters->Heads = 128; + DiskParameters->Sectors = 32; + break; + } else if (PartitionEntryEndHead == 255 - 1) { + DiskParameters->Heads = 255; + DiskParameters->Sectors = 63; + break; + } + PartitionEntry++; + } + if (PartitionNumber == 4) { + PartitionEntryEndHead = FirstPartitionEntry->end_head; + PartitionEntryEndSector = FirstPartitionEntry->end_sector & 0x3F; + } + DiskParameters->Cylinders = (unsigned long) capacity / (DiskParameters->Heads * DiskParameters->Sectors); + if (PartitionNumber < 4 && PartitionEntryEndSector == DiskParameters->Sectors) { + if (DiskParameters->Cylinders != SavedCylinders) + BusLogic_Warning("Adopting Geometry %d/%d from Partition Table\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors); + } else if (PartitionEntryEndHead > 0 || PartitionEntryEndSector > 0) { + BusLogic_Warning("Warning: Partition Table appears to " "have Geometry %d/%d which is\n", HostAdapter, PartitionEntryEndHead + 1, PartitionEntryEndSector); + BusLogic_Warning("not compatible with current BusLogic " "Host Adapter Geometry %d/%d\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors); + } + } + kfree(buf); + return 0; +} + + +/* + BugLogic_ProcDirectoryInfo implements /proc/scsi/BusLogic/<N>. +*/ + +static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *shost, char *ProcBuffer, char **StartPointer, off_t Offset, int BytesAvailable, int WriteFlag) +{ + struct BusLogic_HostAdapter *HostAdapter = (struct BusLogic_HostAdapter *) shost->hostdata; + struct BusLogic_TargetStatistics *TargetStatistics; + int TargetID, Length; + char *Buffer; + + TargetStatistics = HostAdapter->TargetStatistics; + if (WriteFlag) { + HostAdapter->ExternalHostAdapterResets = 0; + HostAdapter->HostAdapterInternalErrors = 0; + memset(TargetStatistics, 0, BusLogic_MaxTargetDevices * sizeof(struct BusLogic_TargetStatistics)); + return 0; + } + Buffer = HostAdapter->MessageBuffer; + Length = HostAdapter->MessageBufferLength; + Length += sprintf(&Buffer[Length], "\n\ +Current Driver Queue Depth: %d\n\ +Currently Allocated CCBs: %d\n", HostAdapter->DriverQueueDepth, HostAdapter->AllocatedCCBs); + Length += sprintf(&Buffer[Length], "\n\n\ + DATA TRANSFER STATISTICS\n\ +\n\ +Target Tagged Queuing Queue Depth Active Attempted Completed\n\ +====== ============== =========== ====== ========= =========\n"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) + continue; + Length += sprintf(&Buffer[Length], " %2d %s", TargetID, (TargetFlags->TaggedQueuingSupported ? (TargetFlags->TaggedQueuingActive ? " Active" : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID) + ? " Permitted" : " Disabled")) + : "Not Supported")); + Length += sprintf(&Buffer[Length], + " %3d %3u %9u %9u\n", HostAdapter->QueueDepth[TargetID], HostAdapter->ActiveCommands[TargetID], TargetStatistics[TargetID].CommandsAttempted, TargetStatistics[TargetID].CommandsCompleted); + } + Length += sprintf(&Buffer[Length], "\n\ +Target Read Commands Write Commands Total Bytes Read Total Bytes Written\n\ +====== ============= ============== =================== ===================\n"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) + continue; + Length += sprintf(&Buffer[Length], " %2d %9u %9u", TargetID, TargetStatistics[TargetID].ReadCommands, TargetStatistics[TargetID].WriteCommands); + if (TargetStatistics[TargetID].TotalBytesRead.Billions > 0) + Length += sprintf(&Buffer[Length], " %9u%09u", TargetStatistics[TargetID].TotalBytesRead.Billions, TargetStatistics[TargetID].TotalBytesRead.Units); + else + Length += sprintf(&Buffer[Length], " %9u", TargetStatistics[TargetID].TotalBytesRead.Units); + if (TargetStatistics[TargetID].TotalBytesWritten.Billions > 0) + Length += sprintf(&Buffer[Length], " %9u%09u\n", TargetStatistics[TargetID].TotalBytesWritten.Billions, TargetStatistics[TargetID].TotalBytesWritten.Units); + else + Length += sprintf(&Buffer[Length], " %9u\n", TargetStatistics[TargetID].TotalBytesWritten.Units); + } + Length += sprintf(&Buffer[Length], "\n\ +Target Command 0-1KB 1-2KB 2-4KB 4-8KB 8-16KB\n\ +====== ======= ========= ========= ========= ========= =========\n"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) + continue; + Length += + sprintf(&Buffer[Length], + " %2d Read %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].ReadCommandSizeBuckets[0], + TargetStatistics[TargetID].ReadCommandSizeBuckets[1], TargetStatistics[TargetID].ReadCommandSizeBuckets[2], TargetStatistics[TargetID].ReadCommandSizeBuckets[3], TargetStatistics[TargetID].ReadCommandSizeBuckets[4]); + Length += + sprintf(&Buffer[Length], + " %2d Write %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].WriteCommandSizeBuckets[0], + TargetStatistics[TargetID].WriteCommandSizeBuckets[1], TargetStatistics[TargetID].WriteCommandSizeBuckets[2], TargetStatistics[TargetID].WriteCommandSizeBuckets[3], TargetStatistics[TargetID].WriteCommandSizeBuckets[4]); + } + Length += sprintf(&Buffer[Length], "\n\ +Target Command 16-32KB 32-64KB 64-128KB 128-256KB 256KB+\n\ +====== ======= ========= ========= ========= ========= =========\n"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) + continue; + Length += + sprintf(&Buffer[Length], + " %2d Read %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].ReadCommandSizeBuckets[5], + TargetStatistics[TargetID].ReadCommandSizeBuckets[6], TargetStatistics[TargetID].ReadCommandSizeBuckets[7], TargetStatistics[TargetID].ReadCommandSizeBuckets[8], TargetStatistics[TargetID].ReadCommandSizeBuckets[9]); + Length += + sprintf(&Buffer[Length], + " %2d Write %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].WriteCommandSizeBuckets[5], + TargetStatistics[TargetID].WriteCommandSizeBuckets[6], TargetStatistics[TargetID].WriteCommandSizeBuckets[7], TargetStatistics[TargetID].WriteCommandSizeBuckets[8], TargetStatistics[TargetID].WriteCommandSizeBuckets[9]); + } + Length += sprintf(&Buffer[Length], "\n\n\ + ERROR RECOVERY STATISTICS\n\ +\n\ + Command Aborts Bus Device Resets Host Adapter Resets\n\ +Target Requested Completed Requested Completed Requested Completed\n\ + ID \\\\\\\\ Attempted //// \\\\\\\\ Attempted //// \\\\\\\\ Attempted ////\n\ +====== ===== ===== ===== ===== ===== ===== ===== ===== =====\n"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) { + struct BusLogic_TargetFlags *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) + continue; + Length += sprintf(&Buffer[Length], "\ + %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", TargetID, TargetStatistics[TargetID].CommandAbortsRequested, TargetStatistics[TargetID].CommandAbortsAttempted, TargetStatistics[TargetID].CommandAbortsCompleted, TargetStatistics[TargetID].BusDeviceResetsRequested, TargetStatistics[TargetID].BusDeviceResetsAttempted, TargetStatistics[TargetID].BusDeviceResetsCompleted, TargetStatistics[TargetID].HostAdapterResetsRequested, TargetStatistics[TargetID].HostAdapterResetsAttempted, TargetStatistics[TargetID].HostAdapterResetsCompleted); + } + Length += sprintf(&Buffer[Length], "\nExternal Host Adapter Resets: %d\n", HostAdapter->ExternalHostAdapterResets); + Length += sprintf(&Buffer[Length], "Host Adapter Internal Errors: %d\n", HostAdapter->HostAdapterInternalErrors); + if (Length >= BusLogic_MessageBufferSize) + BusLogic_Error("Message Buffer length %d exceeds size %d\n", HostAdapter, Length, BusLogic_MessageBufferSize); + if ((Length -= Offset) <= 0) + return 0; + if (Length >= BytesAvailable) + Length = BytesAvailable; + memcpy(ProcBuffer, HostAdapter->MessageBuffer + Offset, Length); + *StartPointer = ProcBuffer; + return Length; +} + + +/* + BusLogic_Message prints Driver Messages. +*/ + +static void BusLogic_Message(enum BusLogic_MessageLevel MessageLevel, char *Format, struct BusLogic_HostAdapter *HostAdapter, ...) +{ + static char Buffer[BusLogic_LineBufferSize]; + static boolean BeginningOfLine = true; + va_list Arguments; + int Length = 0; + va_start(Arguments, HostAdapter); + Length = vsprintf(Buffer, Format, Arguments); + va_end(Arguments); + if (MessageLevel == BusLogic_AnnounceLevel) { + static int AnnouncementLines = 0; + strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength], Buffer); + HostAdapter->MessageBufferLength += Length; + if (++AnnouncementLines <= 2) + printk("%sscsi: %s", BusLogic_MessageLevelMap[MessageLevel], Buffer); + } else if (MessageLevel == BusLogic_InfoLevel) { + strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength], Buffer); + HostAdapter->MessageBufferLength += Length; + if (BeginningOfLine) { + if (Buffer[0] != '\n' || Length > 1) + printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], HostAdapter->HostNumber, Buffer); + } else + printk("%s", Buffer); + } else { + if (BeginningOfLine) { + if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized) + printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], HostAdapter->HostNumber, Buffer); + else + printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer); + } else + printk("%s", Buffer); + } + BeginningOfLine = (Buffer[Length - 1] == '\n'); +} + + +/* + BusLogic_ParseKeyword parses an individual option keyword. It returns true + and updates the pointer if the keyword is recognized and false otherwise. +*/ + +static boolean __init BusLogic_ParseKeyword(char **StringPointer, char *Keyword) +{ + char *Pointer = *StringPointer; + while (*Keyword != '\0') { + char StringChar = *Pointer++; + char KeywordChar = *Keyword++; + if (StringChar >= 'A' && StringChar <= 'Z') + StringChar += 'a' - 'Z'; + if (KeywordChar >= 'A' && KeywordChar <= 'Z') + KeywordChar += 'a' - 'Z'; + if (StringChar != KeywordChar) + return false; + } + *StringPointer = Pointer; + return true; +} + + +/* + BusLogic_ParseDriverOptions handles processing of BusLogic Driver Options + specifications. + + BusLogic Driver Options may be specified either via the Linux Kernel Command + Line or via the Loadable Kernel Module Installation Facility. Driver Options + for multiple host adapters may be specified either by separating the option + strings by a semicolon, or by specifying multiple "BusLogic=" strings on the + command line. Individual option specifications for a single host adapter are + separated by commas. The Probing and Debugging Options apply to all host + adapters whereas the remaining options apply individually only to the + selected host adapter. + + The BusLogic Driver Probing Options are described in + <file:Documentation/scsi/BusLogic.txt>. +*/ + +static int __init BusLogic_ParseDriverOptions(char *OptionsString) +{ + while (true) { + struct BusLogic_DriverOptions *DriverOptions = &BusLogic_DriverOptions[BusLogic_DriverOptionsCount++]; + int TargetID; + memset(DriverOptions, 0, sizeof(struct BusLogic_DriverOptions)); + while (*OptionsString != '\0' && *OptionsString != ';') { + /* Probing Options. */ + if (BusLogic_ParseKeyword(&OptionsString, "IO:")) { + unsigned long IO_Address = simple_strtoul(OptionsString, &OptionsString, 0); + BusLogic_ProbeOptions.LimitedProbeISA = true; + switch (IO_Address) { + case 0x330: + BusLogic_ProbeOptions.Probe330 = true; + break; + case 0x334: + BusLogic_ProbeOptions.Probe334 = true; + break; + case 0x230: + BusLogic_ProbeOptions.Probe230 = true; + break; + case 0x234: + BusLogic_ProbeOptions.Probe234 = true; + break; + case 0x130: + BusLogic_ProbeOptions.Probe130 = true; + break; + case 0x134: + BusLogic_ProbeOptions.Probe134 = true; + break; + default: + BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid I/O Address 0x%X)\n", NULL, IO_Address); + return 0; + } + } else if (BusLogic_ParseKeyword(&OptionsString, "NoProbeISA")) + BusLogic_ProbeOptions.NoProbeISA = true; + else if (BusLogic_ParseKeyword(&OptionsString, "NoProbePCI")) + BusLogic_ProbeOptions.NoProbePCI = true; + else if (BusLogic_ParseKeyword(&OptionsString, "NoProbe")) + BusLogic_ProbeOptions.NoProbe = true; + else if (BusLogic_ParseKeyword(&OptionsString, "NoSortPCI")) + BusLogic_ProbeOptions.NoSortPCI = true; + else if (BusLogic_ParseKeyword(&OptionsString, "MultiMasterFirst")) + BusLogic_ProbeOptions.MultiMasterFirst = true; + else if (BusLogic_ParseKeyword(&OptionsString, "FlashPointFirst")) + BusLogic_ProbeOptions.FlashPointFirst = true; + /* Tagged Queuing Options. */ + else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:[") || BusLogic_ParseKeyword(&OptionsString, "QD:[")) { + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) { + unsigned short QueueDepth = simple_strtoul(OptionsString, &OptionsString, 0); + if (QueueDepth > BusLogic_MaxTaggedQueueDepth) { + BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Queue Depth %d)\n", NULL, QueueDepth); + return 0; + } + DriverOptions->QueueDepth[TargetID] = QueueDepth; + if (*OptionsString == ',') + OptionsString++; + else if (*OptionsString == ']') + break; + else { + BusLogic_Error("BusLogic: Invalid Driver Options " "(',' or ']' expected at '%s')\n", NULL, OptionsString); + return 0; + } + } + if (*OptionsString != ']') { + BusLogic_Error("BusLogic: Invalid Driver Options " "(']' expected at '%s')\n", NULL, OptionsString); + return 0; + } else + OptionsString++; + } else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:") || BusLogic_ParseKeyword(&OptionsString, "QD:")) { + unsigned short QueueDepth = simple_strtoul(OptionsString, &OptionsString, 0); + if (QueueDepth == 0 || QueueDepth > BusLogic_MaxTaggedQueueDepth) { + BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Queue Depth %d)\n", NULL, QueueDepth); + return 0; + } + DriverOptions->CommonQueueDepth = QueueDepth; + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + DriverOptions->QueueDepth[TargetID] = QueueDepth; + } else if (BusLogic_ParseKeyword(&OptionsString, "TaggedQueuing:") || BusLogic_ParseKeyword(&OptionsString, "TQ:")) { + if (BusLogic_ParseKeyword(&OptionsString, "Default")) { + DriverOptions->TaggedQueuingPermitted = 0x0000; + DriverOptions->TaggedQueuingPermittedMask = 0x0000; + } else if (BusLogic_ParseKeyword(&OptionsString, "Enable")) { + DriverOptions->TaggedQueuingPermitted = 0xFFFF; + DriverOptions->TaggedQueuingPermittedMask = 0xFFFF; + } else if (BusLogic_ParseKeyword(&OptionsString, "Disable")) { + DriverOptions->TaggedQueuingPermitted = 0x0000; + DriverOptions->TaggedQueuingPermittedMask = 0xFFFF; + } else { + unsigned short TargetBit; + for (TargetID = 0, TargetBit = 1; TargetID < BusLogic_MaxTargetDevices; TargetID++, TargetBit <<= 1) + switch (*OptionsString++) { + case 'Y': + DriverOptions->TaggedQueuingPermitted |= TargetBit; + DriverOptions->TaggedQueuingPermittedMask |= TargetBit; + break; + case 'N': + DriverOptions->TaggedQueuingPermitted &= ~TargetBit; + DriverOptions->TaggedQueuingPermittedMask |= TargetBit; + break; + case 'X': + break; + default: + OptionsString--; + TargetID = BusLogic_MaxTargetDevices; + break; + } + } + } + /* Miscellaneous Options. */ + else if (BusLogic_ParseKeyword(&OptionsString, "BusSettleTime:") || BusLogic_ParseKeyword(&OptionsString, "BST:")) { + unsigned short BusSettleTime = simple_strtoul(OptionsString, &OptionsString, 0); + if (BusSettleTime > 5 * 60) { + BusLogic_Error("BusLogic: Invalid Driver Options " "(invalid Bus Settle Time %d)\n", NULL, BusSettleTime); + return 0; + } + DriverOptions->BusSettleTime = BusSettleTime; + } else if (BusLogic_ParseKeyword(&OptionsString, "InhibitTargetInquiry")) + DriverOptions->LocalOptions.InhibitTargetInquiry = true; + /* Debugging Options. */ + else if (BusLogic_ParseKeyword(&OptionsString, "TraceProbe")) + BusLogic_GlobalOptions.TraceProbe = true; + else if (BusLogic_ParseKeyword(&OptionsString, "TraceHardwareReset")) + BusLogic_GlobalOptions.TraceHardwareReset = true; + else if (BusLogic_ParseKeyword(&OptionsString, "TraceConfiguration")) + BusLogic_GlobalOptions.TraceConfiguration = true; + else if (BusLogic_ParseKeyword(&OptionsString, "TraceErrors")) + BusLogic_GlobalOptions.TraceErrors = true; + else if (BusLogic_ParseKeyword(&OptionsString, "Debug")) { + BusLogic_GlobalOptions.TraceProbe = true; + BusLogic_GlobalOptions.TraceHardwareReset = true; + BusLogic_GlobalOptions.TraceConfiguration = true; + BusLogic_GlobalOptions.TraceErrors = true; + } + if (*OptionsString == ',') + OptionsString++; + else if (*OptionsString != ';' && *OptionsString != '\0') { + BusLogic_Error("BusLogic: Unexpected Driver Option '%s' " "ignored\n", NULL, OptionsString); + *OptionsString = '\0'; + } + } + if (!(BusLogic_DriverOptionsCount == 0 || BusLogic_ProbeInfoCount == 0 || BusLogic_DriverOptionsCount == BusLogic_ProbeInfoCount)) { + BusLogic_Error("BusLogic: Invalid Driver Options " "(all or no I/O Addresses must be specified)\n", NULL); + return 0; + } + /* + Tagged Queuing is disabled when the Queue Depth is 1 since queuing + multiple commands is not possible. + */ + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + if (DriverOptions->QueueDepth[TargetID] == 1) { + unsigned short TargetBit = 1 << TargetID; + DriverOptions->TaggedQueuingPermitted &= ~TargetBit; + DriverOptions->TaggedQueuingPermittedMask |= TargetBit; + } + if (*OptionsString == ';') + OptionsString++; + if (*OptionsString == '\0') + return 0; + } + return 1; +} + +/* + Get it all started +*/ + +static struct scsi_host_template Bus_Logic_template = { + .module = THIS_MODULE, + .proc_name = "BusLogic", + .proc_info = BusLogic_ProcDirectoryInfo, + .name = "BusLogic", + .info = BusLogic_DriverInfo, + .queuecommand = BusLogic_QueueCommand, + .slave_configure = BusLogic_SlaveConfigure, + .bios_param = BusLogic_BIOSDiskParameters, + .eh_host_reset_handler = BusLogic_host_reset, +#if 0 + .eh_abort_handler = BusLogic_AbortCommand, +#endif + .unchecked_isa_dma = 1, + .max_sectors = 128, + .use_clustering = ENABLE_CLUSTERING, +}; + +/* + BusLogic_Setup handles processing of Kernel Command Line Arguments. +*/ + +static int __init BusLogic_Setup(char *str) +{ + int ints[3]; + + (void) get_options(str, ARRAY_SIZE(ints), ints); + + if (ints[0] != 0) { + BusLogic_Error("BusLogic: Obsolete Command Line Entry " "Format Ignored\n", NULL); + return 0; + } + if (str == NULL || *str == '\0') + return 0; + return BusLogic_ParseDriverOptions(str); +} + +/* + * Exit function. Deletes all hosts associated with this driver. + */ + +static void __exit BusLogic_exit(void) +{ + struct BusLogic_HostAdapter *ha, *next; + + list_for_each_entry_safe(ha, next, &BusLogic_host_list, host_list) + BusLogic_ReleaseHostAdapter(ha); +} + +__setup("BusLogic=", BusLogic_Setup); + +module_init(BusLogic_init); +module_exit(BusLogic_exit); |