diff options
author | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:58:44 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:58:44 +0000 |
commit | 86297c41a26f18d924e64fc93321c59cbc4c48dd (patch) | |
tree | 376954c6b95b735d361875319a1a2a9db6a27527 /linux/src/drivers/scsi/tmscsiw.c | |
parent | 851137902d3e7ad87af177487df3eea53e940a1c (diff) |
1998-11-30 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
Clean up linux emulation code to make it architecture-independent
as much as possible.
* linux: Renamed from linuxdev.
* Makefile.in (objfiles): Add linux.o instead of linuxdev.o.
(MAKE): New variable. Used for the linux.o target.
* configure.in: Add AC_CHECK_TOOL(MAKE, make).
* i386/i386/spl.h: Include <i386/ipl.h>, for compatibility with
OSF Mach 3.0. Suggested by Elgin Lee <ehl@funghi.com>.
* linux/src: Renamed from linux/linux.
* linux/dev: Renamed from linux/mach.
* linux/Drivers.in (AC_INIT): Use dev/include/linux/autoconf.h,
instead of mach/include/linux/autoconf.h.
* Makefile.in (all): Target ../linux.o instead of ../linuxdev.o.
* linux/dev/drivers/block/genhd.c: Include <machine/spl.h> instead
of <i386/ipl.h>.
* linux/dev/drivers/net/auto_irq.c: Remove unneeded header files,
<i386/ipl.h> and <i386/pic.h>.
* linux/dev/init/main.c: Many i386-dependent codes moved to ...
* linux/dev/arch/i386/irq.c: ... here.
* linux/dev/arch/i386/setup.c: New file.
* linux/dev/arch/i386/linux_emul.h: Likewise.
* linux/dev/arch/i386/glue/timer.c: Merged into sched.c.
* linux/dev/arch/i386/glue/sched.c: Include <machine/spl.h> instead
of <i386/ipl.h>, and moved to ...
* linux/dev/kernel/sched.c: ... here.
* linux/dev/arch/i386/glue/block.c: Include <machine/spl.h> and
<linux_emul.h>, instead of i386-dependent header files, and
moved to ...
* linux/dev/glue/blocl.c: ... here.
* linux/dev/arch/i386/glue/net.c: Include <machine/spl.h> and
<linux_emul.h>, instead of i386-dependent header files, and
moved to ...
* linux/dev/glue/net.c: ... here.
* linux/dev/arch/i386/glue/misc.c: Remove `x86' and moved to ...
* linux/dev/glue/misc.c: ... here.
* linux/dev/arch/i386/glue/kmem.c: Moved to ...
* linux/dev/glue/kmem.c: ... here.
Diffstat (limited to 'linux/src/drivers/scsi/tmscsiw.c')
-rw-r--r-- | linux/src/drivers/scsi/tmscsiw.c | 2096 |
1 files changed, 2096 insertions, 0 deletions
diff --git a/linux/src/drivers/scsi/tmscsiw.c b/linux/src/drivers/scsi/tmscsiw.c new file mode 100644 index 0000000..d32b9fc --- /dev/null +++ b/linux/src/drivers/scsi/tmscsiw.c @@ -0,0 +1,2096 @@ +/*********************************************************************** + * FILE NAME : TMSCSIW.C * + * BY : C.L. Huang (ching@tekram.com.tw) * + * Description: Device Driver for Tekram DC-390W/U/F (T) PCI SCSI * + * Bus Master Host Adapter * + * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. * + ***********************************************************************/ +/* Minor enhancements and bugfixes by * + * Kurt Garloff <K.Garloff@ping.de> * + ***********************************************************************/ +/* HISTORY: * + * * + * REV# DATE NAME DESCRIPTION * + * 1.00 04/03/96 CLH First release * + * 1.01 04/11/96 CLH Maximum support up to 4 Adapters, * + * support KV 1_3_85 * + * 1.02 04/26/96 CLH fixed bug about EEpromBuf when >1 HA * + * 1.03 06/12/96 CLH fixed bug of Media Change for Removable * + * Device, scan all LUN. Support Pre2.0.10 * + * 1.04 06/18/96 CLH fixed bug of Command timeout .... * + * 1.05 10/04/96 CLH Updating for support KV 2.0.0, 2.0.20 * + * 1.06 10/30/96 KG Fixed bug in DC390W_abort(), module * + * support, added tmscsiw_proc_info() * + * 1.07 11/09/96 KG Fixed bug in tmscsiw_proc_info() * + * 1.08 11/18/96 CLH/KG ditto, null ptr in DC390W_Disconnect() * + * 1.09 11/30/96 KG Fixed bug in CheckEEpromCheckSum(), * + * add register the allocated IO space * + * 1.10 12/05/96 CLH Modify in tmscsiw_proc_info() and add * + * in DC390W_initAdapter() for 53C875 * + * Rev. F with double clock. * + * 1.11 02/04/97 CLH Fixed bug of Formatting a partition that* + * across 1GB boundary, with bad sector * + * checking. * + * 1.12 02/17/97 CLH Fixed bug in CheckEEpromCheckSum() * + ***********************************************************************/ + + +#define DC390W_DEBUG +/* #define CHK_UNDER_RUN */ + +#define SCSI_MALLOC + +#ifdef MODULE +#include <linux/module.h> +#endif + +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/system.h> +#include <linux/delay.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/config.h> + +#include <linux/version.h> +#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */ +#include "../block/blk.h" +#else +#include <linux/blk.h> +#endif + +#include "scsi.h" +#include "hosts.h" +#include "tmscsiw.h" +#include "constants.h" +#include "sd.h" +#include "scripts.h" +#include <linux/stat.h> + +#include "dc390w.h" + +#ifndef VERSION_ELF_1_2_13 +struct proc_dir_entry proc_scsi_tmscsiw ={ + PROC_SCSI_DC390WUF, 7 ,"tmscsiw", + S_IFDIR | S_IRUGO | S_IXUGO, 2 + }; +#endif + +static void DC390W_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void PrepareSG( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void DoingSRB_Done( PACB pACB ); +static void ExceptionHandler(ULONG wlval, PACB pACB, PDCB pDCB); +static void ParityError( PACB pACB, PDCB pDCB ); +static void PhaseMismatch( PACB pACB ); +static void DC390W_ScsiRstDetect( PACB pACB ); +static void DC390W_ResetSCSIBus( PACB pACB ); +static void DC390W_ResetSCSIBus2( PACB pACB ); +static void AdjustTemp( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void SetXferRate( PACB pACB, PDCB pDCB ); +static void DataIOcommon( PACB pACB, ULONG Swlval, ULONG Cwlval ); +static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ); + +static void DC390W_CmdCompleted( PACB pACB ); +static void DC390W_Reselected( PACB pACB ); +static void DC390W_Reselected1( PACB pACB ); +static void DC390W_ReselectedT( PACB pACB ); +static void DC390W_Disconnected( PACB pACB ); +static void DC390W_MessageExtnd( PACB pACB ); +static void DC390W_Signal( PACB pACB ); +static void DC390W_UnknownMsg( PACB pACB ); +static void DC390W_MessageOut( PACB pACB ); +static void DC390W_FatalError( PACB pACB ); +static void DC390W_MessageSync( PACB pACB ); +static void DC390W_MessageWide( PACB pACB ); +static void DC390W_RestorePtr( PACB pACB ); +static void DC390W_MsgReject( PACB pACB ); +static void DC390W_Debug( PACB pACB ); +static void DC390W_download_script (struct Scsi_Host *host); + +int DC390W_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index); +void DC390W_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ); +void MyDelay( void ); +void EnDisableCE( UCHAR Flag, USHORT scsiIOPort ); +void EEpromOutDI( USHORT Carry, USHORT scsiIOPort ); +void EEpromPrepare( UCHAR EEpromCmd, USHORT scsiIOPort ); +void ReadEEprom( PUCHAR EEpromBuf, USHORT scsiIOPort ); +UCHAR EEpromInDo(USHORT scsiIOPort); +USHORT EEpromGetData(USHORT scsiIOPort); +USHORT CheckEEpromCheckSum( PUCHAR EEpromBuf, USHORT scsiIOPort); + +#ifdef MODULE +static int DC390W_release(struct Scsi_Host *host); +static int DC390W_shutdown (struct Scsi_Host *host); +#endif + + +static ULONG jmp_table16; +static ULONG jmp_din16; +static ULONG jmp_dout16; +static PSHT pSHT_start = NULL; +static PSH pSH_start = NULL; +static PSH pSH_current = NULL; +static PACB pACB_start= NULL; +static PACB pACB_current = NULL; +static PDCB pPrevDCB = NULL; +static USHORT adapterCnt = 0; +static USHORT InitialTime = 0; +static USHORT CurrDCBscntl3 = 0; +static UCHAR pad_buffer[128]; + +static PVOID IntVector[]={ + DC390W_CmdCompleted, + DC390W_Reselected, + DC390W_Reselected1, + DC390W_ReselectedT, + DC390W_Disconnected, + DC390W_MessageExtnd, + DC390W_Signal, + DC390W_UnknownMsg, + DC390W_MessageOut, + DC390W_FatalError, + DC390W_MessageSync, + DC390W_MessageWide, + DC390W_RestorePtr, + DC390W_MsgReject, + DC390W_Debug, + DC390W_FatalError + }; + +UCHAR eepromBuf[MAX_ADAPTER_NUM][128]; + +UCHAR clock_period[12] = {25, 31, 37, 43, 50, 62, 75, 125, 12, 15, 18, 21}; +UCHAR baddevname[2][28] ={ + "SEAGATE ST3390N 9546", + "SEAGATE ST3390N ??? 0399"}; + +#define BADDEVCNT 2 + +/*********************************************************************** + * + * + * + **********************************************************************/ +static void +QLinkcmd( PSCSICMD cmd, PDCB pDCB ) +{ + ULONG flags; + PSCSICMD pcmd; + + save_flags(flags); + cli(); + + if( !pDCB->QIORBCnt ) + { + pDCB->pQIORBhead = cmd; + pDCB->pQIORBtail = cmd; + pDCB->QIORBCnt++; + cmd->next = NULL; + } + else + { + pcmd = pDCB->pQIORBtail; + pcmd->next = cmd; + pDCB->pQIORBtail = cmd; + pDCB->QIORBCnt++; + cmd->next = NULL; + } + + restore_flags(flags); +} + + +static PSCSICMD +Getcmd( PDCB pDCB ) +{ + ULONG flags; + PSCSICMD pcmd; + + save_flags(flags); + cli(); + + pcmd = pDCB->pQIORBhead; + pDCB->pQIORBhead = pcmd->next; + pcmd->next = NULL; + pDCB->QIORBCnt--; + + restore_flags(flags); + return( pcmd ); +} + + +static PSRB +GetSRB( PACB pACB ) +{ + ULONG flags; + PSRB pSRB; + + save_flags(flags); + cli(); + + pSRB = pACB->pFreeSRB; + if( pSRB ) + { + pACB->pFreeSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + restore_flags(flags); + return( pSRB ); +} + + +static void +RewaitSRB( PDCB pDCB, PSRB pSRB ) +{ + PSRB psrb1; + ULONG flags; + UCHAR bval; + + save_flags(flags); + cli(); + pDCB->GoingSRBCnt--; + psrb1 = pDCB->pGoingSRB; + if( pSRB == psrb1 ) + { + pDCB->pGoingSRB = psrb1->pNextSRB; + } + else + { + while( pSRB != psrb1->pNextSRB ) + psrb1 = psrb1->pNextSRB; + psrb1->pNextSRB = pSRB->pNextSRB; + if( pSRB == pDCB->pGoingLast ) + pDCB->pGoingLast = psrb1; + } + if( (psrb1 = pDCB->pWaitingSRB) ) + { + pSRB->pNextSRB = psrb1; + pDCB->pWaitingSRB = pSRB; + } + else + { + pSRB->pNextSRB = NULL; + pDCB->pWaitingSRB = pSRB; + pDCB->pWaitLast = pSRB; + } + + bval = pSRB->TagNumber; + pDCB->TagMask &= (~(1 << bval)); /* Free TAG number */ + restore_flags(flags); +} + + +static void +DoWaitingSRB( PACB pACB ) +{ + ULONG flags; + PDCB ptr, ptr1; + PSRB pSRB; + + save_flags(flags); + cli(); + + if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) ) + { + ptr = pACB->pDCBRunRobin; + if( !ptr ) + { + ptr = pACB->pLinkDCB; + pACB->pDCBRunRobin = ptr; + } + ptr1 = ptr; + for( ;ptr1; ) + { + pACB->pDCBRunRobin = ptr1->pNextDCB; + if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) || + !( pSRB = ptr1->pWaitingSRB ) ) + { + if(pACB->pDCBRunRobin == ptr) + break; + ptr1 = ptr1->pNextDCB; + } + else + { + DC390W_StartSCSI(pACB, ptr1, pSRB); + ptr1->GoingSRBCnt++; + if( ptr1->pWaitLast == pSRB ) + { + ptr1->pWaitingSRB = NULL; + ptr1->pWaitLast = NULL; + } + else + { + ptr1->pWaitingSRB = pSRB->pNextSRB; + } + pSRB->pNextSRB = NULL; + + if( ptr1->pGoingSRB ) + ptr1->pGoingLast->pNextSRB = pSRB; + else + ptr1->pGoingSRB = pSRB; + ptr1->pGoingLast = pSRB; + + break; + } + } + } + restore_flags(flags); + return; +} + + +static void +SRBwaiting( PDCB pDCB, PSRB pSRB) +{ + if( pDCB->pWaitingSRB ) + { + pDCB->pWaitLast->pNextSRB = pSRB; + pDCB->pWaitLast = pSRB; + pSRB->pNextSRB = NULL; + } + else + { + pDCB->pWaitingSRB = pSRB; + pDCB->pWaitLast = pSRB; + } +} + + +static void +SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB ) +{ + ULONG flags; + PDCB pDCB; + + save_flags(flags); + cli(); + + pDCB = pSRB->pSRBDCB; + PrepareSG( pACB, pDCB, pSRB ); + if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) || + (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) ) + { + SRBwaiting(pDCB, pSRB); + goto SND_EXIT; + } + + if( pDCB->pWaitingSRB ) + { + SRBwaiting(pDCB, pSRB); +/* pSRB = GetWaitingSRB(pDCB); */ + pSRB = pDCB->pWaitingSRB; + pDCB->pWaitingSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + + DC390W_StartSCSI(pACB, pDCB, pSRB); + pDCB->GoingSRBCnt++; + if( pDCB->pGoingSRB ) + { + pDCB->pGoingLast->pNextSRB = pSRB; + pDCB->pGoingLast = pSRB; + } + else + { + pDCB->pGoingSRB = pSRB; + pDCB->pGoingLast = pSRB; + } + +SND_EXIT: + restore_flags(flags); + return; +} + + +/*********************************************************************** + * Function : static int DC390W_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - function called on completion, with + * a pointer to the command descriptor. + * + * Returns : 0 + * + ***********************************************************************/ + +int +DC390W_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) +{ + USHORT ioport, i; + Scsi_Cmnd *pcmd; + struct Scsi_Host *psh; + PACB pACB; + PDCB pDCB; + PSRB pSRB; + ULONG flags; + PUCHAR ptr,ptr1; + + psh = cmd->host; + pACB = (PACB ) psh->hostdata; + ioport = pACB->IOPortBase; + +#ifdef DC390W_DEBUG0 + printk("Cmd=%x,",cmd->cmnd[0]); +#endif + + if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) ) + { + pACB->scan_devices = 0; + pPrevDCB->pNextDCB = pACB->pLinkDCB; + } + else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) ) + { + pACB->scan_devices = 0; + pPrevDCB->pNextDCB = pACB->pLinkDCB; + } + + if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) ) + { +/* printk("DC390W: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return( 0 ); + } + + if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + { + if( pACB->DeviceCnt < MAX_DEVICES ) + { + pACB->DCBmap[cmd->target] |= (1 << cmd->lun); + pDCB = pACB->pDCB_free; +#ifdef DC390W_DEBUG0 + printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target); +#endif + DC390W_initDCB( pACB, pDCB, cmd ); + } + else /* ???? */ + { +/* printk("DC390W: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return(0); + } + } + else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + { +/* printk("DC390W: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + return(0); + } + else + { + pDCB = pACB->pLinkDCB; + while( (pDCB->UnitSCSIID != cmd->target) || + (pDCB->UnitSCSILUN != cmd->lun) ) + { + pDCB = pDCB->pNextDCB; + } +#ifdef DC390W_DEBUG0 + printk("pDCB=%8x,ID=%2x,Scan=%1x", (UINT) pDCB, cmd->target, + pACB->scan_devices); +#endif + } + + cmd->scsi_done = done; + cmd->result = 0; + + save_flags(flags); + cli(); + + if( pDCB->QIORBCnt ) + { + QLinkcmd( cmd, pDCB ); + pcmd = Getcmd( pDCB ); + } + else + pcmd = cmd; + + pSRB = GetSRB( pACB ); + + if( !pSRB ) + { + QLinkcmd( pcmd, pDCB ); + restore_flags(flags); + return(0); + } + +/* BuildSRB(pSRB); */ + + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + ptr = (PUCHAR) pSRB->CmdBlock; + ptr1 = (PUCHAR) pcmd->cmnd; + (UCHAR) pSRB->__command[0] = pcmd->cmd_len; + for(i=0; i< pcmd->cmd_len; i++) + { + *ptr = *ptr1; + ptr++; + ptr1++; + } + if( pcmd->use_sg ) + { + pSRB->SGcount = (UCHAR) pcmd->use_sg; + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + } + else if( pcmd->request_buffer ) + { + pSRB->SGcount = 1; + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + else + pSRB->SGcount = 0; + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + if( pDCB->DevType != TYPE_TAPE ) + pSRB->RetryCnt = 1; + else + pSRB->RetryCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->ScratchABuf = 0; + pSRB->SRBState = 0; + pSRB->RemainSegPtr = 0; + pSRB->XferredLen = 0; + SendSRB( pcmd, pACB, pSRB ); + + restore_flags(flags); + return(0); +} + + +static void +DoNextCmd( PACB pACB, PDCB pDCB ) +{ + Scsi_Cmnd *pcmd; + PSRB pSRB; + ULONG flags; + PUCHAR ptr,ptr1; + USHORT i; + + + if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) + return; + save_flags(flags); + cli(); + + pcmd = Getcmd( pDCB ); + pSRB = GetSRB( pACB ); + if( !pSRB ) + { + QLinkcmd( pcmd, pDCB ); + restore_flags(flags); + return; + } + + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + ptr = (PUCHAR) pSRB->CmdBlock; + ptr1 = (PUCHAR) pcmd->cmnd; + (UCHAR) pSRB->__command[0] = pcmd->cmd_len; + for(i=0; i< pcmd->cmd_len; i++) + { + *ptr = *ptr1; + ptr++; + ptr1++; + } + if( pcmd->use_sg ) + { + pSRB->SGcount = (UCHAR) pcmd->use_sg; + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + } + else if( pcmd->request_buffer ) + { + pSRB->SGcount = 1; + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + else + pSRB->SGcount = 0; + + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + if( pDCB->DevType != TYPE_TAPE ) + pSRB->RetryCnt = 1; + else + pSRB->RetryCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->ScratchABuf = 0; + pSRB->SRBState = 0; + SendSRB( pcmd, pACB, pSRB ); + + restore_flags(flags); + return; +} + + +/*********************************************************************** + * Function: + * DC390W_bios_param + * + * Description: + * Return the disk geometry for the given SCSI device. + ***********************************************************************/ +#ifdef VERSION_ELF_1_2_13 +int DC390W_bios_param(Disk *disk, int devno, int geom[]) +#else +int DC390W_bios_param(Disk *disk, kdev_t devno, int geom[]) +#endif +{ + int heads, sectors, cylinders; + PACB pACB; + + pACB = (PACB) disk->device->host->hostdata; + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) ) + { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return (0); +} + + +/*********************************************************************** + * Function : int DC390W_abort (Scsi_Cmnd *cmd) + * + * Purpose : Abort an errant SCSI command + * + * Inputs : cmd - command to abort + * + * Returns : 0 on success, -1 on failure. + ***********************************************************************/ + +int +DC390W_abort (Scsi_Cmnd *cmd) +{ + ULONG flags; + PACB pACB; + PDCB pDCB, pdcb; + PSRB pSRB, psrb; + USHORT count, i; + PSCSICMD pcmd, pcmd1; + int status; + + +#ifdef DC390W_DEBUG0 + printk("DC390W : Abort Cmd."); +#endif + + save_flags(flags); + cli(); + + pACB = (PACB) cmd->host->hostdata; + pDCB = pACB->pLinkDCB; + pdcb = pDCB; + while( (pDCB->UnitSCSIID != cmd->target) || + (pDCB->UnitSCSILUN != cmd->lun) ) + { + pDCB = pDCB->pNextDCB; + if( pDCB == pdcb ) + goto NOT_RUN; + } + + if( pDCB->QIORBCnt ) + { + pcmd = pDCB->pQIORBhead; + if( pcmd == cmd ) + { + pDCB->pQIORBhead = pcmd->next; + pcmd->next = NULL; + pDCB->QIORBCnt--; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + for( count = pDCB->QIORBCnt, i=0; i<count-1; i++) + { + if( pcmd->next == cmd ) + { + pcmd1 = pcmd->next; + pcmd->next = pcmd1->next; + pcmd1->next = NULL; + pDCB->QIORBCnt--; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + else + { + pcmd = pcmd->next; + } + } + } + + pSRB = pDCB->pWaitingSRB; + if( !pSRB ) + goto ON_GOING; + if( pSRB->pcmd == cmd ) + { + pDCB->pWaitingSRB = pSRB->pNextSRB; + goto IN_WAIT; + } + else + { + psrb = pSRB; + if( !(psrb->pNextSRB) ) + goto ON_GOING; + while( psrb->pNextSRB->pcmd != cmd ) + { + psrb = psrb->pNextSRB; + if( !(psrb->pNextSRB) ) + goto ON_GOING; + } + pSRB = psrb->pNextSRB; + psrb->pNextSRB = pSRB->pNextSRB; + if( pSRB == pDCB->pWaitLast ) + pDCB->pWaitLast = psrb; +IN_WAIT: + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; + cmd->next = NULL; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + +ON_GOING: + pSRB = pDCB->pGoingSRB; + for( count = pDCB->GoingSRBCnt, i=0; i<count; i++) + { + if( pSRB->pcmd != cmd ) + pSRB = pSRB->pNextSRB; + else + { + if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) ) + { + status = SCSI_ABORT_BUSY; + goto ABO_X; + } + else + { + status = SCSI_ABORT_SNOOZE; + goto ABO_X; + } + } + } + +NOT_RUN: + status = SCSI_ABORT_NOT_RUNNING; + +ABO_X: + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + restore_flags(flags); + return( status ); +} + + +static void +ResetDevParam( PACB pACB ) +{ + PDCB pDCB, pdcb; + + pDCB = pACB->pLinkDCB; + if( pDCB == NULL ) + return; + pdcb = pDCB; + do + { + if( pACB->AdaptType == DC390W ) + pdcb->DCBscntl3 = SYNC_CLK_F2+ASYNC_CLK_F2; + else + pdcb->DCBscntl3 = SYNC_CLK_F4+ASYNC_CLK_F4; + pdcb->DCBsxfer = 0; + pdcb = pdcb->pNextDCB; + } + while( pdcb != pDCB ); +} + + +static void +RecoverSRB( PACB pACB ) +{ + PDCB pDCB, pdcb; + PSRB psrb, psrb2; + USHORT cnt, i; + + pDCB = pACB->pLinkDCB; + if( pDCB == NULL ) + return; + pdcb = pDCB; + do + { + cnt = pdcb->GoingSRBCnt; + psrb = pdcb->pGoingSRB; + for (i=0; i<cnt; i++) + { + PrepareSG( pACB, pdcb, psrb ); + psrb2 = psrb; + psrb = psrb->pNextSRB; +/* RewaitSRB( pDCB, psrb ); */ + if( pdcb->pWaitingSRB ) + { + psrb2->pNextSRB = pdcb->pWaitingSRB; + pdcb->pWaitingSRB = psrb2; + } + else + { + pdcb->pWaitingSRB = psrb2; + pdcb->pWaitLast = psrb2; + psrb2->pNextSRB = NULL; + } + } + pdcb->GoingSRBCnt = 0; + pdcb->pGoingSRB = NULL; + pdcb->TagMask = 0; + pdcb = pdcb->pNextDCB; + } + while( pdcb != pDCB ); +} + + +/*********************************************************************** + * Function : int DC390W_reset (Scsi_Cmnd *cmd, ...) + * + * Purpose : perform a hard reset on the SCSI bus( and NCR chip). + * + * Inputs : cmd - command which caused the SCSI RESET + * + * Returns : 0 on success. + ***********************************************************************/ + +#ifdef VERSION_2_0_0 +int DC390W_reset(Scsi_Cmnd *cmd, unsigned int resetFlags) +#else +int DC390W_reset (Scsi_Cmnd *cmd) +#endif +{ + USHORT ioport; + unsigned long flags; + PACB pACB; + ULONG wlval; + UCHAR bval; + USHORT wval; + USHORT i; + + +#ifdef DC390W_DEBUG0 + printk("DC390W : Reset Cmd0,"); +#endif + + pACB = (PACB ) cmd->host->hostdata; + ioport = pACB->IOPortBase; + save_flags(flags); + cli(); + bval = inb(ioport+DCNTL); + bval |= IRQ_DISABLE; + outb(bval,ioport+DCNTL); /* disable interrupt */ + DC390W_ResetSCSIBus( pACB ); + for( i=0; i<500; i++ ) + udelay(1000); + for(;;) + { + bval = inb(ioport+ISTAT); + if( bval & SCSI_INT_PENDING ) + { + wval = inw( ioport+SIST0 ); + if( wval & (SCSI_RESET+SCSI_GERROR) ) + break; + } + if(bval & DMA_INT_PENDING) + { + bval = inb(ioport+DSTAT); + if(bval & ABORT_) + { + wval = inw( ioport+SIST0 ); + break; + } + } + } + bval = inb(ioport+DCNTL); + bval &= ~IRQ_DISABLE; + outb(bval,ioport+DCNTL); /* re-enable interrupt */ + + ioport = pACB->IOPortBase; + bval = inb(ioport+STEST3); + bval |= CLR_SCSI_FIFO; + outb(bval,ioport+STEST3); + bval = CLR_DMA_FIFO; + outb(bval,ioport+CTEST3); + ResetDevParam( pACB ); + DoingSRB_Done( pACB ); + pACB->pActiveDCB = NULL; + wlval = pACB->jmp_reselect; + outl(wlval,(ioport+DSP)); + + pACB->ACBFlag = 0; + DoWaitingSRB( pACB ); + restore_flags(flags); + return( SCSI_RESET_SUCCESS ); +} + + +#include "scsiio.c" + + +/*********************************************************************** + * Function : static void DC390W_initDCB + * + * Purpose : initialize the internal structures for a given DCB + * + * Inputs : cmd - pointer to this scsi cmd request block structure + * + ***********************************************************************/ +void DC390W_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ) +{ + PEEprom prom; + UCHAR bval; + USHORT index; + + if( pACB->DeviceCnt == 0 ) + { + pACB->pLinkDCB = pDCB; + pACB->pDCBRunRobin = pDCB; + pDCB->pNextDCB = pDCB; + pPrevDCB = pDCB; + } + else + pPrevDCB->pNextDCB = pDCB; + + pDCB->pDCBACB = pACB; + pDCB->QIORBCnt = 0; + pDCB->DCBselect = 0; + pDCB->DCBsxfer = 0; + pDCB->DCBsdid = cmd->target; + pDCB->UnitSCSIID = cmd->target; + pDCB->UnitSCSILUN = cmd->lun; + pDCB->pWaitingSRB = NULL; + pDCB->GoingSRBCnt = 0; + pDCB->TagMask = 0; + + pDCB->MaxCommand = 1; + pDCB->AdaptIndex = pACB->AdapterIndex; + index = pACB->AdapterIndex; + + prom = (PEEprom) &eepromBuf[index][cmd->target << 2]; + pDCB->DevMode = prom->EE_MODE1; + pDCB->NegoPeriod = clock_period[prom->EE_SPEED]; + + if( pACB->AdaptType == DC390W ) + pDCB->DCBscntl3 = SYNC_CLK_F2+ASYNC_CLK_F2; + else + pDCB->DCBscntl3 = SYNC_CLK_F4+ASYNC_CLK_F4; + + if( pDCB->DevMode & PARITY_CHK_ ) + pDCB->DCBscntl0 = EN_PARITY_CHK+SATN_IF_PARITY_ERR+FULL_ARBITRATION; + else + pDCB->DCBscntl0 = FULL_ARBITRATION; + + pDCB->AdpMode = eepromBuf[index][EE_MODE2]; + + if( pDCB->DevMode & EN_DISCONNECT_ ) + bval = 0xC0; + else + bval = 0x80; + bval |= cmd->lun; + pDCB->IdentifyMsg = bval; + + if( pDCB->DevMode & SYNC_NEGO_ ) + { + pDCB->SyncMode = SYNC_ENABLE; + pDCB->SyncOffset = SYNC_NEGO_OFFSET; + } + + if( pDCB->DevMode & WIDE_NEGO_ ) + { + if( cmd->lun ) + { + if( !(CurrDCBscntl3 & EN_WIDE_SCSI) ) + pDCB->DevMode &= ~WIDE_NEGO_; + } + else + CurrDCBscntl3 = 0; + } + pDCB->DCBFlag = 0; +} + + +/*********************************************************************** + * Function : static void DC390W_initSRB + * + * Purpose : initialize the internal structures for a given SRB + * + * Inputs : psrb - pointer to this scsi request block structure + * + ***********************************************************************/ +void DC390W_initSRB( PSRB psrb ) +{ +#ifndef VERSION_ELF_1_2_13 + psrb->PhysSRB = virt_to_phys( psrb ); + psrb->__command[1] = virt_to_phys( psrb->CmdBlock ); + psrb->__msgout0[0] = 1; + psrb->__msgout0[1] = virt_to_phys( psrb->MsgOutBuf ); + psrb->SegmentPad[0] = 16; + psrb->SegmentPad[1] = virt_to_phys( pad_buffer ); +#else + psrb->PhysSRB = (ULONG) psrb; + psrb->__command[1] = (ULONG) psrb->CmdBlock; + psrb->__msgout0[0] = 1; + psrb->__msgout0[1] = (ULONG) psrb->MsgOutBuf; + psrb->SegmentPad[0] = 16; + psrb->SegmentPad[1] = (ULONG) pad_buffer; +#endif +} + + +void DC390W_linkSRB( PACB pACB ) +{ + USHORT count, i; + PSRB psrb; + + count = pACB->SRBCount; + + for( i=0; i< count; i++) + { + if( i != count - 1) + pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1]; + else + pACB->SRB_array[i].pNextSRB = NULL; + psrb = (PSRB) &pACB->SRB_array[i]; + DC390W_initSRB( psrb ); + } +} + + +/*********************************************************************** + * Function : static void DC390W_initACB + * + * Purpose : initialize the internal structures for a given SCSI host + * + * Inputs : psh - pointer to this host adapter's structure + * + ***********************************************************************/ +void DC390W_initACB( PSH psh, USHORT chipType, ULONG io_port, UCHAR Irq, USHORT index ) +{ + PACB pACB; + USHORT i; + UCHAR adaptType, bval; + + + psh->can_queue = MAX_CMD_QUEUE; + psh->cmd_per_lun = MAX_CMD_PER_LUN; + psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID]; + psh->io_port = io_port; + psh->n_io_port = 0x80; + psh->irq = Irq; + + if( chipType == PCI_DEVICE_ID_NCR53C825A ) + adaptType = DC390W; + else + { + outb( 2, io_port+GPREG ); + bval = inb( io_port+GPREG ); + if( bval & 8 ) + adaptType = DC390U; + else + adaptType = DC390F; + } + + pACB = (PACB) psh->hostdata; + +#ifndef VERSION_ELF_1_2_13 + if( adaptType == DC390U ) + { + psh->max_id = 8; + pACB->max_id = 7; + } + else + { + psh->max_id = 16; + pACB->max_id = 15; + } + +#ifdef CONFIG_SCSI_MULTI_LUN + if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) + psh->max_lun = 8; + else +#endif + psh->max_lun = 1; + +#else + pACB->max_id = 7; +#endif + if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] ) + pACB->max_id--; + +#ifdef CONFIG_SCSI_MULTI_LUN + if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) + pACB->max_lun = 7; + else +#endif + pACB->max_lun = 0; + + pACB->pScsiHost = psh; + pACB->IOPortBase = (USHORT) io_port; + pACB->pLinkDCB = NULL; + pACB->pDCBRunRobin = NULL; + pACB->pActiveDCB = NULL; + pACB->pFreeSRB = pACB->SRB_array; + pACB->SRBCount = MAX_SRB_CNT; + pACB->AdapterIndex = index; + pACB->status = 0; + pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID]; + pACB->AdaptSCSILUN = 0; + pACB->DeviceCnt = 0; + pACB->IRQLevel = Irq; + pACB->AdaptType = adaptType; + pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2; + pACB->ACBFlag = 0; + pACB->scan_devices = 1; + pACB->Gmode2 = eepromBuf[index][EE_MODE2]; + if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) + pACB->LUNchk = 1; + pACB->pDCB_free = &pACB->DCB_array[0]; + DC390W_linkSRB( pACB ); + for(i=0; i<MAX_SCSI_ID; i++) + pACB->DCBmap[i] = 0; +} + + +/*********************************************************************** + * Function : static int DC390W_initAdapter + * + * Purpose : initialize the SCSI chip ctrl registers + * + * Inputs : psh - pointer to this host adapter's structure + * + ***********************************************************************/ +int DC390W_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +{ + USHORT ioport, wval; + UCHAR bval; + PACB pACB, pacb; + USHORT used_irq = 0; + + pacb = pACB_start; + if( pacb != NULL ) + { + for ( ; (pacb != (PACB) -1) ; ) + { + if( pacb->IRQLevel == Irq ) + { + used_irq = 1; + break; + } + else + pacb = pacb->pNextACB; + } + } + + if( !used_irq ) + { +#ifdef VERSION_ELF_1_2_13 + if( request_irq(Irq, DC390W_Interrupt, SA_INTERRUPT, "tmscsiw")) +#else + if( request_irq(Irq, DC390W_Interrupt, SA_INTERRUPT | SA_SHIRQ, "tmscsiw", NULL)) +#endif + { + printk("DC390W : register IRQ error!\n"); + return( -1 ); + } + } + request_region(io_port,psh->n_io_port,"tmscsiw"); + + ioport = (USHORT) io_port; + outb(IRQ_DISABLE, ioport+DCNTL); + outb(ABORT_OP, ioport+ISTAT); + udelay(100000); + outb(0, ioport+ISTAT); + bval = inb(ioport+DSTAT); + bval = inb(ioport+ISTAT); + wval = inw(ioport+SIST0); + + pACB = (PACB) psh->hostdata; + bval = pACB->AdaptSCSIID; + bval |= ENABLE_RESEL; + outb(bval,ioport+SCID); + + if(pACB->AdaptType == DC390W) + bval = SYNC_CLK_F2+ASYNC_CLK_F2; + else + { /* @1.09 */ + bval = inb(ioport+CTEST3); + if( (bval & CHIP_REV_MASK) < 0x30 ) /* 53C875 Rev. F or later ? */ + goto REVF; + bval = inb(ioport+STEST1); + if( (bval & 0x0C) == 0x0C ) /* double clock already enable ? */ + goto REVF; + outb(8,ioport+STEST1); /* enable clock doubler */ + udelay(20); + outb(HALT_SCSI_CLK,ioport+STEST3); /* halt clock */ + outb(0x0C,ioport+STEST1); /* select double SCSI clock */ + outb(0,ioport+STEST3); /* re-enable clock */ +REVF: + bval = SYNC_CLK_F4+ASYNC_CLK_F4; + } + outb(bval,ioport+SCNTL3); + + bval = SYNC_PERIOD_F4+ASYNCHRONOUS; /* set to async */ + outb(bval,ioport+SXFER); + + bval = WRT_EN_INVALIDATE; /* Enable write and invalidate */ + outb(bval,ioport+CTEST3); + + bval = EN_DMA_FIFO_536+BURST_LEN_MSB; /* select 536 bytes DMA FIFO, burst len bit2=1 */ + outb(bval,ioport+CTEST5); + + bval = BURST_LEN8+EN_READ_LINE+EN_READ_MULTIPLE+BURST_OPCODE_FETCH+AUTO_START; /* set DMA parameter */ + outb(bval,ioport+DMODE); + + bval = EN_ABORTED+EN_SCRIPT_INT+EN_ILLEGAL_INST; /* enable DMA interrupt */ + outb(bval,ioport+DIEN); + + bval = EN_CACHE_LINE_SIZE+EN_PRE_FETCH+TOTEM_POLE_IRQ+COMPATIBLE_700; + outb(bval,ioport+DCNTL); + + bval = EN_PHASE_MISMATCH+EN_SCSI_GERROR+EN_UNEXPECT_DISC+EN_SCSI_RESET+EN_PARITY_ERROR; + outb(bval,ioport+SIEN0); + + bval = EN_SEL_TIMEOUT+EN_GENERAL_TIMEOUT; + outb(bval,ioport+SIEN1); + + bval = SEL_TO_204ms; /* 250ms selection timeout */ + outb(bval,ioport+STIME0); + + wval = 1 << (eepromBuf[index][EE_ADAPT_SCSI_ID]); /* @1.11 */ + outw(wval,ioport+RESPID0); + + bval = DIS_SINGLE_INIT; + if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION ) + bval |= ACTIVE_NEGATION_; + outb(bval,ioport+STEST3); + + return(0); +} + + +/*********************************************************************** + * Function : static int DC390W_init (struct Scsi_Host *host) + * + * Purpose : initialize the internal structures for a given SCSI host + * + * Inputs : host - pointer to this host adapter's structure/ + * + * Preconditions : when this function is called, the chip_type + * field of the pACB structure MUST have been set. + ***********************************************************************/ + +static int +DC390W_init (PSHT psht, USHORT chipType, ULONG io_port, UCHAR Irq, USHORT index) +{ + PSH psh; + PACB pACB; + + if( ! CheckEEpromCheckSum( &eepromBuf[index][0], (USHORT) io_port) ) + { + psh = scsi_register( psht, sizeof(DC390W_ACB) ); + if( !psh ) + return( -1 ); + if( !pSH_start ) + { + pSH_start = psh; + pSH_current = psh; + } + else + { + pSH_current->next = psh; + pSH_current = psh; + } + +#ifdef DC390W_DEBUG0 + printk("DC390W : pSH = %8x,", (UINT) psh); +#endif + + DC390W_initACB( psh, chipType, io_port, Irq, index ); + if( !DC390W_initAdapter( psh, io_port, Irq, index ) ) + { + pACB = (PACB) psh->hostdata; + if( !pACB_start ) + { + pACB_start = pACB; + pACB_current = pACB; + pACB->pNextACB = (PACB) -1; + } + else + { + pACB_current->pNextACB = pACB; + pACB_current = pACB; + pACB->pNextACB = (PACB) -1; + } + +#ifdef DC390W_DEBUG0 + printk("DC390W : pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n", + (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array); + printk("DC390W : ACB size= %4x, DCB size= %4x, SRB size= %4x\n", + sizeof(DC390W_ACB), sizeof(DC390W_DCB), sizeof(DC390W_SRB) ); +#endif + + } + else + { + pSH_start = NULL; + scsi_unregister( psh ); + return( -1 ); + } + DC390W_download_script( psh ); + return( 0 ); + } + else + { + printk("DC390W_init: EEPROM reading error!\n"); + return( -1 ); + } +} + + +void MyDelay( void ) +{ + UCHAR i,j; + + j = inb(0x61) & 0x10; + + for(;;) + { + i = inb(0x61) & 0x10; + if( j ^ i) + break; + } +} + + +void EnDisableCE( UCHAR Flag, USHORT scsiIOPort ) +{ + + UCHAR bval; + USHORT port; + + port = (scsiIOPort & 0xff00) + GPREG; + if(Flag == ENABLE_CE) + bval = 0x10; + else + bval = 0x00; + outb(bval,port); + udelay(8); /* Delay();*/ +} + + +void EEpromOutDI( USHORT Carry, USHORT scsiIOPort ) +{ + UCHAR bval; + USHORT port; + + port = (scsiIOPort & 0xff00) + GPREG; + bval = 0x10; + if(Carry) + bval |= 0x02; /* SK=0, DI */ + outb(bval,port); + udelay(8); /* Delay();*/ + bval |= 0x04; /* SK=1, DI */ + outb(bval,port); + udelay(8); /* Delay();*/ + bval &= 0xfb; /* SK=0, DI */ + outb(bval,port); + udelay(8); /* Delay();*/ +} + + +void EEpromPrepare( UCHAR EEpromCmd, USHORT scsiIOPort ) +{ + UCHAR i,j; + USHORT carryFlag; + + carryFlag = 1; + j = 0x80; + for(i=0;i<9;i++) + { + EEpromOutDI(carryFlag,scsiIOPort); + carryFlag = (EEpromCmd & j) ? 1 : 0; + j >>= 1; + } +} + + +UCHAR EEpromInDo(USHORT scsiIOPort) +{ + UCHAR bval; + USHORT port; + + port = (scsiIOPort & 0xff00) + GPREG; + bval = 0x14; /* SK=1 */ + outb(bval,port); + udelay(8); /* Delay();*/ + bval = 0x10; /* SK=0 */ + outb(bval,port); + udelay(8); /* Delay();*/ + bval = inb(port); + if(bval & 0x01) + return( 1 ); + else + return( 0 ); +} + + +USHORT EEpromGetData(USHORT scsiIOPort) +{ + UCHAR i; + UCHAR carryFlag; + USHORT wval; + + wval = 0; + for(i=0;i<16;i++) + { + wval <<= 1; + carryFlag = EEpromInDo(scsiIOPort); + wval |= carryFlag; + } + return( wval ); +} + + +void ReadEEprom( PUCHAR EEpromBuf, USHORT scsiIOPort ) +{ + UCHAR cmd; + + cmd = EEPROM_READ; +loop_rd: + EnDisableCE(ENABLE_CE, scsiIOPort); + EEpromPrepare(cmd, scsiIOPort); + *((PUSHORT)EEpromBuf) = EEpromGetData(scsiIOPort); + EEpromBuf++; + EEpromBuf++; + cmd++; + EnDisableCE(DISABLE_CE, scsiIOPort); + if(cmd & 0x3f) + goto loop_rd; +} + + +USHORT CheckEEpromCheckSum( PUCHAR EEpromBuf, USHORT scsiIOPort) +{ + USHORT wval,port, *ptr; + UCHAR i,bval; + + port = (scsiIOPort & 0xff00) + GPCNTL; + bval = 0x09; /* configure IO Pin */ + outb(bval,port); + ReadEEprom(EEpromBuf,scsiIOPort); /* read eeprom data */ + wval = 0; + ptr = (PUSHORT) EEpromBuf; + for(i=0; i<128 ;i+=2, ptr++) + wval += *ptr; + return( (wval == 0x1234) ? 0 : -1); +} + + + +static void +DC390W_download_script (struct Scsi_Host *host) +{ + ULONG wlval, wlval1, length, alignm; + USHORT j, k, m; + USHORT ioport; + UCHAR bval; + PACB pACB; + PSRB pSRB; + void *pSrc, *pSrc1; + ULONG *pStart; + ULONG Ent_reselected; + ULONG Ent_reselecttag; + ULONG Ent_select0; + ULONG Ent_select1; + ULONG Ent_check_phase; + ULONG Ent_status1_phase; + ULONG Ent_command_phase; + ULONG Ent_jump_table0; + ULONG Ent_din_phaseB; + ULONG Ent_dout_phaseB; + ULONG Ent_din_pad_0; + ULONG Ent_dout_pad_0; + ULONG Ent_jump_tablew; + ULONG Ent_din_pad_1; + ULONG Ent_dout_pad_1; + ULONG Ent_mout_phase; + ULONG Ent_status_phase; + ULONG Ent_min_phase; + ULONG Ent_jump_msgok; + ULONG Ent_msg__1; + ULONG Ent_msg___3; + ULONG Ent_msg___2; + ULONG Ent_set_atn; + ULONG Ent_msg__a; + ULONG Ent_msg__23; + ULONG Ent_msg__3; + ULONG Ent_msg__4; + ULONG Ent_clr_atn; + ULONG Ent_din_phaseW; + ULONG Ent_dout_phaseW; + ULONG Ent_din_pad_addrB; + ULONG Ent_dout_pad_addrB; + ULONG Ent_din_pad_addrW; + ULONG Ent_dout_pad_addrW; + + + pACB = (PACB) host->hostdata; + ioport = pACB->IOPortBase; + bval = SCRATCHAB_AS_BASE; /* set scratchB contains 4K RAM base address */ + outb(bval,ioport+CTEST2); + + wlval = inl((ioport+SCRATCHB)); /* get starting address of 4K RAM */ +/* wlval += 0x800; */ /* point to Upper 2K RAM */ + DesPhysAddr[0] = wlval; /* destination address */ + +#ifdef DC390W_DEBUG0 + printk("DesAddr=%8x,",(UINT) wlval); +#endif + bval = 0; /* set Scratch_A and Scratch_B to normal mode */ + outb(bval,ioport+CTEST2); + + /*------------------------------------------------------------------- + * patch the label in jump instruction: using offset relative + * to start_script + *------------------------------------------------------------------*/ + + Ent_reselected = (ULONG) reselected - (ULONG) start_script; + Ent_reselecttag = (ULONG) reselecttag - (ULONG) start_script; + Ent_select0 = (ULONG) select0 - (ULONG) start_script; + Ent_select1 = (ULONG) select1 - (ULONG) start_script; + Ent_check_phase = (ULONG) check_phase - (ULONG) start_script; + Ent_status1_phase = (ULONG) status1_phase - (ULONG) start_script; + Ent_command_phase = (ULONG) command_phase - (ULONG) start_script; + Ent_din_phaseB = (ULONG) din_phaseB - (ULONG) start_script; + Ent_dout_phaseB = (ULONG) dout_phaseB - (ULONG) start_script; + Ent_din_phaseW = (ULONG) din_phaseW - (ULONG) start_script; + Ent_dout_phaseW = (ULONG) dout_phaseW - (ULONG) start_script; + Ent_jump_table0 = (ULONG) jump_table0 - (ULONG) start_script; + Ent_din_pad_0 = (ULONG) din_pad_0 - (ULONG) start_script; + Ent_din_pad_addrB = (ULONG) din_pad_addrB - (ULONG) start_script; + Ent_dout_pad_0 = (ULONG) dout_pad_0 - (ULONG) start_script; + Ent_dout_pad_addrB = (ULONG) dout_pad_addrB - (ULONG) start_script; + Ent_jump_tablew = (ULONG) jump_tablew - (ULONG) start_script; + Ent_din_pad_1 = (ULONG) din_pad_1 - (ULONG) start_script; + Ent_din_pad_addrW = (ULONG) din_pad_addrW - (ULONG) start_script; + Ent_dout_pad_1 = (ULONG) dout_pad_1 - (ULONG) start_script; + Ent_dout_pad_addrW = (ULONG) dout_pad_addrW - (ULONG) start_script; + Ent_mout_phase = (ULONG) mout_phase - (ULONG) start_script; + Ent_status_phase = (ULONG) status_phase - (ULONG) start_script; + Ent_min_phase = (ULONG) min_phase - (ULONG) start_script; + Ent_jump_msgok = (ULONG) jump_msgok - (ULONG) start_script; + Ent_msg__1 = (ULONG) msg__1 - (ULONG) start_script; + Ent_msg___3 = (ULONG) msg___3 - (ULONG) start_script; + Ent_msg___2 = (ULONG) msg___2 - (ULONG) start_script; + Ent_set_atn = (ULONG) set_atn - (ULONG) start_script; + Ent_msg__a = (ULONG) msg__a - (ULONG) start_script; + Ent_msg__23 = (ULONG) msg__23 - (ULONG) start_script; + Ent_msg__3 = (ULONG) msg__3 - (ULONG) start_script; + Ent_msg__4 = (ULONG) msg__4 - (ULONG) start_script; + Ent_clr_atn = (ULONG) clr_atn - (ULONG) start_script; + + jmp_select0[0] = Ent_select0 + wlval; + jmp_reselected[0] = Ent_reselected + wlval; + jmp_check_phase[0] = Ent_check_phase + wlval; + jmp_check_phase1[0] = Ent_check_phase + wlval; + jmp_check_phase2[0] = Ent_check_phase + wlval; + jmp_check_phase3[0] = Ent_check_phase + wlval; + jmp_check_phase4[0] = Ent_check_phase + wlval; + jmp_check_phase5[0] = Ent_check_phase + wlval; + jmp_check_phase6[0] = Ent_check_phase + wlval; + jmp_status1_phase[0] = Ent_status1_phase + wlval; + jmp_status1_phase1[0] = Ent_status1_phase + wlval; + jmp_status1_phase2[0] = Ent_status1_phase + wlval; + jmp_status1_phase3[0] = Ent_status1_phase + wlval; + jmp_command_phase[0] = Ent_command_phase + wlval; + for(j=0,k=1,m=0; j< (MAX_SG_LIST_BUF+1); j++) + { + jmp_dio_phaseB[k] = Ent_din_phaseB + m + wlval; + jmp_dio_phaseW[k] = Ent_din_phaseW + m + wlval; + k += 2; + jmp_dio_phaseB[k] = Ent_dout_phaseB + m + wlval; + jmp_dio_phaseW[k] = Ent_dout_phaseW + m + wlval; + k += 2; + m += 8; + } + jmp_din_pad_0[0] = Ent_din_pad_0 + wlval; + jmp_dout_pad_0[0] = Ent_dout_pad_0 + wlval; + jmp_din_pad_addrB[0] = Ent_din_pad_addrB + wlval; + jmp_dout_pad_addrB[0] = Ent_dout_pad_addrB + wlval; + jmp_din_pad_addrW[0] = Ent_din_pad_addrW + wlval; + jmp_dout_pad_addrW[0] = Ent_dout_pad_addrW + wlval; + jmp_din_pad_1[0] = Ent_din_pad_1 + wlval; + jmp_dout_pad_1[0] = Ent_dout_pad_1 + wlval; + jmp_status_phase[0] = Ent_status_phase + wlval; + jmp_min_phase[0] = Ent_min_phase + wlval; + jmp_mout_phase[0] = Ent_mout_phase + wlval; + jmp_jump_msgok[0] = Ent_jump_msgok + wlval; + jmp_msg__1[0] = Ent_msg__1 + wlval; + jmp_msg___3[0] = Ent_msg___3 + wlval; + jmp_msg___2[0] = Ent_msg___2 + wlval; + jmp_msg__a[0] = Ent_msg__a + wlval; + jmp_msg__a1[0] = Ent_msg__a + wlval; + jmp_msg__a2[0] = Ent_msg__a + wlval; + jmp_msg__23[0] = Ent_msg__23 + wlval; + jmp_msg__3[0] = Ent_msg__3 + wlval; + jmp_msg__4[0] = Ent_msg__4 + wlval; + + /*-------------------------------------------------------------------- + // patch the element in ACB struct: using Physical address + //-------------------------------------------------------------------*/ + +#ifndef VERSION_ELF_1_2_13 + wlval1 = virt_to_phys( pACB->msgin123 ); +#else + wlval1 = (ULONG) pACB->msgin123; +#endif + ACB_msgin123_1[0] = wlval1; + ACB_msgin123_2[0] = wlval1; + ACB_msgin123_3[0] = wlval1; + ACB_msgin123_4[0] = wlval1; + ACB_msgin123_5[0] = wlval1; + ACB_msgin123_6[0] = wlval1; + ACB_msgin123_7[0] = wlval1; + +#ifndef VERSION_ELF_1_2_13 + ACB_status[0] = virt_to_phys( &pACB->status ); +#else + ACB_status[0] = (ULONG) &pACB->status; +#endif + /*-------------------------------------------------------------------- + // patch the element in SRB struct: using offset in struct + //-------------------------------------------------------------------*/ + + pSRB = (PSRB) pACB->SRB_array; + select1[0] = (select1[0] & 0xffff0000) + ((ULONG) &pSRB->__select - (ULONG) &pSRB->CmdBlock); + SRB_msgout0[0] = (ULONG) &pSRB->__msgout0 - (ULONG) &pSRB->CmdBlock; + SRB_msgout01[0] = (ULONG) &pSRB->__msgout0 - (ULONG) &pSRB->CmdBlock; + SRB_command[0] = (ULONG) &pSRB->__command - (ULONG) &pSRB->CmdBlock; + SRB_SegmentPad[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock; + SRB_SegmentPad1[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock; + SRB_SegmentPad2[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock; + SRB_SegmentPad3[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock; + wlval = (ULONG) &pSRB->Segment0 - (ULONG) &pSRB->CmdBlock; + for(j=0,k=1; j<(MAX_SG_LIST_BUF+1); j++) + { + din_phaseB[k] = wlval; + dout_phaseB[k] = wlval; + din_phaseW[k] = wlval; + dout_phaseW[k] = wlval; + k += 2; + wlval += 8; + } + + + bval = inb(ioport+DCNTL); + bval |= IRQ_DISABLE; + outb(bval,ioport+DCNTL); /* disable interrupt */ + +/* pSrc = scsi_init_malloc( 2048, GFP_ATOMIC); */ + pSrc = scsi_init_malloc( 4096, GFP_ATOMIC); /* 1.11 */ +#ifdef DC390W_DEBUG0 + printk("SrcAlloc=%8x,",(UINT) pSrc); +#endif + alignm = 4 - (((ULONG) pSrc) & 3); + pSrc1 = (void *)(((ULONG) pSrc) + alignm); + length = (ULONG) end_script - (ULONG) start_script; + memcpy( pSrc1, (void *) start_script, length); + pStart = (ULONG *) ((ULONG) start_mov - (ULONG) start_script); + pStart =(ULONG *) (((ULONG) pStart) + ((ULONG) pSrc1)); + +#ifdef DC390W_DEBUG0 + printk("SrcAddr=%8x,\n",(UINT) pSrc1); +#endif +#ifndef VERSION_ELF_1_2_13 + (ULONG *)pStart[1] = virt_to_phys( pSrc1 ); +#else + (ULONG *)pStart[1] = (ULONG) pSrc1; +#endif + +/* wlval = virt_to_phys( start_script ); */ /* physical address of start_script */ +/* SrcPhysAddr[0] = wlval; */ /* sources address */ + +/* start to download SCRIPT instruction to the RAM of NCR53c825A,875 */ + +/* wlval = virt_to_phys( start_mov ); */ + +#ifndef VERSION_ELF_1_2_13 + wlval = virt_to_phys( pStart ); +#else + wlval = (ULONG) pStart; +#endif + + outl(wlval,ioport+DSP); + + bval = inb(ioport+ISTAT); + while(!(bval & DMA_INT_PENDING)) /* check load start_script is finished? */ + bval = inb(ioport+ISTAT); + + bval = inb(ioport+DSTAT); /* clear interrupt */ + + bval = inb(ioport+DCNTL); + bval &= ~IRQ_DISABLE; + outb(bval,ioport+DCNTL); /* re-enable interrupt */ + + scsi_init_free((char *) pSrc, 4096); + + wlval = DesPhysAddr[0]; /* starting addr of RAM */ + wlval -= (ULONG) start_script; + + pACB->jmp_reselect = wlval + (ULONG) start_script; + pACB->jmp_select = wlval + (ULONG) select1; + pACB->jmp_table8 = wlval + (ULONG) jump_table0; + pACB->jmp_set_atn = wlval + (ULONG) set_atn; + pACB->jmp_clear_ack = wlval + (ULONG) msg__a; + pACB->jmp_next = wlval + (ULONG) check_phase; + pACB->jmp_din8 = wlval + (ULONG) din_phaseB+8; + pACB->jmp_dout8 = wlval + (ULONG) dout_phaseB+8; + pACB->jmp_clear_atn = wlval + (ULONG) clr_atn; + pACB->jmp_reselecttag = wlval + (ULONG) reselecttag; + + wlval = pACB->jmp_reselect; + outl(wlval,(ioport+DSP)); + return; +} + + +/*********************************************************************** + * Function : int DC390W_detect(Scsi_Host_Template *psht) + * + * Purpose : detects and initializes NCR53c825A,875 SCSI chips + * that were autoprobed, overridden on the LILO command line, + * or specified at compile time. + * + * Inputs : psht - template for this SCSI adapter + * + * Returns : number of host adapters detected + * + ***********************************************************************/ + +int +DC390W_detect(Scsi_Host_Template *psht) +{ + UCHAR pci_bus, pci_device_fn, irq; +#ifndef VERSION_ELF_1_2_13 + UINT io_port, ram_base; +#else + ULONG io_port, ram_base; +#endif + USHORT i; + int error = 0; + USHORT adaptCnt = 0; /* Number of boards detected */ + USHORT pci_index = 0; /* Device index to PCI BIOS calls */ + USHORT pci_index2 = 0; /* Device index to PCI BIOS calls */ + USHORT chipType = 0; + + +#ifndef VERSION_ELF_1_2_13 + psht->proc_dir = &proc_scsi_tmscsiw; +#endif + + InitialTime = 1; + pSHT_start = psht; + jmp_table16 = (ULONG) jump_tablew - (ULONG) jump_table0; + jmp_din16 = (ULONG) din_phaseW - (ULONG) din_phaseB; + jmp_dout16 = (ULONG) dout_phaseW - (ULONG) dout_phaseB; + pACB_start = NULL; + + if ( pcibios_present() ) + { + for (i = 0; i < MAX_ADAPTER_NUM; ++i) + { + if( !pcibios_find_device( PCI_VENDOR_ID_NCR, + PCI_DEVICE_ID_NCR53C825A, + pci_index, &pci_bus, &pci_device_fn) ) + { + chipType = PCI_DEVICE_ID_NCR53C825A; + pci_index++; + } + else if( !pcibios_find_device( PCI_VENDOR_ID_NCR, + PCI_DEVICE_ID_NCR53C875, + pci_index2, &pci_bus, &pci_device_fn) ) + { + chipType = PCI_DEVICE_ID_NCR53C875; + pci_index2++; + } + + if( chipType ) + { + error = pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &io_port); + error |= pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &ram_base); + error |= pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + if( error ) + { + printk("DC390W_detect: reading configuration registers error!\n"); + InitialTime = 0; + return( 0 ); + } + + (USHORT) io_port = (USHORT) io_port & 0xFFFE; +#ifdef DC390W_DEBUG0 + printk("DC390W : IO_PORT=%4x,RAM_BASE=%8x,IRQ=%x,CHIPID=%x,\n", + (UINT) io_port, (UINT) ram_base, irq, (UCHAR)chipType); +#endif + + if( !DC390W_init(psht, chipType, io_port, irq, i) ) + adaptCnt++; + chipType = 0; + } + else + break; + } + } + InitialTime = 0; + adapterCnt = adaptCnt; + return( adaptCnt ); +} + +#ifndef VERSION_ELF_1_2_13 + +/******************************************************************** + * Function: tmscsiw_set_info() + * + * Purpose: Set adapter info (!) + * + * Not yet implemented + * + *******************************************************************/ + +int tmscsiw_set_info(char *buffer, int length, struct Scsi_Host *shpnt) +{ + return(-ENOSYS); /* Currently this is a no-op */ +} + +/******************************************************************** + * Function: tmscsiw_proc_info(char* buffer, char **start, + * off_t offset, int length, int hostno, int inout) + * + * Purpose: return SCSI Adapter/Device Info + * + * Input: buffer: Pointer to a buffer where to write info + * start : + * offset: + * hostno: Host adapter index + * inout : Read (=0) or set(!=0) info + * + * Output: buffer: contains info + * length; length of info in buffer + * + * return value: length + * + ********************************************************************/ + +/* KG: proc_info taken from driver aha152x.c */ + +#undef SPRINTF +#define SPRINTF(args...) pos += sprintf(pos, ## args) + +#define YESNO(YN)\ +if (YN) SPRINTF(" Yes ");\ +else SPRINTF(" No ") + +int tmscsiw_proc_info(char *buffer, char **start, + off_t offset, int length, int hostno, int inout) +{ + int dev, spd, spd1; + char *pos = buffer; + PSH shpnt; + PACB acbpnt; + PDCB dcbpnt; + unsigned long flags; +/* Scsi_Cmnd *ptr; */ + + acbpnt = pACB_start; + + while(acbpnt != (PACB)-1) + { + shpnt = acbpnt->pScsiHost; + if (shpnt->host_no == hostno) break; + acbpnt = acbpnt->pNextACB; + } + + if (acbpnt == (PACB)-1) return(-ESRCH); + if (!shpnt) return(-ESRCH); + + if(inout) /* Has data been written to the file ? */ + return(tmscsiw_set_info(buffer, length, shpnt)); + + SPRINTF("Tekram DC390W/U/F (T) PCI SCSI Host Adadpter, "); + SPRINTF("Driver Version 1.12, 1997/02/17\n"); + + save_flags(flags); + cli(); + + SPRINTF("SCSI Host Nr %i, ", hostno); + SPRINTF("DC390WUF Adapter Nr %i\n", acbpnt->AdapterIndex); + SPRINTF("IOPortBase 0x%04x, ", acbpnt -> IOPortBase); + SPRINTF("IRQLevel 0x%02x\n",acbpnt -> IRQLevel); + + SPRINTF("Adapter Type: "); + switch(acbpnt->AdaptType) + { + case DC390W: SPRINTF("DC390W, Fast Wide SCSI \n"); break; + case DC390U: SPRINTF("DC390U, Ultra SCSI\n"); break; + case DC390F: SPRINTF("DC390F, Ultra Wide SCSI\n"); break; + default: SPRINTF("Unknown !\n"); + } + + SPRINTF("MaxID %i, MaxLUN %i, ", acbpnt->max_id, acbpnt->max_lun); + SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN); + + SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status); + + SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt); + + SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ Wide NegoPeriod SyncSpeed SyncOffs\n"); + dcbpnt = acbpnt->pLinkDCB; + + for (dev = 0; dev < acbpnt->DeviceCnt; dev++) + { + SPRINTF("%02i %02i %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN); + YESNO(dcbpnt->DevMode & PARITY_CHK_); + YESNO(dcbpnt->DCBsxfer & OFFSET_MASK); + YESNO(dcbpnt->DevMode & EN_DISCONNECT_); + YESNO(dcbpnt->DevMode & SEND_START_); + YESNO(dcbpnt->MaxCommand > 1); + YESNO(dcbpnt->DCBscntl3 & EN_WIDE_SCSI); + SPRINTF(" %03i ns ", (dcbpnt->NegoPeriod) << 2); + if (dcbpnt->DCBsxfer & OFFSET_MASK) + { + spd = 1000/(dcbpnt->SyncPeriod <<2); + spd1 = 1000%(dcbpnt->SyncPeriod <<2); + spd1 = (spd1 * 10)/(dcbpnt->SyncPeriod <<2); + SPRINTF(" %2i.%1i M %02i\n", spd, spd1, dcbpnt->DCBsxfer & OFFSET_MASK); + } + else SPRINTF("\n"); + /* Add more info ...*/ + dcbpnt = dcbpnt->pNextDCB; + } + + restore_flags(flags); + *start = buffer + offset; + + if (pos - buffer < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + else + return length; +} +#endif /* VERSION_ELF_1_2_13 */ + +#ifdef MODULE + +/*********************************************************************** + * Function : static int DC390W_shutdown (struct Scsi_Host *host) + * + * Purpose : does a clean (we hope) shutdown of the NCR SCSI chip. + * Use prior to dumping core, unloading the NCR driver, etc. + * + * Returns : 0 on success + ***********************************************************************/ +static int +DC390W_shutdown (struct Scsi_Host *host) +{ + USHORT ioport; + unsigned long flags; + PACB pACB = (PACB) host->hostdata; + + ioport = (unsigned int) pACB->IOPortBase; + + save_flags (flags); + cli(); + +/* pACB->soft_reset(host); */ +/* + * For now, we take the simplest solution : reset the SCSI bus. Eventually, + * - If a command is connected, kill it with an ABORT message + * - If commands are disconnected, connect to each target/LUN and + * do a ABORT, followed by a SOFT reset, followed by a hard + * reset. + */ + +#ifdef DC390W_DEBUG0 + printk("DC390W: shutdown,"); +#endif + outb(ASSERT_RST, ioport+SCNTL1); + udelay(25); /* Minimum amount of time to assert RST */ + outb(0, ioport+SCNTL1); + restore_flags (flags); + return( 0 ); +} + + +int DC390W_release(struct Scsi_Host *host) +{ + int irq_count; + struct Scsi_Host *tmp; + + DC390W_shutdown (host); + + if (host->irq != IRQ_NONE) + { + for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next) + { + if ( tmp->irq == host->irq ) + ++irq_count; + } + if (irq_count == 1) + { +#ifdef DC390W_DEBUG0 + printk("DC390W: Free IRQ %i.",host->irq); +#endif +#ifndef VERSION_ELF_1_2_13 + free_irq(host->irq,NULL); +#else + free_irq(host->irq); +#endif + } + } + + release_region(host->io_port,host->n_io_port); + + return( 1 ); +} + +Scsi_Host_Template driver_template = DC390WUF; +#include "scsi_module.c" +#endif /* def MODULE */ + |