NT4/private/ntos/miniport/qlogic/qlisp.c
2020-09-30 17:12:29 +02:00

3051 lines
120 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/************************************************************************/
/* */
/* Driver Name: QL10WNT.SYS - NT Miniport Driver for QLogic ISP1020 */
/* */
/* Source File Name: QLISP.C */
/* */
/* Function: Main driver module containing code for processing I/O */
/* requests from NT and all code for interfacing to the */
/* ISP1020 chip. */
/* */
/************************************************************************/
/* */
/* NOTICE */
/* */
/* COPYRIGHT 1994-1995 QLOGIC CORPORATION */
/* ALL RIGHTS RESERVED */
/* */
/* This computer program is CONFIDENTIAL and a TRADE SECRET */
/* of QLOGIC CORPORATION. The receipt or possesion of this */
/* program does not convey any rights to reproduce or disclose */
/* its contents, or to manufacture, use, or sell anything that */
/* it may describe, in whole or in part, without the specific */
/* written consent of QLOGIC CORPORATION. Any reproduction of */
/* this program without the express written consent of QLOGIC */
/* CORPORATION is a violation of the copyright laws and may */
/* subject you to criminal prosecution. */
/* */
/************************************************************************/
/* */
/* Revision history: */
/* */
/* 1.00 2/23/94 TWT Initial version for NT Version 3.1 */
/* */
/* 1.01 5/2/94 TWT Fix interrupt sharing problem (set */
/* InterruptMode to LevelSensitive) */
/* */
/* 1.02 5/6/94 TWT Force PCI bus master enabled */
/* */
/* 1.03 5/23/94 TWT Add conditional assembly for supporting */
/* new features of NT Version 3.5, set bus */
/* type to PCI and let SCSI port driver */
/* get PCI configuration, support SCSI */
/* IDs up to 15 (NT 3.5 driver only) */
/* */
/* 1.10 6/3/94 TWT Modify QLFindAdapter routine for NT 3.5 */
/* to use NT service calls to access PCI */
/* config space (this will allow driver to */
/* operate on all PCI systems supported by */
/* NT), modify QLFindAdapter routine for */
/* NT 3.1 to support both methods of */
/* accessing PCI config space */
/* */
/* 1.11 6/8/94 TWT Fix problem with accessing config space */
/* on Opti chip set (can only read data */
/* reg one time per write to address reg) */
/* */
/* 1.12 7/26/94 TWT Update RISC code to version 1.14 */
/* */
/* 1.13 8/17/94 TWT Set only 1 RISC retry and no delay */
/* (NT 3.5 Disk Administrator problem) */
/* */
/* 2.00 8/11/94 TWT Modifications for the Chicago OS, add */
/* QLAdapterState entry, remove path from */
/* includes, modify NovramDelay to call */
/* ScsiPortStallExecution, update NOVRAM */
/* drive default parameters */
/* */
/* 2.01 8/25/94 TWT Replaced QLFindAdapter with code from */
/* CHC, added support for SRB flag to */
/* disable auto request sense (requires */
/* RISC code version 1.15), add code to */
/* QLAdapterState for returning control to */
/* ROM BIOS on shutdown, check for passed */
/* in InitiatorBusId, update RISC code to */
/* version 1.15, add temporary conditional */
/* assembly code for Chicago beta versions */
/* not supporting ConfigInfo->SlotNumber */
/* */
/* 2.02 11/17/94 TWT Don't issue error log message for */
/* selection timeout on Inquiry command, */
/* restart RISC command queue after check */
/* condition and auto-sense disabled, add */
/* support for soft termination, add */
/* support for 60 MHz adapters, fix bug */
/* leaving tagged queueing disabled if */
/* NVRAM not present or not programmed */
/* */
/* 2.03 11/28/94 TWT Update RISC code to version 1.16 */
/* */
/* 2.04 11/30/94 TWT Remove temporary code for pre-beta 2 */
/* version of Chicago (SlotNumber is now */
/* supported) */
/* */
/* 2.05 12/19/94 TWT Added reset delays to QLResetIsp to fix */
/* problem on faster Alpha machines */
/* */
/* 2.06 1/4/95 TWT Set MaximumNumberOfTargets to 15 instead*/
/* of 16 for Chicago Beta 2 (if passed in */
/* value is 7) */
/* */
/* 2.07 1/6/95 TWT Add support for QLVER utility and */
/* copyright string */
/* */
/* 2.08 2/7/95 TWT Update build and install scripts for */
/* driver name change to match MS release, */
/* modify init code to skip RISC code load */
/* if newer version already loaded, update */
/* RISC code to version 1.18, add driver */
/* delay to match RISC SCSI reset delay, */
/* cleanup unneeded test in MailboxCommand */
/* */
/* 2.09 3/28/95 TWT Update RISC code to version 1.22 */
/* */
/* 2.10 4/14/95 TWT Update RISC code to version 1.25 */
/* */
/* 2.11 4/27/95 TWT Update RISC code to version 1.27, add */
/* software termination modification for */
/* DEC boards with ISP1020A */
/* */
/* 2.12 5/5/95 TWT Fix problem with system hang when RISC */
/* request queue full and SRB returned to */
/* port driver with FALSE status (thanks */
/* to Chao Chen for finding this problem) */
/* */
/* 2.13 5/9/95 TWT Renamed subroutine WaitQueueSpace to */
/* CheckQueueSpace and modified to not wait*/
/* for space in the RISC request queue, */
/* modified QLStartIo to throttle back cmds*/
/* from port driver when request queue is */
/* nearly full, add delays to QLResetIsp */
/* */
/************************************************************************/
// #define NT_VERSION_31 1 // If defined, compile for NT Version 3.1
#include "miniport.h"
#include "scsi.h"
#include "qlisp.h"
#if DBG
#define QLPrint(arg) ScsiDebugPrint arg
#else
#define QLPrint(arg)
#endif
// Name and version strings for QLVER utility
CHAR CompanyName[] = "$$QLNAME$$" \
"QLogic Corporation \0";
CHAR Version[] = "$$QLVER$$ 2.13\0";
// Copyright string
CHAR QLogicCR[] = "Copyright (C) QLogic Corporation 1994-1995. All rights reserved.";
/* External RISC code module definitions */
extern USHORT risc_code_version;
extern USHORT risc_code_addr01;
extern USHORT risc_code01[];
extern USHORT risc_code_length01;
/************************************************************************/
/* Driver SRB extension */
/************************************************************************/
typedef struct _SRB_EXTENSION
{
ULONG SrbExtensionFlags;
} SRB_EXTENSION, *PSRB_EXTENSION;
#define SRB_EXT(x) ((PSRB_EXTENSION)(x->SrbExtension))
/* SrbExtensionFlags bit definitions */
/************************************************************************/
/* Driver logical unit extension */
/************************************************************************/
typedef struct _HW_LUN_EXTENSION
{
ULONG LunFlags;
} HW_LUN_EXTENSION, *PHW_LUN_EXTENSION;
/* LunFlags bit definitions */
#define LFLG_INIT_COMPLETE 0x00000001 // completed initialization
/************************************************************************/
/* Driver noncached memory extension (for DMA interface to ISP1020) */
/************************************************************************/
typedef struct _NONCACHED_EXTENSION
{
QUEUE_ENTRY RequestQueue[REQUEST_QUEUE_DEPTH];
QUEUE_ENTRY ResponseQueue[RESPONSE_QUEUE_DEPTH];
} NONCACHED_EXTENSION, *PNONCACHED_EXTENSION;
/************************************************************************/
/* Driver device object extension */
/************************************************************************/
typedef struct _HW_DEVICE_EXTENSION
{
ULONG AdapterFlags;
PISP_REGS Adapter; // Address of the ISP
// Request and Response queues and pointers
PNONCACHED_EXTENSION NoncachedExtension;
ULONG ppRequestQueue;
ULONG ppResponseQueue;
PQUEUE_ENTRY pRequestQueue;
PQUEUE_ENTRY pResponseQueue;
USHORT request_in;
USHORT request_out;
USHORT response_out;
USHORT queue_space;
PQUEUE_ENTRY request_ptr;
PQUEUE_ENTRY response_ptr;
// Configuration NOVRAM paramters
UCHAR Config_Reg;
UCHAR Initiator_SCSI_Id :4;
UCHAR Host_Adapter_Enable :1;
UCHAR DisableLoadRiscCode :1;
UCHAR Bus_Reset_Delay;
UCHAR Retry_Count;
UCHAR Retry_Delay;
UCHAR ASync_Data_Setup_Time :4;
UCHAR REQ_ACK_Active_Negation :1;
UCHAR DATA_Active_Negation :1;
UCHAR Data_DMA_Burst_Enable :1;
UCHAR Cmd_DMA_Burst_Enable :1;
UCHAR Tag_Age_Limit;
UCHAR Termination_Low_Enable :1;
UCHAR Termination_High_Enable :1;
UCHAR PCMC_Burst_Enable :1;
UCHAR Sixty_MHz_Enable :1;
USHORT Selection_Timeout;
USHORT Max_Queue_Depth;
struct
{
UCHAR Capability;
UCHAR Execution_Throttle;
UCHAR Sync_Period;
UCHAR Sync_Offset :4;
UCHAR Device_Enable :1;
} Id[NUM_SCSI_IDS];
PSCSI_REQUEST_BLOCK WaitingSrb;
} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
/* AdapterFlags bit definitions */
#define AFLG_INIT_COMPLETE 0x00000001 // completed initialization
#define AFLG_TAGGED_QUEUING 0x00000002 // enable tagged queuing
#define AFLG_SEND_MARKER 0x00000004 // need to send marker to ISP
#define AFLG_SRB_WAITING 0x00000008 // SRB waiting for room in request Q
/* Target "Capability" bit definitions */
#define CAP_STOP_QUEUE_ON_CHECK 0x02
#define CAP_AUTO_REQ_SENSE 0x04
#define CAP_TAGGED_QUEUING 0x08
#define CAP_SYNC_TRANSFER 0x10
#define CAP_WIDE_TRANSFER 0x20
/* Read and write macros for ISP chip registers */
#define ISP_READ(ChipAddr,Register) ScsiPortReadPortUshort(&((ChipAddr)->Register))
#define ISP_WRITE(ChipAddr,Register,Value) ScsiPortWritePortUshort(&((ChipAddr)->Register),(Value))
/* Table to map ISP completion status to SRB error status */
UCHAR CmpStsErrorMap[] =
{
SRB_STATUS_SUCCESS, // 00h No error
SRB_STATUS_SELECTION_TIMEOUT, // 01h Incomplete transport (selection timeout)
SRB_STATUS_ERROR, // 02h DMA direction error
SRB_STATUS_ERROR, // 03h Transport error
SRB_STATUS_BUS_RESET, // 04h SCSI reset abort
SRB_STATUS_ABORTED, // 05h Aborted by system
SRB_STATUS_TIMEOUT, // 06h Timeout
SRB_STATUS_DATA_OVERRUN, // 07h Data overrun
SRB_STATUS_PHASE_SEQUENCE_FAILURE, // 08h Command overrun
SRB_STATUS_PHASE_SEQUENCE_FAILURE, // 09h Status overrun
SRB_STATUS_PHASE_SEQUENCE_FAILURE, // 0Ah Bad message
SRB_STATUS_PHASE_SEQUENCE_FAILURE, // 0Bh No message out
SRB_STATUS_MESSAGE_REJECTED, // 0Ch Extended ID failed
SRB_STATUS_MESSAGE_REJECTED, // 0Dh IDE message failed
SRB_STATUS_MESSAGE_REJECTED, // 0Eh Abort message failed
SRB_STATUS_MESSAGE_REJECTED, // 0Fh Reject message failed
SRB_STATUS_MESSAGE_REJECTED, // 10h NOP message failed
SRB_STATUS_MESSAGE_REJECTED, // 11h Parity error message failed
SRB_STATUS_MESSAGE_REJECTED, // 12h Device reset message failed
SRB_STATUS_MESSAGE_REJECTED, // 13h ID message failed
SRB_STATUS_UNEXPECTED_BUS_FREE, // 14h Unexpected bus free
SRB_STATUS_DATA_OVERRUN, // 15h Data underrun
SRB_STATUS_ERROR, // 16h
SRB_STATUS_ERROR, // 17h
SRB_STATUS_ERROR, // 18h Transaction error 1
SRB_STATUS_ERROR, // 19h Transaction error 2
SRB_STATUS_ERROR // 1Ah Transaction error 3
};
/* PCI Vendor ID and Device ID strings */
CHAR QLVendorId[4] = "1077"; // QLogic PCI Vendor ID
CHAR ISP1020DeviceId[4] = "1020"; // ISP1020 PCI Device ID
USHORT findMethod = 1; // Method for scanning PCI config space
/* Functions passed to the OS-specific port driver */
ULONG QLFindAdapter(IN PVOID ServiceContext, IN PVOID Context, IN PVOID BusInformation,
IN PCHAR ArgumentString, IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again);
BOOLEAN QLInitializeAdapter(IN PVOID ServiceContext);
BOOLEAN QLInterruptServiceRoutine(IN PVOID ServiceContext);
BOOLEAN QLResetScsiBus(IN PVOID ServiceContext, IN ULONG PathId);
BOOLEAN QLStartIo(IN PVOID ServiceContext,IN PSCSI_REQUEST_BLOCK pSrb);
BOOLEAN QLAdapterState(IN PVOID ServiceContext,IN PVOID Context,IN BOOLEAN SaveState);
/* Internal mini-port driver functions */
ULONG FindAdapter_M1(IN PVOID ServiceContext, IN OUT PULONG AdapterCount,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo);
ULONG FindAdapter_M2(IN PVOID ServiceContext, IN OUT PULONG AdapterCount,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo);
BOOLEAN QLResetIsp(IN PHW_DEVICE_EXTENSION pDevExt);
BOOLEAN LoadRiscCode(IN PHW_DEVICE_EXTENSION pDevExt);
BOOLEAN InitDeviceParameters(IN PHW_DEVICE_EXTENSION pDevExt,
IN PHW_LUN_EXTENSION pLunExt, IN PSCSI_REQUEST_BLOCK pSrb);
BOOLEAN MailboxCommand(IN PHW_DEVICE_EXTENSION pDevExt,
USHORT *mbox_sts, UCHAR out_cnt, UCHAR in_cnt,
USHORT reg0, USHORT reg1, USHORT reg2,
USHORT reg3, USHORT reg4, USHORT reg5);
VOID GetNovramParameters(IN PHW_DEVICE_EXTENSION pDevExt);
BOOLEAN ReadAllNovram(IN PHW_DEVICE_EXTENSION pDevExt, UCHAR* buf);
VOID ReadNovramWord(IN PHW_DEVICE_EXTENSION pDevExt,
USHORT addr, USHORT* ptr);
VOID NovramDelay();
BOOLEAN SendCommandToIsp(PHW_DEVICE_EXTENSION pDevExt, PSCSI_REQUEST_BLOCK pSrb);
VOID SetErrorStatus(PHW_DEVICE_EXTENSION pDevExt, PSCSI_REQUEST_BLOCK pSrb,
PSTATUS_ENTRY pStsEntry);
VOID QLLogError(IN PHW_DEVICE_EXTENSION pDevExt, IN PSCSI_REQUEST_BLOCK pSrb,
IN ULONG ErrorCode, IN ULONG UniqueId);
BOOLEAN SendMarker(IN PHW_DEVICE_EXTENSION pDevExt);
BOOLEAN CheckQueueSpace(IN PHW_DEVICE_EXTENSION pDevExt, USHORT slotcnt);
VOID QLCleanupAfterReset(IN PHW_DEVICE_EXTENSION pDevExt);
/************************************************************************/
/* */
/* DriverEntry */
/* */
/* Initial miniport driver entry routine called by NT. This routine */
/* builds and returns to NT the HW_INITIALIZATION_DATA structure. */
/* */
/* Arguments: */
/* */
/* Driver Object is passed to ScsiPortInitialize() */
/* */
/* Return Value: */
/* */
/* Status from ScsiPortInitialize() */
/* */
/************************************************************************/
ULONG DriverEntry(IN PVOID DriverObject, IN PVOID Argument2)
{
HW_INITIALIZATION_DATA hwInitializationData;
ULONG adapterCount;
ULONG i, status;
QLPrint((1, "\nDriverEntry: entering\n"));
// Zero out structure and set size of init data
for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++)
{
((PUCHAR)&hwInitializationData)[i] = 0;
}
hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
// Set driver entry points
hwInitializationData.HwInitialize = QLInitializeAdapter;
hwInitializationData.HwStartIo = QLStartIo;
hwInitializationData.HwInterrupt = QLInterruptServiceRoutine;
hwInitializationData.HwFindAdapter = QLFindAdapter;
hwInitializationData.HwResetBus = QLResetScsiBus;
hwInitializationData.HwAdapterState = QLAdapterState;
#ifdef NT_VERSION_31
// Note: NT Version 3.1 rejects "PCIBus", set AdapterInterfaceType to "Isa"
hwInitializationData.AdapterInterfaceType = Isa;
#else
hwInitializationData.AdapterInterfaceType = PCIBus;
hwInitializationData.VendorIdLength = 4;
hwInitializationData.VendorId = (PVOID)&QLVendorId;
hwInitializationData.DeviceIdLength = 4;
hwInitializationData.DeviceId = (PVOID)&ISP1020DeviceId;
#endif
hwInitializationData.NumberOfAccessRanges = 1;
hwInitializationData.Reserved = 0;
hwInitializationData.MapBuffers = FALSE;
hwInitializationData.NeedPhysicalAddresses = TRUE;
hwInitializationData.TaggedQueuing = TRUE;
hwInitializationData.AutoRequestSense = TRUE;
hwInitializationData.MultipleRequestPerLu = TRUE;
hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
hwInitializationData.SpecificLuExtensionSize = sizeof(HW_LUN_EXTENSION);
hwInitializationData.SrbExtensionSize = sizeof(SRB_EXTENSION);
adapterCount = 0;
status = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData,
&adapterCount);
QLPrint((1, "DriverEntry: exiting, status = %lx\n", status));
return(status);
}
#ifndef NT_VERSION_31
/************************************************************************/
/* */
/* QLFindAdapter NT Version 3.5 and above */
/* */
/* This function is called by ScsiPortInitialize to find the next */
/* adapter and fill in the configuration information structure and */
/* map the SCSI protocol chip for access. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* AdapterCount - Count of adapter I/O register slots tested */
/* BusInformation - Unused */
/* ArgumentString - Unused */
/* ConfigInfo - Pointer to the configuration information */
/* structure to be filled in */
/* Again - Returns a request to call this function again */
/* */
/* Return Value: */
/* */
/* Returns a status value for the initialization */
/* */
/************************************************************************/
ULONG QLFindAdapter(IN PVOID ServiceContext, IN OUT PULONG AdapterCount,
IN PVOID BusInformation, IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
PACCESS_RANGE AccessRange;
ULONG retcode;
ULONG length;
QLPrint((1, "QLFindAdapter: entering\n"));
*Again = FALSE;
// Get access range.
AccessRange = &((*(ConfigInfo->AccessRanges))[0]);
if (AccessRange->RangeLength != 0)
{
// BUGBUG - Currently, when scsiport scans the PCI bus and finds
// a SCSI adapter, it will not automatically enable the
// Bus Master bit in the PCI command register. We need
// to enable this for the adapter to function properly.
PCI_COMMON_CONFIG pciBuffer;
QLPrint((1, "QLFindAdapter: found ISP1020 at address %lx\n", AccessRange->RangeStart));
QLPrint((1, "QLFindAdapter: BusInterruptLevel = %lx\n", ConfigInfo->BusInterruptLevel));
ScsiPortGetBusData(pDevExt,
PCIConfiguration,
ConfigInfo->SystemIoBusNumber,
ConfigInfo->SlotNumber,
&pciBuffer,
PCI_COMMON_HDR_LENGTH);
//
// Enable Bus Master bit
//
pciBuffer.Command |= BUS_MASTER_ENABLE;
//
// Zero bit 0 of the ROM address to take the chip
// out of the its reset state
//
pciBuffer.u.type0.ROMBaseAddress &= 0xfffffffe;
ScsiPortSetBusDataByOffset(pDevExt,
PCIConfiguration,
ConfigInfo->SystemIoBusNumber,
ConfigInfo->SlotNumber,
&pciBuffer,
0,
PCI_COMMON_HDR_LENGTH);
// Map I/O registers for adapter
pDevExt->Adapter = ScsiPortGetDeviceBase(pDevExt,
ConfigInfo->AdapterInterfaceType,
ConfigInfo->SystemIoBusNumber,
AccessRange->RangeStart,
AccessRange->RangeLength,
(BOOLEAN)!AccessRange->RangeInMemory);
if (pDevExt->Adapter == NULL)
{
QLPrint((1, "QLFindAdapter: Failed to map ISP registers\n"));
return(SP_RETURN_ERROR);
}
// Reset ISP chip and read in NOVRAM parameters
if (!QLResetIsp(pDevExt))
{
ScsiPortFreeDeviceBase(pDevExt, pDevExt->Adapter);
return(SP_RETURN_ERROR);
}
GetNovramParameters(pDevExt);
// Check for disabled adapter
if (!pDevExt->Host_Adapter_Enable)
{
ScsiPortFreeDeviceBase(pDevExt, pDevExt->Adapter);
return(SP_RETURN_ERROR);
}
// Check for passed in SCSI ID and override NVRAM setting
if (ConfigInfo->InitiatorBusId[0] == (CCHAR)SP_UNINITIALIZED_VALUE)
ConfigInfo->InitiatorBusId[0] = pDevExt->Initiator_SCSI_Id;
else
pDevExt->Initiator_SCSI_Id = ConfigInfo->InitiatorBusId[0];
// Fill in other port configuration information
ConfigInfo->MaximumTransferLength = 0xFFFFFFFF; // unlimited
ConfigInfo->NumberOfPhysicalBreaks = MAX_SG_SEGMENTS - 1;
ConfigInfo->AlignmentMask = 0x00000003;
ConfigInfo->DmaWidth = 32;
ConfigInfo->NumberOfBuses = 1;
ConfigInfo->ScatterGather = TRUE;
ConfigInfo->Master = TRUE;
ConfigInfo->Dma32BitAddresses = TRUE;
if (!(pDevExt->AdapterFlags & AFLG_TAGGED_QUEUING))
ConfigInfo->TaggedQueuing = FALSE;
ConfigInfo->BufferAccessScsiPortControlled = TRUE;
// Work around for Chicago Beta 2 bug
if (ConfigInfo->MaximumNumberOfTargets == 7)
ConfigInfo->MaximumNumberOfTargets = 15;
else
ConfigInfo->MaximumNumberOfTargets = 16;
// Allocate a Noncached Extension request/response queues
pDevExt->NoncachedExtension = ScsiPortGetUncachedExtension(pDevExt,
ConfigInfo, sizeof(NONCACHED_EXTENSION));
if (pDevExt->NoncachedExtension == NULL)
{
QLPrint((1, "QLFindAdapter: Failed to allocate noncached memory\n"));
ScsiPortFreeDeviceBase(pDevExt, pDevExt->Adapter);
return(SP_RETURN_ERROR);
}
// Init virtual and physical queue addresses
pDevExt->pRequestQueue = pDevExt->NoncachedExtension->RequestQueue;
pDevExt->pResponseQueue = pDevExt->NoncachedExtension->ResponseQueue;
pDevExt->ppRequestQueue = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(pDevExt, NULL,
pDevExt->NoncachedExtension->RequestQueue, &length));
pDevExt->ppResponseQueue = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(pDevExt, NULL,
pDevExt->NoncachedExtension->ResponseQueue, &length));
retcode = SP_RETURN_FOUND;
*Again = TRUE;
}
else
{
// Stop searching for adapters
retcode = SP_RETURN_NOT_FOUND;
*Again = FALSE;
}
QLPrint((1, "QLFindAdapter: exiting, retcode = %lx\n", retcode));
return(retcode);
}
#endif
#ifdef NT_VERSION_31
/************************************************************************/
/* */
/* QLFindAdapter NT Version 3.1 */
/* */
/* This function is called by ScsiPortInitialize to find the next */
/* adapter and fill in the configuration information structure and */
/* map the SCSI protocol chip for access. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* AdapterCount - Count of adapter I/O register slots tested */
/* BusInformation - Unused */
/* ArgumentString - Unused */
/* ConfigInfo - Pointer to the configuration information */
/* structure to be filled in */
/* Again - Returns a request to call this function again */
/* */
/* Return Value: */
/* */
/* Returns a status value for the initialization */
/* */
/************************************************************************/
ULONG QLFindAdapter(IN PVOID ServiceContext, IN OUT PULONG AdapterCount,
IN PVOID BusInformation, IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
ULONG retcode;
ULONG length;
QLPrint((1, "QLFindAdapter: entering\n"));
*Again = FALSE; // preset no return call
// Look for adapter, try both methods of accessing PCI config space
find_adapter:
if (findMethod == 1)
{
retcode = FindAdapter_M1(ServiceContext, AdapterCount, ConfigInfo);
}
else
{
retcode = FindAdapter_M2(ServiceContext, AdapterCount, ConfigInfo);
}
if (retcode != SP_RETURN_FOUND)
{
if (findMethod == 1 && *AdapterCount == 0)
{
findMethod = 2; // Switch to method 2
goto find_adapter; // Try again
}
}
else
{
// Found next adapter
// Map I/O registers for adapter
pDevExt->Adapter = ScsiPortGetDeviceBase(
pDevExt, // HwDeviceExtension
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
(*ConfigInfo->AccessRanges)[0].RangeStart,
(ULONG)(sizeof(ISP_REGS)), // NumberOfBytes
TRUE); // InIoSpace
if (pDevExt->Adapter == NULL)
{
QLPrint((1, "QLFindAdapter: Failed to map ISP registers.\n"));
return(SP_RETURN_ERROR);
}
// Reset ISP chip and read in NOVRAM parameters
if (!QLResetIsp(pDevExt))
{
ScsiPortFreeDeviceBase(pDevExt, pDevExt->Adapter);
return(SP_RETURN_ERROR);
}
GetNovramParameters(pDevExt);
// Check for disabled adapter
if (!pDevExt->Host_Adapter_Enable)
{
ScsiPortFreeDeviceBase(pDevExt, pDevExt->Adapter);
(*AdapterCount)++; // skip this adapter
goto find_adapter; // Try again
}
// Check for passed in SCSI ID and override NVRAM setting
if (ConfigInfo->InitiatorBusId[0] == (CCHAR)SP_UNINITIALIZED_VALUE)
ConfigInfo->InitiatorBusId[0] = pDevExt->Initiator_SCSI_Id;
else
pDevExt->Initiator_SCSI_Id = ConfigInfo->InitiatorBusId[0];
// Fill in other port configuration information
ConfigInfo->MaximumTransferLength = 0xFFFFFFFF; // unlimited
ConfigInfo->NumberOfPhysicalBreaks = MAX_SG_SEGMENTS - 1;
ConfigInfo->AlignmentMask = 0x00000003;
ConfigInfo->DmaWidth = 32;
ConfigInfo->NumberOfBuses = 1;
ConfigInfo->ScatterGather = TRUE;
ConfigInfo->Master = TRUE;
ConfigInfo->Dma32BitAddresses = TRUE;
if (!(pDevExt->AdapterFlags & AFLG_TAGGED_QUEUING))
ConfigInfo->TaggedQueuing = FALSE;
ConfigInfo->InterruptMode = LevelSensitive; // Set this for shared IRQ level
// Allocate a Noncached Extension request/response queues
pDevExt->NoncachedExtension = ScsiPortGetUncachedExtension(
pDevExt, ConfigInfo, sizeof(NONCACHED_EXTENSION));
if (pDevExt->NoncachedExtension == NULL)
{
QLPrint((1, "QLFindAdapter: Failed to allocate noncached memory\n"));
ScsiPortFreeDeviceBase(pDevExt, pDevExt->Adapter);
return(SP_RETURN_ERROR);
}
// Init virtual and physical queue addresses
pDevExt->pRequestQueue = pDevExt->NoncachedExtension->RequestQueue;
pDevExt->pResponseQueue = pDevExt->NoncachedExtension->ResponseQueue;
pDevExt->ppRequestQueue = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(pDevExt, NULL,
pDevExt->NoncachedExtension->RequestQueue, &length));
pDevExt->ppResponseQueue = pDevExt->ppRequestQueue +
REQUEST_QUEUE_DEPTH * sizeof(QUEUE_ENTRY);
(*AdapterCount)++;
*Again = TRUE;
}
QLPrint((1, "QLFindAdapter: exiting, retcode = %lx\n", retcode));
return(retcode);
}
/************************************************************************/
/* */
/* FindAdapter_M1 NT Version 3.1 */
/* */
/* This function is called by QLFindAdapter to find the next */
/* adapter using PCI configuration mechanism #1. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* AdapterCount - Count of adapter I/O register slots tested */
/* ConfigInfo - Pointer to the configuration information */
/* structure to be filled in */
/* */
/* Return Value: */
/* */
/* Returns a status value for the initialization */
/* */
/************************************************************************/
ULONG FindAdapter_M1(IN PVOID ServiceContext, IN OUT PULONG AdapterCount,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
PPCI_CHIP_REGISTERS_M1 PciChip;
PPCI_REGS PciConfig = 0;
ULONG retcode = SP_RETURN_NOT_FOUND;
USHORT index, VendorId, DeviceId;
ULONG BusNum, DevNum;
ULONG ha_base;
ULONG RomBase;
ULONG cmd_reg;
ULONG dataread;
// Map PCI chip registers
PciChip = ScsiPortGetDeviceBase(
pDevExt, // HwDeviceExtension
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
ScsiPortConvertUlongToPhysicalAddress((ULONG)PCI_CONFIG_ADDRESS),
sizeof(PCI_CHIP_REGISTERS_M1), // NumberOfBytes
TRUE); // InIoSpace
if (PciChip == NULL)
{
QLPrint((1, "FindAdapter_M1: Failed to map PCI chip registers.\n"));
return(SP_RETURN_ERROR);
}
// Scan configuration space headers for next adapter
for (index=0, BusNum=0; BusNum < PCI_MAXBUSNUM; BusNum++)
{
for (DevNum = 0; DevNum < PCI_MAXDEVNUM; DevNum++)
{
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->Vendor_Id)));
dataread = ScsiPortReadPortUlong(&PciChip->Config_Data);
VendorId = (USHORT)dataread;
DeviceId = (USHORT)(dataread >> 16);
if (VendorId == QLogic_VENDOR_ID && DeviceId == QLogic_DEVICE_ID)
{
if (index == *AdapterCount)
{
// Found next adapter
// Get base I/O address and interrupt level
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->IO_Base_Address)));
ha_base = ScsiPortReadPortUlong(&PciChip->Config_Data) & 0xfffffffc;
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->Interrupt_Line)));
ConfigInfo->BusInterruptLevel =
ScsiPortReadPortUlong(&PciChip->Config_Data) & 0x000000ff;
QLPrint((1, "FindAdapter_M1: found ISP1020 at address %lx\n", ha_base));
// Fill in the access array information
(*ConfigInfo->AccessRanges)[0].RangeStart =
ScsiPortConvertUlongToPhysicalAddress(ha_base);
(*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(ISP_REGS);
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
// Must clear bit 0 of ROM base address register
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->ROM_Base_Address)));
RomBase = ScsiPortReadPortUlong(&PciChip->Config_Data);
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->ROM_Base_Address)));
ScsiPortWritePortUlong(&PciChip->Config_Data, (RomBase & 0xfffffffe));
// Make sure bus master enabled
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->Command)));
cmd_reg = ScsiPortReadPortUlong(&PciChip->Config_Data);
ScsiPortWritePortUlong(&PciChip->Config_Address,
(PCI_ENABLE_CONFIG | BusNum << 16 | DevNum << 11 |
(ULONG)(&PciConfig->Command)));
ScsiPortWritePortUlong(&PciChip->Config_Data, (cmd_reg | BUS_MASTER_ENABLE));
retcode = SP_RETURN_FOUND;
goto find_exit;
}
index++;
}
}
}
find_exit:
// Disable PCI chip registers
ScsiPortWritePortUlong(&PciChip->Config_Address, PCI_DISABLE_CONFIG);
ScsiPortFreeDeviceBase(pDevExt, PciChip);
return(retcode);
}
/************************************************************************/
/* */
/* FindAdapter_M2 NT Version 3.1 */
/* */
/* This function is called by QLFindAdapter to find the next */
/* adapter using PCI configuration mechanism #2. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* AdapterCount - Count of adapter I/O register slots tested */
/* ConfigInfo - Pointer to the configuration information */
/* structure to be filled in */
/* */
/* Return Value: */
/* */
/* Returns a status value for the initialization */
/* */
/************************************************************************/
ULONG FindAdapter_M2(IN PVOID ServiceContext, IN OUT PULONG AdapterCount,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
PPCI_CHIP_REGISTERS PciChip;
UCHAR config_reg;
ULONG retcode = SP_RETURN_NOT_FOUND;
PPCI_REGS PciConfig, PciSlot;
USHORT index, slot, VendorId, DeviceId;
ULONG ha_base;
USHORT RomBase;
USHORT cmd_reg;
// Map PCI chip registers and enable configuration space
PciChip = ScsiPortGetDeviceBase(
pDevExt, // HwDeviceExtension
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
ScsiPortConvertUlongToPhysicalAddress((ULONG)PCI_CONFIG),
sizeof(PCI_CHIP_REGISTERS), // NumberOfBytes
TRUE); // InIoSpace
if (PciChip == NULL)
{
QLPrint((1, "FindAdapter_M2: Failed to map PCI chip registers.\n"));
return(SP_RETURN_ERROR);
}
config_reg = ScsiPortReadPortUchar(&PciChip->pci_config);
ScsiPortWritePortUchar(&PciChip->pci_config, (UCHAR)(config_reg | PCI_ENABLE));
// Map PCI configuration space
PciConfig = ScsiPortGetDeviceBase(
pDevExt, // HwDeviceExtension
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
ScsiPortConvertUlongToPhysicalAddress((ULONG)PCI_START),
sizeof(PCI_REGS) * PCI_SLOT_CNT, // NumberOfBytes
TRUE); // InIoSpace
if (PciConfig == NULL)
{
QLPrint((1, "FindAdapter_M2: Failed to map PCI config space.\n"));
ScsiPortFreeDeviceBase(pDevExt, PciChip);
return(SP_RETURN_ERROR);
}
// Scan configuration space headers for next adapter
for (index=0, slot=0, PciSlot=PciConfig; slot<PCI_SLOT_CNT; slot++, PciSlot++)
{
VendorId = ScsiPortReadPortUshort(&PciSlot->Vendor_Id);
DeviceId = ScsiPortReadPortUshort(&PciSlot->Device_Id);
if (VendorId == QLogic_VENDOR_ID && DeviceId == QLogic_DEVICE_ID)
{
if (index == *AdapterCount)
{
// Found next adapter
// Get base I/O address and interrupt level
ha_base = ScsiPortReadPortUshort((PUSHORT)&PciSlot->IO_Base_Address) & 0xfffc;
ConfigInfo->BusInterruptLevel =
ScsiPortReadPortUchar(&PciSlot->Interrupt_Line);
QLPrint((1, "FindAdapter_M2: found ISP1020 at address %lx\n", ha_base));
// Fill in the access array information
(*ConfigInfo->AccessRanges)[0].RangeStart =
ScsiPortConvertUlongToPhysicalAddress(ha_base);
(*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(ISP_REGS);
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
// Must clear bit 0 of ROM base address register
RomBase = ScsiPortReadPortUshort((PUSHORT)&PciSlot->ROM_Base_Address);
ScsiPortWritePortUshort((PUSHORT)&PciSlot->ROM_Base_Address, (USHORT)(RomBase & 0xfffe));
// Make sure bus master enabled
cmd_reg = ScsiPortReadPortUshort(&PciSlot->Command);
ScsiPortWritePortUshort(&PciSlot->Command, (USHORT)(cmd_reg | BUS_MASTER_ENABLE));
retcode = SP_RETURN_FOUND;
break;
}
index++;
}
}
// Disable configuration space
ScsiPortWritePortUchar(&PciChip->pci_config, config_reg);
ScsiPortFreeDeviceBase(pDevExt, PciChip);
ScsiPortFreeDeviceBase(pDevExt, PciConfig);
return(retcode);
}
#endif
/************************************************************************/
/* */
/* QLInitializeAdapter */
/* */
/* This function is called to initialize the adapter after boot or */
/* after a power failure. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* Returns TRUE if initialization is complete */
/* Returns FALSE if error */
/* */
/************************************************************************/
BOOLEAN QLInitializeAdapter(IN PVOID ServiceContext)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
USHORT mbox_sts[6];
UCHAR *c_ptr;
USHORT i;
USHORT scsi_id;
USHORT ram_addr, ram_data;
USHORT running_version;
QLPrint((1, "QLInitializeAdapter: entering\n"));
// Check version of loaded RISC code, don't reload if same or newer version
// Must restart RISC firmware first
if (MailboxCommand(pDevExt, mbox_sts, 2, 0,
MBOX_CMD_EXECUTE_FIRMWARE,
risc_code_addr01,
0,0,0,0))
{
// Get running RISC code version
if (MailboxCommand(pDevExt, mbox_sts, 1, 3,
MBOX_CMD_ABOUT_FIRMWARE,
0,0,0,0,0))
{
// Compare running version to version linked with driver
running_version = mbox_sts[1] << 10 | mbox_sts[2];
QLPrint((1, "QLInitializeAdapter: running RISC code version %x\n", running_version));
QLPrint((1, "QLInitializeAdapter: driver RISC code version %x\n", risc_code_version));
if (risc_code_version <= running_version)
{
pDevExt->DisableLoadRiscCode = 1;
}
}
else
{
QLPrint((1, "QLInitializeAdapter: ABOUT FIRMWARE command failed\n"));
}
}
else
{
QLPrint((1, "QLInitializeAdapter: EXECUTE FIRMWARE command failed\n"));
}
if (!pDevExt->DisableLoadRiscCode)
{
// Reset ISP chip
if (!QLResetIsp(pDevExt))
{
return(FALSE);
}
// Must manually clear Burst Enable, chip reset not working correctly
ISP_WRITE(pDevExt->Adapter, bus_config1, 0);
// Load RISC code
if (!LoadRiscCode(pDevExt))
{
QLPrint((1, "QLInitializeAdapter: Load RISC code failed\n"));
return(FALSE);
}
// Start ISP firmware
if (!MailboxCommand(pDevExt, mbox_sts, 2, 0,
MBOX_CMD_EXECUTE_FIRMWARE,
risc_code_addr01,
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: EXECUTE FIRMWARE command failed\n"));
return(FALSE);
}
QLPrint((1, "QLInitializeAdapter: RISC code loaded and started\n"));
}
else
{
QLPrint((1, "QLInitializeAdapter: RISC not reloaded\n"));
}
// Set Bus Control Parameters
ISP_WRITE(pDevExt->Adapter, bus_config1,
(USHORT)pDevExt->Config_Reg);
if (!MailboxCommand(pDevExt, mbox_sts, 3, 3,
MBOX_CMD_SET_BUS_CONTROL_PARAMETERS,
(USHORT)(pDevExt->Data_DMA_Burst_Enable << 1),
(USHORT)(pDevExt->Cmd_DMA_Burst_Enable << 1),
0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET BUS CONTROL PARAMS cmd failed\n"));
return(FALSE);
}
// Set ISP1020 clock rate
if (pDevExt->Sixty_MHz_Enable)
{
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_SET_CLOCK_RATE,
(USHORT)60,
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET CLOCK RATE cmd failed\n"));
return(FALSE);
}
}
// Set software controlled SCSI bus termination
// Must reset and enable termination PAL first
for (i = 0, ram_addr = 0xFF00; i < 4; i++, ram_addr += 0x0010)
{
if (!MailboxCommand(pDevExt, mbox_sts, 3, 3,
MBOX_CMD_WRITE_RAM_WORD,
ram_addr,
(USHORT)0,
0,0,0))
{
QLPrint((1, "QLInitializeAdapter: WRITE RAM WORD cmd failed\n"));
return(FALSE);
}
}
if (pDevExt->Termination_High_Enable)
{
if (pDevExt->Termination_Low_Enable)
ram_addr = 0xFF00;
else
ram_addr = 0xFF40;
}
else
{
if (pDevExt->Termination_Low_Enable)
ram_addr = 0xFF80;
else
ram_addr = 0xFFC0;
}
ram_data = (USHORT)(pDevExt->Termination_High_Enable) << 1 |
(USHORT)(pDevExt->Termination_Low_Enable);
if (!MailboxCommand(pDevExt, mbox_sts, 3, 3,
MBOX_CMD_WRITE_RAM_WORD,
ram_addr,
ram_data,
0,0,0))
{
QLPrint((1, "QLInitializeAdapter: WRITE RAM WORD cmd failed\n"));
return(FALSE);
}
// Clear request queue area and initialize queue
c_ptr = (UCHAR *)pDevExt->pRequestQueue;
for (i = 0; i < REQUEST_QUEUE_DEPTH * sizeof(QUEUE_ENTRY); i++)
*c_ptr++ = 0;
pDevExt->queue_space = REQUEST_QUEUE_DEPTH - 1;
pDevExt->request_in = 0;
pDevExt->request_out = 0;
pDevExt->request_ptr = pDevExt->pRequestQueue;
if (!MailboxCommand(pDevExt, mbox_sts, 5, 6,
MBOX_CMD_INIT_REQUEST_QUEUE,
REQUEST_QUEUE_DEPTH,
(USHORT)(pDevExt->ppRequestQueue >> 16),
(USHORT)(pDevExt->ppRequestQueue & 0xFFFF),
pDevExt->request_in,
0))
{
QLPrint((1, "QLInitializeAdapter: INIT REQUEST QUEUE cmd failed\n"));
return(FALSE);
}
// Clear response queue area and initialize queue
c_ptr = (UCHAR *)pDevExt->pResponseQueue;
for (i = 0; i < RESPONSE_QUEUE_DEPTH * sizeof(QUEUE_ENTRY); i++)
*c_ptr++ = 0;
pDevExt->response_out = 0;
pDevExt->response_ptr = pDevExt->pResponseQueue;
if (!MailboxCommand(pDevExt, mbox_sts, 6, 6,
MBOX_CMD_INIT_RESPONSE_QUEUE,
RESPONSE_QUEUE_DEPTH,
(USHORT)(pDevExt->ppResponseQueue >> 16),
(USHORT)(pDevExt->ppResponseQueue & 0xFFFF),
0,
pDevExt->response_out))
{
QLPrint((1, "QLInitializeAdapter: INIT RESPONSE QUEUE cmd failed\n"));
return(FALSE);
}
// Set Initiator SCSI ID
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_SET_INITIATOR_SCSI_ID,
(USHORT)pDevExt->Initiator_SCSI_Id,
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET INITIATOR SCSI ID cmd failed\n"));
return(FALSE);
}
// Set Selection Timeout
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_SET_SELECTION_TIMEOUT,
(USHORT)pDevExt->Selection_Timeout,
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET SELECTION TIMEOUT cmd failed\n"));
return(FALSE);
}
// Disable retrys for autoconfigure
if (!MailboxCommand(pDevExt, mbox_sts, 3, 3,
MBOX_CMD_SET_RETRY_COUNT,
0,0,
0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET RETRY COUNT cmd failed\n"));
return(FALSE);
}
// Set Active Negation
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_SET_ACTIVE_NEGATION_STATE,
(USHORT)((pDevExt->REQ_ACK_Active_Negation << 5) +
(pDevExt->DATA_Active_Negation << 4)),
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET ACTIVE NEGATION STATE cmd failed\n"));
return(FALSE);
}
// Set Tag Age Limits
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_SET_TAG_AGE_LIMIT,
pDevExt->Tag_Age_Limit,
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET TAG AGE LIMIT cmd failed\n"));
return(FALSE);
}
// Set Async Data Setup Time
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_SET_ASYNC_DATA_SETUP_TIME,
pDevExt->ASync_Data_Setup_Time,
0,0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET ASYNC DATA SETUP TIME cmd failed\n"));
return(FALSE);
}
// Set target parameters for doing SCSI autoconfigure
for (scsi_id = 0; scsi_id < NUM_SCSI_IDS; scsi_id++)
{
if (!MailboxCommand(pDevExt, mbox_sts, 4, 4,
MBOX_CMD_SET_TARGET_PARAMETERS,
(USHORT)(scsi_id << 8),
(USHORT)(CAP_AUTO_REQ_SENSE << 8),
0,0,0))
{
QLPrint((1, "QLInitializeAdapter: SET TARGET PARAMETERS cmd failed\n"));
return(FALSE);
}
}
// Reset SCSI bus
QLResetScsiBus(pDevExt, 0);
// Enable ISP interrupts
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_CMD_CLEAR_RISC_INT);
ISP_WRITE(pDevExt->Adapter, bus_sema, 0);
ISP_WRITE(pDevExt->Adapter, bus_icr, ICR_ENABLE_ALL_INTS | ICR_ENABLE_RISC_INT);
QLPrint((1, "QLInitializeAdapter: exiting\n"));
return( TRUE );
}
/************************************************************************/
/* */
/* QLResetIsp */
/* */
/* This function is called to reset the ISP chip. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* Returns TRUE if reset completed OK */
/* Returns FALSE if error */
/* */
/************************************************************************/
BOOLEAN QLResetIsp(IN PHW_DEVICE_EXTENSION pDevExt)
{
USHORT i;
// Reset ISP chip and disable BIOS
ISP_WRITE(pDevExt->Adapter, bus_icr, ICR_SOFT_RESET);
// Small delay after reset
ScsiPortStallExecution(10);
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_CMD_RESET);
// Small delay after reset
ScsiPortStallExecution(10);
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_CMD_RELEASE);
// Small delay after reset
ScsiPortStallExecution(10);
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_WRITE_BIOS_ENABLE);
// Small delay after reset
ScsiPortStallExecution(10);
// Wait for mailbox 0 to clear
for (i = 0; i < 1000; i++)
{
if (ISP_READ(pDevExt->Adapter, mailbox0) == 0)
{
break;
}
}
if (ISP_READ(pDevExt->Adapter, mailbox0) != 0)
{
QLPrint((1, "QLResetIsp: Chip reset timeout\n"));
return(FALSE);
}
// Check product ID of chip
if (ISP_READ(pDevExt->Adapter, mailbox1) != PROD_ID_1 ||
(ISP_READ(pDevExt->Adapter, mailbox2) != PROD_ID_2 &&
ISP_READ(pDevExt->Adapter, mailbox2) != PROD_ID_2a) ||
ISP_READ(pDevExt->Adapter, mailbox3) != PROD_ID_3 ||
ISP_READ(pDevExt->Adapter, mailbox4) != PROD_ID_4)
{
QLPrint((1, "QLResetIsp: Failed chip product ID test\n"));
return(FALSE);
}
return(TRUE);
}
/************************************************************************/
/* */
/* LoadRiscCode */
/* */
/* This function is called to load the RISC firmware. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* Returns TRUE if load completed OK */
/* Returns FALSE if error */
/* */
/************************************************************************/
BOOLEAN LoadRiscCode(IN PHW_DEVICE_EXTENSION pDevExt)
{
USHORT i, j;
USHORT RiscCodeAddr;
USHORT *pCode, *pBuffer;
USHORT mbox_sts[6];
USHORT BufSize;
// Use request/response queues buffer for loading RISC code
// (having trouble loading directly from driver code space?)
BufSize = (REQUEST_QUEUE_DEPTH + RESPONSE_QUEUE_DEPTH) * sizeof(QUEUE_ENTRY);
pCode = risc_code01;
for (i = 0, RiscCodeAddr = risc_code_addr01;
i < risc_code_length01;
RiscCodeAddr += BufSize/2)
{
pBuffer = (USHORT *)pDevExt->pRequestQueue;
for (j = 0; j < BufSize/2 && i < risc_code_length01; j++, i++)
{
*pBuffer++ = *pCode++;
}
if (!MailboxCommand(pDevExt, mbox_sts, 5, 5,
MBOX_CMD_LOAD_RAM,
RiscCodeAddr,
(USHORT)(pDevExt->ppRequestQueue >> 16),
(USHORT)(pDevExt->ppRequestQueue & 0xFFFF),
(USHORT)(BufSize / 2),
0))
{
QLPrint((1, "LoadRiscCode: LOAD RAM command failed\n"));
return(FALSE);
}
}
return(TRUE);
}
/************************************************************************/
/* */
/* InitDeviceParameters */
/* */
/* This function is called to set target device parameters after */
/* autoconfigure Inquiry commands are done */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* pLunExt - Pointer to lun extension */
/* pSrb - Pointer to SCSI request block */
/* */
/* Return Value: */
/* */
/* Returns TRUE if completed OK */
/* Returns FALSE if error */
/* */
/************************************************************************/
BOOLEAN InitDeviceParameters(IN PHW_DEVICE_EXTENSION pDevExt,
IN PHW_LUN_EXTENSION pLunExt, IN PSCSI_REQUEST_BLOCK pSrb)
{
USHORT scsi_id;
USHORT mbox_sts[6];
// If 1st call for this adapter, set retries and target parameters
if (!(pDevExt->AdapterFlags & AFLG_INIT_COMPLETE))
{
QLPrint((1, "InitDeviceParameters: set adapter init complete\n"));
pDevExt->AdapterFlags |= AFLG_INIT_COMPLETE;
// Reset target parameters from NOVRAM parameters
for (scsi_id = 0; scsi_id < NUM_SCSI_IDS; scsi_id++)
{
if (!MailboxCommand(pDevExt, mbox_sts, 4, 4,
MBOX_CMD_SET_TARGET_PARAMETERS,
(USHORT)(scsi_id << 8),
(USHORT)(pDevExt->Id[scsi_id].Capability << 8),
(USHORT)((pDevExt->Id[scsi_id].Sync_Offset << 8) |
(pDevExt->Id[scsi_id].Sync_Period)),
0,0))
{
QLPrint((1, "InitDeviceParameters: SET TARGET PARAMS cmd failed\n"));
return(FALSE);
}
}
// Set RETRY limits
if (!MailboxCommand(pDevExt, mbox_sts, 3, 3,
MBOX_CMD_SET_RETRY_COUNT,
pDevExt->Retry_Count,
pDevExt->Retry_Delay,
0,0,0))
{
QLPrint((1, "InitDeviceParameters: SET RETRY COUNT cmd failed\n"));
return(FALSE);
}
}
QLPrint((1, "InitDeviceParameters: set lun init complete\n"));
pLunExt->LunFlags |= LFLG_INIT_COMPLETE;
// Set device queue parameters
if (!MailboxCommand(pDevExt, mbox_sts, 4, 4,
MBOX_CMD_SET_DEVICE_QUEUE_PARAMETERS,
(USHORT)(((USHORT)(pSrb->TargetId) << 8) | pSrb->Lun),
pDevExt->Max_Queue_Depth,
(USHORT)(pDevExt->Id[pSrb->TargetId].Execution_Throttle),
0,0))
{
QLPrint((1, "InitDeviceParameters: SET QUEUE PARAMS cmd failed\n"));
return(FALSE);
}
return(TRUE);
}
/************************************************************************/
/* */
/* QLStartIo */
/* */
/* This function is called by the port driver to start execution */
/* of an I/O request. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* pSrb - Supplies the SCSI request block to be started. */
/* */
/* Return Value: */
/* */
/* TRUE - If the request can be accepted at this time */
/* FALSE - If the request must be submitted later */
/* */
/************************************************************************/
BOOLEAN QLStartIo(IN PVOID ServiceContext, IN PSCSI_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
PHW_LUN_EXTENSION pLunExt;
USHORT mbox_sts[6];
/*
QLPrint((1, "QLStartIo: SRB function: %x", pSrb->Function));
QLPrint((1, " flags: %lx", pSrb->SrbFlags));
QLPrint((1, " cdb: %x\n", (USHORT)(pSrb->Cdb[0])));
*/
switch (pSrb->Function)
{
case SRB_FUNCTION_EXECUTE_SCSI:
// Get LUN extension pointer
pLunExt = ScsiPortGetLogicalUnit(pDevExt, pSrb->PathId,
pSrb->TargetId, pSrb->Lun);
// Finish up init stuff on 1st request after Inquiry commands
if (!(pLunExt->LunFlags & LFLG_INIT_COMPLETE) &&
pSrb->Cdb[0] != SCSIOP_INQUIRY)
{
InitDeviceParameters(pDevExt, pLunExt, pSrb);
}
// Build request and send to ISP
if (!SendCommandToIsp(pDevExt, pSrb))
{
pDevExt->WaitingSrb = pSrb;
pDevExt->AdapterFlags |= AFLG_SRB_WAITING;
return(TRUE); // start command later
}
break;
case SRB_FUNCTION_ABORT_COMMAND:
case SRB_FUNCTION_TERMINATE_IO:
QLPrint((1, "QLStartIo: abort command function\n"));
// Send abort command to ISP
if (MailboxCommand(pDevExt, mbox_sts, 4, 4,
MBOX_CMD_ABORT,
(USHORT)(((USHORT)(pSrb->TargetId) << 8) | pSrb->Lun),
(USHORT)((ULONG)(pSrb->NextSrb) & 0x0000ffff),
(USHORT)((ULONG)(pSrb->NextSrb) >> 16 & 0x0000ffff),
0,0))
{
pSrb->SrbStatus = SRB_STATUS_SUCCESS;
}
else
{
QLPrint((1, "QLStartIo: ABORT cmd failed\n"));
pSrb->SrbStatus = SRB_STATUS_ABORT_FAILED;
}
ScsiPortNotification(RequestComplete, pDevExt, pSrb);
break;
case SRB_FUNCTION_RESET_DEVICE:
QLPrint((1, "QLStartIo: reset device function\n"));
// Send abort target command to ISP
if (MailboxCommand(pDevExt, mbox_sts, 3, 3,
MBOX_CMD_ABORT_TARGET,
(USHORT)((USHORT)pSrb->TargetId << 8),
(USHORT)pDevExt->Bus_Reset_Delay,
0,0,0))
{
pSrb->SrbStatus = SRB_STATUS_SUCCESS;
}
else
{
QLPrint((1, "QLStartIo: ABORT TARGET cmd failed\n"));
pSrb->SrbStatus = SRB_STATUS_ERROR;
}
// Send marker to unlock queues
if (!SendMarker(pDevExt))
{
pDevExt->AdapterFlags |= AFLG_SEND_MARKER;
}
ScsiPortNotification(RequestComplete, pDevExt, pSrb);
break;
case SRB_FUNCTION_RESET_BUS:
QLPrint((1, "QLStartIo: reset bus function\n"));
// Reset SCSI bus and send marker
if (QLResetScsiBus(pDevExt, 0))
{
pSrb->SrbStatus = SRB_STATUS_SUCCESS;
}
else
{
pSrb->SrbStatus = SRB_STATUS_ERROR;
}
ScsiPortNotification(RequestComplete, pDevExt, pSrb);
break;
default:
QLPrint((1, "QLStartIo: unsupported function %x\n", pSrb->Function));
// Unsupported function code, complete request with error status
pSrb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
ScsiPortNotification(RequestComplete, pDevExt, pSrb);
break;
}
// Request next command
if (pDevExt->queue_space > 5)
{
ScsiPortNotification(NextLuRequest, pDevExt, pSrb->PathId,
pSrb->TargetId, pSrb->Lun);
}
else
{
ScsiPortNotification(NextRequest, pDevExt);
}
return(TRUE);
}
/************************************************************************/
/* */
/* SendCommandToIsp */
/* */
/* This function is called by QLStartIo to build an ISP command */
/* entry and pass it to the ISP for execution. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* pSrb - Supplies the SCSI request block to be started. */
/* */
/* Return Value: */
/* */
/* TRUE - If command passed to ISP */
/* FALSE - If no ISP request slot available */
/* */
/************************************************************************/
BOOLEAN SendCommandToIsp(PHW_DEVICE_EXTENSION pDevExt, PSCSI_REQUEST_BLOCK pSrb)
{
PCOMMAND_ENTRY pCmdEntry;
USHORT i, segmentCnt;
USHORT controlFlags = 0;
ULONG transferCnt = pSrb->DataTransferLength;
PVOID dataPointer = pSrb->DataBuffer;
ULONG length;
// Send a Marker to ISP if needed
if (pDevExt->AdapterFlags & AFLG_SEND_MARKER)
{
if (!SendMarker(pDevExt))
{
return(FALSE); // Error sending marker
}
}
// Disable ISP interrupts
ISP_WRITE(pDevExt->Adapter, bus_icr, 0);
// Check available request slots
if (MAX_CONT_ENTRYS + 1 > pDevExt->queue_space)
{
if (!CheckQueueSpace(pDevExt, MAX_CONT_ENTRYS + 1))
{
ISP_WRITE(pDevExt->Adapter, bus_icr,
ICR_ENABLE_ALL_INTS | ICR_ENABLE_RISC_INT);
return(FALSE); // No queue space
}
}
// Get pointer to the queue entry for the command
pCmdEntry = (PCOMMAND_ENTRY)pDevExt->request_ptr;
// Advance request queue pointer
if (pDevExt->request_in == (REQUEST_QUEUE_DEPTH - 1))
{
pDevExt->request_in = 0;
pDevExt->request_ptr = pDevExt->pRequestQueue;
}
else
{
pDevExt->request_in++;
pDevExt->request_ptr++;
}
pDevExt->queue_space--;
// Setup request to send to ISP
pCmdEntry->hdr.entry_type = ET_COMMAND;
pCmdEntry->hdr.entry_cnt = 1; // start with 1
pCmdEntry->hdr.flags = 0;
pCmdEntry->hdr.sys_def_1 = 0;
pCmdEntry->handle = (ULONG)pSrb;
pCmdEntry->target_id = pSrb->TargetId;
pCmdEntry->target_lun = pSrb->Lun;
pCmdEntry->reserved = 0;
// Set command timeout value
if (pSrb->TimeOutValue > 65535 || pSrb->TimeOutValue == 0)
{
pCmdEntry->time_out = 0;
}
else
{
pCmdEntry->time_out = (USHORT)(pSrb->TimeOutValue + 1);
}
// Setup control flags
if (pSrb->SrbFlags & SRB_FLAGS_DATA_IN)
{
controlFlags = CF_READ;
}
if (pSrb->SrbFlags & SRB_FLAGS_DATA_OUT)
{
controlFlags = CF_WRITE;
}
if (pSrb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT)
{
controlFlags |= CF_NO_DISCONNECTS;
}
if (pSrb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE)
{
controlFlags |= CF_NO_REQUEST_SENSE;
}
if ((pSrb->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) &&
(pDevExt->Id[pSrb->TargetId].Capability & CAP_TAGGED_QUEUING))
{
if (pSrb->QueueAction == SRB_SIMPLE_TAG_REQUEST)
controlFlags |= CF_SIMPLE_TAG;
else if (pSrb->QueueAction == SRB_HEAD_OF_QUEUE_TAG_REQUEST)
controlFlags |= CF_HEAD_TAG;
else
controlFlags |= CF_ORDERED_TAG;
}
pCmdEntry->control_flags = controlFlags;
// Move SCSI CDB
pCmdEntry->cdb_length = pSrb->CdbLength;
for (i = 0; i < pCmdEntry->cdb_length; i++)
{
pCmdEntry->cdb[i] = pSrb->Cdb[i];
}
// Setup data segments
for (segmentCnt = 0; transferCnt && segmentCnt < 4; segmentCnt++)
{
pCmdEntry->dseg[segmentCnt].base =
ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(pDevExt, pSrb, dataPointer, &length));
if (length > transferCnt)
{
length = transferCnt;
}
pCmdEntry->dseg[segmentCnt].count = length;
transferCnt -= length;
dataPointer = (PUCHAR)dataPointer + length;
}
while (transferCnt)
{
PCONTINUATION_ENTRY pContEntry;
// Get ptr to next queue entry for command and bump entry count
pContEntry = (PCONTINUATION_ENTRY)pDevExt->request_ptr;
pCmdEntry->hdr.entry_cnt++;
// Advance request queue pointer
if (pDevExt->request_in == (REQUEST_QUEUE_DEPTH - 1))
{
pDevExt->request_in = 0;
pDevExt->request_ptr = pDevExt->pRequestQueue;
}
else
{
pDevExt->request_in++;
pDevExt->request_ptr++;
}
pDevExt->queue_space--;
// Setup header
pContEntry->hdr.entry_type = ET_CONTINUATION;
pContEntry->hdr.entry_cnt = 1; // hopefully RISC doesn't need total here
pContEntry->hdr.flags = EF_CONTINUATION;
pContEntry->hdr.sys_def_1 = 0;
// Fill in data segments
for (i = 0; transferCnt && i < 7; i++, segmentCnt++)
{
pContEntry->dseg[i].base =
ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(pDevExt, pSrb, dataPointer, &length));
if (length > transferCnt)
{
length = transferCnt;
}
pContEntry->dseg[i].count = length;
transferCnt -= length;
dataPointer = (PUCHAR)dataPointer + length;
}
}
pCmdEntry->segment_cnt = segmentCnt;
// Tell ISP it's got a new I/O request
ISP_WRITE(pDevExt->Adapter, mailbox4, pDevExt->request_in);
// Enable ISP interrupts
ISP_WRITE(pDevExt->Adapter, bus_icr, ICR_ENABLE_ALL_INTS | ICR_ENABLE_RISC_INT);
return(TRUE);
}
/************************************************************************/
/* */
/* QLInterruptServiceRoutine */
/* */
/* This routine is the interrupt service routine for the ISP1020. */
/* The response queue is checked for completed commands. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* TRUE - Indicates that an interrupt was found. */
/* FALSE - Indicates the device was not interrupting. */
/* */
/************************************************************************/
BOOLEAN QLInterruptServiceRoutine(PVOID ServiceContext)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
PSCSI_REQUEST_BLOCK pSrb;
USHORT response_in;
PSTATUS_ENTRY pStsEntry;
USHORT status;
// QLPrint((1, "ISR: Entering, pDevExt = %lx\n", (ULONG)pDevExt));
if (!(ISP_READ(pDevExt->Adapter, bus_isr) & BUS_ISR_RISC_INT))
{
// QLPrint((1, "ISR: spurious interrupt?\n"));
return(FALSE); // not my interrupt
}
// Loop to process all responses from ISP
do
{
// Check for async mailbox event
if (ISP_READ(pDevExt->Adapter, bus_sema) & BUS_SEMA_LOCK)
{
// Check mailbox status
status = ISP_READ(pDevExt->Adapter, mailbox0);
switch (status)
{
case MBOX_ASTS_SCSI_BUS_RESET:
QLPrint((1, "ISR: SCSI bus reset detected\n"));
QLCleanupAfterReset(pDevExt);
break;
case MBOX_ASTS_TIMEOUT_RESET:
QLPrint((1, "ISR: command timeout reset\n"));
QLCleanupAfterReset(pDevExt);
break;
default:
QLPrint((1, "ISR: Unexpected MailBox Response: %x", status));
QLPrint((1, " %x", ISP_READ(pDevExt->Adapter, mailbox1)));
QLPrint((1, " %x", ISP_READ(pDevExt->Adapter, mailbox2)));
QLPrint((1, " %x", ISP_READ(pDevExt->Adapter, mailbox3)));
QLPrint((1, " %x", ISP_READ(pDevExt->Adapter, mailbox4)));
QLPrint((1, " %x\n", ISP_READ(pDevExt->Adapter, mailbox5)));
}
QLLogError(pDevExt, NULL, SP_INTERNAL_ADAPTER_ERROR, (ULONG)status);
// Clear the semaphore lock
ISP_WRITE(pDevExt->Adapter, bus_sema, 0);
}
// Get current "response in" pointer
response_in = ISP_READ(pDevExt->Adapter, mailbox5);
// Clear risc interrupt
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_CMD_CLEAR_RISC_INT);
// Process all responses from response queue
while (pDevExt->response_out != response_in)
{
// Get pointer to next response entry
pStsEntry = (PSTATUS_ENTRY)pDevExt->response_ptr;
// Advance pointers for next entry
if (pDevExt->response_out == (RESPONSE_QUEUE_DEPTH - 1))
{
pDevExt->response_out = 0;
pDevExt->response_ptr = pDevExt->pResponseQueue;
}
else
{
pDevExt->response_out++;
pDevExt->response_ptr++;
}
// Verify we have a valid Status queue entry
if (pStsEntry->hdr.entry_type != ET_STATUS ||
pStsEntry->hdr.flags & EF_ERROR_MASK)
{
QLPrint((1, "ISR - Invalid Status queue entry\n"));
}
// Set SRB pointer from queue entry handle
pSrb = (PSCSI_REQUEST_BLOCK)pStsEntry->handle;
// Check for normal completion
if (pStsEntry->completion_status == SCS_COMPLETE &&
pStsEntry->scsi_status == SCSISTAT_GOOD)
{
pSrb->SrbStatus = SRB_STATUS_SUCCESS;
pSrb->ScsiStatus = SCSISTAT_GOOD;
ScsiPortNotification(RequestComplete, pDevExt, pSrb);
}
// Error handling
else
{
// Convert ISP/SCSI error status to SRB error status
SetErrorStatus(pDevExt, pSrb, pStsEntry);
// Looks like retries are handled at a higher level
// Just return the error status (and sense data)
QLPrint((1, "ISR: Request completed with error = %x\n", pSrb->SrbStatus));
if (pSrb->SrbStatus == SRB_STATUS_DATA_OVERRUN)
{
pSrb->DataTransferLength -= pStsEntry->residual;
}
ScsiPortNotification(RequestComplete, pDevExt, pSrb);
}
}
// Done with responses, update the ISP
ISP_WRITE(pDevExt->Adapter, mailbox5, pDevExt->response_out);
} while (ISP_READ(pDevExt->Adapter, bus_isr) & BUS_ISR_RISC_INT);
// Check for waiting SRB and try to start it again
if (pDevExt->AdapterFlags & AFLG_SRB_WAITING)
{
pSrb = pDevExt->WaitingSrb;
if (SendCommandToIsp(pDevExt, pSrb))
{
// Command queued OK this time, notify port driver
pDevExt->AdapterFlags &= ~AFLG_SRB_WAITING;
ScsiPortNotification(NextLuRequest, pDevExt, pSrb->PathId,
pSrb->TargetId, pSrb->Lun);
}
}
return(TRUE);
}
/************************************************************************/
/* */
/* SetErrorStatus */
/* */
/* This routine is called by the interrupt service routine to */
/* convert an error status from the ISP into a SRB error status. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* pSrb - Pointer to SRB */
/* pStsEntry - Pointer to status entry from the ISP */
/* */
/* Return Value: */
/* */
/* None */
/* */
/************************************************************************/
VOID SetErrorStatus(PHW_DEVICE_EXTENSION pDevExt, PSCSI_REQUEST_BLOCK pSrb,
PSTATUS_ENTRY pStsEntry)
{
ULONG length;
USHORT mbox_sts[6];
QLPrint((1, "ISR: completion sts=%x", pStsEntry->completion_status));
QLPrint((1, " SCSI ID=%x", pSrb->TargetId));
QLPrint((1, " SCSI sts=%x", pStsEntry->scsi_status));
QLPrint((1, " sense key=%x\n", (USHORT)(pStsEntry->req_sense_data[2])));
// Convert ISP completion status to SRB error code
if (pStsEntry->completion_status != SCS_COMPLETE)
{
if (pStsEntry->completion_status < sizeof(CmpStsErrorMap))
{
pSrb->SrbStatus = CmpStsErrorMap[pStsEntry->completion_status];
}
else
{
pSrb->SrbStatus = SRB_STATUS_ERROR;
}
if (pStsEntry->completion_status == SCS_RESET_OCCURRED ||
pStsEntry->completion_status == SCS_TIMEOUT)
{
QLCleanupAfterReset(pDevExt);
}
// Log error (if not data underrun and not Inquiry selection timeout)
if (pSrb->SrbStatus != SRB_STATUS_DATA_OVERRUN &&
(pStsEntry->completion_status != SCS_INCOMPLETE ||
pSrb->Cdb[0] != SCSIOP_INQUIRY))
{
QLLogError(pDevExt, pSrb, SP_INTERNAL_ADAPTER_ERROR,
(ULONG)pStsEntry->completion_status);
}
}
// Handle SCSI error status, return sense data
else
{
// Return scsi status
pSrb->ScsiStatus = (UCHAR)pStsEntry->scsi_status;
pSrb->SrbStatus = SRB_STATUS_ERROR;
switch(pStsEntry->scsi_status)
{
case SCSISTAT_CHECK_CONDITION:
// Return request sense data
if ((pStsEntry->state_flags & SS_GOT_SENSE) &&
!(pSrb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
pSrb->SenseInfoBufferLength)
{
QLPrint((1, "ISR: Returning sense data\n"));
if (pStsEntry->req_sense_length > pSrb->SenseInfoBufferLength)
length = (ULONG)(pSrb->SenseInfoBufferLength);
else
length = (ULONG)(pStsEntry->req_sense_length);
ScsiPortMoveMemory(pSrb->SenseInfoBuffer,
pStsEntry->req_sense_data, length);
pSrb->SenseInfoBufferLength = (UCHAR)length;
pSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
// If check condition and autosense disabled, need to
// restart command queue
if (pSrb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE)
{
// Restart RISC command queue
QLPrint((1, "ISR: Restart RISC queue\n"));
if (!MailboxCommand(pDevExt, mbox_sts, 2, 3,
MBOX_CMD_START_QUEUE,
(USHORT)(((USHORT)(pSrb->TargetId) << 8) | pSrb->Lun),
0,0,0,0))
{
QLPrint((1, "ISR: START QUEUE command failed\n"));
}
}
break;
}
}
}
/************************************************************************/
/* */
/* QLLogError */
/* */
/* This routine is called to log an error to the system. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* pSrb - Supplies pointer to SRB or NULL if no SRB */
/* ErrorCode - Supplies the error code to log with the error */
/* UniqueId - Supplies the unique error identifier */
/* */
/* Return Value: */
/* */
/* None */
/* */
/************************************************************************/
VOID QLLogError(IN PHW_DEVICE_EXTENSION pDevExt, IN PSCSI_REQUEST_BLOCK pSrb,
IN ULONG ErrorCode, IN ULONG UniqueId)
{
QLPrint((1, "QLLogError called\n"));
if (pSrb == NULL)
{
ScsiPortLogError(
pDevExt, // HwDeviceExtension
NULL, // Srb
0, // PathId
0, // TargetId
0, // Lun
ErrorCode, // ErrorCode
UniqueId // UniqueId
);
}
else
{
ScsiPortLogError(
pDevExt, // HwDeviceExtension
pSrb, // Srb
pSrb->PathId, // PathId
pSrb->TargetId, // TargetId
pSrb->Lun, // Lun
ErrorCode, // ErrorCode
UniqueId // UniqueId
);
}
}
/************************************************************************/
/* */
/* MailboxCommand */
/* */
/* This routine issues a mailbox command and waits for completion */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* TRUE if command completed OK */
/* FALSE if error */
/* */
/************************************************************************/
BOOLEAN MailboxCommand(IN PHW_DEVICE_EXTENSION pDevExt,
USHORT *mbox_sts, UCHAR out_cnt, UCHAR in_cnt,
USHORT reg0, USHORT reg1, USHORT reg2,
USHORT reg3, USHORT reg4, USHORT reg5)
{
ULONG waitcnt;
USHORT retrycnt;
USHORT controlReg;
/*
QLPrint((1, " MailboxCommand %x", reg0));
QLPrint((1, " %x", reg1));
QLPrint((1, " %x", reg2));
QLPrint((1, " %x", reg3));
QLPrint((1, " %x", reg4));
QLPrint((1, " %x\n", reg5));
*/
// Disable adapter interrupts
controlReg = ISP_READ(pDevExt->Adapter, bus_icr);
if (controlReg & ICR_ENABLE_ALL_INTS)
{
ISP_WRITE(pDevExt->Adapter, bus_icr, 0);
}
// Use loop to retry mailbox command if busy status returned
retrycnt = 16; // retry a few times if busy
do
{
// Make sure host interrupt is clear
waitcnt = 10000; // wait a little bit if necessary
while (ISP_READ(pDevExt->Adapter, hccr) & HCCR_HOST_INT)
{
if (waitcnt-- == 0)
{
QLPrint((1, "MailboxCommand: host interrupt timeout\n"));
ISP_WRITE(pDevExt->Adapter, bus_icr, controlReg);
return(FALSE);
}
}
// Load data to the mailbox registers
switch (out_cnt)
{
case 6: ISP_WRITE(pDevExt->Adapter, mailbox5, reg5);
case 5: if (reg0 != MBOX_CMD_INIT_RESPONSE_QUEUE)
ISP_WRITE(pDevExt->Adapter, mailbox4, reg4);
case 4: ISP_WRITE(pDevExt->Adapter, mailbox3, reg3);
case 3: ISP_WRITE(pDevExt->Adapter, mailbox2, reg2);
case 2: ISP_WRITE(pDevExt->Adapter, mailbox1, reg1);
case 1: ISP_WRITE(pDevExt->Adapter, mailbox0, reg0);
}
// Wake up the ISP
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_CMD_SET_HOST_INT);
// Wait for command to complete
waitcnt = 100000;
while (!(ISP_READ(pDevExt->Adapter, bus_isr) & BUS_ISR_RISC_INT) ||
!(ISP_READ(pDevExt->Adapter, bus_sema) & BUS_SEMA_LOCK))
{
if (waitcnt-- == 0)
{
QLPrint((1, "MailboxCommand: cmd completion timeout\n"));
ISP_WRITE(pDevExt->Adapter, bus_icr, controlReg);
return(FALSE);
}
}
// Save away status registers
mbox_sts[0] = ISP_READ(pDevExt->Adapter, mailbox0);
switch (in_cnt)
{
case 6: mbox_sts[5] = ISP_READ(pDevExt->Adapter, mailbox5);
case 5: if (reg0 != MBOX_CMD_INIT_RESPONSE_QUEUE)
mbox_sts[4] = ISP_READ(pDevExt->Adapter, mailbox4);
case 4: mbox_sts[3] = ISP_READ(pDevExt->Adapter, mailbox3);
case 3: mbox_sts[2] = ISP_READ(pDevExt->Adapter, mailbox2);
case 2: mbox_sts[1] = ISP_READ(pDevExt->Adapter, mailbox1);
}
// Clear the semaphore lock
ISP_WRITE(pDevExt->Adapter, bus_sema, 0);
// Clear interrupt
ISP_WRITE(pDevExt->Adapter, hccr, HCCR_CMD_CLEAR_RISC_INT);
// Check status from ISP
switch (mbox_sts[0])
{
case MBOX_STS_BUSY:
QLPrint((1, "MailboxCommand: ISP busy\n"));
break; // retry command
case MBOX_STS_COMMAND_COMPLETE:
ISP_WRITE(pDevExt->Adapter, bus_icr, controlReg);
return(TRUE); // return good status
case MBOX_ASTS_SCSI_BUS_RESET:
pDevExt->AdapterFlags |= AFLG_SEND_MARKER;
QLPrint((1, "MailboxCommand: ISP SCSI bus RESET seen\n"));
break; // retry command
default:
QLPrint((1, "MailboxCommand: error status = %lx\n", mbox_sts[0]));
ISP_WRITE(pDevExt->Adapter, bus_icr, controlReg);
QLLogError(pDevExt, NULL, SP_INTERNAL_ADAPTER_ERROR,
(ULONG)mbox_sts[0]);
return(FALSE); // return error status
}
} while (retrycnt--);
QLPrint((1, "MailboxCommand: busy timeout\n"));
ISP_WRITE(pDevExt->Adapter, bus_icr, controlReg);
return(FALSE);
}
/************************************************************************/
/* */
/* SendMarker */
/* */
/* This call sends a Marker packet to the ISP */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* TRUE - marker sent */
/* FALSE - no slot in request queue */
/* */
/************************************************************************/
BOOLEAN SendMarker(IN PHW_DEVICE_EXTENSION pDevExt)
{
PMARKER_ENTRY pMarkEntry;
// Disable ISP interrupts
ISP_WRITE(pDevExt->Adapter, bus_icr, 0);
// Check available queue space
if (!CheckQueueSpace(pDevExt, 1))
{
ISP_WRITE(pDevExt->Adapter, bus_icr, ICR_ENABLE_ALL_INTS | ICR_ENABLE_RISC_INT);
return(FALSE); // No queue space, return error
}
// Reset the flag so we don't send another marker
pDevExt->AdapterFlags &= ~AFLG_SEND_MARKER;
// Get pointer to the queue entry for the marker
pMarkEntry = (PMARKER_ENTRY)pDevExt->request_ptr;
// Move the internal pointers for the request queue
if (pDevExt->request_in == (REQUEST_QUEUE_DEPTH - 1))
{
pDevExt->request_in = 0;
pDevExt->request_ptr = pDevExt->pRequestQueue;
}
else
{
pDevExt->request_in++;
pDevExt->request_ptr++;
}
// Put the marker in the request queue
pMarkEntry->hdr.entry_type = ET_MARKER;
pMarkEntry->hdr.entry_cnt = 1;
pMarkEntry->hdr.flags = 0;
pMarkEntry->hdr.sys_def_1 = 0;
pMarkEntry->reserved0 = 0;
pMarkEntry->target_id = 0;
pMarkEntry->target_lun = 0;
pMarkEntry->reserved1 = 0;
pMarkEntry->modifier = 2;
// Tell ISP it's got a new I/O request
ISP_WRITE(pDevExt->Adapter, mailbox4, pDevExt->request_in);
// One less I/O slot
pDevExt->queue_space--;
// Enable ISP interrupts
ISP_WRITE(pDevExt->Adapter, bus_icr, ICR_ENABLE_ALL_INTS | ICR_ENABLE_RISC_INT);
return(TRUE);
}
/************************************************************************/
/* */
/* CheckQueueSpace */
/* */
/* This call checks if enough request queue slots are available */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* slotcnt - number of needed slots */
/* */
/* Return Value: */
/* */
/* TRUE - request slots are available */
/* FALSE - request slots not available */
/* */
/************************************************************************/
BOOLEAN CheckQueueSpace(IN PHW_DEVICE_EXTENSION pDevExt, USHORT slotcnt)
{
USHORT mailbox4;
// Update available slot count
mailbox4 = ISP_READ(pDevExt->Adapter, mailbox4);
pDevExt->request_out = mailbox4;
if (pDevExt->request_in == pDevExt->request_out)
{
pDevExt->queue_space = REQUEST_QUEUE_DEPTH - 1;
}
else if (pDevExt->request_in > pDevExt->request_out)
{
pDevExt->queue_space = ((REQUEST_QUEUE_DEPTH - 1) -
(pDevExt->request_in - pDevExt->request_out));
}
else
{
pDevExt->queue_space = (pDevExt->request_out-pDevExt->request_in)-1;
}
// Check if enough are now available
if (pDevExt->queue_space >= slotcnt)
{
return(TRUE); // Queue space available, return
}
else
{
QLPrint((1, "CheckQueueSpace: no request queue space\n"));
return(FALSE); // No space, return error status
}
}
/************************************************************************/
/* */
/* QLResetScsiBus */
/* */
/* This function resets the SCSI bus and calls the reset cleanup */
/* function. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* PathId - Supplies the path id of the bus */
/* */
/* Return Value: */
/* */
/* TRUE - Indicating the reset is complete */
/* FALSE - if error doing reset */
/* */
/************************************************************************/
BOOLEAN QLResetScsiBus(IN PVOID ServiceContext, IN ULONG PathId)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
USHORT mbox_sts[6];
USHORT i;
QLPrint((1, "QLResetScsiBus: Resetting the SCSI bus\n"));
// Reset SCSI bus
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_BUS_RESET,
pDevExt->Bus_Reset_Delay,
0,0,0,0))
{
QLPrint((1, "QLResetScsiBus: SCSI BUS RESET command failed\n"));
return(FALSE);
}
// Wait for RISC reset delay
for (i = 0; i < pDevExt->Bus_Reset_Delay * 1000; i++)
ScsiPortStallExecution((ULONG)1000);
QLCleanupAfterReset(pDevExt);
return(TRUE);
}
/************************************************************************/
/* */
/* QLCleanupAfterReset */
/* */
/* This routine is called after a SCSI bus reset occurs. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* None */
/* */
/************************************************************************/
VOID QLCleanupAfterReset(IN PHW_DEVICE_EXTENSION pDevExt)
{
// Reenable ISP queues
if (!SendMarker(pDevExt))
{
QLPrint((1, "QLCleanupAfterReset: Failed to send marker\n"));
pDevExt->AdapterFlags |= AFLG_SEND_MARKER;
}
ScsiPortNotification(ResetDetected, pDevExt, NULL);
}
/************************************************************************/
/* */
/* QLAdapterState */
/* */
/* This function is called for switching states between real and */
/* protected mode drivers. */
/* */
/* Arguments: */
/* */
/* ServiceContext - Supplies a pointer to the device extension */
/* Context - */
/* SaveState - TRUE indicates adapter state should be saved */
/* FALSE indicates adapter state should be restored */
/* */
/* Return Value: */
/* */
/* TRUE - Indicating the operation is complete */
/* FALSE - if error */
/* */
/************************************************************************/
BOOLEAN QLAdapterState(IN PVOID ServiceContext,IN PVOID Context,IN BOOLEAN SaveState)
{
PHW_DEVICE_EXTENSION pDevExt = ServiceContext;
USHORT mbox_sts[6];
if (SaveState)
{
QLPrint((1, "QLAdapterState: save adapter state\n"));
}
else
{
QLPrint((1, "QLAdapterState: restore adapter state\n"));
// Disable ISP interrupts for ROM BIOS
ISP_WRITE(pDevExt->Adapter, bus_icr, ICR_DISABLE_ALL_INTS);
// Disable RISC request queue
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_INIT_REQUEST_QUEUE,
0,
0,0,0,0))
{
QLPrint((1, "QLAdapterState: INIT REQUEST QUEUE cmd failed\n"));
return(FALSE);
}
// Disable RISC response queue
if (!MailboxCommand(pDevExt, mbox_sts, 2, 2,
MBOX_CMD_INIT_RESPONSE_QUEUE,
0,
0,0,0,0))
{
QLPrint((1, "QLAdapterState: INIT RESPONSE QUEUE cmd failed\n"));
return(FALSE);
}
}
return(TRUE);
}
/************************************************************************/
/* */
/* GetNovramParameters */
/* */
/* This routine gets NOVRAM parameters and sets up in host adapter */
/* block, use default values if NOVRAM data not valid. */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* */
/* Return Value: */
/* */
/* None */
/* */
/************************************************************************/
VOID GetNovramParameters(IN PHW_DEVICE_EXTENSION pDevExt)
{
NOVRAM NovramParameters;
PNOVRAM nvram_ptr = &NovramParameters;
USHORT i;
// Read in entire NOVRAM and perform checksum to make sure data is valid
if (ReadAllNovram(pDevExt, (UCHAR*)nvram_ptr))
{
QLPrint((1, "GetNovramParameters: parameters valid\n"));
// NOVRAM valid, use paramters
pDevExt->Config_Reg = nvram_ptr->Fifo_Threshold;
pDevExt->Host_Adapter_Enable = nvram_ptr->Host_Adapter_Enable;
pDevExt->Initiator_SCSI_Id = nvram_ptr->Initiator_SCSI_Id;
pDevExt->Bus_Reset_Delay = nvram_ptr->Bus_Reset_Delay;
// Only 1 retry and no delay for NT driver (Disk Administrator problem)
// pDevExt->Retry_Count = nvram_ptr->Retry_Count;
// pDevExt->Retry_Delay = nvram_ptr->Retry_Delay;
pDevExt->Retry_Count = NVRAM_DEF_RETRY_COUNT;
pDevExt->Retry_Delay = NVRAM_DEF_RETRY_DELAY;
pDevExt->ASync_Data_Setup_Time = nvram_ptr->ASync_Data_Setup_Time;
pDevExt->REQ_ACK_Active_Negation= nvram_ptr->REQ_ACK_Active_Negation;
pDevExt->DATA_Active_Negation = nvram_ptr->DATA_Line_Active_Negation;
pDevExt->Data_DMA_Burst_Enable = nvram_ptr->Data_DMA_Burst_Enable;
pDevExt->Cmd_DMA_Burst_Enable = nvram_ptr->Command_DMA_Burst_Enable;
pDevExt->Tag_Age_Limit = nvram_ptr->Tag_Age_Limit;
pDevExt->Selection_Timeout = nvram_ptr->Selection_Timeout;
pDevExt->Max_Queue_Depth = nvram_ptr->Max_Queue_Depth;
pDevExt->Termination_Low_Enable = nvram_ptr->Termination_Low_Enable;
pDevExt->Termination_High_Enable= nvram_ptr->Termination_High_Enable;
pDevExt->PCMC_Burst_Enable = nvram_ptr->PCMC_Burst_Enable;
pDevExt->Sixty_MHz_Enable = nvram_ptr->Sixty_MHz_Enable;
for (i = 0; i < 16; i++)
{
UCHAR temp = 0;
temp |= nvram_ptr->Id[i].Renegotiate_on_Error << 0;
temp |= nvram_ptr->Id[i].Stop_Queue_on_Check << 1;
temp |= nvram_ptr->Id[i].Auto_Request_Sense << 2;
temp |= nvram_ptr->Id[i].Tagged_Queuing << 3;
temp |= nvram_ptr->Id[i].Sync_Data_Transfers << 4;
temp |= nvram_ptr->Id[i].Wide_Data_Transfers << 5;
temp |= nvram_ptr->Id[i].Parity_Checking << 6;
temp |= nvram_ptr->Id[i].Disconnect_Allowed << 7;
pDevExt->Id[i].Capability = temp;
pDevExt->Id[i].Execution_Throttle = nvram_ptr->Id[i].Execution_Throttle;
pDevExt->Id[i].Sync_Period = nvram_ptr->Id[i].Sync_Period;
pDevExt->Id[i].Sync_Offset = nvram_ptr->Id[i].Sync_Offset;
pDevExt->Id[i].Device_Enable= nvram_ptr->Id[i].Device_Enable;
// Force auto request sense ON and stop queue OFF
pDevExt->Id[i].Capability |= CAP_AUTO_REQ_SENSE;
pDevExt->Id[i].Capability &= ~CAP_STOP_QUEUE_ON_CHECK;
if (pDevExt->Id[i].Capability & CAP_TAGGED_QUEUING)
{
pDevExt->AdapterFlags |= AFLG_TAGGED_QUEUING;
}
}
}
else
{
QLPrint((1, "GetNovramParameters: parameters NOT valid, using defaults\n"));
// Use defaults, NOVRAM was not programmed
pDevExt->Config_Reg = NVRAM_DEF_FIFO_THRESHOLD;
pDevExt->Host_Adapter_Enable = NVRAM_DEF_ADAPTER_ENABLE;
pDevExt->Initiator_SCSI_Id = NVRAM_DEF_INITIATOR_SCSI_ID;
pDevExt->Bus_Reset_Delay = NVRAM_DEF_BUS_RESET_DELAY;
pDevExt->Retry_Count = NVRAM_DEF_RETRY_COUNT;
pDevExt->Retry_Delay = NVRAM_DEF_RETRY_DELAY;
pDevExt->ASync_Data_Setup_Time = NVRAM_DEF_ASYNC_SETUP_TIME;
pDevExt->REQ_ACK_Active_Negation= NVRAM_DEF_REQ_ACK_ACTIVE_NEGATION;
pDevExt->DATA_Active_Negation = NVRAM_DEF_DATA_ACTIVE_NEGATION;
pDevExt->Data_DMA_Burst_Enable = NVRAM_DEF_DATA_DMA_BURST_ENABLE;
pDevExt->Cmd_DMA_Burst_Enable = NVRAM_DEF_CMD_DMA_BURST_ENABLE;
pDevExt->Tag_Age_Limit = NVRAM_DEF_TAG_AGE_LIMIT;
pDevExt->Selection_Timeout = NVRAM_DEF_SELECTION_TIMEOUT;
pDevExt->Max_Queue_Depth = NVRAM_DEF_MAX_QUEUE_DEPTH;
pDevExt->Termination_Low_Enable = NVRAM_DEF_TERMINATION_LOW_ENABLE;
pDevExt->Termination_High_Enable= NVRAM_DEF_TERMINATION_HIGH_ENABLE;
pDevExt->PCMC_Burst_Enable = NVRAM_DEF_PCMC_BURST_ENABLE;
pDevExt->Sixty_MHz_Enable = NVRAM_DEF_SIXTY_MHZ_ENABLE;
for (i = 0; i < 16; i++)
{
UCHAR temp = 0;
temp |= NVRAM_DEF_RENEGOTIATE_ON_ERROR << 0;
temp |= NVRAM_DEF_STOP_QUEUE_ON_CHECK << 1;
temp |= NVRAM_DEF_AUTO_REQUEST_SENSE << 2;
temp |= NVRAM_DEF_TAGGED_QUEUING << 3;
temp |= NVRAM_DEF_SYNC_DATA_TRANSFERS << 4;
temp |= NVRAM_DEF_WIDE_DATA_TRANSFERS << 5;
temp |= NVRAM_DEF_PARITY_CHECKING << 6;
temp |= NVRAM_DEF_DISCONNECT_ALLOWED << 7;
pDevExt->Id[i].Capability = temp;
pDevExt->Id[i].Execution_Throttle = NVRAM_DEF_EXECUTION_THROTTLE;
pDevExt->Id[i].Sync_Period = NVRAM_DEF_SYNC_PERIOD;
pDevExt->Id[i].Sync_Offset = NVRAM_DEF_SYNC_OFFSET;
pDevExt->Id[i].Device_Enable= NVRAM_DEF_DEVICE_ENABLE;
if (pDevExt->Id[i].Capability & CAP_TAGGED_QUEUING)
{
pDevExt->AdapterFlags |= AFLG_TAGGED_QUEUING;
}
}
}
// Enable burst mode
if (pDevExt->Data_DMA_Burst_Enable || pDevExt->Cmd_DMA_Burst_Enable)
{
pDevExt->Config_Reg |= NVRAM_CONFIG_BURST_ENABLE;
}
}
/************************************************************************/
/* */
/* ReadAllNovram */
/* */
/* This routine is called to read the entire NOVRAM into buffer */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* buf - pointer to parameter buffer */
/* */
/* Return Value: */
/* */
/* TRUE if NOVRAM read OK */
/* FALSE if NOVRAM not valid */
/* */
/************************************************************************/
BOOLEAN ReadAllNovram(IN PHW_DEVICE_EXTENSION pDevExt, UCHAR* buf)
{
UCHAR* ptr;
UCHAR sum;
USHORT index;
// Loop through the entire NOVRAM (64 words)
ptr = (UCHAR*)buf;
for (index = 0; index < 64; index++, ptr += 2)
{
// Read next word from NOVRAM
ReadNovramWord(pDevExt, index, (USHORT*)ptr);
}
// Check for a valid signiture
if (buf[0] != 'I' ||
buf[1] != 'S' ||
buf[2] != 'P' ||
buf[3] != ' ')
{
return(FALSE); // invalid signature
}
// Check version number for 2 or above
if (buf[4] < 2)
{
QLPrint((1, "NOVRAM wrong version\n"));
return(FALSE); // wrong version (old NOVRAM layout)
}
// Verify correct checksum
ptr = (UCHAR*)buf;
for (sum = 0, index = 0; index < 128; index++, ptr++)
{
sum += *ptr;
}
if (sum != 0)
{
return(FALSE); // bad checksum
}
return(TRUE);
}
/************************************************************************/
/* */
/* ReadNovramWord */
/* */
/* This routine is called to read a word from NOVRAM */
/* */
/* Arguments: */
/* */
/* pDevExt - Supplies a pointer to the device extension */
/* addr - NOVRAM word offset to read */
/* ptr - pointer to parameter buffer */
/* */
/* Return Value: */
/* */
/* None */
/* */
/************************************************************************/
VOID ReadNovramWord(IN PHW_DEVICE_EXTENSION pDevExt,
USHORT addr, USHORT* ptr)
{
USHORT word_val;
USHORT bit_val;
SHORT index;
// Select the chip
ISP_WRITE(pDevExt->Adapter, NvRam_reg, NVRAM_SELECT);
NovramDelay();
ISP_WRITE(pDevExt->Adapter, NvRam_reg, (NVRAM_SELECT | NVRAM_CLOCK));
NovramDelay();
// Setup 9 bit command word: 1 start bit, 2 opcode bits, 6 addr bits
word_val = (NVRAM_READ_OP << 6) | (addr & 0x3F);
// Send the request
for (index = 8; index >= 0; index--)
{
// determine if we should send a one or a zero
bit_val = 0;
if ((word_val >> index) & 1)
{
bit_val = NVRAM_DATA_OUT;
}
// send the bit to the NvRam
// 1. Select + data bit, delay
// 2. Select + data bit + clock, delay
// 3. Select + data bit (remove clock), delay
ISP_WRITE(pDevExt->Adapter, NvRam_reg, (USHORT)(NVRAM_SELECT | bit_val));
NovramDelay();
ISP_WRITE(pDevExt->Adapter, NvRam_reg,
(USHORT)(NVRAM_SELECT | bit_val | NVRAM_CLOCK));
NovramDelay();
ISP_WRITE(pDevExt->Adapter, NvRam_reg, (USHORT)(NVRAM_SELECT | bit_val));
NovramDelay();
}
// Zap the location we're reading to before starting
*ptr = 0;
// read the NvRam data
for (index = 15; index >= 0; index--)
{
// move all of bits over (bits are read in MSB manner)
*ptr <<= 1;
// cycle the clock
ISP_WRITE(pDevExt->Adapter, NvRam_reg, (NVRAM_SELECT | NVRAM_CLOCK));
NovramDelay();
// see if the incoming bit should be set or not
word_val = ISP_READ(pDevExt->Adapter, NvRam_reg);
if (word_val & NVRAM_DATA_IN)
{
*ptr |= 1;
}
// remove the clock
ISP_WRITE(pDevExt->Adapter, NvRam_reg, NVRAM_SELECT);
NovramDelay();
}
// Done with this request, clear the selection
ISP_WRITE(pDevExt->Adapter, NvRam_reg, NVRAM_DESELECT);
}
/************************************************************************/
/* */
/* NovramDelay */
/* */
/* This routine waits a little bit to let signals settle */
/* */
/* Arguments: */
/* */
/* None */
/* */
/* Return Value: */
/* */
/* None */
/* */
/************************************************************************/
VOID NovramDelay()
{
ScsiPortStallExecution((ULONG)NVRAM_DELAY_COUNT);
}