/***********************************************************************
 *	FILE NAME : TEK390.C					       *
 *	     BY   : C.L. Huang	(ching@tekram.com.tw)		       *
 *	Description: Device Driver for Tekram DC-390(T) PCI SCSI       *
 *		     Bus Master Host Adapter			       *
 * (C)Copyright 1995-1996 Tekram Technology Co., Ltd.		       *
 ***********************************************************************/
/***********************************************************************
 *	HISTORY:						       *
 *								       *
 *	REV#	DATE	NAME	DESCRIPTION			       *
 *	1.00  07/02/96	CLH	First release for RELEASE-2.1.0        *
 *	1.01  08/20/96	CLH	Update for RELEASE-2.1.5	       *
 *								       *
 ***********************************************************************/

/**************************************************************************
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **************************************************************************/

/**************************************************************************/
/* Imported into FreeBSD source repository, and updated to compile under  */
/* FreeBSD-3.0-DEVELOPMENT, by Stefan Esser <se@FreeBSD.Org>, 1996-12-17  */
/**************************************************************************/

/* #define REL_2_1_0 */
#define REL_2_1_5

#define DC390_DEBUG

#include <sys/param.h>

/* XXX this doesn't actually compile unless KERNEL is defined. */
#ifdef KERNEL
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/kernel.h>

#include <vm/vm.h>
#include <vm/pmap.h>
#endif /* KERNEL */

#include <pci/pcivar.h>
#include <pci/pcireg.h>

#include <scsi/scsiconf.h>

#include <machine/clock.h>

#include <pci/tek390.h>

#define INT32	  int32
#define U_INT32   u_int32


#define OutB(val, port) 		outb(port, val)
#define OutW(val, port) 		outw(port, val)
#define OutL(val, port) 		outl(port, val)

#define PCI_DEVICE_ID_AMD53C974 	0x20201022ul
#define PCI_BASE_ADDR0			0x10


#ifdef	REL_2_1_0
static int  DC390_Interrupt (PACB pACB);
#endif
#ifdef	REL_2_1_5
static void DC390_Interrupt (PACB pACB);
#endif
static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus);

static void SetXferRate( PACB pACB, PDCB pDCB );
static void DC390_Disconnect( PACB pACB );
static void DC390_Reselect( PACB pACB );
static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
static void DoingSRB_Done( PACB pACB );
static void DC390_ScsiRstDetect( PACB pACB );
static void DC390_ResetSCSIBus( PACB pACB );
static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
static void EnableMsgOut2( PACB pACB, PSRB pSRB );
static void EnableMsgOut( PACB pACB, PSRB pSRB );
static void DC390_InvalidCmd( PACB pACB );

static void DC390_timeout( void *arg1);
static void DC390_reset (PACB pACB);
static PUCHAR  phystovirt( PSRB pSRB, ULONG xferCnt );

void   DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd );
void   DC390_initSRB( PSRB psrb );
void   DC390_linkSRB( PACB pACB );
void   DC390_initACB( PACB pACB, ULONG io_port, UCHAR Irq, USHORT index );
int    DC390_initAdapter( PACB pACB, ULONG io_port, UCHAR Irq, USHORT index,
			  pcici_t config_id );
void   DC390_EnableCfg( USHORT mechnum, UCHAR regval );
void   DC390_DisableCfg( USHORT mechnum );
UCHAR  DC390_inByte( USHORT mechnum, UCHAR regval );
USHORT DC390_inWord( USHORT mechnum, UCHAR regval );
ULONG  DC390_inDword(USHORT mechnum, UCHAR regval );
void   DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval );
void   DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval );
void   DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry );
UCHAR  DC390_EEpromInDO( USHORT mechnum );
USHORT EEpromGetData1( USHORT mechnum );
void   DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd );
void   DC390_ReadEEprom( USHORT mechnum, USHORT index );
USHORT DC390_DefaultEEprom( USHORT mechnum, USHORT index );
USHORT DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index );
USHORT DC390_ToMech( USHORT Mechnum, pcici_t config_id );


#ifdef KERNEL

static	char*	trmamd_probe( pcici_t tag, pcidi_t type);
static	void	trmamd_attach( pcici_t tag, int unit);

#ifdef	REL_2_1_0
static	int32	trmamd_scsi_cmd( struct scsi_xfer *sx);
#endif

#ifdef	REL_2_1_5
static	int32_t trmamd_scsi_cmd( struct scsi_xfer *sx);
#endif

static	void	trmamd_min_phys( struct buf *pbuf);

#ifdef	REL_2_1_0
static	u_int32 trmamd_info( int unit );
#endif

#endif /* KERNEL */


static u_long	trmamd_count;

struct	pci_device   trmamd_device = {
	"amd",
	trmamd_probe,
	trmamd_attach,
	&trmamd_count,
	NULL
};

DATA_SET (pcidevice_set, trmamd_device);



struct scsi_adapter trmamd_switch =
{
	trmamd_scsi_cmd,
	trmamd_min_phys,
	0,
	0,
#ifdef	REL_2_1_0
	trmamd_info,
#endif
#ifdef	REL_2_1_5
	0,
#endif
	"amd",
};

struct scsi_device trmamd_dev =
{
	NULL,			/* Use default error handler */
	NULL,			/* have a queue, served by this */
	NULL,			/* have no async handler */
	NULL,			/* Use default 'done' routine */
	"amd",
};


static PACB	pACB0[MAX_ADAPTER_NUM]={0};
static PACB	pACB_start= NULL;
static PACB	pACB_current = NULL;
static PDCB	pPrevDCB = NULL;
static USHORT	adapterCnt = 0;
static USHORT	CurrSyncOffset = 0;
static USHORT	mech2Agent;
static ULONG	mech1addr;
static UCHAR	mech2bus, mech2CfgSPenR, CurrentID, CurrentLUN;

static PVOID DC390_phase0[]={
       DC390_DataOut_0,
       DC390_DataIn_0,
       DC390_Command_0,
       DC390_Status_0,
       DC390_Nop_0,
       DC390_Nop_0,
       DC390_MsgOut_0,
       DC390_MsgIn_0,
       DC390_Nop_1
       };

static PVOID DC390_phase1[]={
       DC390_DataOutPhase,
       DC390_DataInPhase,
       DC390_CommandPhase,
       DC390_StatusPhase,
       DC390_Nop_0,
       DC390_Nop_0,
       DC390_MsgOutPhase,
       DC390_MsgInPhase,
       DC390_Nop_1,
       };

UCHAR  eepromBuf[MAX_ADAPTER_NUM][128];


UCHAR  clock_period1[] = {4, 5, 6, 7 ,8, 10, 13, 20};

UCHAR  baddevname1[2][28] ={
       "SEAGATE ST3390N  ???    9546",
       "HP      C3323-300       4269"};

#define BADDEVCNT	2


/***********************************************************************
 *
 *
 *
 **********************************************************************/
static PSRB
GetSRB( PACB pACB )
{
    int    flags;
    PSRB   pSRB;

    flags = splbio();

    pSRB = pACB->pFreeSRB;
    if( pSRB )
    {
	pACB->pFreeSRB = pSRB->pNextSRB;
	pSRB->pNextSRB = NULL;
    }
    splx(flags);
    return( pSRB );
}


static void
RewaitSRB0( PDCB pDCB, PSRB pSRB )
{
    PSRB   psrb1;
    int    flags;

    flags = splbio();

    if( (psrb1 = pDCB->pWaitingSRB) )
    {
	pSRB->pNextSRB = psrb1;
	pDCB->pWaitingSRB = pSRB;
    }
    else
    {
	pSRB->pNextSRB = NULL;
	pDCB->pWaitingSRB = pSRB;
	pDCB->pWaitLast = pSRB;
    }
    splx(flags);
}


static void
RewaitSRB( PDCB pDCB, PSRB pSRB )
{
    PSRB   psrb1;
    int    flags;
    UCHAR  bval;

    flags = splbio();

    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 */
    splx(flags);
}


static void
DoWaitingSRB( PACB pACB )
{
    int    flags;
    PDCB   ptr, ptr1;
    PSRB   pSRB;

    flags = splbio();

    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
	    {
		if( !DC390_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;
	    }
	}
    }
    splx(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 )
{
    int    flags;
    PDCB   pDCB;

    flags = splbio();

    pDCB = pSRB->pSRBDCB;
    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;
    }

    if( !DC390_StartSCSI(pACB, pDCB, pSRB) )
    {
	pDCB->GoingSRBCnt++;
	if( pDCB->pGoingSRB )
	{
	    pDCB->pGoingLast->pNextSRB = pSRB;
	    pDCB->pGoingLast = pSRB;
	}
	else
	{
	    pDCB->pGoingSRB = pSRB;
	    pDCB->pGoingLast = pSRB;
	}
    }
    else
	RewaitSRB0( pDCB, pSRB );

SND_EXIT:
    splx(flags);
    return;
}


/***********************************************************************
 * Function : static int32 dc390_scsi_cmd (struct scsi_xfer *cmd)
 * Purpose : enqueues a SCSI command
 ***********************************************************************/

#ifdef	REL_2_1_0
int32
#endif
#ifdef	REL_2_1_5
int32_t
#endif
trmamd_scsi_cmd ( PSCSICMD cmd )
{
    USHORT ioport, i;
    PSCSICMD  pcmd;
    PSCLINK   plink;
    PACB   pACB;
    PDCB   pDCB;
    PSRB   pSRB;
    int    flags, cflags, unit, CurrPgVaddr;
    ULONG  sglen, pglen, datalen, CurrPgPaddr, NextPgPaddr;
    PUCHAR ptr,ptr1;
    PSEG   psg;
    UCHAR  sgc, sstatus;

    plink = cmd->sc_link;
    unit = plink->adapter_unit;
    pACB = pACB0[unit];
    ioport = pACB->IOPortBase;

#ifdef DC390_DEBUG0
	printf("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmd->opcode,
		plink->target, plink->lun);
#endif

    if( pACB->scan_devices )
    {
	if( (plink->target > CurrentID) ||
	    (plink->target == CurrentID) && (plink->lun >= CurrentLUN) )
	{
	    CurrentID = plink->target;
	    CurrentLUN = plink->lun;
	}
	else
	{
	    pACB->scan_devices = 0;
	    pPrevDCB->pNextDCB = pACB->pLinkDCB;
	}
    }

    if ( ( plink->target > pACB->max_id ) || ( plink->lun > pACB->max_lun ) )
    {
#ifdef DC390_DEBUG0
	printf("DC390: Ignore target %d lun %d\n",
		plink->target, plink->lun);
#endif
	cmd->error = XS_DRIVER_STUFFUP;
	return( COMPLETE );
    }

    if( (pACB->scan_devices) && !(pACB->DCBmap[plink->target] & (1 << plink->lun)) )
    {
	if( pACB->DeviceCnt < MAX_DEVICES )
	{
	    pACB->DCBmap[plink->target] |= (1 << plink->lun);
	    pDCB = pACB->pDCB_free;
#ifdef DC390_DEBUG0
	    printf("pDCB=%8x,ID=%2x,", (UINT) pDCB, plink->target);
#endif
	    DC390_initDCB( pACB, pDCB, cmd );
	}
	else	/* ???? */
	{
#ifdef DC390_DEBUG0
	    printf("DC390: Ignore target %d lun %d\n",
		    plink->target, plink->lun);
#endif
	    cmd->error = XS_DRIVER_STUFFUP;
	    return( COMPLETE );
	}
    }
    else if( !(pACB->scan_devices) && !(pACB->DCBmap[plink->target] & (1 << plink->lun)) )
    {
#ifdef DC390_DEBUG0
	printf("DC390: Ignore target %d lun %d\n",
		plink->target, plink->lun);
#endif
	cmd->error = XS_DRIVER_STUFFUP;
	return( COMPLETE );
    }
    else
    {
	pDCB = pACB->pLinkDCB;
	while( (pDCB->UnitSCSIID != plink->target) ||
	       (pDCB->UnitSCSILUN != plink->lun) )
	{
	    pDCB = pDCB->pNextDCB;
	}
#ifdef DC390_DEBUG0
	    printf("pDCB=%8x,ID=%2x,", (UINT) pDCB, plink->target);
#endif
    }

    cflags = cmd->flags;
    if(cflags & SCSI_RESET)
    {
	DC390_reset (pACB);
	cmd->error = XS_NOERROR;
	return(COMPLETE);
    }

    if( cflags & ITSDONE )
    {
	printf("DC390: Is it done?\n");
	cmd->flags &= ~ITSDONE;
    }
    if( !(cflags & INUSE) )
    {
	printf("DC390: In Use?\n");
	cmd->flags |= INUSE;
    }

    cmd->error = 0;
    cmd->resid = 0;

    flags = splbio();

    pcmd = cmd;

    pSRB = GetSRB( pACB );

    if( !pSRB )
    {
	pcmd->error = XS_DRIVER_STUFFUP;
	splx(flags);
	return( TRY_AGAIN_LATER);
    }

/*  BuildSRB(pSRB); */

    pSRB->pSRBDCB = pDCB;
    pSRB->pcmd = pcmd;
    ptr = (PUCHAR) pSRB->CmdBlock;
    ptr1 = (PUCHAR) pcmd->cmd;
    pSRB->ScsiCmdLen = pcmd->cmdlen;
    for(i=0; i< pcmd->cmdlen; i++)
    {
	*ptr = *ptr1;
	ptr++;
	ptr1++;
    }
    if( pcmd->datalen )
    {
	psg = (PSEG) &pSRB->SGsegment[0];
	pSRB->pSegmentList = psg;
	sgc = 0;

	/* Set up the scatter gather list */
	datalen = pcmd->datalen;
	CurrPgVaddr = (int) pcmd->data;
	CurrPgPaddr = vtophys(CurrPgVaddr);

	while ((datalen) && (sgc < MAX_SG_ENTRY))
	{
	    sglen = 0;
	    psg->SGXPtr = CurrPgPaddr;
	    NextPgPaddr = CurrPgPaddr;
	    while ((datalen) && (CurrPgPaddr == NextPgPaddr))
	    {
		/*
		 * This page is contiguous (physically) with the the last,
		 * just extend the length
		 */

		NextPgPaddr = (CurrPgPaddr & (~(PAGELEN - 1))) + PAGELEN;
		pglen = NextPgPaddr - CurrPgPaddr;

		if( datalen < pglen )
		    pglen = datalen;
		sglen += pglen;
		datalen -= pglen;
		CurrPgVaddr = (CurrPgVaddr & (~(PAGELEN - 1))) + PAGELEN;
		if( datalen )
		    CurrPgPaddr = vtophys(CurrPgVaddr);
	    }
	    /*
	       next page isn't contiguous, finish this segment
	     */
	    psg->SGXLen = sglen;
	    psg++;
	    sgc++;
	}
	pSRB->SGcount = sgc;

	if (datalen)
	{
	    printf("DC390: Out Of Segment Buffer!\n");
	    pSRB->pNextSRB = pACB->pFreeSRB;
	    pACB->pFreeSRB = pSRB;
	    pcmd->error = XS_DRIVER_STUFFUP;
	    splx(flags);
	    return (HAD_ERROR);
	}
    }
    else
	pSRB->SGcount = 0;

    pSRB->SGIndex = 0;
    pSRB->AdaptStatus = 0;
    pSRB->TargetStatus = 0;
    pSRB->MsgCnt = 0;
    if( pDCB->DevType != SCSI_SEQACESS )
	pSRB->RetryCnt = 1;
    else
	pSRB->RetryCnt = 0;
    pSRB->SRBStatus = 0;
    pSRB->SRBFlag = 0;
    pSRB->SRBState = 0;
    pSRB->TotalXferredLen = 0;
    pSRB->SGPhysAddr = 0;
    pSRB->SGToBeXferLen = 0;
    pSRB->ScsiPhase = 0;
    pSRB->EndMessage = 0;
    splx(flags);

    if( !(cflags & SCSI_NOMASK) )
    {
	flags = splbio();
	SendSRB( pcmd, pACB, pSRB );
	timeout(DC390_timeout, (caddr_t)pSRB, (pcmd->timeout * hz)/1000);
	splx(flags);
	return( SUCCESSFULLY_QUEUED);
    }
    else
    {
	SendSRB( pcmd, pACB, pSRB );
	do
	{
	    while(--pcmd->timeout)
	    {
		DELAY(1000);
		sstatus = inb( ioport+Scsi_Status );
		if( sstatus & INTERRUPT )
		    break;
	    }
	    if( pcmd->timeout == 0 )
	    {
		return(HAD_ERROR);
	    }
	    else
	    {
		DC390_Interrupt( pACB );
	    }
	}
	while( !(pcmd->flags & ITSDONE) );
	if( pcmd->error == XS_TIMEOUT)
	    return(HAD_ERROR);
	else
	    return(COMPLETE);
    }
}


void
trmamd_min_phys( struct buf *bp )
{
    if (bp->b_bcount > ((MAX_SG_ENTRY - 1) * PAGELEN))
	bp->b_bcount = ((MAX_SG_ENTRY - 1) * PAGELEN);
}


#ifdef	REL_2_1_0
u_int32
trmamd_info( int unit )
{
	return (MAX_CMD_PER_LUN);	/* outstanding requests at a time per device */
}
#endif


static PUCHAR  phystovirt( PSRB pSRB, ULONG xferCnt )
{
    int  dataPtr;
    PSCSICMD  pcmd;
    UCHAR     i;
    PSEG      pseg;

    pcmd = pSRB->pcmd;
    dataPtr = (int) pcmd->data;
    pseg = pSRB->SGsegment;
    for(i=0; i < pSRB->SGIndex; i++)
    {
	dataPtr += (int) pseg->SGXLen;
	pseg++;
    }
    dataPtr += (int) xferCnt;
    return( (PUCHAR) dataPtr);
}


/***********************************************************************
 * Function : int DC390_abort (SCSICMD *cmd)
 *
 * Purpose : Abort an errant SCSI command
 *
 * Inputs : cmd - command to abort
 *
 * Returns : 0 on success, -1 on failure.
 ***********************************************************************/
/*
int
DC390_abort (SCSICMD *cmd)
{
    int   flags;
    PACB  pACB;
    PDCB  pDCB, pdcb;
    PSRB  pSRB, psrb;
    USHORT count, i;
    PSCSICMD  pcmd, pcmd1;
    int   status;


#ifdef DC390_DEBUG0
    printf("DC390 : Abort Cmd.");
#endif

    flags = splbio();

    pACB = (PACB) cmd->host->hostdata;
    pDCB = pACB->pLinkDCB;
    pdcb = pDCB;
    while( (pDCB->UnitSCSIID != cmd->sc_link->target) ||
	   (pDCB->UnitSCSILUN != cmd->sc_link->lun) )
    {
	pDCB = pDCB->pNextDCB;
	if( pDCB == pdcb )
	    goto  NOT_RUN;
    }


    pSRB = pDCB->pWaitingSRB;
    if( !pSRB )
	goto  ON_GOING;
    if( pSRB->pcmd == cmd )
    {
	pDCB->pWaitingSRB = pSRB->pNextSRB;
	goto  IN_WAIT;
    }
    else
    {
	psrb = pSRB;
	while( psrb->pNextSRB->pcmd != cmd )
	{
	    psrb = psrb->pNextSRB;
	    if( !psrb )
		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->error = XS_NOERROR;
    scsi_done(cmd);
    splx(flags);
    return( status );
}
*/

static void
ResetDevParam( PACB pACB )
{
    PDCB   pDCB, pdcb;

    pDCB = pACB->pLinkDCB;
    if( pDCB == NULL )
	return;
    pdcb = pDCB;
    do
    {
	pDCB->SyncMode &= ~SYNC_NEGO_DONE;
	pDCB->SyncPeriod = 0;
	pDCB->SyncOffset = 0;
	pDCB->CtrlR3 = FAST_CLK;
	pDCB->CtrlR4 &= NEGATE_REQACKDATA;
	pDCB->CtrlR4 |= EATER_25NS;
	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++)
	{
	    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 : DC390_reset (PACB pACB)
 *
 * Purpose : perform a hard reset on the SCSI bus( and AMD chip).
 *
 * Inputs : cmd - command which caused the SCSI RESET
 *
 ***********************************************************************/

static void
DC390_reset (PACB pACB)
{
    USHORT   ioport;
    int   flags;
    UCHAR    bval;
    USHORT  i;


#ifdef DC390_DEBUG0
    printf("DC390: RESET,");
#endif

    flags = splbio();

    ioport = pACB->IOPortBase;
    bval = inb(ioport+CtrlReg1);
    bval |= DIS_INT_ON_SCSI_RST;
    OutB(bval,ioport+CtrlReg1);  /* disable interrupt */
    DC390_ResetSCSIBus( pACB );
    for( i=0; i<500; i++ )
	DELAY(1000);
    bval = inb(ioport+CtrlReg1);
    bval &= ~DIS_INT_ON_SCSI_RST;
    OutB(bval,ioport+CtrlReg1); /* re-enable interrupt */

    bval = DMA_IDLE_CMD;
    OutB(bval,ioport+DMA_Cmd);
    bval = CLEAR_FIFO_CMD;
    OutB(bval,ioport+ScsiCmd);

    ResetDevParam( pACB );
    DoingSRB_Done( pACB );
    pACB->pActiveDCB = NULL;

    pACB->ACBFlag = 0;
    DoWaitingSRB( pACB );
    splx(flags);
    return;
}


void
DC390_timeout( void *arg1)
{
    PSRB  pSRB;

    pSRB = (PSRB) arg1;
}


#include <pci/scsiiom.c>


/***********************************************************************
 * Function : static void DC390_initDCB
 *
 * Purpose :  initialize the internal structures for a given DCB
 *
 * Inputs : cmd - pointer to this scsi cmd request block structure
 *
 ***********************************************************************/
void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
{
    PEEprom	prom;
    UCHAR	bval;
    USHORT	index;
    PSCLINK	plink;

    if( pACB->DeviceCnt == 0 )
    {
	pACB->pLinkDCB = pDCB;
	pACB->pDCBRunRobin = pDCB;
	pDCB->pNextDCB = pDCB;
	pPrevDCB = pDCB;
    }
    else
	pPrevDCB->pNextDCB = pDCB;

    plink = cmd->sc_link;
    pDCB->pDCBACB = pACB;
    pDCB->UnitSCSIID = plink->target;
    pDCB->UnitSCSILUN = plink->lun;
    pDCB->pWaitingSRB = NULL;
    pDCB->pGoingSRB = NULL;
    pDCB->GoingSRBCnt = 0;
    pDCB->pActiveSRB = NULL;
    pDCB->TagMask = 0;
    pDCB->MaxCommand = 1;
    pDCB->AdaptIndex = pACB->AdapterIndex;
    index = pACB->AdapterIndex;
    pDCB->DCBFlag = 0;

    prom = (PEEprom) &eepromBuf[index][plink->target << 2];
    pDCB->DevMode = prom->EE_MODE1;
    pDCB->AdpMode = eepromBuf[index][EE_MODE2];

    if( pDCB->DevMode & EN_DISCONNECT_ )
	bval = 0xC0;
    else
	bval = 0x80;
    bval |= plink->lun;
    pDCB->IdentifyMsg = bval;

    pDCB->SyncMode = 0;
    if( pDCB->DevMode & SYNC_NEGO_ )
    {
	if( !(plink->lun) || CurrSyncOffset )
	    pDCB->SyncMode = SYNC_ENABLE;
    }

    pDCB->SyncPeriod = 0;
    pDCB->SyncOffset = 0;
    pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2;

    pDCB->CtrlR1 = pACB->AdaptSCSIID;
    if( pDCB->DevMode & PARITY_CHK_ )
	pDCB->CtrlR1 |= PARITY_ERR_REPO;

    pDCB->CtrlR3 = FAST_CLK;

    pDCB->CtrlR4 = EATER_25NS;
    if( pDCB->AdpMode & ACTIVE_NEGATION)
	pDCB->CtrlR4 |= NEGATE_REQACKDATA;
}


/***********************************************************************
 * Function : static void DC390_initSRB
 *
 * Purpose :  initialize the internal structures for a given SRB
 *
 * Inputs : psrb - pointer to this scsi request block structure
 *
 ***********************************************************************/
void DC390_initSRB( PSRB psrb )
{
	psrb->PhysSRB = vtophys( psrb );
}


void DC390_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];
	DC390_initSRB( psrb );
    }
}


/***********************************************************************
 * Function : static void DC390_initACB
 *
 * Purpose :  initialize the internal structures for a given SCSI host
 *
 * Inputs : psh - pointer to this host adapter's structure
 *
 ***********************************************************************/
void DC390_initACB( PACB pACB, ULONG io_port, UCHAR Irq, USHORT index )
{
    USHORT  i;


    pACB->max_id = 7;
    if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
	pACB->max_id--;
    if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
	pACB->max_lun = 7;
    else
	pACB->max_lun = 0;

    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->HostID_Bit = (1 << pACB->AdaptSCSIID);
    pACB->AdaptSCSILUN = 0;
    pACB->DeviceCnt = 0;
    pACB->IRQLevel = Irq;
    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];
    DC390_linkSRB( pACB );
    pACB->pTmpSRB = &pACB->TmpSRB;
    DC390_initSRB( pACB->pTmpSRB );
    for(i=0; i<MAX_SCSI_ID; i++)
	pACB->DCBmap[i] = 0;

    pACB->ScsiLink.adapter_unit = index;
    pACB->ScsiLink.adapter_targ = pACB->AdaptSCSIID;
    pACB->ScsiLink.fordriver	= 0;
    pACB->ScsiLink.opennings = 2;
    pACB->ScsiLink.adapter	= &trmamd_switch;
    pACB->ScsiLink.device	= &trmamd_dev;
    pACB->ScsiLink.flags	= 0;
}


/***********************************************************************
 * Function : static int DC390_initAdapter
 *
 * Purpose :  initialize the SCSI chip ctrl registers
 *
 * Inputs : psh - pointer to this host adapter's structure
 *
 ***********************************************************************/
int DC390_initAdapter( PACB pACB, ULONG io_port, UCHAR Irq, USHORT index,
		       pcici_t config_id )
{
    USHORT ioport;
    UCHAR  bval;

#ifdef	CHECK_SHARE_INT
    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 )
    {
#endif
	if( !pci_map_int (config_id, (PVOID)DC390_Interrupt, pACB, &bio_imask) )
	{
	    if(bootverbose)
		printf("DC390: register Interrupt handler error!\n");
	    return( -1 );
	}

#ifdef	CHECK_SHARE_INT
    }
#endif

    ioport = (USHORT) io_port;
    bval = 153; 			/* 250ms selection timeout */
    OutB(bval,ioport+Scsi_TimeOut);

    bval = CLK_FREQ_40MHZ;		/* Conversion factor = 0 , 40MHz clock */
    OutB(bval,ioport+Clk_Factor);

    bval = NOP_CMD;			/* NOP cmd - clear command register */
    OutB(bval,ioport+ScsiCmd);

    bval = EN_FEATURE+EN_SCSI2_CMD;	/* Enable Feature and SCSI-2 */
    OutB(bval,ioport+CtrlReg2);

    bval = FAST_CLK;			/* fast clock */
    OutB(bval,ioport+CtrlReg3);

    bval = EATER_25NS;
    if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION )
	 bval |= NEGATE_REQACKDATA;
    OutB(bval,ioport+CtrlReg4);

    bval = DIS_INT_ON_SCSI_RST; 	/* Disable SCSI bus reset interrupt */
    OutB(bval,ioport+CtrlReg1);

    return(0);
}


#ifdef PCI_COMPAT
static pcicfgregs *cfg;
#define DC390_EnableCfg(a,b)
#define DC390_DisableCfg(a)
#define DC390_inByte(a,reg) pci_cfgread(cfg,reg,1)
#define DC390_inWord(a,reg) pci_cfgread(cfg,reg,2)
#define DC390_inDword(a,reg) pci_cfgread(cfg,reg,4)
#define DC390_OutB(a,reg,val) pci_cfgwrite(cfg,reg,val,1)
#else

void
DC390_EnableCfg( USHORT mechnum, UCHAR regval )
{
    ULONG wlval;

    if(mechnum == 2)
    {
	OutB(mech2bus, PCI_CFG2_FORWARD_REG);
	OutB(mech2CfgSPenR, PCI_CFG2_ENABLE_REG);
    }
    else
    {
	regval &= 0xFC;
	wlval = mech1addr;
	wlval |= (((ULONG)regval) & 0xff);
	OutL(wlval, PCI_CFG1_ADDRESS_REG);
    }
}


void
DC390_DisableCfg( USHORT mechnum )
{

    if(mechnum == 2)
	OutB(0, PCI_CFG2_ENABLE_REG);
    else
	OutL(0, PCI_CFG1_ADDRESS_REG);
}


UCHAR
DC390_inByte( USHORT mechnum, UCHAR regval )
{
    UCHAR bval;
    USHORT wval;
    int   flags;

    flags = splbio();
    DC390_EnableCfg( mechnum, regval );
    if(mechnum == 2)
    {
	wval = mech2Agent;
	wval <<= 8;
	wval |= ((USHORT) regval) & 0xff;
	bval = inb(wval);
    }
    else
    {
	regval &= 3;
	bval = inb(PCI_CFG1_DATA_REG | regval);
    }
    DC390_DisableCfg(mechnum);
    splx(flags);
    return(bval);
}


USHORT
DC390_inWord( USHORT mechnum, UCHAR regval )
{
    USHORT wval;
    int   flags;

    flags = splbio();
    DC390_EnableCfg(mechnum,regval);
    if(mechnum == 2)
    {
	wval = mech2Agent;
	wval <<= 8;
	wval |= regval;
	wval = inw(wval);
    }
    else
    {
	regval &= 3;
	wval = inw(PCI_CFG1_DATA_REG | regval);
    }
    DC390_DisableCfg(mechnum);
    splx(flags);
    return(wval);
}


ULONG
DC390_inDword(USHORT mechnum, UCHAR regval )
{
    ULONG wlval;
    int   flags;
    USHORT wval;

    flags = splbio();
    DC390_EnableCfg(mechnum,regval);
    if(mechnum == 2)
    {
	wval = mech2Agent;
	wval <<= 8;
	wval |= regval;
	wlval = inl(wval);
    }
    else
    {
	wlval = inl(PCI_CFG1_DATA_REG);
    }
    DC390_DisableCfg(mechnum);
    splx(flags);
    return(wlval);
}


void
DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval )
{

    USHORT wval;
    int    flags;

    flags = splbio();
    DC390_EnableCfg(mechnum,regval);
    if(mechnum == 2)
    {
	wval = mech2Agent;
	wval <<= 8;
	wval |= regval;
	OutB(bval, wval);
    }
    else
    {
	regval &= 3;
	OutB(bval, PCI_CFG1_DATA_REG | regval);
    }
    DC390_DisableCfg(mechnum);
    splx(flags);
}

#endif /PCI_COMPAT */

void
DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval )
{

    UCHAR bval;

    bval = 0;
    if(mode == ENABLE_CE)
	*regval = 0xc0;
    else
	*regval = 0x80;
    DC390_OutB(mechnum,*regval,bval);
    if(mode == DISABLE_CE)
	DC390_OutB(mechnum,*regval,bval);
    DELAY(160);
}


void
DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry )
{
    UCHAR bval;

    bval = 0;
    if(Carry)
    {
	bval = 0x40;
	*regval = 0x80;
	DC390_OutB(mechnum,*regval,bval);
    }
    DELAY(160);
    bval |= 0x80;
    DC390_OutB(mechnum,*regval,bval);
    DELAY(160);
    bval = 0;
    DC390_OutB(mechnum,*regval,bval);
    DELAY(160);
}


UCHAR
DC390_EEpromInDO( USHORT mechnum )
{
    UCHAR bval,regval;

    regval = 0x80;
    bval = 0x80;
    DC390_OutB(mechnum,regval,bval);
    DELAY(160);
    bval = 0x40;
    DC390_OutB(mechnum,regval,bval);
    DELAY(160);
    regval = 0x0;
    bval = DC390_inByte(mechnum,regval);
    if(bval == 0x22)
	return(1);
    else
	return(0);
}


USHORT
EEpromGetData1( USHORT mechnum )
{
    UCHAR i;
    UCHAR carryFlag;
    USHORT wval;

    wval = 0;
    for(i=0; i<16; i++)
    {
	wval <<= 1;
	carryFlag = DC390_EEpromInDO(mechnum);
	wval |= carryFlag;
    }
    return(wval);
}


void
DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd )
{
    UCHAR i,j;
    USHORT carryFlag;

    carryFlag = 1;
    j = 0x80;
    for(i=0; i<9; i++)
    {
	DC390_EEpromOutDI(mechnum,regval,carryFlag);
	carryFlag = (EEpromCmd & j) ? 1 : 0;
	j >>= 1;
    }
}


void
DC390_ReadEEprom( USHORT mechnum, USHORT index )
{
    UCHAR   regval,cmd;
    PUSHORT ptr;
    USHORT  i;

    ptr = (PUSHORT) &eepromBuf[index][0];
    cmd = EEPROM_READ;
    for(i=0; i<0x40; i++)
    {
	DC390_EnDisableCE(ENABLE_CE, mechnum, &regval);
	DC390_Prepare(mechnum, &regval, cmd);
	*ptr = EEpromGetData1(mechnum);
	ptr++;
	cmd++;
	DC390_EnDisableCE(DISABLE_CE,mechnum,&regval);
    }
}


USHORT
DC390_DefaultEEprom( USHORT mechnum, USHORT index )
{
    PUCHAR  ptr;
    USHORT  i;

    ptr = (PUCHAR) &eepromBuf[index][0];
    bzero (ptr, sizeof eepromBuf[index]);
    for(i=0; i<0x40; i++)
    {
	*ptr = (TAG_QUEUING_|EN_DISCONNECT_|SYNC_NEGO_|PARITY_CHK_);
	ptr += 4;
    }
    ptr[EE_ADAPT_SCSI_ID] = 7;
    ptr[EE_MODE2] = (LUN_CHECK|ACTIVE_NEGATION);
    ptr[EE_TAG_CMD_NUM] = 4;
    return 0;
}


USHORT
DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index )
{
    USHORT wval, rc, *ptr;
    UCHAR  i;

    DC390_ReadEEprom( MechNum, index );
    wval = 0;
    ptr = (PUSHORT) &eepromBuf[index][0];
    for(i=0; i<128 ;i+=2, ptr++)
	wval += *ptr;
    if( wval == 0x1234 )
	rc = 0;
    else
	rc = DC390_DefaultEEprom( MechNum, index);
    return( rc );
}


USHORT
DC390_ToMech( USHORT Mechnum, pcici_t config_id )
{

#ifdef PCI_COMPAT
    cfg = config_id;
#else
    if(Mechnum == 2)
    {
	mech2bus = config_id.cfg2.forward;	/* Bus num */
	mech2Agent = config_id.cfg2.port >> 8;	/* Dev num */
	mech2CfgSPenR = config_id.cfg2.enable;	/* Fun num */
    }
    else	/* use mech #1 method */
    {
	mech1addr = config_id.cfg1;
    }
#endif /* PCI_COMPAT */
    return(0);
}

/***********************************************************************
 * Function : static int DC390_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
DC390_init( ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum,
	     pcici_t config_id)
{
    PACB  pACB;

    if( !DC390_CheckEEpromCheckSum( MechNum, index) )
    {
	pACB = (PACB) malloc (sizeof (struct _ACB), M_DEVBUF, M_WAITOK);
	if( !pACB )
	{
	    printf("DC390%d: cannot allocate ACB !\n", index);
	    return( -1 );
	}
	bzero (pACB, sizeof (struct _ACB));
	DC390_initACB( pACB, io_port, Irq, index );
	if( !DC390_initAdapter( pACB, io_port, Irq, index, config_id) )
	{
	    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;
	    }
	    pACB0[index] = pACB;

#ifdef DC390_DEBUG0
	printf("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n",
	      (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array);
	printf("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",
	      sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );
#endif

	    return( 0 );
	}
	else
	{
	    free( pACB, M_DEVBUF);
	    return( -1 );
	}
    }
    else
    {
	printf("DC390_init: EEPROM reading error!\n");
	return( -1 );
    }
}



void
trmamd_attach (pcici_t config_id, int unit)
{
    struct scsibus_data *scbus;
    UCHAR   irq;
    USHORT  MechNum;
    ULONG   io_port, wlval;
    PACB    pACB = 0;
    int     flags;

    if( unit >= MAX_ADAPTER_NUM )
	return;

    if( pACB0[unit] )
	return;

    CurrentID = 0;
    CurrentLUN = 0;
    MechNum = pci_mechanism;

#ifdef DC390_DEBUG0
    if(bootverbose)
	printf("DC390: Mech=%2x,\n",(UCHAR) MechNum);
#endif

    if( !DC390_ToMech( MechNum, config_id ) )
    {
	wlval = DC390_inDword( MechNum, PCI_ID_REG);
	if(wlval == PCI_DEVICE_ID_AMD53C974 )
	{
	    io_port =DC390_inDword(MechNum,PCI_BASE_ADDR0) & 0xFFFE;
	    irq = DC390_inByte( MechNum, PCI_INTERRUPT_REG);
#ifdef DC390_DEBUG0
	    if(bootverbose)
		printf("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
#endif
	    if( !DC390_init( io_port, irq, (USHORT) unit, MechNum, config_id) )
	    {
		adapterCnt++;
	    }
	    else
		return;
	}
    }

    pACB = pACB0[unit];

/*
  Now let the generic SCSI driver look for the SCSI devices on the bus
*/

    flags = splbio();

    scbus = scsi_alloc_bus();
    if(!scbus)
    {
	splx(flags);
	return;
    }
    scbus->adapter_link = &pACB->ScsiLink;
    scbus->maxtarg = pACB->max_id;

#ifdef	DC390_DEBUG
    if(bootverbose)
	printf("\nDC390: scanning for devices ...\n\n");
#endif

    scsi_attachdevs (scbus);
    scbus = NULL;   /* Upper-level SCSI code owns this now */

#ifdef	DC390_DEBUG
    if(bootverbose)
	printf("\n\nDC390: Attach devices return\n");
#endif

    splx(flags);
}


static	char*
trmamd_probe (pcici_t tag, pcidi_t type)
{
	if( type == PCI_DEVICE_ID_AMD53C974 )
	    return ("amd 53c974 scsi");
	else
	    return (NULL);
}

