/*

  FlashPoint.c -- FlashPoint SCCB Manager for Linux

  This file contains the FlashPoint SCCB Manager from BusLogic's FlashPoint
  Driver Developer's Kit, with minor modifications by Leonard N. Zubkoff for
  Linux compatibility.  It was provided by BusLogic in the form of 16 separate
  source files, which would have unnecessarily cluttered the scsi directory, so
  the individual files have been combined into this single file.

  Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved

  This file is available under both the GNU General Public License
  and a BSD-style copyright; see LICENSE.FlashPoint for details.

*/


#ifdef CONFIG_SCSI_FLASHPOINT

#define MAX_CARDS	8
#undef BUSTYPE_PCI

#define CRCMASK	0xA001

#define FAILURE         0xFFFFFFFFL

struct sccb;
typedef void (*CALL_BK_FN) (struct sccb *);

struct sccb_mgr_info {
	u32 si_baseaddr;
	unsigned char si_present;
	unsigned char si_intvect;
	unsigned char si_id;
	unsigned char si_lun;
	u16 si_fw_revision;
	u16 si_per_targ_init_sync;
	u16 si_per_targ_fast_nego;
	u16 si_per_targ_ultra_nego;
	u16 si_per_targ_no_disc;
	u16 si_per_targ_wide_nego;
	u16 si_mflags;
	unsigned char si_card_family;
	unsigned char si_bustype;
	unsigned char si_card_model[3];
	unsigned char si_relative_cardnum;
	unsigned char si_reserved[4];
	u32 si_OS_reserved;
	unsigned char si_XlatInfo[4];
	u32 si_reserved2[5];
	u32 si_secondary_range;
};

#define SCSI_PARITY_ENA		  0x0001
#define LOW_BYTE_TERM		  0x0010
#define HIGH_BYTE_TERM		  0x0020
#define BUSTYPE_PCI	  0x3

#define SUPPORT_16TAR_32LUN	  0x0002
#define SOFT_RESET		  0x0004
#define EXTENDED_TRANSLATION	  0x0008
#define POST_ALL_UNDERRRUNS	  0x0040
#define FLAG_SCAM_ENABLED	  0x0080
#define FLAG_SCAM_LEVEL2	  0x0100

#define HARPOON_FAMILY        0x02

/* SCCB struct used for both SCCB and UCB manager compiles! 
 * The UCB Manager treats the SCCB as it's 'native hardware structure' 
 */

/*#pragma pack(1)*/
struct sccb {
	unsigned char OperationCode;
	unsigned char ControlByte;
	unsigned char CdbLength;
	unsigned char RequestSenseLength;
	u32 DataLength;
	void *DataPointer;
	unsigned char CcbRes[2];
	unsigned char HostStatus;
	unsigned char TargetStatus;
	unsigned char TargID;
	unsigned char Lun;
	unsigned char Cdb[12];
	unsigned char CcbRes1;
	unsigned char Reserved1;
	u32 Reserved2;
	u32 SensePointer;

	CALL_BK_FN SccbCallback;	/* VOID (*SccbCallback)(); */
	u32 SccbIOPort;			/* Identifies board base port */
	unsigned char SccbStatus;
	unsigned char SCCBRes2;
	u16 SccbOSFlags;

	u32 Sccb_XferCnt;	/* actual transfer count */
	u32 Sccb_ATC;
	u32 SccbVirtDataPtr;	/* virtual addr for OS/2 */
	u32 Sccb_res1;
	u16 Sccb_MGRFlags;
	u16 Sccb_sgseg;
	unsigned char Sccb_scsimsg;	/* identify msg for selection */
	unsigned char Sccb_tag;
	unsigned char Sccb_scsistat;
	unsigned char Sccb_idmsg;	/* image of last msg in */
	struct sccb *Sccb_forwardlink;
	struct sccb *Sccb_backlink;
	u32 Sccb_savedATC;
	unsigned char Save_Cdb[6];
	unsigned char Save_CdbLen;
	unsigned char Sccb_XferState;
	u32 Sccb_SGoffset;
};

#pragma pack()

#define SCATTER_GATHER_COMMAND    0x02
#define RESIDUAL_COMMAND          0x03
#define RESIDUAL_SG_COMMAND       0x04
#define RESET_COMMAND             0x81

#define F_USE_CMD_Q              0x20	/*Inidcates TAGGED command. */
#define TAG_TYPE_MASK            0xC0	/*Type of tag msg to send. */
#define SCCB_DATA_XFER_OUT       0x10	/* Write */
#define SCCB_DATA_XFER_IN        0x08	/* Read */

#define NO_AUTO_REQUEST_SENSE    0x01	/* No Request Sense Buffer */

#define BUS_FREE_ST     0
#define SELECT_ST       1
#define SELECT_BDR_ST   2	/* Select w\ Bus Device Reset */
#define SELECT_SN_ST    3	/* Select w\ Sync Nego */
#define SELECT_WN_ST    4	/* Select w\ Wide Data Nego */
#define SELECT_Q_ST     5	/* Select w\ Tagged Q'ing */
#define COMMAND_ST      6
#define DATA_OUT_ST     7
#define DATA_IN_ST      8
#define DISCONNECT_ST   9
#define ABORT_ST        11

#define F_HOST_XFER_DIR                0x01
#define F_ALL_XFERRED                  0x02
#define F_SG_XFER                      0x04
#define F_AUTO_SENSE                   0x08
#define F_ODD_BALL_CNT                 0x10
#define F_NO_DATA_YET                  0x80

#define F_STATUSLOADED                 0x01
#define F_DEV_SELECTED                 0x04

#define SCCB_COMPLETE               0x00	/* SCCB completed without error */
#define SCCB_DATA_UNDER_RUN         0x0C
#define SCCB_SELECTION_TIMEOUT      0x11	/* Set SCSI selection timed out */
#define SCCB_DATA_OVER_RUN          0x12
#define SCCB_PHASE_SEQUENCE_FAIL    0x14	/* Target bus phase sequence failure */

#define SCCB_GROSS_FW_ERR           0x27	/* Major problem! */
#define SCCB_BM_ERR                 0x30	/* BusMaster error. */
#define SCCB_PARITY_ERR             0x34	/* SCSI parity error */

#define SCCB_IN_PROCESS            0x00
#define SCCB_SUCCESS               0x01
#define SCCB_ABORT                 0x02
#define SCCB_ERROR                 0x04

#define  ORION_FW_REV      3110

#define QUEUE_DEPTH     254+1	/*1 for Normal disconnect 32 for Q'ing. */

#define	MAX_MB_CARDS	4	/* Max. no of cards suppoerted on Mother Board */

#define MAX_SCSI_TAR    16
#define MAX_LUN         32
#define LUN_MASK			0x1f

#define SG_BUF_CNT      16	/*Number of prefetched elements. */

#define SG_ELEMENT_SIZE 8	/*Eight byte per element. */

#define RD_HARPOON(ioport)          inb((u32)ioport)
#define RDW_HARPOON(ioport)         inw((u32)ioport)
#define RD_HARP32(ioport,offset,data) (data = inl((u32)(ioport + offset)))
#define WR_HARPOON(ioport,val)      outb((u8) val, (u32)ioport)
#define WRW_HARPOON(ioport,val)       outw((u16)val, (u32)ioport)
#define WR_HARP32(ioport,offset,data)  outl(data, (u32)(ioport + offset))

#define  TAR_SYNC_MASK     (BIT(7)+BIT(6))
#define  SYNC_TRYING               BIT(6)
#define  SYNC_SUPPORTED    (BIT(7)+BIT(6))

#define  TAR_WIDE_MASK     (BIT(5)+BIT(4))
#define  WIDE_ENABLED              BIT(4)
#define  WIDE_NEGOCIATED   BIT(5)

#define  TAR_TAG_Q_MASK    (BIT(3)+BIT(2))
#define  TAG_Q_TRYING              BIT(2)
#define  TAG_Q_REJECT      BIT(3)

#define  TAR_ALLOW_DISC    BIT(0)

#define  EE_SYNC_MASK      (BIT(0)+BIT(1))
#define  EE_SYNC_5MB       BIT(0)
#define  EE_SYNC_10MB      BIT(1)
#define  EE_SYNC_20MB      (BIT(0)+BIT(1))

#define  EE_WIDE_SCSI      BIT(7)

struct sccb_mgr_tar_info {

	struct sccb *TarSelQ_Head;
	struct sccb *TarSelQ_Tail;
	unsigned char TarLUN_CA;	/*Contingent Allgiance */
	unsigned char TarTagQ_Cnt;
	unsigned char TarSelQ_Cnt;
	unsigned char TarStatus;
	unsigned char TarEEValue;
	unsigned char TarSyncCtrl;
	unsigned char TarReserved[2];	/* for alignment */
	unsigned char LunDiscQ_Idx[MAX_LUN];
	unsigned char TarLUNBusy[MAX_LUN];
};

struct nvram_info {
	unsigned char niModel;		/* Model No. of card */
	unsigned char niCardNo;		/* Card no. */
	u32 niBaseAddr;			/* Port Address of card */
	unsigned char niSysConf;	/* Adapter Configuration byte -
					   Byte 16 of eeprom map */
	unsigned char niScsiConf;	/* SCSI Configuration byte -
					   Byte 17 of eeprom map */
	unsigned char niScamConf;	/* SCAM Configuration byte -
					   Byte 20 of eeprom map */
	unsigned char niAdapId;		/* Host Adapter ID -
					   Byte 24 of eerpom map */
	unsigned char niSyncTbl[MAX_SCSI_TAR / 2];	/* Sync/Wide byte
							   of targets */
	unsigned char niScamTbl[MAX_SCSI_TAR][4];	/* Compressed Scam name
							   string of Targets */
};

#define	MODEL_LT		1
#define	MODEL_DL		2
#define	MODEL_LW		3
#define	MODEL_DW		4

struct sccb_card {
	struct sccb *currentSCCB;
	struct sccb_mgr_info *cardInfo;

	u32 ioPort;

	unsigned short cmdCounter;
	unsigned char discQCount;
	unsigned char tagQ_Lst;
	unsigned char cardIndex;
	unsigned char scanIndex;
	unsigned char globalFlags;
	unsigned char ourId;
	struct nvram_info *pNvRamInfo;
	struct sccb *discQ_Tbl[QUEUE_DEPTH];

};

#define F_TAG_STARTED		0x01
#define F_CONLUN_IO			0x02
#define F_DO_RENEGO			0x04
#define F_NO_FILTER			0x08
#define F_GREEN_PC			0x10
#define F_HOST_XFER_ACT		0x20
#define F_NEW_SCCB_CMD		0x40
#define F_UPDATE_EEPROM		0x80

#define  ID_STRING_LENGTH  32
#define  TYPE_CODE0        0x63	/*Level2 Mstr (bits 7-6),  */

#define  SLV_TYPE_CODE0    0xA3	/*Priority Bit set (bits 7-6),  */

#define  ASSIGN_ID   0x00
#define  SET_P_FLAG  0x01
#define  CFG_CMPLT   0x03
#define  DOM_MSTR    0x0F
#define  SYNC_PTRN   0x1F

#define  ID_0_7      0x18
#define  ID_8_F      0x11
#define  MISC_CODE   0x14
#define  CLR_P_FLAG  0x18

#define  INIT_SELTD  0x01
#define  LEVEL2_TAR  0x02

enum scam_id_st { ID0, ID1, ID2, ID3, ID4, ID5, ID6, ID7, ID8, ID9, ID10, ID11,
	    ID12,
	ID13, ID14, ID15, ID_UNUSED, ID_UNASSIGNED, ID_ASSIGNED, LEGACY,
	CLR_PRIORITY, NO_ID_AVAIL
};

typedef struct SCCBscam_info {

	unsigned char id_string[ID_STRING_LENGTH];
	enum scam_id_st state;

} SCCBSCAM_INFO;


#define  SMIDENT                 0x80
#define  DISC_PRIV               0x40

#define  SM8BIT                  0x00
#define  SM16BIT                 0x01

#define  SIX_BYTE_CMD            0x06
#define  TWELVE_BYTE_CMD         0x0C

#define  ASYNC                   0x00
#define  MAX_OFFSET              0x0F	/* Maxbyteoffset for Sync Xfers */

#define  EEPROM_WD_CNT     256

#define  EEPROM_CHECK_SUM  0
#define  FW_SIGNATURE      2
#define  MODEL_NUMB_0      4
#define  MODEL_NUMB_2      6
#define  MODEL_NUMB_4      8
#define  SYSTEM_CONFIG     16
#define  SCSI_CONFIG       17
#define  BIOS_CONFIG       18
#define  SCAM_CONFIG       20
#define  ADAPTER_SCSI_ID   24

#define  IGNORE_B_SCAN     32
#define  SEND_START_ENA    34
#define  DEVICE_ENABLE     36

#define  SYNC_RATE_TBL     38
#define  SYNC_RATE_TBL01   38
#define  SYNC_RATE_TBL23   40
#define  SYNC_RATE_TBL45   42
#define  SYNC_RATE_TBL67   44
#define  SYNC_RATE_TBL89   46
#define  SYNC_RATE_TBLab   48
#define  SYNC_RATE_TBLcd   50
#define  SYNC_RATE_TBLef   52

#define  EE_SCAMBASE      256

#define  SCAM_ENABLED   BIT(2)
#define  SCAM_LEVEL2    BIT(3)

#define	RENEGO_ENA		BIT(10)
#define	CONNIO_ENA		BIT(11)
#define  GREEN_PC_ENA   BIT(12)

#define  AUTO_RATE_00   00
#define  AUTO_RATE_05   01
#define  AUTO_RATE_10   02
#define  AUTO_RATE_20   03

#define  WIDE_NEGO_BIT     BIT(7)
#define  DISC_ENABLE_BIT   BIT(6)

#define  hp_vendor_id_0       0x00	/* LSB */
#define  ORION_VEND_0   0x4B

#define  hp_vendor_id_1       0x01	/* MSB */
#define  ORION_VEND_1   0x10

#define  hp_device_id_0       0x02	/* LSB */
#define  ORION_DEV_0    0x30

#define  hp_device_id_1       0x03	/* MSB */
#define  ORION_DEV_1    0x81

	/* Sub Vendor ID and Sub Device ID only available in
	   Harpoon Version 2 and higher */

#define  hp_sub_device_id_0   0x06	/* LSB */

#define  hp_semaphore         0x0C
#define SCCB_MGR_ACTIVE    BIT(0)
#define TICKLE_ME          BIT(1)
#define SCCB_MGR_PRESENT   BIT(3)
#define BIOS_IN_USE        BIT(4)

#define  hp_sys_ctrl          0x0F

#define  STOP_CLK          BIT(0)	/*Turn off BusMaster Clock */
#define  DRVR_RST          BIT(1)	/*Firmware Reset to 80C15 chip */
#define  HALT_MACH         BIT(3)	/*Halt State Machine      */
#define  HARD_ABORT        BIT(4)	/*Hard Abort              */

#define  hp_host_blk_cnt      0x13

#define  XFER_BLK64        0x06	/*     1 1 0 64 byte per block */

#define  BM_THRESHOLD      0x40	/* PCI mode can only xfer 16 bytes */

#define  hp_int_mask          0x17

#define  INT_CMD_COMPL     BIT(0)	/* DMA command complete   */
#define  INT_EXT_STATUS    BIT(1)	/* Extended Status Set    */

#define  hp_xfer_cnt_lo       0x18
#define  hp_xfer_cnt_hi       0x1A
#define  hp_xfer_cmd          0x1B

#define  XFER_HOST_DMA     0x00	/*     0 0 0 Transfer Host -> DMA */
#define  XFER_DMA_HOST     0x01	/*     0 0 1 Transfer DMA  -> Host */

#define  XFER_HOST_AUTO    0x00	/*     0 0 Auto Transfer Size   */

#define  XFER_DMA_8BIT     0x20	/*     0 1 8 BIT  Transfer Size */

#define  DISABLE_INT       BIT(7)	/*Do not interrupt at end of cmd. */

#define  HOST_WRT_CMD      ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_8BIT))
#define  HOST_RD_CMD       ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_8BIT))

#define  hp_host_addr_lo      0x1C
#define  hp_host_addr_hmi     0x1E

#define  hp_ee_ctrl           0x22

#define  EXT_ARB_ACK       BIT(7)
#define  SCSI_TERM_ENA_H   BIT(6)	/* SCSI high byte terminator */
#define  SEE_MS            BIT(5)
#define  SEE_CS            BIT(3)
#define  SEE_CLK           BIT(2)
#define  SEE_DO            BIT(1)
#define  SEE_DI            BIT(0)

#define  EE_READ           0x06
#define  EE_WRITE          0x05
#define  EWEN              0x04
#define  EWEN_ADDR         0x03C0
#define  EWDS              0x04
#define  EWDS_ADDR         0x0000

#define  hp_bm_ctrl           0x26

#define  SCSI_TERM_ENA_L   BIT(0)	/*Enable/Disable external terminators */
#define  FLUSH_XFER_CNTR   BIT(1)	/*Flush transfer counter */
#define  FORCE1_XFER       BIT(5)	/*Always xfer one byte in byte mode */
#define  FAST_SINGLE       BIT(6)	/*?? */

#define  BMCTRL_DEFAULT    (FORCE1_XFER|FAST_SINGLE|SCSI_TERM_ENA_L)

#define  hp_sg_addr           0x28
#define  hp_page_ctrl         0x29

#define  SCATTER_EN        BIT(0)
#define  SGRAM_ARAM        BIT(1)
#define  G_INT_DISABLE     BIT(3)	/* Enable/Disable all Interrupts */
#define  NARROW_SCSI_CARD  BIT(4)	/* NARROW/WIDE SCSI config pin */

#define  hp_pci_stat_cfg      0x2D

#define  REC_MASTER_ABORT  BIT(5)	/*received Master abort */

#define  hp_rev_num           0x33

#define  hp_stack_data        0x34
#define  hp_stack_addr        0x35

#define  hp_ext_status        0x36

#define  BM_FORCE_OFF      BIT(0)	/*Bus Master is forced to get off */
#define  PCI_TGT_ABORT     BIT(0)	/*PCI bus master transaction aborted */
#define  PCI_DEV_TMOUT     BIT(1)	/*PCI Device Time out */
#define  CMD_ABORTED       BIT(4)	/*Command aborted */
#define  BM_PARITY_ERR     BIT(5)	/*parity error on data received   */
#define  PIO_OVERRUN       BIT(6)	/*Slave data overrun */
#define  BM_CMD_BUSY       BIT(7)	/*Bus master transfer command busy */
#define  BAD_EXT_STATUS    (BM_FORCE_OFF | PCI_DEV_TMOUT | CMD_ABORTED | \
                                  BM_PARITY_ERR | PIO_OVERRUN)

#define  hp_int_status        0x37

#define  EXT_STATUS_ON     BIT(1)	/*Extended status is valid */
#define  SCSI_INTERRUPT    BIT(2)	/*Global indication of a SCSI int. */
#define  INT_ASSERTED      BIT(5)	/* */

#define  hp_fifo_cnt          0x38

#define  hp_intena		 0x40

#define  RESET		 BIT(7)
#define  PROG_HLT		 BIT(6)
#define  PARITY		 BIT(5)
#define  FIFO		 BIT(4)
#define  SEL		 BIT(3)
#define  SCAM_SEL		 BIT(2)
#define  RSEL		 BIT(1)
#define  TIMEOUT		 BIT(0)
#define  BUS_FREE		 BIT(15)
#define  XFER_CNT_0	 BIT(14)
#define  PHASE		 BIT(13)
#define  IUNKWN		 BIT(12)
#define  ICMD_COMP	 BIT(11)
#define  ITICKLE		 BIT(10)
#define  IDO_STRT		 BIT(9)
#define  ITAR_DISC	 BIT(8)
#define  AUTO_INT		 (BIT(12)+BIT(11)+BIT(10)+BIT(9)+BIT(8))
#define  CLR_ALL_INT	 0xFFFF
#define  CLR_ALL_INT_1	 0xFF00

#define  hp_intstat		 0x42

#define  hp_scsisig           0x44

#define  SCSI_SEL          BIT(7)
#define  SCSI_BSY          BIT(6)
#define  SCSI_REQ          BIT(5)
#define  SCSI_ACK          BIT(4)
#define  SCSI_ATN          BIT(3)
#define  SCSI_CD           BIT(2)
#define  SCSI_MSG          BIT(1)
#define  SCSI_IOBIT        BIT(0)

#define  S_SCSI_PHZ        (BIT(2)+BIT(1)+BIT(0))
#define  S_MSGO_PH         (BIT(2)+BIT(1)       )
#define  S_MSGI_PH         (BIT(2)+BIT(1)+BIT(0))
#define  S_DATAI_PH        (              BIT(0))
#define  S_DATAO_PH        0x00
#define  S_ILL_PH          (       BIT(1)       )

#define  hp_scsictrl_0        0x45

#define  SEL_TAR           BIT(6)
#define  ENA_ATN           BIT(4)
#define  ENA_RESEL         BIT(2)
#define  SCSI_RST          BIT(1)
#define  ENA_SCAM_SEL      BIT(0)

#define  hp_portctrl_0        0x46

#define  SCSI_PORT         BIT(7)
#define  SCSI_INBIT        BIT(6)
#define  DMA_PORT          BIT(5)
#define  DMA_RD            BIT(4)
#define  HOST_PORT         BIT(3)
#define  HOST_WRT          BIT(2)
#define  SCSI_BUS_EN       BIT(1)
#define  START_TO          BIT(0)

#define  hp_scsireset         0x47

#define  SCSI_INI          BIT(6)
#define  SCAM_EN           BIT(5)
#define  DMA_RESET         BIT(3)
#define  HPSCSI_RESET      BIT(2)
#define  PROG_RESET        BIT(1)
#define  FIFO_CLR          BIT(0)

#define  hp_xfercnt_0         0x48
#define  hp_xfercnt_2         0x4A

#define  hp_fifodata_0        0x4C
#define  hp_addstat           0x4E

#define  SCAM_TIMER        BIT(7)
#define  SCSI_MODE8        BIT(3)
#define  SCSI_PAR_ERR      BIT(0)

#define  hp_prgmcnt_0         0x4F

#define  hp_selfid_0          0x50
#define  hp_selfid_1          0x51
#define  hp_arb_id            0x52

#define  hp_select_id         0x53

#define  hp_synctarg_base     0x54
#define  hp_synctarg_12       0x54
#define  hp_synctarg_13       0x55
#define  hp_synctarg_14       0x56
#define  hp_synctarg_15       0x57

#define  hp_synctarg_8        0x58
#define  hp_synctarg_9        0x59
#define  hp_synctarg_10       0x5A
#define  hp_synctarg_11       0x5B

#define  hp_synctarg_4        0x5C
#define  hp_synctarg_5        0x5D
#define  hp_synctarg_6        0x5E
#define  hp_synctarg_7        0x5F

#define  hp_synctarg_0        0x60
#define  hp_synctarg_1        0x61
#define  hp_synctarg_2        0x62
#define  hp_synctarg_3        0x63

#define  NARROW_SCSI       BIT(4)
#define  DEFAULT_OFFSET    0x0F

#define  hp_autostart_0       0x64
#define  hp_autostart_1       0x65
#define  hp_autostart_3       0x67

#define  AUTO_IMMED    BIT(5)
#define  SELECT   BIT(6)
#define  END_DATA (BIT(7)+BIT(6))

#define  hp_gp_reg_0          0x68
#define  hp_gp_reg_1          0x69
#define  hp_gp_reg_3          0x6B

#define  hp_seltimeout        0x6C

#define  TO_4ms            0x67	/* 3.9959ms */

#define  TO_5ms            0x03	/* 4.9152ms */
#define  TO_10ms           0x07	/* 11.xxxms */
#define  TO_250ms          0x99	/* 250.68ms */
#define  TO_290ms          0xB1	/* 289.99ms */

#define  hp_clkctrl_0         0x6D

#define  PWR_DWN           BIT(6)
#define  ACTdeassert       BIT(4)
#define  CLK_40MHZ         (BIT(1) + BIT(0))

#define  CLKCTRL_DEFAULT   (ACTdeassert | CLK_40MHZ)

#define  hp_fiforead          0x6E
#define  hp_fifowrite         0x6F

#define  hp_offsetctr         0x70
#define  hp_xferstat          0x71

#define  FIFO_EMPTY        BIT(6)

#define  hp_portctrl_1        0x72

#define  CHK_SCSI_P        BIT(3)
#define  HOST_MODE8        BIT(0)

#define  hp_xfer_pad          0x73

#define  ID_UNLOCK         BIT(3)

#define  hp_scsidata_0        0x74
#define  hp_scsidata_1        0x75

#define  hp_aramBase          0x80
#define  BIOS_DATA_OFFSET     0x60
#define  BIOS_RELATIVE_CARD   0x64

#define  AR3      (BIT(9) + BIT(8))
#define  SDATA    BIT(10)

#define  CRD_OP   BIT(11)	/* Cmp Reg. w/ Data */

#define  CRR_OP   BIT(12)	/* Cmp Reg. w. Reg. */

#define  CPE_OP   (BIT(14)+BIT(11))	/* Cmp SCSI phs & Branch EQ */

#define  CPN_OP   (BIT(14)+BIT(12))	/* Cmp SCSI phs & Branch NOT EQ */

#define  ADATA_OUT   0x00
#define  ADATA_IN    BIT(8)
#define  ACOMMAND    BIT(10)
#define  ASTATUS     (BIT(10)+BIT(8))
#define  AMSG_OUT    (BIT(10)+BIT(9))
#define  AMSG_IN     (BIT(10)+BIT(9)+BIT(8))

#define  BRH_OP   BIT(13)	/* Branch */

#define  ALWAYS   0x00
#define  EQUAL    BIT(8)
#define  NOT_EQ   BIT(9)

#define  TCB_OP   (BIT(13)+BIT(11))	/* Test condition & branch */

#define  FIFO_0      BIT(10)

#define  MPM_OP   BIT(15)	/* Match phase and move data */

#define  MRR_OP   BIT(14)	/* Move DReg. to Reg. */

#define  S_IDREG  (BIT(2)+BIT(1)+BIT(0))

#define  D_AR0    0x00
#define  D_AR1    BIT(0)
#define  D_BUCKET (BIT(2) + BIT(1) + BIT(0))

#define  RAT_OP      (BIT(14)+BIT(13)+BIT(11))

#define  SSI_OP      (BIT(15)+BIT(11))

#define  SSI_ITAR_DISC	(ITAR_DISC >> 8)
#define  SSI_IDO_STRT	(IDO_STRT >> 8)

#define  SSI_ICMD_COMP	(ICMD_COMP >> 8)
#define  SSI_ITICKLE	(ITICKLE >> 8)

#define  SSI_IUNKWN	(IUNKWN >> 8)
#define  SSI_INO_CC	(IUNKWN >> 8)
#define  SSI_IRFAIL	(IUNKWN >> 8)

#define  NP    0x10		/*Next Phase */
#define  NTCMD 0x02		/*Non- Tagged Command start */
#define  CMDPZ 0x04		/*Command phase */
#define  DINT  0x12		/*Data Out/In interrupt */
#define  DI    0x13		/*Data Out */
#define  DC    0x19		/*Disconnect Message */
#define  ST    0x1D		/*Status Phase */
#define  UNKNWN 0x24		/*Unknown bus action */
#define  CC    0x25		/*Command Completion failure */
#define  TICK  0x26		/*New target reselected us. */
#define  SELCHK 0x28		/*Select & Check SCSI ID latch reg */

#define  ID_MSG_STRT    hp_aramBase + 0x00
#define  NON_TAG_ID_MSG hp_aramBase + 0x06
#define  CMD_STRT       hp_aramBase + 0x08
#define  SYNC_MSGS      hp_aramBase + 0x08

#define  TAG_STRT          0x00
#define  DISCONNECT_START  0x10/2
#define  END_DATA_START    0x14/2
#define  CMD_ONLY_STRT     CMDPZ/2
#define  SELCHK_STRT     SELCHK/2

#define GET_XFER_CNT(port, xfercnt) {RD_HARP32(port,hp_xfercnt_0,xfercnt); xfercnt &= 0xFFFFFF;}
/* #define GET_XFER_CNT(port, xfercnt) (xfercnt = RD_HARPOON(port+hp_xfercnt_2), \
                                 xfercnt <<= 16,\
                                 xfercnt |= RDW_HARPOON((unsigned short)(port+hp_xfercnt_0)))
 */
#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((port+hp_host_addr_lo), (unsigned short)(addr & 0x0000FFFFL)),\
         addr >>= 16,\
         WRW_HARPOON((port+hp_host_addr_hmi), (unsigned short)(addr & 0x0000FFFFL)),\
         WR_HARP32(port,hp_xfercnt_0,count),\
         WRW_HARPOON((port+hp_xfer_cnt_lo), (unsigned short)(count & 0x0000FFFFL)),\
         count >>= 16,\
         WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))

#define ACCEPT_MSG(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
                          WR_HARPOON(port+hp_scsisig, S_ILL_PH);}

#define ACCEPT_MSG_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
                          WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}

#define DISABLE_AUTO(port) (WR_HARPOON(port+hp_scsireset, PROG_RESET),\
                        WR_HARPOON(port+hp_scsireset, 0x00))

#define ARAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
                             (RD_HARPOON(p_port+hp_page_ctrl) | SGRAM_ARAM)))

#define SGRAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
                             (RD_HARPOON(p_port+hp_page_ctrl) & ~SGRAM_ARAM)))

#define MDISABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
                             (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE)))

#define MENABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
                             (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)))

static unsigned char FPT_sisyncn(u32 port, unsigned char p_card,
				 unsigned char syncFlag);
static void FPT_ssel(u32 port, unsigned char p_card);
static void FPT_sres(u32 port, unsigned char p_card,
		     struct sccb_card *pCurrCard);
static void FPT_shandem(u32 port, unsigned char p_card,
			struct sccb *pCurrSCCB);
static void FPT_stsyncn(u32 port, unsigned char p_card);
static void FPT_sisyncr(u32 port, unsigned char sync_pulse,
			unsigned char offset);
static void FPT_sssyncv(u32 p_port, unsigned char p_id,
			unsigned char p_sync_value,
			struct sccb_mgr_tar_info *currTar_Info);
static void FPT_sresb(u32 port, unsigned char p_card);
static void FPT_sxfrp(u32 p_port, unsigned char p_card);
static void FPT_schkdd(u32 port, unsigned char p_card);
static unsigned char FPT_RdStack(u32 port, unsigned char index);
static void FPT_WrStack(u32 portBase, unsigned char index,
			unsigned char data);
static unsigned char FPT_ChkIfChipInitialized(u32 ioPort);

static void FPT_SendMsg(u32 port, unsigned char message);
static void FPT_queueFlushTargSccb(unsigned char p_card, unsigned char thisTarg,
				   unsigned char error_code);

static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card);
static void FPT_RNVRamData(struct nvram_info *pNvRamInfo);

static unsigned char FPT_siwidn(u32 port, unsigned char p_card);
static void FPT_stwidn(u32 port, unsigned char p_card);
static void FPT_siwidr(u32 port, unsigned char width);

static void FPT_queueSelectFail(struct sccb_card *pCurrCard,
				unsigned char p_card);
static void FPT_queueDisconnect(struct sccb *p_SCCB, unsigned char p_card);
static void FPT_queueCmdComplete(struct sccb_card *pCurrCard,
				 struct sccb *p_SCCB, unsigned char p_card);
static void FPT_queueSearchSelect(struct sccb_card *pCurrCard,
				  unsigned char p_card);
static void FPT_queueFlushSccb(unsigned char p_card, unsigned char error_code);
static void FPT_queueAddSccb(struct sccb *p_SCCB, unsigned char card);
static unsigned char FPT_queueFindSccb(struct sccb *p_SCCB,
				       unsigned char p_card);
static void FPT_utilUpdateResidual(struct sccb *p_SCCB);
static unsigned short FPT_CalcCrc16(unsigned char buffer[]);
static unsigned char FPT_CalcLrc(unsigned char buffer[]);

static void FPT_Wait1Second(u32 p_port);
static void FPT_Wait(u32 p_port, unsigned char p_delay);
static void FPT_utilEEWriteOnOff(u32 p_port, unsigned char p_mode);
static void FPT_utilEEWrite(u32 p_port, unsigned short ee_data,
			    unsigned short ee_addr);
static unsigned short FPT_utilEERead(u32 p_port,
				     unsigned short ee_addr);
static unsigned short FPT_utilEEReadOrg(u32 p_port,
					unsigned short ee_addr);
static void FPT_utilEESendCmdAddr(u32 p_port, unsigned char ee_cmd,
				  unsigned short ee_addr);

static void FPT_phaseDataOut(u32 port, unsigned char p_card);
static void FPT_phaseDataIn(u32 port, unsigned char p_card);
static void FPT_phaseCommand(u32 port, unsigned char p_card);
static void FPT_phaseStatus(u32 port, unsigned char p_card);
static void FPT_phaseMsgOut(u32 port, unsigned char p_card);
static void FPT_phaseMsgIn(u32 port, unsigned char p_card);
static void FPT_phaseIllegal(u32 port, unsigned char p_card);

static void FPT_phaseDecode(u32 port, unsigned char p_card);
static void FPT_phaseChkFifo(u32 port, unsigned char p_card);
static void FPT_phaseBusFree(u32 p_port, unsigned char p_card);

static void FPT_XbowInit(u32 port, unsigned char scamFlg);
static void FPT_BusMasterInit(u32 p_port);
static void FPT_DiagEEPROM(u32 p_port);

static void FPT_dataXferProcessor(u32 port,
				  struct sccb_card *pCurrCard);
static void FPT_busMstrSGDataXferStart(u32 port,
				       struct sccb *pCurrSCCB);
static void FPT_busMstrDataXferStart(u32 port,
				     struct sccb *pCurrSCCB);
static void FPT_hostDataXferAbort(u32 port, unsigned char p_card,
				  struct sccb *pCurrSCCB);
static void FPT_hostDataXferRestart(struct sccb *currSCCB);

static unsigned char FPT_SccbMgr_bad_isr(u32 p_port,
					 unsigned char p_card,
					 struct sccb_card *pCurrCard,
					 unsigned short p_int);

static void FPT_SccbMgrTableInitAll(void);
static void FPT_SccbMgrTableInitCard(struct sccb_card *pCurrCard,
				     unsigned char p_card);
static void FPT_SccbMgrTableInitTarget(unsigned char p_card,
				       unsigned char target);

static void FPT_scini(unsigned char p_card, unsigned char p_our_id,
		      unsigned char p_power_up);

static int FPT_scarb(u32 p_port, unsigned char p_sel_type);
static void FPT_scbusf(u32 p_port);
static void FPT_scsel(u32 p_port);
static void FPT_scasid(unsigned char p_card, u32 p_port);
static unsigned char FPT_scxferc(u32 p_port, unsigned char p_data);
static unsigned char FPT_scsendi(u32 p_port,
				 unsigned char p_id_string[]);
static unsigned char FPT_sciso(u32 p_port,
			       unsigned char p_id_string[]);
static void FPT_scwirod(u32 p_port, unsigned char p_data_bit);
static void FPT_scwiros(u32 p_port, unsigned char p_data_bit);
static unsigned char FPT_scvalq(unsigned char p_quintet);
static unsigned char FPT_scsell(u32 p_port, unsigned char targ_id);
static void FPT_scwtsel(u32 p_port);
static void FPT_inisci(unsigned char p_card, u32 p_port,
		       unsigned char p_our_id);
static void FPT_scsavdi(unsigned char p_card, u32 p_port);
static unsigned char FPT_scmachid(unsigned char p_card,
				  unsigned char p_id_string[]);

static void FPT_autoCmdCmplt(u32 p_port, unsigned char p_card);
static void FPT_autoLoadDefaultMap(u32 p_port);

static struct sccb_mgr_tar_info FPT_sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] =
    { {{0}} };
static struct sccb_card FPT_BL_Card[MAX_CARDS] = { {0} };
static SCCBSCAM_INFO FPT_scamInfo[MAX_SCSI_TAR] = { {{0}} };
static struct nvram_info FPT_nvRamInfo[MAX_MB_CARDS] = { {0} };

static unsigned char FPT_mbCards = 0;
static unsigned char FPT_scamHAString[] =
    { 0x63, 0x07, 'B', 'U', 'S', 'L', 'O', 'G', 'I', 'C',
	' ', 'B', 'T', '-', '9', '3', '0',
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
};

static unsigned short FPT_default_intena = 0;

static void (*FPT_s_PhaseTbl[8]) (u32, unsigned char) = {
0};

/*---------------------------------------------------------------------
 *
 * Function: FlashPoint_ProbeHostAdapter
 *
 * Description: Setup and/or Search for cards and return info to caller.
 *
 *---------------------------------------------------------------------*/

static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
{
	static unsigned char first_time = 1;

	unsigned char i, j, id, ScamFlg;
	unsigned short temp, temp2, temp3, temp4, temp5, temp6;
	u32 ioport;
	struct nvram_info *pCurrNvRam;

	ioport = pCardInfo->si_baseaddr;

	if (RD_HARPOON(ioport + hp_vendor_id_0) != ORION_VEND_0)
		return (int)FAILURE;

	if ((RD_HARPOON(ioport + hp_vendor_id_1) != ORION_VEND_1))
		return (int)FAILURE;

	if ((RD_HARPOON(ioport + hp_device_id_0) != ORION_DEV_0))
		return (int)FAILURE;

	if ((RD_HARPOON(ioport + hp_device_id_1) != ORION_DEV_1))
		return (int)FAILURE;

	if (RD_HARPOON(ioport + hp_rev_num) != 0x0f) {

/* For new Harpoon then check for sub_device ID LSB
   the bits(0-3) must be all ZERO for compatible with
   current version of SCCBMgr, else skip this Harpoon
	device. */

		if (RD_HARPOON(ioport + hp_sub_device_id_0) & 0x0f)
			return (int)FAILURE;
	}

	if (first_time) {
		FPT_SccbMgrTableInitAll();
		first_time = 0;
		FPT_mbCards = 0;
	}

	if (FPT_RdStack(ioport, 0) != 0x00) {
		if (FPT_ChkIfChipInitialized(ioport) == 0) {
			pCurrNvRam = NULL;
			WR_HARPOON(ioport + hp_semaphore, 0x00);
			FPT_XbowInit(ioport, 0);	/*Must Init the SCSI before attempting */
			FPT_DiagEEPROM(ioport);
		} else {
			if (FPT_mbCards < MAX_MB_CARDS) {
				pCurrNvRam = &FPT_nvRamInfo[FPT_mbCards];
				FPT_mbCards++;
				pCurrNvRam->niBaseAddr = ioport;
				FPT_RNVRamData(pCurrNvRam);
			} else
				return (int)FAILURE;
		}
	} else
		pCurrNvRam = NULL;

	WR_HARPOON(ioport + hp_clkctrl_0, CLKCTRL_DEFAULT);
	WR_HARPOON(ioport + hp_sys_ctrl, 0x00);

	if (pCurrNvRam)
		pCardInfo->si_id = pCurrNvRam->niAdapId;
	else
		pCardInfo->si_id =
		    (unsigned
		     char)(FPT_utilEERead(ioport,
					  (ADAPTER_SCSI_ID /
					   2)) & (unsigned char)0x0FF);

	pCardInfo->si_lun = 0x00;
	pCardInfo->si_fw_revision = ORION_FW_REV;
	temp2 = 0x0000;
	temp3 = 0x0000;
	temp4 = 0x0000;
	temp5 = 0x0000;
	temp6 = 0x0000;

	for (id = 0; id < (16 / 2); id++) {

		if (pCurrNvRam) {
			temp = (unsigned short)pCurrNvRam->niSyncTbl[id];
			temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
			    (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
		} else
			temp =
			    FPT_utilEERead(ioport,
					   (unsigned short)((SYNC_RATE_TBL / 2)
							    + id));

		for (i = 0; i < 2; temp >>= 8, i++) {

			temp2 >>= 1;
			temp3 >>= 1;
			temp4 >>= 1;
			temp5 >>= 1;
			temp6 >>= 1;
			switch (temp & 0x3) {
			case AUTO_RATE_20:	/* Synchronous, 20 mega-transfers/second */
				temp6 |= 0x8000;
				fallthrough;
			case AUTO_RATE_10:	/* Synchronous, 10 mega-transfers/second */
				temp5 |= 0x8000;
				fallthrough;
			case AUTO_RATE_05:	/* Synchronous, 5 mega-transfers/second */
				temp2 |= 0x8000;
				fallthrough;
			case AUTO_RATE_00:	/* Asynchronous */
				break;
			}

			if (temp & DISC_ENABLE_BIT)
				temp3 |= 0x8000;

			if (temp & WIDE_NEGO_BIT)
				temp4 |= 0x8000;

		}
	}

	pCardInfo->si_per_targ_init_sync = temp2;
	pCardInfo->si_per_targ_no_disc = temp3;
	pCardInfo->si_per_targ_wide_nego = temp4;
	pCardInfo->si_per_targ_fast_nego = temp5;
	pCardInfo->si_per_targ_ultra_nego = temp6;

	if (pCurrNvRam)
		i = pCurrNvRam->niSysConf;
	else
		i = (unsigned
		     char)(FPT_utilEERead(ioport, (SYSTEM_CONFIG / 2)));

	if (pCurrNvRam)
		ScamFlg = pCurrNvRam->niScamConf;
	else
		ScamFlg =
		    (unsigned char)FPT_utilEERead(ioport, SCAM_CONFIG / 2);

	pCardInfo->si_mflags = 0x0000;

	if (i & 0x01)
		pCardInfo->si_mflags |= SCSI_PARITY_ENA;

	if (!(i & 0x02))
		pCardInfo->si_mflags |= SOFT_RESET;

	if (i & 0x10)
		pCardInfo->si_mflags |= EXTENDED_TRANSLATION;

	if (ScamFlg & SCAM_ENABLED)
		pCardInfo->si_mflags |= FLAG_SCAM_ENABLED;

	if (ScamFlg & SCAM_LEVEL2)
		pCardInfo->si_mflags |= FLAG_SCAM_LEVEL2;

	j = (RD_HARPOON(ioport + hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
	if (i & 0x04) {
		j |= SCSI_TERM_ENA_L;
	}
	WR_HARPOON(ioport + hp_bm_ctrl, j);

	j = (RD_HARPOON(ioport + hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
	if (i & 0x08) {
		j |= SCSI_TERM_ENA_H;
	}
	WR_HARPOON(ioport + hp_ee_ctrl, j);

	if (!(RD_HARPOON(ioport + hp_page_ctrl) & NARROW_SCSI_CARD))

		pCardInfo->si_mflags |= SUPPORT_16TAR_32LUN;

	pCardInfo->si_card_family = HARPOON_FAMILY;
	pCardInfo->si_bustype = BUSTYPE_PCI;

	if (pCurrNvRam) {
		pCardInfo->si_card_model[0] = '9';
		switch (pCurrNvRam->niModel & 0x0f) {
		case MODEL_LT:
			pCardInfo->si_card_model[1] = '3';
			pCardInfo->si_card_model[2] = '0';
			break;
		case MODEL_LW:
			pCardInfo->si_card_model[1] = '5';
			pCardInfo->si_card_model[2] = '0';
			break;
		case MODEL_DL:
			pCardInfo->si_card_model[1] = '3';
			pCardInfo->si_card_model[2] = '2';
			break;
		case MODEL_DW:
			pCardInfo->si_card_model[1] = '5';
			pCardInfo->si_card_model[2] = '2';
			break;
		}
	} else {
		temp = FPT_utilEERead(ioport, (MODEL_NUMB_0 / 2));
		pCardInfo->si_card_model[0] = (unsigned char)(temp >> 8);
		temp = FPT_utilEERead(ioport, (MODEL_NUMB_2 / 2));

		pCardInfo->si_card_model[1] = (unsigned char)(temp & 0x00FF);
		pCardInfo->si_card_model[2] = (unsigned char)(temp >> 8);
	}

	if (pCardInfo->si_card_model[1] == '3') {
		if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
			pCardInfo->si_mflags |= LOW_BYTE_TERM;
	} else if (pCardInfo->si_card_model[2] == '0') {
		temp = RD_HARPOON(ioport + hp_xfer_pad);
		WR_HARPOON(ioport + hp_xfer_pad, (temp & ~BIT(4)));
		if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
			pCardInfo->si_mflags |= LOW_BYTE_TERM;
		WR_HARPOON(ioport + hp_xfer_pad, (temp | BIT(4)));
		if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
			pCardInfo->si_mflags |= HIGH_BYTE_TERM;
		WR_HARPOON(ioport + hp_xfer_pad, temp);
	} else {
		temp = RD_HARPOON(ioport + hp_ee_ctrl);
		temp2 = RD_HARPOON(ioport + hp_xfer_pad);
		WR_HARPOON(ioport + hp_ee_ctrl, (temp | SEE_CS));
		WR_HARPOON(ioport + hp_xfer_pad, (temp2 | BIT(4)));
		temp3 = 0;
		for (i = 0; i < 8; i++) {
			temp3 <<= 1;
			if (!(RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7)))
				temp3 |= 1;
			WR_HARPOON(ioport + hp_xfer_pad, (temp2 & ~BIT(4)));
			WR_HARPOON(ioport + hp_xfer_pad, (temp2 | BIT(4)));
		}
		WR_HARPOON(ioport + hp_ee_ctrl, temp);
		WR_HARPOON(ioport + hp_xfer_pad, temp2);
		if (!(temp3 & BIT(7)))
			pCardInfo->si_mflags |= LOW_BYTE_TERM;
		if (!(temp3 & BIT(6)))
			pCardInfo->si_mflags |= HIGH_BYTE_TERM;
	}

	ARAM_ACCESS(ioport);

	for (i = 0; i < 4; i++) {

		pCardInfo->si_XlatInfo[i] =
		    RD_HARPOON(ioport + hp_aramBase + BIOS_DATA_OFFSET + i);
	}

	/* return with -1 if no sort, else return with
	   logical card number sorted by BIOS (zero-based) */

	pCardInfo->si_relative_cardnum =
	    (unsigned
	     char)(RD_HARPOON(ioport + hp_aramBase + BIOS_RELATIVE_CARD) - 1);

	SGRAM_ACCESS(ioport);

	FPT_s_PhaseTbl[0] = FPT_phaseDataOut;
	FPT_s_PhaseTbl[1] = FPT_phaseDataIn;
	FPT_s_PhaseTbl[2] = FPT_phaseIllegal;
	FPT_s_PhaseTbl[3] = FPT_phaseIllegal;
	FPT_s_PhaseTbl[4] = FPT_phaseCommand;
	FPT_s_PhaseTbl[5] = FPT_phaseStatus;
	FPT_s_PhaseTbl[6] = FPT_phaseMsgOut;
	FPT_s_PhaseTbl[7] = FPT_phaseMsgIn;

	pCardInfo->si_present = 0x01;

	return 0;
}

/*---------------------------------------------------------------------
 *
 * Function: FlashPoint_HardwareResetHostAdapter
 *
 * Description: Setup adapter for normal operation (hard reset).
 *
 *---------------------------------------------------------------------*/

static void *FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
							 *pCardInfo)
{
	struct sccb_card *CurrCard = NULL;
	struct nvram_info *pCurrNvRam;
	unsigned char i, j, thisCard, ScamFlg;
	unsigned short temp, sync_bit_map, id;
	u32 ioport;

	ioport = pCardInfo->si_baseaddr;

	for (thisCard = 0; thisCard <= MAX_CARDS; thisCard++) {

		if (thisCard == MAX_CARDS)
			return (void *)FAILURE;

		if (FPT_BL_Card[thisCard].ioPort == ioport) {

			CurrCard = &FPT_BL_Card[thisCard];
			FPT_SccbMgrTableInitCard(CurrCard, thisCard);
			break;
		}

		else if (FPT_BL_Card[thisCard].ioPort == 0x00) {

			FPT_BL_Card[thisCard].ioPort = ioport;
			CurrCard = &FPT_BL_Card[thisCard];

			if (FPT_mbCards)
				for (i = 0; i < FPT_mbCards; i++) {
					if (CurrCard->ioPort ==
					    FPT_nvRamInfo[i].niBaseAddr)
						CurrCard->pNvRamInfo =
						    &FPT_nvRamInfo[i];
				}
			FPT_SccbMgrTableInitCard(CurrCard, thisCard);
			CurrCard->cardIndex = thisCard;
			CurrCard->cardInfo = pCardInfo;

			break;
		}
	}

	pCurrNvRam = CurrCard->pNvRamInfo;

	if (pCurrNvRam) {
		ScamFlg = pCurrNvRam->niScamConf;
	} else {
		ScamFlg =
		    (unsigned char)FPT_utilEERead(ioport, SCAM_CONFIG / 2);
	}

	FPT_BusMasterInit(ioport);
	FPT_XbowInit(ioport, ScamFlg);

	FPT_autoLoadDefaultMap(ioport);

	for (i = 0, id = 0x01; i != pCardInfo->si_id; i++, id <<= 1) {
	}

	WR_HARPOON(ioport + hp_selfid_0, id);
	WR_HARPOON(ioport + hp_selfid_1, 0x00);
	WR_HARPOON(ioport + hp_arb_id, pCardInfo->si_id);
	CurrCard->ourId = pCardInfo->si_id;

	i = (unsigned char)pCardInfo->si_mflags;
	if (i & SCSI_PARITY_ENA)
		WR_HARPOON(ioport + hp_portctrl_1, (HOST_MODE8 | CHK_SCSI_P));

	j = (RD_HARPOON(ioport + hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
	if (i & LOW_BYTE_TERM)
		j |= SCSI_TERM_ENA_L;
	WR_HARPOON(ioport + hp_bm_ctrl, j);

	j = (RD_HARPOON(ioport + hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
	if (i & HIGH_BYTE_TERM)
		j |= SCSI_TERM_ENA_H;
	WR_HARPOON(ioport + hp_ee_ctrl, j);

	if (!(pCardInfo->si_mflags & SOFT_RESET)) {

		FPT_sresb(ioport, thisCard);

		FPT_scini(thisCard, pCardInfo->si_id, 0);
	}

	if (pCardInfo->si_mflags & POST_ALL_UNDERRRUNS)
		CurrCard->globalFlags |= F_NO_FILTER;

	if (pCurrNvRam) {
		if (pCurrNvRam->niSysConf & 0x10)
			CurrCard->globalFlags |= F_GREEN_PC;
	} else {
		if (FPT_utilEERead(ioport, (SYSTEM_CONFIG / 2)) & GREEN_PC_ENA)
			CurrCard->globalFlags |= F_GREEN_PC;
	}

	/* Set global flag to indicate Re-Negotiation to be done on all
	   ckeck condition */
	if (pCurrNvRam) {
		if (pCurrNvRam->niScsiConf & 0x04)
			CurrCard->globalFlags |= F_DO_RENEGO;
	} else {
		if (FPT_utilEERead(ioport, (SCSI_CONFIG / 2)) & RENEGO_ENA)
			CurrCard->globalFlags |= F_DO_RENEGO;
	}

	if (pCurrNvRam) {
		if (pCurrNvRam->niScsiConf & 0x08)
			CurrCard->globalFlags |= F_CONLUN_IO;
	} else {
		if (FPT_utilEERead(ioport, (SCSI_CONFIG / 2)) & CONNIO_ENA)
			CurrCard->globalFlags |= F_CONLUN_IO;
	}

	temp = pCardInfo->si_per_targ_no_disc;

	for (i = 0, id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {

		if (temp & id)
			FPT_sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
	}

	sync_bit_map = 0x0001;

	for (id = 0; id < (MAX_SCSI_TAR / 2); id++) {

		if (pCurrNvRam) {
			temp = (unsigned short)pCurrNvRam->niSyncTbl[id];
			temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
			    (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
		} else
			temp =
			    FPT_utilEERead(ioport,
					   (unsigned short)((SYNC_RATE_TBL / 2)
							    + id));

		for (i = 0; i < 2; temp >>= 8, i++) {

			if (pCardInfo->si_per_targ_init_sync & sync_bit_map) {

				FPT_sccbMgrTbl[thisCard][id * 2 +
							 i].TarEEValue =
				    (unsigned char)temp;
			}

			else {
				FPT_sccbMgrTbl[thisCard][id * 2 +
							 i].TarStatus |=
				    SYNC_SUPPORTED;
				FPT_sccbMgrTbl[thisCard][id * 2 +
							 i].TarEEValue =
				    (unsigned char)(temp & ~EE_SYNC_MASK);
			}

/*         if ((pCardInfo->si_per_targ_wide_nego & sync_bit_map) ||
            (id*2+i >= 8)){
*/
			if (pCardInfo->si_per_targ_wide_nego & sync_bit_map) {

				FPT_sccbMgrTbl[thisCard][id * 2 +
							 i].TarEEValue |=
				    EE_WIDE_SCSI;

			}

			else {	/* NARROW SCSI */
				FPT_sccbMgrTbl[thisCard][id * 2 +
							 i].TarStatus |=
				    WIDE_NEGOCIATED;
			}

			sync_bit_map <<= 1;

		}
	}

	WR_HARPOON((ioport + hp_semaphore),
		   (unsigned char)(RD_HARPOON((ioport + hp_semaphore)) |
				   SCCB_MGR_PRESENT));

	return (void *)CurrCard;
}

static void FlashPoint_ReleaseHostAdapter(void *pCurrCard)
{
	unsigned char i;
	u32 portBase;
	u32 regOffset;
	u32 scamData;
	u32 *pScamTbl;
	struct nvram_info *pCurrNvRam;

	pCurrNvRam = ((struct sccb_card *)pCurrCard)->pNvRamInfo;

	if (pCurrNvRam) {
		FPT_WrStack(pCurrNvRam->niBaseAddr, 0, pCurrNvRam->niModel);
		FPT_WrStack(pCurrNvRam->niBaseAddr, 1, pCurrNvRam->niSysConf);
		FPT_WrStack(pCurrNvRam->niBaseAddr, 2, pCurrNvRam->niScsiConf);
		FPT_WrStack(pCurrNvRam->niBaseAddr, 3, pCurrNvRam->niScamConf);
		FPT_WrStack(pCurrNvRam->niBaseAddr, 4, pCurrNvRam->niAdapId);

		for (i = 0; i < MAX_SCSI_TAR / 2; i++)
			FPT_WrStack(pCurrNvRam->niBaseAddr,
				    (unsigned char)(i + 5),
				    pCurrNvRam->niSyncTbl[i]);

		portBase = pCurrNvRam->niBaseAddr;

		for (i = 0; i < MAX_SCSI_TAR; i++) {
			regOffset = hp_aramBase + 64 + i * 4;
			pScamTbl = (u32 *)&pCurrNvRam->niScamTbl[i];
			scamData = *pScamTbl;
			WR_HARP32(portBase, regOffset, scamData);
		}

	} else {
		FPT_WrStack(((struct sccb_card *)pCurrCard)->ioPort, 0, 0);
	}
}

static void FPT_RNVRamData(struct nvram_info *pNvRamInfo)
{
	unsigned char i;
	u32 portBase;
	u32 regOffset;
	u32 scamData;
	u32 *pScamTbl;

	pNvRamInfo->niModel = FPT_RdStack(pNvRamInfo->niBaseAddr, 0);
	pNvRamInfo->niSysConf = FPT_RdStack(pNvRamInfo->niBaseAddr, 1);
	pNvRamInfo->niScsiConf = FPT_RdStack(pNvRamInfo->niBaseAddr, 2);
	pNvRamInfo->niScamConf = FPT_RdStack(pNvRamInfo->niBaseAddr, 3);
	pNvRamInfo->niAdapId = FPT_RdStack(pNvRamInfo->niBaseAddr, 4);

	for (i = 0; i < MAX_SCSI_TAR / 2; i++)
		pNvRamInfo->niSyncTbl[i] =
		    FPT_RdStack(pNvRamInfo->niBaseAddr, (unsigned char)(i + 5));

	portBase = pNvRamInfo->niBaseAddr;

	for (i = 0; i < MAX_SCSI_TAR; i++) {
		regOffset = hp_aramBase + 64 + i * 4;
		RD_HARP32(portBase, regOffset, scamData);
		pScamTbl = (u32 *)&pNvRamInfo->niScamTbl[i];
		*pScamTbl = scamData;
	}

}

static unsigned char FPT_RdStack(u32 portBase, unsigned char index)
{
	WR_HARPOON(portBase + hp_stack_addr, index);
	return RD_HARPOON(portBase + hp_stack_data);
}

static void FPT_WrStack(u32 portBase, unsigned char index, unsigned char data)
{
	WR_HARPOON(portBase + hp_stack_addr, index);
	WR_HARPOON(portBase + hp_stack_data, data);
}

static unsigned char FPT_ChkIfChipInitialized(u32 ioPort)
{
	if ((RD_HARPOON(ioPort + hp_arb_id) & 0x0f) != FPT_RdStack(ioPort, 4))
		return 0;
	if ((RD_HARPOON(ioPort + hp_clkctrl_0) & CLKCTRL_DEFAULT)
	    != CLKCTRL_DEFAULT)
		return 0;
	if ((RD_HARPOON(ioPort + hp_seltimeout) == TO_250ms) ||
	    (RD_HARPOON(ioPort + hp_seltimeout) == TO_290ms))
		return 1;
	return 0;

}

/*---------------------------------------------------------------------
 *
 * Function: FlashPoint_StartCCB
 *
 * Description: Start a command pointed to by p_Sccb. When the
 *              command is completed it will be returned via the
 *              callback function.
 *
 *---------------------------------------------------------------------*/
static void FlashPoint_StartCCB(void *curr_card, struct sccb *p_Sccb)
{
	u32 ioport;
	unsigned char thisCard, lun;
	struct sccb *pSaveSccb;
	CALL_BK_FN callback;
	struct sccb_card *pCurrCard = curr_card;

	thisCard = pCurrCard->cardIndex;
	ioport = pCurrCard->ioPort;

	if ((p_Sccb->TargID >= MAX_SCSI_TAR) || (p_Sccb->Lun >= MAX_LUN)) {

		p_Sccb->HostStatus = SCCB_COMPLETE;
		p_Sccb->SccbStatus = SCCB_ERROR;
		callback = (CALL_BK_FN) p_Sccb->SccbCallback;
		if (callback)
			callback(p_Sccb);

		return;
	}

	FPT_sinits(p_Sccb, thisCard);

	if (!pCurrCard->cmdCounter) {
		WR_HARPOON(ioport + hp_semaphore,
			   (RD_HARPOON(ioport + hp_semaphore)
			    | SCCB_MGR_ACTIVE));

		if (pCurrCard->globalFlags & F_GREEN_PC) {
			WR_HARPOON(ioport + hp_clkctrl_0, CLKCTRL_DEFAULT);
			WR_HARPOON(ioport + hp_sys_ctrl, 0x00);
		}
	}

	pCurrCard->cmdCounter++;

	if (RD_HARPOON(ioport + hp_semaphore) & BIOS_IN_USE) {

		WR_HARPOON(ioport + hp_semaphore,
			   (RD_HARPOON(ioport + hp_semaphore)
			    | TICKLE_ME));
		if (p_Sccb->OperationCode == RESET_COMMAND) {
			pSaveSccb =
			    pCurrCard->currentSCCB;
			pCurrCard->currentSCCB = p_Sccb;
			FPT_queueSelectFail(&FPT_BL_Card[thisCard], thisCard);
			pCurrCard->currentSCCB =
			    pSaveSccb;
		} else {
			FPT_queueAddSccb(p_Sccb, thisCard);
		}
	}

	else if ((RD_HARPOON(ioport + hp_page_ctrl) & G_INT_DISABLE)) {

		if (p_Sccb->OperationCode == RESET_COMMAND) {
			pSaveSccb =
			    pCurrCard->currentSCCB;
			pCurrCard->currentSCCB = p_Sccb;
			FPT_queueSelectFail(&FPT_BL_Card[thisCard], thisCard);
			pCurrCard->currentSCCB =
			    pSaveSccb;
		} else {
			FPT_queueAddSccb(p_Sccb, thisCard);
		}
	}

	else {

		MDISABLE_INT(ioport);

		if ((pCurrCard->globalFlags & F_CONLUN_IO) &&
		    ((FPT_sccbMgrTbl[thisCard][p_Sccb->TargID].
		      TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
			lun = p_Sccb->Lun;
		else
			lun = 0;
		if ((pCurrCard->currentSCCB == NULL) &&
		    (FPT_sccbMgrTbl[thisCard][p_Sccb->TargID].TarSelQ_Cnt == 0)
		    && (FPT_sccbMgrTbl[thisCard][p_Sccb->TargID].TarLUNBusy[lun]
			== 0)) {

			pCurrCard->currentSCCB = p_Sccb;
			FPT_ssel(p_Sccb->SccbIOPort, thisCard);
		}

		else {

			if (p_Sccb->OperationCode == RESET_COMMAND) {
				pSaveSccb = pCurrCard->currentSCCB;
				pCurrCard->currentSCCB = p_Sccb;
				FPT_queueSelectFail(&FPT_BL_Card[thisCard],
						    thisCard);
				pCurrCard->currentSCCB = pSaveSccb;
			} else {
				FPT_queueAddSccb(p_Sccb, thisCard);
			}
		}

		MENABLE_INT(ioport);
	}

}

/*---------------------------------------------------------------------
 *
 * Function: FlashPoint_AbortCCB
 *
 * Description: Abort the command pointed to by p_Sccb.  When the
 *              command is completed it will be returned via the
 *              callback function.
 *
 *---------------------------------------------------------------------*/
static int FlashPoint_AbortCCB(void *pCurrCard, struct sccb *p_Sccb)
{
	u32 ioport;

	unsigned char thisCard;
	CALL_BK_FN callback;
	struct sccb *pSaveSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	ioport = ((struct sccb_card *)pCurrCard)->ioPort;

	thisCard = ((struct sccb_card *)pCurrCard)->cardIndex;

	if (!(RD_HARPOON(ioport + hp_page_ctrl) & G_INT_DISABLE)) {

		if (FPT_queueFindSccb(p_Sccb, thisCard)) {

			((struct sccb_card *)pCurrCard)->cmdCounter--;

			if (!((struct sccb_card *)pCurrCard)->cmdCounter)
				WR_HARPOON(ioport + hp_semaphore,
					   (RD_HARPOON(ioport + hp_semaphore)
					    & (unsigned
					       char)(~(SCCB_MGR_ACTIVE |
						       TICKLE_ME))));

			p_Sccb->SccbStatus = SCCB_ABORT;
			callback = p_Sccb->SccbCallback;
			callback(p_Sccb);

			return 0;
		}

		else {
			if (((struct sccb_card *)pCurrCard)->currentSCCB ==
			    p_Sccb) {
				p_Sccb->SccbStatus = SCCB_ABORT;
				return 0;

			}

			else {
				if (p_Sccb->Sccb_tag) {
					MDISABLE_INT(ioport);
					if (((struct sccb_card *)pCurrCard)->
					    discQ_Tbl[p_Sccb->Sccb_tag] ==
					    p_Sccb) {
						p_Sccb->SccbStatus = SCCB_ABORT;
						p_Sccb->Sccb_scsistat =
						    ABORT_ST;
						p_Sccb->Sccb_scsimsg =
						    ABORT_TASK;

						if (((struct sccb_card *)
						     pCurrCard)->currentSCCB ==
						    NULL) {
							((struct sccb_card *)
							 pCurrCard)->
					currentSCCB = p_Sccb;
							FPT_ssel(ioport,
								 thisCard);
						} else {
							pSaveSCCB =
							    ((struct sccb_card
							      *)pCurrCard)->
							    currentSCCB;
							((struct sccb_card *)
							 pCurrCard)->
					currentSCCB = p_Sccb;
							FPT_queueSelectFail((struct sccb_card *)pCurrCard, thisCard);
							((struct sccb_card *)
							 pCurrCard)->
					currentSCCB = pSaveSCCB;
						}
					}
					MENABLE_INT(ioport);
					return 0;
				} else {
					currTar_Info =
					    &FPT_sccbMgrTbl[thisCard][p_Sccb->
								      TargID];

					if (FPT_BL_Card[thisCard].
					    discQ_Tbl[currTar_Info->
						      LunDiscQ_Idx[p_Sccb->Lun]]
					    == p_Sccb) {
						p_Sccb->SccbStatus = SCCB_ABORT;
						return 0;
					}
				}
			}
		}
	}
	return -1;
}

/*---------------------------------------------------------------------
 *
 * Function: FlashPoint_InterruptPending
 *
 * Description: Do a quick check to determine if there is a pending
 *              interrupt for this card and disable the IRQ Pin if so.
 *
 *---------------------------------------------------------------------*/
static unsigned char FlashPoint_InterruptPending(void *pCurrCard)
{
	u32 ioport;

	ioport = ((struct sccb_card *)pCurrCard)->ioPort;

	if (RD_HARPOON(ioport + hp_int_status) & INT_ASSERTED) {
		return 1;
	}

	else

		return 0;
}

/*---------------------------------------------------------------------
 *
 * Function: FlashPoint_HandleInterrupt
 *
 * Description: This is our entry point when an interrupt is generated
 *              by the card and the upper level driver passes it on to
 *              us.
 *
 *---------------------------------------------------------------------*/
static int FlashPoint_HandleInterrupt(void *pcard)
{
	struct sccb *currSCCB;
	unsigned char thisCard, result, bm_status;
	unsigned short hp_int;
	unsigned char i, target;
	struct sccb_card *pCurrCard = pcard;
	u32 ioport;

	thisCard = pCurrCard->cardIndex;
	ioport = pCurrCard->ioPort;

	MDISABLE_INT(ioport);

	if (RD_HARPOON(ioport + hp_int_status) & EXT_STATUS_ON)
		bm_status = RD_HARPOON(ioport + hp_ext_status) &
					(unsigned char)BAD_EXT_STATUS;
	else
		bm_status = 0;

	WR_HARPOON(ioport + hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));

	while ((hp_int = RDW_HARPOON((ioport + hp_intstat)) &
				FPT_default_intena) | bm_status) {

		currSCCB = pCurrCard->currentSCCB;

		if (hp_int & (FIFO | TIMEOUT | RESET | SCAM_SEL) || bm_status) {
			result =
			    FPT_SccbMgr_bad_isr(ioport, thisCard, pCurrCard,
						hp_int);
			WRW_HARPOON((ioport + hp_intstat),
				    (FIFO | TIMEOUT | RESET | SCAM_SEL));
			bm_status = 0;

			if (result) {

				MENABLE_INT(ioport);
				return result;
			}
		}

		else if (hp_int & ICMD_COMP) {

			if (!(hp_int & BUS_FREE)) {
				/* Wait for the BusFree before starting a new command.  We
				   must also check for being reselected since the BusFree
				   may not show up if another device reselects us in 1.5us or
				   less.  SRR Wednesday, 3/8/1995.
				 */
				while (!
				       (RDW_HARPOON((ioport + hp_intstat)) &
					(BUS_FREE | RSEL))) ;
			}

			if (pCurrCard->globalFlags & F_HOST_XFER_ACT)

				FPT_phaseChkFifo(ioport, thisCard);

/*         WRW_HARPOON((ioport+hp_intstat),
            (BUS_FREE | ICMD_COMP | ITAR_DISC | XFER_CNT_0));
         */

			WRW_HARPOON((ioport + hp_intstat), CLR_ALL_INT_1);

			FPT_autoCmdCmplt(ioport, thisCard);

		}

		else if (hp_int & ITAR_DISC) {

			if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
				FPT_phaseChkFifo(ioport, thisCard);

			if (RD_HARPOON(ioport + hp_gp_reg_1) ==
					SAVE_POINTERS) {

				WR_HARPOON(ioport + hp_gp_reg_1, 0x00);
				currSCCB->Sccb_XferState |= F_NO_DATA_YET;

				currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
			}

			currSCCB->Sccb_scsistat = DISCONNECT_ST;
			FPT_queueDisconnect(currSCCB, thisCard);

			/* Wait for the BusFree before starting a new command.  We
			   must also check for being reselected since the BusFree
			   may not show up if another device reselects us in 1.5us or
			   less.  SRR Wednesday, 3/8/1995.
			 */
			while (!
			       (RDW_HARPOON((ioport + hp_intstat)) &
				(BUS_FREE | RSEL))
			       && !((RDW_HARPOON((ioport + hp_intstat)) & PHASE)
				    && RD_HARPOON((ioport + hp_scsisig)) ==
				    (SCSI_BSY | SCSI_REQ | SCSI_CD | SCSI_MSG |
				     SCSI_IOBIT))) ;

			/*
			   The additional loop exit condition above detects a timing problem
			   with the revision D/E harpoon chips.  The caller should reset the
			   host adapter to recover when 0xFE is returned.
			 */
			if (!
			    (RDW_HARPOON((ioport + hp_intstat)) &
			     (BUS_FREE | RSEL))) {
				MENABLE_INT(ioport);
				return 0xFE;
			}

			WRW_HARPOON((ioport + hp_intstat),
				    (BUS_FREE | ITAR_DISC));

			pCurrCard->globalFlags |= F_NEW_SCCB_CMD;

		}

		else if (hp_int & RSEL) {

			WRW_HARPOON((ioport + hp_intstat),
				    (PROG_HLT | RSEL | PHASE | BUS_FREE));

			if (RDW_HARPOON((ioport + hp_intstat)) & ITAR_DISC) {
				if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
					FPT_phaseChkFifo(ioport, thisCard);

				if (RD_HARPOON(ioport + hp_gp_reg_1) ==
				    SAVE_POINTERS) {
					WR_HARPOON(ioport + hp_gp_reg_1, 0x00);
					currSCCB->Sccb_XferState |=
					    F_NO_DATA_YET;
					currSCCB->Sccb_savedATC =
					    currSCCB->Sccb_ATC;
				}

				WRW_HARPOON((ioport + hp_intstat),
					    (BUS_FREE | ITAR_DISC));
				currSCCB->Sccb_scsistat = DISCONNECT_ST;
				FPT_queueDisconnect(currSCCB, thisCard);
			}

			FPT_sres(ioport, thisCard, pCurrCard);
			FPT_phaseDecode(ioport, thisCard);

		}

		else if ((hp_int & IDO_STRT) && (!(hp_int & BUS_FREE))) {

			WRW_HARPOON((ioport + hp_intstat),
				    (IDO_STRT | XFER_CNT_0));
			FPT_phaseDecode(ioport, thisCard);

		}

		else if ((hp_int & IUNKWN) || (hp_int & PROG_HLT)) {
			WRW_HARPOON((ioport + hp_intstat),
				    (PHASE | IUNKWN | PROG_HLT));
			if ((RD_HARPOON(ioport + hp_prgmcnt_0) & (unsigned char)
			     0x3f) < (unsigned char)SELCHK) {
				FPT_phaseDecode(ioport, thisCard);
			} else {
				/* Harpoon problem some SCSI target device respond to selection
				   with short BUSY pulse (<400ns) this will make the Harpoon is not able
				   to latch the correct Target ID into reg. x53.
				   The work around require to correct this reg. But when write to this
				   reg. (0x53) also increment the FIFO write addr reg (0x6f), thus we
				   need to read this reg first then restore it later. After update to 0x53 */

				i = (unsigned
				     char)(RD_HARPOON(ioport + hp_fifowrite));
				target =
				    (unsigned
				     char)(RD_HARPOON(ioport + hp_gp_reg_3));
				WR_HARPOON(ioport + hp_xfer_pad,
					   (unsigned char)ID_UNLOCK);
				WR_HARPOON(ioport + hp_select_id,
					   (unsigned char)(target | target <<
							   4));
				WR_HARPOON(ioport + hp_xfer_pad,
					   (unsigned char)0x00);
				WR_HARPOON(ioport + hp_fifowrite, i);
				WR_HARPOON(ioport + hp_autostart_3,
					   (AUTO_IMMED + TAG_STRT));
			}
		}

		else if (hp_int & XFER_CNT_0) {

			WRW_HARPOON((ioport + hp_intstat), XFER_CNT_0);

			FPT_schkdd(ioport, thisCard);

		}

		else if (hp_int & BUS_FREE) {

			WRW_HARPOON((ioport + hp_intstat), BUS_FREE);

			if (pCurrCard->globalFlags & F_HOST_XFER_ACT) {

				FPT_hostDataXferAbort(ioport, thisCard,
						      currSCCB);
			}

			FPT_phaseBusFree(ioport, thisCard);
		}

		else if (hp_int & ITICKLE) {

			WRW_HARPOON((ioport + hp_intstat), ITICKLE);
			pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
		}

		if (((struct sccb_card *)pCurrCard)->
		    globalFlags & F_NEW_SCCB_CMD) {

			pCurrCard->globalFlags &= ~F_NEW_SCCB_CMD;

			if (pCurrCard->currentSCCB == NULL)
				FPT_queueSearchSelect(pCurrCard, thisCard);

			if (pCurrCard->currentSCCB != NULL) {
				pCurrCard->globalFlags &= ~F_NEW_SCCB_CMD;
				FPT_ssel(ioport, thisCard);
			}

			break;

		}

	}			/*end while */

	MENABLE_INT(ioport);

	return 0;
}

/*---------------------------------------------------------------------
 *
 * Function: Sccb_bad_isr
 *
 * Description: Some type of interrupt has occurred which is slightly
 *              out of the ordinary.  We will now decode it fully, in
 *              this routine.  This is broken up in an attempt to save
 *              processing time.
 *
 *---------------------------------------------------------------------*/
static unsigned char FPT_SccbMgr_bad_isr(u32 p_port, unsigned char p_card,
					 struct sccb_card *pCurrCard,
					 unsigned short p_int)
{
	unsigned char temp, ScamFlg;
	struct sccb_mgr_tar_info *currTar_Info;
	struct nvram_info *pCurrNvRam;

	if (RD_HARPOON(p_port + hp_ext_status) &
	    (BM_FORCE_OFF | PCI_DEV_TMOUT | BM_PARITY_ERR | PIO_OVERRUN)) {

		if (pCurrCard->globalFlags & F_HOST_XFER_ACT) {

			FPT_hostDataXferAbort(p_port, p_card,
					      pCurrCard->currentSCCB);
		}

		if (RD_HARPOON(p_port + hp_pci_stat_cfg) & REC_MASTER_ABORT)
		{
			WR_HARPOON(p_port + hp_pci_stat_cfg,
				   (RD_HARPOON(p_port + hp_pci_stat_cfg) &
				    ~REC_MASTER_ABORT));

			WR_HARPOON(p_port + hp_host_blk_cnt, 0x00);

		}

		if (pCurrCard->currentSCCB != NULL) {

			if (!pCurrCard->currentSCCB->HostStatus)
				pCurrCard->currentSCCB->HostStatus =
				    SCCB_BM_ERR;

			FPT_sxfrp(p_port, p_card);

			temp = (unsigned char)(RD_HARPOON(p_port + hp_ee_ctrl) &
					       (EXT_ARB_ACK | SCSI_TERM_ENA_H));
			WR_HARPOON(p_port + hp_ee_ctrl,
				   ((unsigned char)temp | SEE_MS | SEE_CS));
			WR_HARPOON(p_port + hp_ee_ctrl, temp);

			if (!
			    (RDW_HARPOON((p_port + hp_intstat)) &
			     (BUS_FREE | RESET))) {
				FPT_phaseDecode(p_port, p_card);
			}
		}
	}

	else if (p_int & RESET) {

		WR_HARPOON(p_port + hp_clkctrl_0, CLKCTRL_DEFAULT);
		WR_HARPOON(p_port + hp_sys_ctrl, 0x00);
		if (pCurrCard->currentSCCB != NULL) {

			if (pCurrCard->globalFlags & F_HOST_XFER_ACT)

				FPT_hostDataXferAbort(p_port, p_card,
						      pCurrCard->currentSCCB);
		}

		DISABLE_AUTO(p_port);

		FPT_sresb(p_port, p_card);

		while (RD_HARPOON(p_port + hp_scsictrl_0) & SCSI_RST) {
		}

		pCurrNvRam = pCurrCard->pNvRamInfo;
		if (pCurrNvRam) {
			ScamFlg = pCurrNvRam->niScamConf;
		} else {
			ScamFlg =
			    (unsigned char)FPT_utilEERead(p_port,
							  SCAM_CONFIG / 2);
		}

		FPT_XbowInit(p_port, ScamFlg);

		FPT_scini(p_card, pCurrCard->ourId, 0);

		return 0xFF;
	}

	else if (p_int & FIFO) {

		WRW_HARPOON((p_port + hp_intstat), FIFO);

		if (pCurrCard->currentSCCB != NULL)
			FPT_sxfrp(p_port, p_card);
	}

	else if (p_int & TIMEOUT) {

		DISABLE_AUTO(p_port);

		WRW_HARPOON((p_port + hp_intstat),
			    (PROG_HLT | TIMEOUT | SEL | BUS_FREE | PHASE |
			     IUNKWN));

		pCurrCard->currentSCCB->HostStatus = SCCB_SELECTION_TIMEOUT;

		currTar_Info =
		    &FPT_sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
		if ((pCurrCard->globalFlags & F_CONLUN_IO)
		    && ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) !=
			TAG_Q_TRYING))
			currTar_Info->TarLUNBusy[pCurrCard->currentSCCB->Lun] =
			    0;
		else
			currTar_Info->TarLUNBusy[0] = 0;

		if (currTar_Info->TarEEValue & EE_SYNC_MASK) {
			currTar_Info->TarSyncCtrl = 0;
			currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
		}

		if (currTar_Info->TarEEValue & EE_WIDE_SCSI) {
			currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
		}

		FPT_sssyncv(p_port, pCurrCard->currentSCCB->TargID, NARROW_SCSI,
			    currTar_Info);

		FPT_queueCmdComplete(pCurrCard, pCurrCard->currentSCCB, p_card);

	}

	else if (p_int & SCAM_SEL) {

		FPT_scarb(p_port, LEVEL2_TAR);
		FPT_scsel(p_port);
		FPT_scasid(p_card, p_port);

		FPT_scbusf(p_port);

		WRW_HARPOON((p_port + hp_intstat), SCAM_SEL);
	}

	return 0x00;
}

/*---------------------------------------------------------------------
 *
 * Function: SccbMgrTableInit
 *
 * Description: Initialize all Sccb manager data structures.
 *
 *---------------------------------------------------------------------*/

static void FPT_SccbMgrTableInitAll(void)
{
	unsigned char thisCard;

	for (thisCard = 0; thisCard < MAX_CARDS; thisCard++) {
		FPT_SccbMgrTableInitCard(&FPT_BL_Card[thisCard], thisCard);

		FPT_BL_Card[thisCard].ioPort = 0x00;
		FPT_BL_Card[thisCard].cardInfo = NULL;
		FPT_BL_Card[thisCard].cardIndex = 0xFF;
		FPT_BL_Card[thisCard].ourId = 0x00;
		FPT_BL_Card[thisCard].pNvRamInfo = NULL;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: SccbMgrTableInit
 *
 * Description: Initialize all Sccb manager data structures.
 *
 *---------------------------------------------------------------------*/

static void FPT_SccbMgrTableInitCard(struct sccb_card *pCurrCard,
				     unsigned char p_card)
{
	unsigned char scsiID, qtag;

	for (qtag = 0; qtag < QUEUE_DEPTH; qtag++) {
		FPT_BL_Card[p_card].discQ_Tbl[qtag] = NULL;
	}

	for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++) {
		FPT_sccbMgrTbl[p_card][scsiID].TarStatus = 0;
		FPT_sccbMgrTbl[p_card][scsiID].TarEEValue = 0;
		FPT_SccbMgrTableInitTarget(p_card, scsiID);
	}

	pCurrCard->scanIndex = 0x00;
	pCurrCard->currentSCCB = NULL;
	pCurrCard->globalFlags = 0x00;
	pCurrCard->cmdCounter = 0x00;
	pCurrCard->tagQ_Lst = 0x01;
	pCurrCard->discQCount = 0;

}

/*---------------------------------------------------------------------
 *
 * Function: SccbMgrTableInit
 *
 * Description: Initialize all Sccb manager data structures.
 *
 *---------------------------------------------------------------------*/

static void FPT_SccbMgrTableInitTarget(unsigned char p_card,
				       unsigned char target)
{

	unsigned char lun, qtag;
	struct sccb_mgr_tar_info *currTar_Info;

	currTar_Info = &FPT_sccbMgrTbl[p_card][target];

	currTar_Info->TarSelQ_Cnt = 0;
	currTar_Info->TarSyncCtrl = 0;

	currTar_Info->TarSelQ_Head = NULL;
	currTar_Info->TarSelQ_Tail = NULL;
	currTar_Info->TarTagQ_Cnt = 0;
	currTar_Info->TarLUN_CA = 0;

	for (lun = 0; lun < MAX_LUN; lun++) {
		currTar_Info->TarLUNBusy[lun] = 0;
		currTar_Info->LunDiscQ_Idx[lun] = 0;
	}

	for (qtag = 0; qtag < QUEUE_DEPTH; qtag++) {
		if (FPT_BL_Card[p_card].discQ_Tbl[qtag] != NULL) {
			if (FPT_BL_Card[p_card].discQ_Tbl[qtag]->TargID ==
			    target) {
				FPT_BL_Card[p_card].discQ_Tbl[qtag] = NULL;
				FPT_BL_Card[p_card].discQCount--;
			}
		}
	}
}

/*---------------------------------------------------------------------
 *
 * Function: sfetm
 *
 * Description: Read in a message byte from the SCSI bus, and check
 *              for a parity error.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_sfm(u32 port, struct sccb *pCurrSCCB)
{
	unsigned char message;
	unsigned short TimeOutLoop;

	TimeOutLoop = 0;
	while ((!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) &&
	       (TimeOutLoop++ < 20000)) {
	}

	WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);

	message = RD_HARPOON(port + hp_scsidata_0);

	WR_HARPOON(port + hp_scsisig, SCSI_ACK + S_MSGI_PH);

	if (TimeOutLoop > 20000)
		message = 0x00;	/* force message byte = 0 if Time Out on Req */

	if ((RDW_HARPOON((port + hp_intstat)) & PARITY) &&
	    (RD_HARPOON(port + hp_addstat) & SCSI_PAR_ERR)) {
		WR_HARPOON(port + hp_scsisig, (SCSI_ACK + S_ILL_PH));
		WR_HARPOON(port + hp_xferstat, 0);
		WR_HARPOON(port + hp_fiforead, 0);
		WR_HARPOON(port + hp_fifowrite, 0);
		if (pCurrSCCB != NULL) {
			pCurrSCCB->Sccb_scsimsg = MSG_PARITY_ERROR;
		}
		message = 0x00;
		do {
			ACCEPT_MSG_ATN(port);
			TimeOutLoop = 0;
			while ((!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) &&
			       (TimeOutLoop++ < 20000)) {
			}
			if (TimeOutLoop > 20000) {
				WRW_HARPOON((port + hp_intstat), PARITY);
				return message;
			}
			if ((RD_HARPOON(port + hp_scsisig) & S_SCSI_PHZ) !=
			    S_MSGI_PH) {
				WRW_HARPOON((port + hp_intstat), PARITY);
				return message;
			}
			WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);

			RD_HARPOON(port + hp_scsidata_0);

			WR_HARPOON(port + hp_scsisig, (SCSI_ACK + S_ILL_PH));

		} while (1);

	}
	WR_HARPOON(port + hp_scsisig, (SCSI_ACK + S_ILL_PH));
	WR_HARPOON(port + hp_xferstat, 0);
	WR_HARPOON(port + hp_fiforead, 0);
	WR_HARPOON(port + hp_fifowrite, 0);
	return message;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_ssel
 *
 * Description: Load up automation and select target device.
 *
 *---------------------------------------------------------------------*/

static void FPT_ssel(u32 port, unsigned char p_card)
{

	unsigned char auto_loaded, i, target, *theCCB;

	u32 cdb_reg;
	struct sccb_card *CurrCard;
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;
	unsigned char lastTag, lun;

	CurrCard = &FPT_BL_Card[p_card];
	currSCCB = CurrCard->currentSCCB;
	target = currSCCB->TargID;
	currTar_Info = &FPT_sccbMgrTbl[p_card][target];
	lastTag = CurrCard->tagQ_Lst;

	ARAM_ACCESS(port);

	if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
		currSCCB->ControlByte &= ~F_USE_CMD_Q;

	if (((CurrCard->globalFlags & F_CONLUN_IO) &&
	     ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))

		lun = currSCCB->Lun;
	else
		lun = 0;

	if (CurrCard->globalFlags & F_TAG_STARTED) {
		if (!(currSCCB->ControlByte & F_USE_CMD_Q)) {
			if ((currTar_Info->TarLUN_CA == 0)
			    && ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
				== TAG_Q_TRYING)) {

				if (currTar_Info->TarTagQ_Cnt != 0) {
					currTar_Info->TarLUNBusy[lun] = 1;
					FPT_queueSelectFail(CurrCard, p_card);
					SGRAM_ACCESS(port);
					return;
				}

				else {
					currTar_Info->TarLUNBusy[lun] = 1;
				}

			}
			/*End non-tagged */
			else {
				currTar_Info->TarLUNBusy[lun] = 1;
			}

		}
		/*!Use cmd Q Tagged */
		else {
			if (currTar_Info->TarLUN_CA == 1) {
				FPT_queueSelectFail(CurrCard, p_card);
				SGRAM_ACCESS(port);
				return;
			}

			currTar_Info->TarLUNBusy[lun] = 1;

		}		/*else use cmd Q tagged */

	}
	/*if glob tagged started */
	else {
		currTar_Info->TarLUNBusy[lun] = 1;
	}

	if ((((CurrCard->globalFlags & F_CONLUN_IO) &&
	      ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
	     || (!(currSCCB->ControlByte & F_USE_CMD_Q)))) {
		if (CurrCard->discQCount >= QUEUE_DEPTH) {
			currTar_Info->TarLUNBusy[lun] = 1;
			FPT_queueSelectFail(CurrCard, p_card);
			SGRAM_ACCESS(port);
			return;
		}
		for (i = 1; i < QUEUE_DEPTH; i++) {
			if (++lastTag >= QUEUE_DEPTH)
				lastTag = 1;
			if (CurrCard->discQ_Tbl[lastTag] == NULL) {
				CurrCard->tagQ_Lst = lastTag;
				currTar_Info->LunDiscQ_Idx[lun] = lastTag;
				CurrCard->discQ_Tbl[lastTag] = currSCCB;
				CurrCard->discQCount++;
				break;
			}
		}
		if (i == QUEUE_DEPTH) {
			currTar_Info->TarLUNBusy[lun] = 1;
			FPT_queueSelectFail(CurrCard, p_card);
			SGRAM_ACCESS(port);
			return;
		}
	}

	auto_loaded = 0;

	WR_HARPOON(port + hp_select_id, target);
	WR_HARPOON(port + hp_gp_reg_3, target);	/* Use by new automation logic */

	if (currSCCB->OperationCode == RESET_COMMAND) {
		WRW_HARPOON((port + ID_MSG_STRT), (MPM_OP + AMSG_OUT +
						   (currSCCB->
						    Sccb_idmsg & ~DISC_PRIV)));

		WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + NP);

		currSCCB->Sccb_scsimsg = TARGET_RESET;

		WR_HARPOON(port + hp_autostart_3, (SELECT + SELCHK_STRT));
		auto_loaded = 1;
		currSCCB->Sccb_scsistat = SELECT_BDR_ST;

		if (currTar_Info->TarEEValue & EE_SYNC_MASK) {
			currTar_Info->TarSyncCtrl = 0;
			currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
		}

		if (currTar_Info->TarEEValue & EE_WIDE_SCSI) {
			currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
		}

		FPT_sssyncv(port, target, NARROW_SCSI, currTar_Info);
		FPT_SccbMgrTableInitTarget(p_card, target);

	}

	else if (currSCCB->Sccb_scsistat == ABORT_ST) {
		WRW_HARPOON((port + ID_MSG_STRT), (MPM_OP + AMSG_OUT +
						   (currSCCB->
						    Sccb_idmsg & ~DISC_PRIV)));

		WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + CMDPZ);

		WRW_HARPOON((port + SYNC_MSGS + 0), (MPM_OP + AMSG_OUT +
						     (((unsigned
							char)(currSCCB->
							      ControlByte &
							      TAG_TYPE_MASK)
						       >> 6) | (unsigned char)
						      0x20)));
		WRW_HARPOON((port + SYNC_MSGS + 2),
			    (MPM_OP + AMSG_OUT + currSCCB->Sccb_tag));
		WRW_HARPOON((port + SYNC_MSGS + 4), (BRH_OP + ALWAYS + NP));

		WR_HARPOON(port + hp_autostart_3, (SELECT + SELCHK_STRT));
		auto_loaded = 1;

	}

	else if (!(currTar_Info->TarStatus & WIDE_NEGOCIATED)) {
		auto_loaded = FPT_siwidn(port, p_card);
		currSCCB->Sccb_scsistat = SELECT_WN_ST;
	}

	else if (!((currTar_Info->TarStatus & TAR_SYNC_MASK)
		   == SYNC_SUPPORTED)) {
		auto_loaded = FPT_sisyncn(port, p_card, 0);
		currSCCB->Sccb_scsistat = SELECT_SN_ST;
	}

	if (!auto_loaded) {

		if (currSCCB->ControlByte & F_USE_CMD_Q) {

			CurrCard->globalFlags |= F_TAG_STARTED;

			if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
			    == TAG_Q_REJECT) {
				currSCCB->ControlByte &= ~F_USE_CMD_Q;

				/* Fix up the start instruction with a jump to
				   Non-Tag-CMD handling */
				WRW_HARPOON((port + ID_MSG_STRT),
					    BRH_OP + ALWAYS + NTCMD);

				WRW_HARPOON((port + NON_TAG_ID_MSG),
					    (MPM_OP + AMSG_OUT +
					     currSCCB->Sccb_idmsg));

				WR_HARPOON(port + hp_autostart_3,
					   (SELECT + SELCHK_STRT));

				/* Setup our STATE so we know what happened when
				   the wheels fall off. */
				currSCCB->Sccb_scsistat = SELECT_ST;

				currTar_Info->TarLUNBusy[lun] = 1;
			}

			else {
				WRW_HARPOON((port + ID_MSG_STRT),
					    (MPM_OP + AMSG_OUT +
					     currSCCB->Sccb_idmsg));

				WRW_HARPOON((port + ID_MSG_STRT + 2),
					    (MPM_OP + AMSG_OUT +
					     (((unsigned char)(currSCCB->
							       ControlByte &
							       TAG_TYPE_MASK)
					       >> 6) | (unsigned char)0x20)));

				for (i = 1; i < QUEUE_DEPTH; i++) {
					if (++lastTag >= QUEUE_DEPTH)
						lastTag = 1;
					if (CurrCard->discQ_Tbl[lastTag] ==
					    NULL) {
						WRW_HARPOON((port +
							     ID_MSG_STRT + 6),
							    (MPM_OP + AMSG_OUT +
							     lastTag));
						CurrCard->tagQ_Lst = lastTag;
						currSCCB->Sccb_tag = lastTag;
						CurrCard->discQ_Tbl[lastTag] =
						    currSCCB;
						CurrCard->discQCount++;
						break;
					}
				}

				if (i == QUEUE_DEPTH) {
					currTar_Info->TarLUNBusy[lun] = 1;
					FPT_queueSelectFail(CurrCard, p_card);
					SGRAM_ACCESS(port);
					return;
				}

				currSCCB->Sccb_scsistat = SELECT_Q_ST;

				WR_HARPOON(port + hp_autostart_3,
					   (SELECT + SELCHK_STRT));
			}
		}

		else {

			WRW_HARPOON((port + ID_MSG_STRT),
				    BRH_OP + ALWAYS + NTCMD);

			WRW_HARPOON((port + NON_TAG_ID_MSG),
				    (MPM_OP + AMSG_OUT + currSCCB->Sccb_idmsg));

			currSCCB->Sccb_scsistat = SELECT_ST;

			WR_HARPOON(port + hp_autostart_3,
				   (SELECT + SELCHK_STRT));
		}

		theCCB = (unsigned char *)&currSCCB->Cdb[0];

		cdb_reg = port + CMD_STRT;

		for (i = 0; i < currSCCB->CdbLength; i++) {
			WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + *theCCB));
			cdb_reg += 2;
			theCCB++;
		}

		if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
			WRW_HARPOON(cdb_reg, (BRH_OP + ALWAYS + NP));

	}
	/* auto_loaded */
	WRW_HARPOON((port + hp_fiforead), (unsigned short)0x00);
	WR_HARPOON(port + hp_xferstat, 0x00);

	WRW_HARPOON((port + hp_intstat), (PROG_HLT | TIMEOUT | SEL | BUS_FREE));

	WR_HARPOON(port + hp_portctrl_0, (SCSI_PORT));

	if (!(currSCCB->Sccb_MGRFlags & F_DEV_SELECTED)) {
		WR_HARPOON(port + hp_scsictrl_0,
			   (SEL_TAR | ENA_ATN | ENA_RESEL | ENA_SCAM_SEL));
	} else {

/*      auto_loaded =  (RD_HARPOON(port+hp_autostart_3) & (unsigned char)0x1F);
      auto_loaded |= AUTO_IMMED; */
		auto_loaded = AUTO_IMMED;

		DISABLE_AUTO(port);

		WR_HARPOON(port + hp_autostart_3, auto_loaded);
	}

	SGRAM_ACCESS(port);
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sres
 *
 * Description: Hookup the correct CCB and handle the incoming messages.
 *
 *---------------------------------------------------------------------*/

static void FPT_sres(u32 port, unsigned char p_card,
		     struct sccb_card *pCurrCard)
{

	unsigned char our_target, message, lun = 0, tag, msgRetryCount;

	struct sccb_mgr_tar_info *currTar_Info;
	struct sccb *currSCCB;

	if (pCurrCard->currentSCCB != NULL) {
		currTar_Info =
		    &FPT_sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
		DISABLE_AUTO(port);

		WR_HARPOON((port + hp_scsictrl_0), (ENA_RESEL | ENA_SCAM_SEL));

		currSCCB = pCurrCard->currentSCCB;
		if (currSCCB->Sccb_scsistat == SELECT_WN_ST) {
			currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
			currSCCB->Sccb_scsistat = BUS_FREE_ST;
		}
		if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
			currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
			currSCCB->Sccb_scsistat = BUS_FREE_ST;
		}
		if (((pCurrCard->globalFlags & F_CONLUN_IO) &&
		     ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) !=
		      TAG_Q_TRYING))) {
			currTar_Info->TarLUNBusy[currSCCB->Lun] = 0;
			if (currSCCB->Sccb_scsistat != ABORT_ST) {
				pCurrCard->discQCount--;
				pCurrCard->discQ_Tbl[currTar_Info->
						     LunDiscQ_Idx[currSCCB->
								  Lun]]
				    = NULL;
			}
		} else {
			currTar_Info->TarLUNBusy[0] = 0;
			if (currSCCB->Sccb_tag) {
				if (currSCCB->Sccb_scsistat != ABORT_ST) {
					pCurrCard->discQCount--;
					pCurrCard->discQ_Tbl[currSCCB->
							     Sccb_tag] = NULL;
				}
			} else {
				if (currSCCB->Sccb_scsistat != ABORT_ST) {
					pCurrCard->discQCount--;
					pCurrCard->discQ_Tbl[currTar_Info->
							     LunDiscQ_Idx[0]] =
					    NULL;
				}
			}
		}

		FPT_queueSelectFail(&FPT_BL_Card[p_card], p_card);
	}

	WRW_HARPOON((port + hp_fiforead), (unsigned short)0x00);

	our_target = (unsigned char)(RD_HARPOON(port + hp_select_id) >> 4);
	currTar_Info = &FPT_sccbMgrTbl[p_card][our_target];

	msgRetryCount = 0;
	do {

		currTar_Info = &FPT_sccbMgrTbl[p_card][our_target];
		tag = 0;

		while (!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) {
			if (!(RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) {

				WRW_HARPOON((port + hp_intstat), PHASE);
				return;
			}
		}

		WRW_HARPOON((port + hp_intstat), PHASE);
		if ((RD_HARPOON(port + hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH) {

			message = FPT_sfm(port, pCurrCard->currentSCCB);
			if (message) {

				if (message <= (0x80 | LUN_MASK)) {
					lun = message & (unsigned char)LUN_MASK;

					if ((currTar_Info->
					     TarStatus & TAR_TAG_Q_MASK) ==
					    TAG_Q_TRYING) {
						if (currTar_Info->TarTagQ_Cnt !=
						    0) {

							if (!
							    (currTar_Info->
							     TarLUN_CA)) {
								ACCEPT_MSG(port);	/*Release the ACK for ID msg. */

								message =
								    FPT_sfm
								    (port,
								     pCurrCard->
								     currentSCCB);
								if (message) {
									ACCEPT_MSG
									    (port);
								}

								else
									message
									    = 0;

								if (message !=
								    0) {
									tag =
									    FPT_sfm
									    (port,
									     pCurrCard->
									     currentSCCB);

									if (!
									    (tag))
										message
										    =
										    0;
								}

							}
							/*C.A. exists! */
						}
						/*End Q cnt != 0 */
					}
					/*End Tag cmds supported! */
				}
				/*End valid ID message.  */
				else {

					ACCEPT_MSG_ATN(port);
				}

			}
			/* End good id message. */
			else {

				message = 0;
			}
		} else {
			ACCEPT_MSG_ATN(port);

			while (!
			       (RDW_HARPOON((port + hp_intstat)) &
				(PHASE | RESET))
			       && !(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)
			       && (RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) ;

			return;
		}

		if (message == 0) {
			msgRetryCount++;
			if (msgRetryCount == 1) {
				FPT_SendMsg(port, MSG_PARITY_ERROR);
			} else {
				FPT_SendMsg(port, TARGET_RESET);

				FPT_sssyncv(port, our_target, NARROW_SCSI,
					    currTar_Info);

				if (FPT_sccbMgrTbl[p_card][our_target].
				    TarEEValue & EE_SYNC_MASK) {

					FPT_sccbMgrTbl[p_card][our_target].
					    TarStatus &= ~TAR_SYNC_MASK;

				}

				if (FPT_sccbMgrTbl[p_card][our_target].
				    TarEEValue & EE_WIDE_SCSI) {

					FPT_sccbMgrTbl[p_card][our_target].
					    TarStatus &= ~TAR_WIDE_MASK;
				}

				FPT_queueFlushTargSccb(p_card, our_target,
						       SCCB_COMPLETE);
				FPT_SccbMgrTableInitTarget(p_card, our_target);
				return;
			}
		}
	} while (message == 0);

	if (((pCurrCard->globalFlags & F_CONLUN_IO) &&
	     ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))) {
		currTar_Info->TarLUNBusy[lun] = 1;
		pCurrCard->currentSCCB =
		    pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[lun]];
		if (pCurrCard->currentSCCB != NULL) {
			ACCEPT_MSG(port);
		} else {
			ACCEPT_MSG_ATN(port);
		}
	} else {
		currTar_Info->TarLUNBusy[0] = 1;

		if (tag) {
			if (pCurrCard->discQ_Tbl[tag] != NULL) {
				pCurrCard->currentSCCB =
				    pCurrCard->discQ_Tbl[tag];
				currTar_Info->TarTagQ_Cnt--;
				ACCEPT_MSG(port);
			} else {
				ACCEPT_MSG_ATN(port);
			}
		} else {
			pCurrCard->currentSCCB =
			    pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]];
			if (pCurrCard->currentSCCB != NULL) {
				ACCEPT_MSG(port);
			} else {
				ACCEPT_MSG_ATN(port);
			}
		}
	}

	if (pCurrCard->currentSCCB != NULL) {
		if (pCurrCard->currentSCCB->Sccb_scsistat == ABORT_ST) {
			/* During Abort Tag command, the target could have got re-selected
			   and completed the command. Check the select Q and remove the CCB
			   if it is in the Select Q */
			FPT_queueFindSccb(pCurrCard->currentSCCB, p_card);
		}
	}

	while (!(RDW_HARPOON((port + hp_intstat)) & (PHASE | RESET)) &&
	       !(RD_HARPOON(port + hp_scsisig) & SCSI_REQ) &&
	       (RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) ;
}

static void FPT_SendMsg(u32 port, unsigned char message)
{
	while (!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) {
		if (!(RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) {

			WRW_HARPOON((port + hp_intstat), PHASE);
			return;
		}
	}

	WRW_HARPOON((port + hp_intstat), PHASE);
	if ((RD_HARPOON(port + hp_scsisig) & S_SCSI_PHZ) == S_MSGO_PH) {
		WRW_HARPOON((port + hp_intstat),
			    (BUS_FREE | PHASE | XFER_CNT_0));

		WR_HARPOON(port + hp_portctrl_0, SCSI_BUS_EN);

		WR_HARPOON(port + hp_scsidata_0, message);

		WR_HARPOON(port + hp_scsisig, (SCSI_ACK + S_ILL_PH));

		ACCEPT_MSG(port);

		WR_HARPOON(port + hp_portctrl_0, 0x00);

		if ((message == ABORT_TASK_SET) || (message == TARGET_RESET) ||
		    (message == ABORT_TASK)) {
			while (!
			       (RDW_HARPOON((port + hp_intstat)) &
				(BUS_FREE | PHASE))) {
			}

			if (RDW_HARPOON((port + hp_intstat)) & BUS_FREE) {
				WRW_HARPOON((port + hp_intstat), BUS_FREE);
			}
		}
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sdecm
 *
 * Description: Determine the proper response to the message from the
 *              target device.
 *
 *---------------------------------------------------------------------*/
static void FPT_sdecm(unsigned char message, u32 port, unsigned char p_card)
{
	struct sccb *currSCCB;
	struct sccb_card *CurrCard;
	struct sccb_mgr_tar_info *currTar_Info;

	CurrCard = &FPT_BL_Card[p_card];
	currSCCB = CurrCard->currentSCCB;

	currTar_Info = &FPT_sccbMgrTbl[p_card][currSCCB->TargID];

	if (message == RESTORE_POINTERS) {
		if (!(currSCCB->Sccb_XferState & F_NO_DATA_YET)) {
			currSCCB->Sccb_ATC = currSCCB->Sccb_savedATC;

			FPT_hostDataXferRestart(currSCCB);
		}

		ACCEPT_MSG(port);
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
	}

	else if (message == COMMAND_COMPLETE) {

		if (currSCCB->Sccb_scsistat == SELECT_Q_ST) {
			currTar_Info->TarStatus &=
			    ~(unsigned char)TAR_TAG_Q_MASK;
			currTar_Info->TarStatus |= (unsigned char)TAG_Q_REJECT;
		}

		ACCEPT_MSG(port);

	}

	else if ((message == NOP) || (message >= IDENTIFY_BASE) ||
		 (message == INITIATE_RECOVERY) ||
		 (message == RELEASE_RECOVERY)) {

		ACCEPT_MSG(port);
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
	}

	else if (message == MESSAGE_REJECT) {

		if ((currSCCB->Sccb_scsistat == SELECT_SN_ST) ||
		    (currSCCB->Sccb_scsistat == SELECT_WN_ST) ||
		    ((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING)
		    || ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) ==
			TAG_Q_TRYING))
		{
			WRW_HARPOON((port + hp_intstat), BUS_FREE);

			ACCEPT_MSG(port);

			while ((!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) &&
			       (!(RDW_HARPOON((port + hp_intstat)) & BUS_FREE)))
			{
			}

			if (currSCCB->Lun == 0x00) {
				if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {

					currTar_Info->TarStatus |=
					    (unsigned char)SYNC_SUPPORTED;

					currTar_Info->TarEEValue &=
					    ~EE_SYNC_MASK;
				}

				else if (currSCCB->Sccb_scsistat ==
					  SELECT_WN_ST) {

					currTar_Info->TarStatus =
					    (currTar_Info->
					     TarStatus & ~WIDE_ENABLED) |
					    WIDE_NEGOCIATED;

					currTar_Info->TarEEValue &=
					    ~EE_WIDE_SCSI;

				}

				else if ((currTar_Info->
					  TarStatus & TAR_TAG_Q_MASK) ==
					 TAG_Q_TRYING) {
					currTar_Info->TarStatus =
					    (currTar_Info->
					     TarStatus & ~(unsigned char)
					     TAR_TAG_Q_MASK) | TAG_Q_REJECT;

					currSCCB->ControlByte &= ~F_USE_CMD_Q;
					CurrCard->discQCount--;
					CurrCard->discQ_Tbl[currSCCB->
							    Sccb_tag] = NULL;
					currSCCB->Sccb_tag = 0x00;

				}
			}

			if (RDW_HARPOON((port + hp_intstat)) & BUS_FREE) {

				if (currSCCB->Lun == 0x00) {
					WRW_HARPOON((port + hp_intstat),
						    BUS_FREE);
					CurrCard->globalFlags |= F_NEW_SCCB_CMD;
				}
			}

			else {

				if ((CurrCard->globalFlags & F_CONLUN_IO) &&
				    ((currTar_Info->
				      TarStatus & TAR_TAG_Q_MASK) !=
				     TAG_Q_TRYING))
					currTar_Info->TarLUNBusy[currSCCB->
								 Lun] = 1;
				else
					currTar_Info->TarLUNBusy[0] = 1;

				currSCCB->ControlByte &=
				    ~(unsigned char)F_USE_CMD_Q;

				WR_HARPOON(port + hp_autostart_1,
					   (AUTO_IMMED + DISCONNECT_START));

			}
		}

		else {
			ACCEPT_MSG(port);

			while ((!(RD_HARPOON(port + hp_scsisig) & SCSI_REQ)) &&
			       (!(RDW_HARPOON((port + hp_intstat)) & BUS_FREE)))
			{
			}

			if (!(RDW_HARPOON((port + hp_intstat)) & BUS_FREE)) {
				WR_HARPOON(port + hp_autostart_1,
					   (AUTO_IMMED + DISCONNECT_START));
			}
		}
	}

	else if (message == EXTENDED_MESSAGE) {

		ACCEPT_MSG(port);
		FPT_shandem(port, p_card, currSCCB);
	}

	else if (message == IGNORE_WIDE_RESIDUE) {

		ACCEPT_MSG(port);	/* ACK the RESIDUE MSG */

		message = FPT_sfm(port, currSCCB);

		if (currSCCB->Sccb_scsimsg != MSG_PARITY_ERROR)
			ACCEPT_MSG(port);
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
	}

	else {

		currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
		currSCCB->Sccb_scsimsg = MESSAGE_REJECT;

		ACCEPT_MSG_ATN(port);
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_shandem
 *
 * Description: Decide what to do with the extended message.
 *
 *---------------------------------------------------------------------*/
static void FPT_shandem(u32 port, unsigned char p_card, struct sccb *pCurrSCCB)
{
	unsigned char length, message;

	length = FPT_sfm(port, pCurrSCCB);
	if (length) {

		ACCEPT_MSG(port);
		message = FPT_sfm(port, pCurrSCCB);
		if (message) {

			if (message == EXTENDED_SDTR) {

				if (length == 0x03) {

					ACCEPT_MSG(port);
					FPT_stsyncn(port, p_card);
				} else {

					pCurrSCCB->Sccb_scsimsg = MESSAGE_REJECT;
					ACCEPT_MSG_ATN(port);
				}
			} else if (message == EXTENDED_WDTR) {

				if (length == 0x02) {

					ACCEPT_MSG(port);
					FPT_stwidn(port, p_card);
				} else {

					pCurrSCCB->Sccb_scsimsg = MESSAGE_REJECT;
					ACCEPT_MSG_ATN(port);

					WR_HARPOON(port + hp_autostart_1,
						   (AUTO_IMMED +
						    DISCONNECT_START));
				}
			} else {

				pCurrSCCB->Sccb_scsimsg = MESSAGE_REJECT;
				ACCEPT_MSG_ATN(port);

				WR_HARPOON(port + hp_autostart_1,
					   (AUTO_IMMED + DISCONNECT_START));
			}
		} else {
			if (pCurrSCCB->Sccb_scsimsg != MSG_PARITY_ERROR)
				ACCEPT_MSG(port);
			WR_HARPOON(port + hp_autostart_1,
				   (AUTO_IMMED + DISCONNECT_START));
		}
	} else {
		if (pCurrSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)
			WR_HARPOON(port + hp_autostart_1,
				   (AUTO_IMMED + DISCONNECT_START));
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sisyncn
 *
 * Description: Read in a message byte from the SCSI bus, and check
 *              for a parity error.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_sisyncn(u32 port, unsigned char p_card,
				 unsigned char syncFlag)
{
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;
	currTar_Info = &FPT_sccbMgrTbl[p_card][currSCCB->TargID];

	if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING)) {

		WRW_HARPOON((port + ID_MSG_STRT),
			    (MPM_OP + AMSG_OUT +
			     (currSCCB->
			      Sccb_idmsg & ~(unsigned char)DISC_PRIV)));

		WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + CMDPZ);

		WRW_HARPOON((port + SYNC_MSGS + 0),
			    (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
		WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x03));
		WRW_HARPOON((port + SYNC_MSGS + 4),
			    (MPM_OP + AMSG_OUT + EXTENDED_SDTR));

		if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)

			WRW_HARPOON((port + SYNC_MSGS + 6),
				    (MPM_OP + AMSG_OUT + 12));

		else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) ==
			 EE_SYNC_10MB)

			WRW_HARPOON((port + SYNC_MSGS + 6),
				    (MPM_OP + AMSG_OUT + 25));

		else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) ==
			 EE_SYNC_5MB)

			WRW_HARPOON((port + SYNC_MSGS + 6),
				    (MPM_OP + AMSG_OUT + 50));

		else
			WRW_HARPOON((port + SYNC_MSGS + 6),
				    (MPM_OP + AMSG_OUT + 00));

		WRW_HARPOON((port + SYNC_MSGS + 8), (RAT_OP));
		WRW_HARPOON((port + SYNC_MSGS + 10),
			    (MPM_OP + AMSG_OUT + DEFAULT_OFFSET));
		WRW_HARPOON((port + SYNC_MSGS + 12), (BRH_OP + ALWAYS + NP));

		if (syncFlag == 0) {
			WR_HARPOON(port + hp_autostart_3,
				   (SELECT + SELCHK_STRT));
			currTar_Info->TarStatus =
			    ((currTar_Info->
			      TarStatus & ~(unsigned char)TAR_SYNC_MASK) |
			     (unsigned char)SYNC_TRYING);
		} else {
			WR_HARPOON(port + hp_autostart_3,
				   (AUTO_IMMED + CMD_ONLY_STRT));
		}

		return 1;
	}

	else {

		currTar_Info->TarStatus |= (unsigned char)SYNC_SUPPORTED;
		currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
		return 0;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_stsyncn
 *
 * Description: The has sent us a Sync Nego message so handle it as
 *              necessary.
 *
 *---------------------------------------------------------------------*/
static void FPT_stsyncn(u32 port, unsigned char p_card)
{
	unsigned char sync_msg, offset, sync_reg, our_sync_msg;
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;
	currTar_Info = &FPT_sccbMgrTbl[p_card][currSCCB->TargID];

	sync_msg = FPT_sfm(port, currSCCB);

	if ((sync_msg == 0x00) && (currSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)) {
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
		return;
	}

	ACCEPT_MSG(port);

	offset = FPT_sfm(port, currSCCB);

	if ((offset == 0x00) && (currSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)) {
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
		return;
	}

	if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)

		our_sync_msg = 12;	/* Setup our Message to 20mb/s */

	else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)

		our_sync_msg = 25;	/* Setup our Message to 10mb/s */

	else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)

		our_sync_msg = 50;	/* Setup our Message to 5mb/s */
	else

		our_sync_msg = 0;	/* Message = Async */

	if (sync_msg < our_sync_msg) {
		sync_msg = our_sync_msg;	/*if faster, then set to max. */
	}

	if (offset == ASYNC)
		sync_msg = ASYNC;

	if (offset > MAX_OFFSET)
		offset = MAX_OFFSET;

	sync_reg = 0x00;

	if (sync_msg > 12)

		sync_reg = 0x20;	/* Use 10MB/s */

	if (sync_msg > 25)

		sync_reg = 0x40;	/* Use 6.6MB/s */

	if (sync_msg > 38)

		sync_reg = 0x60;	/* Use 5MB/s */

	if (sync_msg > 50)

		sync_reg = 0x80;	/* Use 4MB/s */

	if (sync_msg > 62)

		sync_reg = 0xA0;	/* Use 3.33MB/s */

	if (sync_msg > 75)

		sync_reg = 0xC0;	/* Use 2.85MB/s */

	if (sync_msg > 87)

		sync_reg = 0xE0;	/* Use 2.5MB/s */

	if (sync_msg > 100) {

		sync_reg = 0x00;	/* Use ASYNC */
		offset = 0x00;
	}

	if (currTar_Info->TarStatus & WIDE_ENABLED)

		sync_reg |= offset;

	else

		sync_reg |= (offset | NARROW_SCSI);

	FPT_sssyncv(port, currSCCB->TargID, sync_reg, currTar_Info);

	if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {

		ACCEPT_MSG(port);

		currTar_Info->TarStatus = ((currTar_Info->TarStatus &
					    ~(unsigned char)TAR_SYNC_MASK) |
					   (unsigned char)SYNC_SUPPORTED);

		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
	}

	else {

		ACCEPT_MSG_ATN(port);

		FPT_sisyncr(port, sync_msg, offset);

		currTar_Info->TarStatus = ((currTar_Info->TarStatus &
					    ~(unsigned char)TAR_SYNC_MASK) |
					   (unsigned char)SYNC_SUPPORTED);
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sisyncr
 *
 * Description: Answer the targets sync message.
 *
 *---------------------------------------------------------------------*/
static void FPT_sisyncr(u32 port, unsigned char sync_pulse,
			unsigned char offset)
{
	ARAM_ACCESS(port);
	WRW_HARPOON((port + SYNC_MSGS + 0),
		    (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
	WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x03));
	WRW_HARPOON((port + SYNC_MSGS + 4),
		    (MPM_OP + AMSG_OUT + EXTENDED_SDTR));
	WRW_HARPOON((port + SYNC_MSGS + 6), (MPM_OP + AMSG_OUT + sync_pulse));
	WRW_HARPOON((port + SYNC_MSGS + 8), (RAT_OP));
	WRW_HARPOON((port + SYNC_MSGS + 10), (MPM_OP + AMSG_OUT + offset));
	WRW_HARPOON((port + SYNC_MSGS + 12), (BRH_OP + ALWAYS + NP));
	SGRAM_ACCESS(port);

	WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);
	WRW_HARPOON((port + hp_intstat), CLR_ALL_INT_1);

	WR_HARPOON(port + hp_autostart_3, (AUTO_IMMED + CMD_ONLY_STRT));

	while (!(RDW_HARPOON((port + hp_intstat)) & (BUS_FREE | AUTO_INT))) {
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_siwidn
 *
 * Description: Read in a message byte from the SCSI bus, and check
 *              for a parity error.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_siwidn(u32 port, unsigned char p_card)
{
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;
	currTar_Info = &FPT_sccbMgrTbl[p_card][currSCCB->TargID];

	if (!((currTar_Info->TarStatus & TAR_WIDE_MASK) == WIDE_NEGOCIATED)) {

		WRW_HARPOON((port + ID_MSG_STRT),
			    (MPM_OP + AMSG_OUT +
			     (currSCCB->
			      Sccb_idmsg & ~(unsigned char)DISC_PRIV)));

		WRW_HARPOON((port + ID_MSG_STRT + 2), BRH_OP + ALWAYS + CMDPZ);

		WRW_HARPOON((port + SYNC_MSGS + 0),
			    (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
		WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x02));
		WRW_HARPOON((port + SYNC_MSGS + 4),
			    (MPM_OP + AMSG_OUT + EXTENDED_WDTR));
		WRW_HARPOON((port + SYNC_MSGS + 6), (RAT_OP));
		WRW_HARPOON((port + SYNC_MSGS + 8),
			    (MPM_OP + AMSG_OUT + SM16BIT));
		WRW_HARPOON((port + SYNC_MSGS + 10), (BRH_OP + ALWAYS + NP));

		WR_HARPOON(port + hp_autostart_3, (SELECT + SELCHK_STRT));

		currTar_Info->TarStatus = ((currTar_Info->TarStatus &
					    ~(unsigned char)TAR_WIDE_MASK) |
					   (unsigned char)WIDE_ENABLED);

		return 1;
	}

	else {

		currTar_Info->TarStatus = ((currTar_Info->TarStatus &
					    ~(unsigned char)TAR_WIDE_MASK) |
					   WIDE_NEGOCIATED);

		currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
		return 0;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_stwidn
 *
 * Description: The has sent us a Wide Nego message so handle it as
 *              necessary.
 *
 *---------------------------------------------------------------------*/
static void FPT_stwidn(u32 port, unsigned char p_card)
{
	unsigned char width;
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;
	currTar_Info = &FPT_sccbMgrTbl[p_card][currSCCB->TargID];

	width = FPT_sfm(port, currSCCB);

	if ((width == 0x00) && (currSCCB->Sccb_scsimsg == MSG_PARITY_ERROR)) {
		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + DISCONNECT_START));
		return;
	}

	if (!(currTar_Info->TarEEValue & EE_WIDE_SCSI))
		width = 0;

	if (width) {
		currTar_Info->TarStatus |= WIDE_ENABLED;
		width = 0;
	} else {
		width = NARROW_SCSI;
		currTar_Info->TarStatus &= ~WIDE_ENABLED;
	}

	FPT_sssyncv(port, currSCCB->TargID, width, currTar_Info);

	if (currSCCB->Sccb_scsistat == SELECT_WN_ST) {

		currTar_Info->TarStatus |= WIDE_NEGOCIATED;

		if (!
		    ((currTar_Info->TarStatus & TAR_SYNC_MASK) ==
		     SYNC_SUPPORTED)) {
			ACCEPT_MSG_ATN(port);
			ARAM_ACCESS(port);
			FPT_sisyncn(port, p_card, 1);
			currSCCB->Sccb_scsistat = SELECT_SN_ST;
			SGRAM_ACCESS(port);
		} else {
			ACCEPT_MSG(port);
			WR_HARPOON(port + hp_autostart_1,
				   (AUTO_IMMED + DISCONNECT_START));
		}
	}

	else {

		ACCEPT_MSG_ATN(port);

		if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
			width = SM16BIT;
		else
			width = SM8BIT;

		FPT_siwidr(port, width);

		currTar_Info->TarStatus |= (WIDE_NEGOCIATED | WIDE_ENABLED);
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_siwidr
 *
 * Description: Answer the targets Wide nego message.
 *
 *---------------------------------------------------------------------*/
static void FPT_siwidr(u32 port, unsigned char width)
{
	ARAM_ACCESS(port);
	WRW_HARPOON((port + SYNC_MSGS + 0),
		    (MPM_OP + AMSG_OUT + EXTENDED_MESSAGE));
	WRW_HARPOON((port + SYNC_MSGS + 2), (MPM_OP + AMSG_OUT + 0x02));
	WRW_HARPOON((port + SYNC_MSGS + 4),
		    (MPM_OP + AMSG_OUT + EXTENDED_WDTR));
	WRW_HARPOON((port + SYNC_MSGS + 6), (RAT_OP));
	WRW_HARPOON((port + SYNC_MSGS + 8), (MPM_OP + AMSG_OUT + width));
	WRW_HARPOON((port + SYNC_MSGS + 10), (BRH_OP + ALWAYS + NP));
	SGRAM_ACCESS(port);

	WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);
	WRW_HARPOON((port + hp_intstat), CLR_ALL_INT_1);

	WR_HARPOON(port + hp_autostart_3, (AUTO_IMMED + CMD_ONLY_STRT));

	while (!(RDW_HARPOON((port + hp_intstat)) & (BUS_FREE | AUTO_INT))) {
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sssyncv
 *
 * Description: Write the desired value to the Sync Register for the
 *              ID specified.
 *
 *---------------------------------------------------------------------*/
static void FPT_sssyncv(u32 p_port, unsigned char p_id,
			unsigned char p_sync_value,
			struct sccb_mgr_tar_info *currTar_Info)
{
	unsigned char index;

	index = p_id;

	switch (index) {

	case 0:
		index = 12;	/* hp_synctarg_0 */
		break;
	case 1:
		index = 13;	/* hp_synctarg_1 */
		break;
	case 2:
		index = 14;	/* hp_synctarg_2 */
		break;
	case 3:
		index = 15;	/* hp_synctarg_3 */
		break;
	case 4:
		index = 8;	/* hp_synctarg_4 */
		break;
	case 5:
		index = 9;	/* hp_synctarg_5 */
		break;
	case 6:
		index = 10;	/* hp_synctarg_6 */
		break;
	case 7:
		index = 11;	/* hp_synctarg_7 */
		break;
	case 8:
		index = 4;	/* hp_synctarg_8 */
		break;
	case 9:
		index = 5;	/* hp_synctarg_9 */
		break;
	case 10:
		index = 6;	/* hp_synctarg_10 */
		break;
	case 11:
		index = 7;	/* hp_synctarg_11 */
		break;
	case 12:
		index = 0;	/* hp_synctarg_12 */
		break;
	case 13:
		index = 1;	/* hp_synctarg_13 */
		break;
	case 14:
		index = 2;	/* hp_synctarg_14 */
		break;
	case 15:
		index = 3;	/* hp_synctarg_15 */

	}

	WR_HARPOON(p_port + hp_synctarg_base + index, p_sync_value);

	currTar_Info->TarSyncCtrl = p_sync_value;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sresb
 *
 * Description: Reset the desired card's SCSI bus.
 *
 *---------------------------------------------------------------------*/
static void FPT_sresb(u32 port, unsigned char p_card)
{
	unsigned char scsiID, i;

	struct sccb_mgr_tar_info *currTar_Info;

	WR_HARPOON(port + hp_page_ctrl,
		   (RD_HARPOON(port + hp_page_ctrl) | G_INT_DISABLE));
	WRW_HARPOON((port + hp_intstat), CLR_ALL_INT);

	WR_HARPOON(port + hp_scsictrl_0, SCSI_RST);

	scsiID = RD_HARPOON(port + hp_seltimeout);
	WR_HARPOON(port + hp_seltimeout, TO_5ms);
	WRW_HARPOON((port + hp_intstat), TIMEOUT);

	WR_HARPOON(port + hp_portctrl_0, (SCSI_PORT | START_TO));

	while (!(RDW_HARPOON((port + hp_intstat)) & TIMEOUT)) {
	}

	WR_HARPOON(port + hp_seltimeout, scsiID);

	WR_HARPOON(port + hp_scsictrl_0, ENA_SCAM_SEL);

	FPT_Wait(port, TO_5ms);

	WRW_HARPOON((port + hp_intstat), CLR_ALL_INT);

	WR_HARPOON(port + hp_int_mask, (RD_HARPOON(port + hp_int_mask) | 0x00));

	for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++) {
		currTar_Info = &FPT_sccbMgrTbl[p_card][scsiID];

		if (currTar_Info->TarEEValue & EE_SYNC_MASK) {
			currTar_Info->TarSyncCtrl = 0;
			currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
		}

		if (currTar_Info->TarEEValue & EE_WIDE_SCSI) {
			currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
		}

		FPT_sssyncv(port, scsiID, NARROW_SCSI, currTar_Info);

		FPT_SccbMgrTableInitTarget(p_card, scsiID);
	}

	FPT_BL_Card[p_card].scanIndex = 0x00;
	FPT_BL_Card[p_card].currentSCCB = NULL;
	FPT_BL_Card[p_card].globalFlags &= ~(F_TAG_STARTED | F_HOST_XFER_ACT
					     | F_NEW_SCCB_CMD);
	FPT_BL_Card[p_card].cmdCounter = 0x00;
	FPT_BL_Card[p_card].discQCount = 0x00;
	FPT_BL_Card[p_card].tagQ_Lst = 0x01;

	for (i = 0; i < QUEUE_DEPTH; i++)
		FPT_BL_Card[p_card].discQ_Tbl[i] = NULL;

	WR_HARPOON(port + hp_page_ctrl,
		   (RD_HARPOON(port + hp_page_ctrl) & ~G_INT_DISABLE));

}

/*---------------------------------------------------------------------
 *
 * Function: FPT_ssenss
 *
 * Description: Setup for the Auto Sense command.
 *
 *---------------------------------------------------------------------*/
static void FPT_ssenss(struct sccb_card *pCurrCard)
{
	unsigned char i;
	struct sccb *currSCCB;

	currSCCB = pCurrCard->currentSCCB;

	currSCCB->Save_CdbLen = currSCCB->CdbLength;

	for (i = 0; i < 6; i++) {

		currSCCB->Save_Cdb[i] = currSCCB->Cdb[i];
	}

	currSCCB->CdbLength = SIX_BYTE_CMD;
	currSCCB->Cdb[0] = REQUEST_SENSE;
	currSCCB->Cdb[1] = currSCCB->Cdb[1] & (unsigned char)0xE0;	/*Keep LUN. */
	currSCCB->Cdb[2] = 0x00;
	currSCCB->Cdb[3] = 0x00;
	currSCCB->Cdb[4] = currSCCB->RequestSenseLength;
	currSCCB->Cdb[5] = 0x00;

	currSCCB->Sccb_XferCnt = (u32)currSCCB->RequestSenseLength;

	currSCCB->Sccb_ATC = 0x00;

	currSCCB->Sccb_XferState |= F_AUTO_SENSE;

	currSCCB->Sccb_XferState &= ~F_SG_XFER;

	currSCCB->Sccb_idmsg = currSCCB->Sccb_idmsg & ~(unsigned char)DISC_PRIV;

	currSCCB->ControlByte = 0x00;

	currSCCB->Sccb_MGRFlags &= F_STATUSLOADED;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sxfrp
 *
 * Description: Transfer data into the bit bucket until the device
 *              decides to switch phase.
 *
 *---------------------------------------------------------------------*/

static void FPT_sxfrp(u32 p_port, unsigned char p_card)
{
	unsigned char curr_phz;

	DISABLE_AUTO(p_port);

	if (FPT_BL_Card[p_card].globalFlags & F_HOST_XFER_ACT) {

		FPT_hostDataXferAbort(p_port, p_card,
				      FPT_BL_Card[p_card].currentSCCB);

	}

	/* If the Automation handled the end of the transfer then do not
	   match the phase or we will get out of sync with the ISR.       */

	if (RDW_HARPOON((p_port + hp_intstat)) &
	    (BUS_FREE | XFER_CNT_0 | AUTO_INT))
		return;

	WR_HARPOON(p_port + hp_xfercnt_0, 0x00);

	curr_phz = RD_HARPOON(p_port + hp_scsisig) & (unsigned char)S_SCSI_PHZ;

	WRW_HARPOON((p_port + hp_intstat), XFER_CNT_0);

	WR_HARPOON(p_port + hp_scsisig, curr_phz);

	while (!(RDW_HARPOON((p_port + hp_intstat)) & (BUS_FREE | RESET)) &&
	       (curr_phz ==
		(RD_HARPOON(p_port + hp_scsisig) & (unsigned char)S_SCSI_PHZ)))
	{
		if (curr_phz & (unsigned char)SCSI_IOBIT) {
			WR_HARPOON(p_port + hp_portctrl_0,
				   (SCSI_PORT | HOST_PORT | SCSI_INBIT));

			if (!(RD_HARPOON(p_port + hp_xferstat) & FIFO_EMPTY)) {
				RD_HARPOON(p_port + hp_fifodata_0);
			}
		} else {
			WR_HARPOON(p_port + hp_portctrl_0,
				   (SCSI_PORT | HOST_PORT | HOST_WRT));
			if (RD_HARPOON(p_port + hp_xferstat) & FIFO_EMPTY) {
				WR_HARPOON(p_port + hp_fifodata_0, 0xFA);
			}
		}
	}			/* End of While loop for padding data I/O phase */

	while (!(RDW_HARPOON((p_port + hp_intstat)) & (BUS_FREE | RESET))) {
		if (RD_HARPOON(p_port + hp_scsisig) & SCSI_REQ)
			break;
	}

	WR_HARPOON(p_port + hp_portctrl_0,
		   (SCSI_PORT | HOST_PORT | SCSI_INBIT));
	while (!(RD_HARPOON(p_port + hp_xferstat) & FIFO_EMPTY)) {
		RD_HARPOON(p_port + hp_fifodata_0);
	}

	if (!(RDW_HARPOON((p_port + hp_intstat)) & (BUS_FREE | RESET))) {
		WR_HARPOON(p_port + hp_autostart_0,
			   (AUTO_IMMED + DISCONNECT_START));
		while (!(RDW_HARPOON((p_port + hp_intstat)) & AUTO_INT)) {
		}

		if (RDW_HARPOON((p_port + hp_intstat)) &
		    (ICMD_COMP | ITAR_DISC))
			while (!
			       (RDW_HARPOON((p_port + hp_intstat)) &
				(BUS_FREE | RSEL))) ;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_schkdd
 *
 * Description: Make sure data has been flushed from both FIFOs and abort
 *              the operations if necessary.
 *
 *---------------------------------------------------------------------*/

static void FPT_schkdd(u32 port, unsigned char p_card)
{
	unsigned short TimeOutLoop;
	unsigned char sPhase;

	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if ((currSCCB->Sccb_scsistat != DATA_OUT_ST) &&
	    (currSCCB->Sccb_scsistat != DATA_IN_ST)) {
		return;
	}

	if (currSCCB->Sccb_XferState & F_ODD_BALL_CNT) {

		currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt - 1);

		currSCCB->Sccb_XferCnt = 1;

		currSCCB->Sccb_XferState &= ~F_ODD_BALL_CNT;
		WRW_HARPOON((port + hp_fiforead), (unsigned short)0x00);
		WR_HARPOON(port + hp_xferstat, 0x00);
	}

	else {

		currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;

		currSCCB->Sccb_XferCnt = 0;
	}

	if ((RDW_HARPOON((port + hp_intstat)) & PARITY) &&
	    (currSCCB->HostStatus == SCCB_COMPLETE)) {

		currSCCB->HostStatus = SCCB_PARITY_ERR;
		WRW_HARPOON((port + hp_intstat), PARITY);
	}

	FPT_hostDataXferAbort(port, p_card, currSCCB);

	while (RD_HARPOON(port + hp_scsisig) & SCSI_ACK) {
	}

	TimeOutLoop = 0;

	while (RD_HARPOON(port + hp_xferstat) & FIFO_EMPTY) {
		if (RDW_HARPOON((port + hp_intstat)) & BUS_FREE) {
			return;
		}
		if (RD_HARPOON(port + hp_offsetctr) & (unsigned char)0x1F) {
			break;
		}
		if (RDW_HARPOON((port + hp_intstat)) & RESET) {
			return;
		}
		if ((RD_HARPOON(port + hp_scsisig) & SCSI_REQ)
		    || (TimeOutLoop++ > 0x3000))
			break;
	}

	sPhase = RD_HARPOON(port + hp_scsisig) & (SCSI_BSY | S_SCSI_PHZ);
	if ((!(RD_HARPOON(port + hp_xferstat) & FIFO_EMPTY)) ||
	    (RD_HARPOON(port + hp_offsetctr) & (unsigned char)0x1F) ||
	    (sPhase == (SCSI_BSY | S_DATAO_PH)) ||
	    (sPhase == (SCSI_BSY | S_DATAI_PH))) {

		WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);

		if (!(currSCCB->Sccb_XferState & F_ALL_XFERRED)) {
			if (currSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
				FPT_phaseDataIn(port, p_card);
			}

			else {
				FPT_phaseDataOut(port, p_card);
			}
		} else {
			FPT_sxfrp(port, p_card);
			if (!(RDW_HARPOON((port + hp_intstat)) &
			      (BUS_FREE | ICMD_COMP | ITAR_DISC | RESET))) {
				WRW_HARPOON((port + hp_intstat), AUTO_INT);
				FPT_phaseDecode(port, p_card);
			}
		}

	}

	else {
		WR_HARPOON(port + hp_portctrl_0, 0x00);
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sinits
 *
 * Description: Setup SCCB manager fields in this SCCB.
 *
 *---------------------------------------------------------------------*/

static void FPT_sinits(struct sccb *p_sccb, unsigned char p_card)
{
	struct sccb_mgr_tar_info *currTar_Info;

	if ((p_sccb->TargID >= MAX_SCSI_TAR) || (p_sccb->Lun >= MAX_LUN)) {
		return;
	}
	currTar_Info = &FPT_sccbMgrTbl[p_card][p_sccb->TargID];

	p_sccb->Sccb_XferState = 0x00;
	p_sccb->Sccb_XferCnt = p_sccb->DataLength;

	if ((p_sccb->OperationCode == SCATTER_GATHER_COMMAND) ||
	    (p_sccb->OperationCode == RESIDUAL_SG_COMMAND)) {

		p_sccb->Sccb_SGoffset = 0;
		p_sccb->Sccb_XferState = F_SG_XFER;
		p_sccb->Sccb_XferCnt = 0x00;
	}

	if (p_sccb->DataLength == 0x00)

		p_sccb->Sccb_XferState |= F_ALL_XFERRED;

	if (p_sccb->ControlByte & F_USE_CMD_Q) {
		if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
			p_sccb->ControlByte &= ~F_USE_CMD_Q;

		else
			currTar_Info->TarStatus |= TAG_Q_TRYING;
	}

/*      For !single SCSI device in system  & device allow Disconnect
	or command is tag_q type then send Cmd with Disconnect Enable
	else send Cmd with Disconnect Disable */

/*
   if (((!(FPT_BL_Card[p_card].globalFlags & F_SINGLE_DEVICE)) &&
      (currTar_Info->TarStatus & TAR_ALLOW_DISC)) ||
      (currTar_Info->TarStatus & TAG_Q_TRYING)) {
*/
	if ((currTar_Info->TarStatus & TAR_ALLOW_DISC) ||
	    (currTar_Info->TarStatus & TAG_Q_TRYING)) {
		p_sccb->Sccb_idmsg = IDENTIFY(true, p_sccb->Lun);
	} else {
		p_sccb->Sccb_idmsg = IDENTIFY(false, p_sccb->Lun);
	}

	p_sccb->HostStatus = 0x00;
	p_sccb->TargetStatus = 0x00;
	p_sccb->Sccb_tag = 0x00;
	p_sccb->Sccb_MGRFlags = 0x00;
	p_sccb->Sccb_sgseg = 0x00;
	p_sccb->Sccb_ATC = 0x00;
	p_sccb->Sccb_savedATC = 0x00;
/*
   p_sccb->SccbVirtDataPtr    = 0x00;
   p_sccb->Sccb_forwardlink   = NULL;
   p_sccb->Sccb_backlink      = NULL;
 */
	p_sccb->Sccb_scsistat = BUS_FREE_ST;
	p_sccb->SccbStatus = SCCB_IN_PROCESS;
	p_sccb->Sccb_scsimsg = NOP;

}

/*---------------------------------------------------------------------
 *
 * Function: Phase Decode
 *
 * Description: Determine the phase and call the appropriate function.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseDecode(u32 p_port, unsigned char p_card)
{
	unsigned char phase_ref;
	void (*phase) (u32, unsigned char);

	DISABLE_AUTO(p_port);

	phase_ref =
	    (unsigned char)(RD_HARPOON(p_port + hp_scsisig) & S_SCSI_PHZ);

	phase = FPT_s_PhaseTbl[phase_ref];

	(*phase) (p_port, p_card);	/* Call the correct phase func */
}

/*---------------------------------------------------------------------
 *
 * Function: Data Out Phase
 *
 * Description: Start up both the BusMaster and Xbow.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseDataOut(u32 port, unsigned char p_card)
{

	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;
	if (currSCCB == NULL) {
		return;		/* Exit if No SCCB record */
	}

	currSCCB->Sccb_scsistat = DATA_OUT_ST;
	currSCCB->Sccb_XferState &= ~(F_HOST_XFER_DIR | F_NO_DATA_YET);

	WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);

	WRW_HARPOON((port + hp_intstat), XFER_CNT_0);

	WR_HARPOON(port + hp_autostart_0, (END_DATA + END_DATA_START));

	FPT_dataXferProcessor(port, &FPT_BL_Card[p_card]);

	if (currSCCB->Sccb_XferCnt == 0) {

		if ((currSCCB->ControlByte & SCCB_DATA_XFER_OUT) &&
		    (currSCCB->HostStatus == SCCB_COMPLETE))
			currSCCB->HostStatus = SCCB_DATA_OVER_RUN;

		FPT_sxfrp(port, p_card);
		if (!(RDW_HARPOON((port + hp_intstat)) & (BUS_FREE | RESET)))
			FPT_phaseDecode(port, p_card);
	}
}

/*---------------------------------------------------------------------
 *
 * Function: Data In Phase
 *
 * Description: Startup the BusMaster and the XBOW.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseDataIn(u32 port, unsigned char p_card)
{

	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if (currSCCB == NULL) {
		return;		/* Exit if No SCCB record */
	}

	currSCCB->Sccb_scsistat = DATA_IN_ST;
	currSCCB->Sccb_XferState |= F_HOST_XFER_DIR;
	currSCCB->Sccb_XferState &= ~F_NO_DATA_YET;

	WR_HARPOON(port + hp_portctrl_0, SCSI_PORT);

	WRW_HARPOON((port + hp_intstat), XFER_CNT_0);

	WR_HARPOON(port + hp_autostart_0, (END_DATA + END_DATA_START));

	FPT_dataXferProcessor(port, &FPT_BL_Card[p_card]);

	if (currSCCB->Sccb_XferCnt == 0) {

		if ((currSCCB->ControlByte & SCCB_DATA_XFER_IN) &&
		    (currSCCB->HostStatus == SCCB_COMPLETE))
			currSCCB->HostStatus = SCCB_DATA_OVER_RUN;

		FPT_sxfrp(port, p_card);
		if (!(RDW_HARPOON((port + hp_intstat)) & (BUS_FREE | RESET)))
			FPT_phaseDecode(port, p_card);

	}
}

/*---------------------------------------------------------------------
 *
 * Function: Command Phase
 *
 * Description: Load the CDB into the automation and start it up.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseCommand(u32 p_port, unsigned char p_card)
{
	struct sccb *currSCCB;
	u32 cdb_reg;
	unsigned char i;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if (currSCCB->OperationCode == RESET_COMMAND) {

		currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
		currSCCB->CdbLength = SIX_BYTE_CMD;
	}

	WR_HARPOON(p_port + hp_scsisig, 0x00);

	ARAM_ACCESS(p_port);

	cdb_reg = p_port + CMD_STRT;

	for (i = 0; i < currSCCB->CdbLength; i++) {

		if (currSCCB->OperationCode == RESET_COMMAND)

			WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + 0x00));

		else
			WRW_HARPOON(cdb_reg,
				    (MPM_OP + ACOMMAND + currSCCB->Cdb[i]));
		cdb_reg += 2;
	}

	if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
		WRW_HARPOON(cdb_reg, (BRH_OP + ALWAYS + NP));

	WR_HARPOON(p_port + hp_portctrl_0, (SCSI_PORT));

	currSCCB->Sccb_scsistat = COMMAND_ST;

	WR_HARPOON(p_port + hp_autostart_3, (AUTO_IMMED | CMD_ONLY_STRT));
	SGRAM_ACCESS(p_port);
}

/*---------------------------------------------------------------------
 *
 * Function: Status phase
 *
 * Description: Bring in the status and command complete message bytes
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseStatus(u32 port, unsigned char p_card)
{
	/* Start-up the automation to finish off this command and let the
	   isr handle the interrupt for command complete when it comes in.
	   We could wait here for the interrupt to be generated?
	 */

	WR_HARPOON(port + hp_scsisig, 0x00);

	WR_HARPOON(port + hp_autostart_0, (AUTO_IMMED + END_DATA_START));
}

/*---------------------------------------------------------------------
 *
 * Function: Phase Message Out
 *
 * Description: Send out our message (if we have one) and handle whatever
 *              else is involed.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseMsgOut(u32 port, unsigned char p_card)
{
	unsigned char message, scsiID;
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if (currSCCB != NULL) {

		message = currSCCB->Sccb_scsimsg;
		scsiID = currSCCB->TargID;

		if (message == TARGET_RESET) {

			currTar_Info = &FPT_sccbMgrTbl[p_card][scsiID];
			currTar_Info->TarSyncCtrl = 0;
			FPT_sssyncv(port, scsiID, NARROW_SCSI, currTar_Info);

			if (FPT_sccbMgrTbl[p_card][scsiID].
			    TarEEValue & EE_SYNC_MASK) {

				FPT_sccbMgrTbl[p_card][scsiID].TarStatus &=
				    ~TAR_SYNC_MASK;

			}

			if (FPT_sccbMgrTbl[p_card][scsiID].
			    TarEEValue & EE_WIDE_SCSI) {

				FPT_sccbMgrTbl[p_card][scsiID].TarStatus &=
				    ~TAR_WIDE_MASK;
			}

			FPT_queueFlushSccb(p_card, SCCB_COMPLETE);
			FPT_SccbMgrTableInitTarget(p_card, scsiID);
		} else if (currSCCB->Sccb_scsistat == ABORT_ST) {
			currSCCB->HostStatus = SCCB_COMPLETE;
			if (FPT_BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] !=
			    NULL) {
				FPT_BL_Card[p_card].discQ_Tbl[currSCCB->
							      Sccb_tag] = NULL;
				FPT_sccbMgrTbl[p_card][scsiID].TarTagQ_Cnt--;
			}

		}

		else if (currSCCB->Sccb_scsistat < COMMAND_ST) {

			if (message == NOP) {
				currSCCB->Sccb_MGRFlags |= F_DEV_SELECTED;

				FPT_ssel(port, p_card);
				return;
			}
		} else {

			if (message == ABORT_TASK_SET)

				FPT_queueFlushSccb(p_card, SCCB_COMPLETE);
		}

	} else {
		message = ABORT_TASK_SET;
	}

	WRW_HARPOON((port + hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));

	WR_HARPOON(port + hp_portctrl_0, SCSI_BUS_EN);

	WR_HARPOON(port + hp_scsidata_0, message);

	WR_HARPOON(port + hp_scsisig, (SCSI_ACK + S_ILL_PH));

	ACCEPT_MSG(port);

	WR_HARPOON(port + hp_portctrl_0, 0x00);

	if ((message == ABORT_TASK_SET) || (message == TARGET_RESET) ||
	    (message == ABORT_TASK)) {

		while (!(RDW_HARPOON((port + hp_intstat)) & (BUS_FREE | PHASE))) {
		}

		if (RDW_HARPOON((port + hp_intstat)) & BUS_FREE) {
			WRW_HARPOON((port + hp_intstat), BUS_FREE);

			if (currSCCB != NULL) {

				if ((FPT_BL_Card[p_card].
				     globalFlags & F_CONLUN_IO)
				    &&
				    ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				      TarStatus & TAR_TAG_Q_MASK) !=
				     TAG_Q_TRYING))
					FPT_sccbMgrTbl[p_card][currSCCB->
							       TargID].
					    TarLUNBusy[currSCCB->Lun] = 0;
				else
					FPT_sccbMgrTbl[p_card][currSCCB->
							       TargID].
					    TarLUNBusy[0] = 0;

				FPT_queueCmdComplete(&FPT_BL_Card[p_card],
						     currSCCB, p_card);
			}

			else {
				FPT_BL_Card[p_card].globalFlags |=
				    F_NEW_SCCB_CMD;
			}
		}

		else {

			FPT_sxfrp(port, p_card);
		}
	}

	else {

		if (message == MSG_PARITY_ERROR) {
			currSCCB->Sccb_scsimsg = NOP;
			WR_HARPOON(port + hp_autostart_1,
				   (AUTO_IMMED + DISCONNECT_START));
		} else {
			FPT_sxfrp(port, p_card);
		}
	}
}

/*---------------------------------------------------------------------
 *
 * Function: Message In phase
 *
 * Description: Bring in the message and determine what to do with it.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseMsgIn(u32 port, unsigned char p_card)
{
	unsigned char message;
	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if (FPT_BL_Card[p_card].globalFlags & F_HOST_XFER_ACT) {

		FPT_phaseChkFifo(port, p_card);
	}

	message = RD_HARPOON(port + hp_scsidata_0);
	if ((message == DISCONNECT) || (message == SAVE_POINTERS)) {

		WR_HARPOON(port + hp_autostart_1,
			   (AUTO_IMMED + END_DATA_START));

	}

	else {

		message = FPT_sfm(port, currSCCB);
		if (message) {

			FPT_sdecm(message, port, p_card);

		} else {
			if (currSCCB->Sccb_scsimsg != MSG_PARITY_ERROR)
				ACCEPT_MSG(port);
			WR_HARPOON(port + hp_autostart_1,
				   (AUTO_IMMED + DISCONNECT_START));
		}
	}

}

/*---------------------------------------------------------------------
 *
 * Function: Illegal phase
 *
 * Description: Target switched to some illegal phase, so all we can do
 *              is report an error back to the host (if that is possible)
 *              and send an ABORT message to the misbehaving target.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseIllegal(u32 port, unsigned char p_card)
{
	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	WR_HARPOON(port + hp_scsisig, RD_HARPOON(port + hp_scsisig));
	if (currSCCB != NULL) {

		currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
		currSCCB->Sccb_scsistat = ABORT_ST;
		currSCCB->Sccb_scsimsg = ABORT_TASK_SET;
	}

	ACCEPT_MSG_ATN(port);
}

/*---------------------------------------------------------------------
 *
 * Function: Phase Check FIFO
 *
 * Description: Make sure data has been flushed from both FIFOs and abort
 *              the operations if necessary.
 *
 *---------------------------------------------------------------------*/

static void FPT_phaseChkFifo(u32 port, unsigned char p_card)
{
	u32 xfercnt;
	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if (currSCCB->Sccb_scsistat == DATA_IN_ST) {

		while ((!(RD_HARPOON(port + hp_xferstat) & FIFO_EMPTY)) &&
		       (RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY)) {
		}

		if (!(RD_HARPOON(port + hp_xferstat) & FIFO_EMPTY)) {
			currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;

			currSCCB->Sccb_XferCnt = 0;

			if ((RDW_HARPOON((port + hp_intstat)) & PARITY) &&
			    (currSCCB->HostStatus == SCCB_COMPLETE)) {
				currSCCB->HostStatus = SCCB_PARITY_ERR;
				WRW_HARPOON((port + hp_intstat), PARITY);
			}

			FPT_hostDataXferAbort(port, p_card, currSCCB);

			FPT_dataXferProcessor(port, &FPT_BL_Card[p_card]);

			while ((!(RD_HARPOON(port + hp_xferstat) & FIFO_EMPTY))
			       && (RD_HARPOON(port + hp_ext_status) &
				   BM_CMD_BUSY)) {
			}

		}
	}

	/*End Data In specific code. */
	GET_XFER_CNT(port, xfercnt);

	WR_HARPOON(port + hp_xfercnt_0, 0x00);

	WR_HARPOON(port + hp_portctrl_0, 0x00);

	currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt - xfercnt);

	currSCCB->Sccb_XferCnt = xfercnt;

	if ((RDW_HARPOON((port + hp_intstat)) & PARITY) &&
	    (currSCCB->HostStatus == SCCB_COMPLETE)) {

		currSCCB->HostStatus = SCCB_PARITY_ERR;
		WRW_HARPOON((port + hp_intstat), PARITY);
	}

	FPT_hostDataXferAbort(port, p_card, currSCCB);

	WR_HARPOON(port + hp_fifowrite, 0x00);
	WR_HARPOON(port + hp_fiforead, 0x00);
	WR_HARPOON(port + hp_xferstat, 0x00);

	WRW_HARPOON((port + hp_intstat), XFER_CNT_0);
}

/*---------------------------------------------------------------------
 *
 * Function: Phase Bus Free
 *
 * Description: We just went bus free so figure out if it was
 *              because of command complete or from a disconnect.
 *
 *---------------------------------------------------------------------*/
static void FPT_phaseBusFree(u32 port, unsigned char p_card)
{
	struct sccb *currSCCB;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	if (currSCCB != NULL) {

		DISABLE_AUTO(port);

		if (currSCCB->OperationCode == RESET_COMMAND) {

			if ((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
			    ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			      TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[currSCCB->Lun] = 0;
			else
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[0] = 0;

			FPT_queueCmdComplete(&FPT_BL_Card[p_card], currSCCB,
					     p_card);

			FPT_queueSearchSelect(&FPT_BL_Card[p_card], p_card);

		}

		else if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
			    (unsigned char)SYNC_SUPPORTED;
			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &=
			    ~EE_SYNC_MASK;
		}

		else if (currSCCB->Sccb_scsistat == SELECT_WN_ST) {
			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
			    (FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			     TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;

			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &=
			    ~EE_WIDE_SCSI;
		}

		else if (currSCCB->Sccb_scsistat == SELECT_Q_ST) {
			/* Make sure this is not a phony BUS_FREE.  If we were
			   reselected or if BUSY is NOT on then this is a
			   valid BUS FREE.  SRR Wednesday, 5/10/1995.     */

			if ((!(RD_HARPOON(port + hp_scsisig) & SCSI_BSY)) ||
			    (RDW_HARPOON((port + hp_intstat)) & RSEL)) {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarStatus &= ~TAR_TAG_Q_MASK;
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarStatus |= TAG_Q_REJECT;
			}

			else {
				return;
			}
		}

		else {

			currSCCB->Sccb_scsistat = BUS_FREE_ST;

			if (!currSCCB->HostStatus) {
				currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
			}

			if ((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
			    ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			      TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[currSCCB->Lun] = 0;
			else
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[0] = 0;

			FPT_queueCmdComplete(&FPT_BL_Card[p_card], currSCCB,
					     p_card);
			return;
		}

		FPT_BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;

	}			/*end if !=null */
}

/*---------------------------------------------------------------------
 *
 * Function: Auto Load Default Map
 *
 * Description: Load the Automation RAM with the default map values.
 *
 *---------------------------------------------------------------------*/
static void FPT_autoLoadDefaultMap(u32 p_port)
{
	u32 map_addr;

	ARAM_ACCESS(p_port);
	map_addr = p_port + hp_aramBase;

	WRW_HARPOON(map_addr, (MPM_OP + AMSG_OUT + 0xC0));	/*ID MESSAGE */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + AMSG_OUT + 0x20));	/*SIMPLE TAG QUEUEING MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, RAT_OP);	/*RESET ATTENTION */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + AMSG_OUT + 0x00));	/*TAG ID MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 0 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 1 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 2 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 3 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 4 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 5 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 6 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 7 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 8 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 9 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 10 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MPM_OP + ACOMMAND + 0x00));	/*CDB BYTE 11 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CPE_OP + ADATA_OUT + DINT));	/*JUMP IF DATA OUT */
	map_addr += 2;
	WRW_HARPOON(map_addr, (TCB_OP + FIFO_0 + DI));	/*JUMP IF NO DATA IN FIFO */
	map_addr += 2;		/*This means AYNC DATA IN */
	WRW_HARPOON(map_addr, (SSI_OP + SSI_IDO_STRT));	/*STOP AND INTERRUPT */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CPE_OP + ADATA_IN + DINT));	/*JUMP IF NOT DATA IN PHZ */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CPN_OP + AMSG_IN + ST));	/*IF NOT MSG IN CHECK 4 DATA IN */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CRD_OP + SDATA + 0x02));	/*SAVE DATA PTR MSG? */
	map_addr += 2;
	WRW_HARPOON(map_addr, (BRH_OP + NOT_EQ + DC));	/*GO CHECK FOR DISCONNECT MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MRR_OP + SDATA + D_AR1));	/*SAVE DATA PTRS MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CPN_OP + AMSG_IN + ST));	/*IF NOT MSG IN CHECK DATA IN */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CRD_OP + SDATA + 0x04));	/*DISCONNECT MSG? */
	map_addr += 2;
	WRW_HARPOON(map_addr, (BRH_OP + NOT_EQ + UNKNWN));	/*UKNKNOWN MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MRR_OP + SDATA + D_BUCKET));	/*XFER DISCONNECT MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, (SSI_OP + SSI_ITAR_DISC));	/*STOP AND INTERRUPT */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CPN_OP + ASTATUS + UNKNWN));	/*JUMP IF NOT STATUS PHZ. */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MRR_OP + SDATA + D_AR0));	/*GET STATUS BYTE */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CPN_OP + AMSG_IN + CC));	/*ERROR IF NOT MSG IN PHZ */
	map_addr += 2;
	WRW_HARPOON(map_addr, (CRD_OP + SDATA + 0x00));	/*CHECK FOR CMD COMPLETE MSG. */
	map_addr += 2;
	WRW_HARPOON(map_addr, (BRH_OP + NOT_EQ + CC));	/*ERROR IF NOT CMD COMPLETE MSG. */
	map_addr += 2;
	WRW_HARPOON(map_addr, (MRR_OP + SDATA + D_BUCKET));	/*GET CMD COMPLETE MSG */
	map_addr += 2;
	WRW_HARPOON(map_addr, (SSI_OP + SSI_ICMD_COMP));	/*END OF COMMAND */
	map_addr += 2;

	WRW_HARPOON(map_addr, (SSI_OP + SSI_IUNKWN));	/*RECEIVED UNKNOWN MSG BYTE */
	map_addr += 2;
	WRW_HARPOON(map_addr, (SSI_OP + SSI_INO_CC));	/*NO COMMAND COMPLETE AFTER STATUS */
	map_addr += 2;
	WRW_HARPOON(map_addr, (SSI_OP + SSI_ITICKLE));	/*BIOS Tickled the Mgr */
	map_addr += 2;
	WRW_HARPOON(map_addr, (SSI_OP + SSI_IRFAIL));	/*EXPECTED ID/TAG MESSAGES AND */
	map_addr += 2;		/* DIDN'T GET ONE */
	WRW_HARPOON(map_addr, (CRR_OP + AR3 + S_IDREG));	/* comp SCSI SEL ID & AR3 */
	map_addr += 2;
	WRW_HARPOON(map_addr, (BRH_OP + EQUAL + 0x00));	/*SEL ID OK then Conti. */
	map_addr += 2;
	WRW_HARPOON(map_addr, (SSI_OP + SSI_INO_CC));	/*NO COMMAND COMPLETE AFTER STATUS */

	SGRAM_ACCESS(p_port);
}

/*---------------------------------------------------------------------
 *
 * Function: Auto Command Complete
 *
 * Description: Post command back to host and find another command
 *              to execute.
 *
 *---------------------------------------------------------------------*/

static void FPT_autoCmdCmplt(u32 p_port, unsigned char p_card)
{
	struct sccb *currSCCB;
	unsigned char status_byte;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;

	status_byte = RD_HARPOON(p_port + hp_gp_reg_0);

	FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA = 0;

	if (status_byte != SAM_STAT_GOOD) {

		if (status_byte == SAM_STAT_TASK_SET_FULL) {

			if (((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
			     ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			       TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))) {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[currSCCB->Lun] = 1;
				if (FPT_BL_Card[p_card].discQCount != 0)
					FPT_BL_Card[p_card].discQCount--;
				FPT_BL_Card[p_card].
				    discQ_Tbl[FPT_sccbMgrTbl[p_card]
					      [currSCCB->TargID].
					      LunDiscQ_Idx[currSCCB->Lun]] =
				    NULL;
			} else {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[0] = 1;
				if (currSCCB->Sccb_tag) {
					if (FPT_BL_Card[p_card].discQCount != 0)
						FPT_BL_Card[p_card].
						    discQCount--;
					FPT_BL_Card[p_card].discQ_Tbl[currSCCB->
								      Sccb_tag]
					    = NULL;
				} else {
					if (FPT_BL_Card[p_card].discQCount != 0)
						FPT_BL_Card[p_card].
						    discQCount--;
					FPT_BL_Card[p_card].
					    discQ_Tbl[FPT_sccbMgrTbl[p_card]
						      [currSCCB->TargID].
						      LunDiscQ_Idx[0]] = NULL;
				}
			}

			currSCCB->Sccb_MGRFlags |= F_STATUSLOADED;

			FPT_queueSelectFail(&FPT_BL_Card[p_card], p_card);

			return;
		}

		if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
			    (unsigned char)SYNC_SUPPORTED;

			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &=
			    ~EE_SYNC_MASK;
			FPT_BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;

			if (((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
			     ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			       TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))) {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[currSCCB->Lun] = 1;
				if (FPT_BL_Card[p_card].discQCount != 0)
					FPT_BL_Card[p_card].discQCount--;
				FPT_BL_Card[p_card].
				    discQ_Tbl[FPT_sccbMgrTbl[p_card]
					      [currSCCB->TargID].
					      LunDiscQ_Idx[currSCCB->Lun]] =
				    NULL;
			} else {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[0] = 1;
				if (currSCCB->Sccb_tag) {
					if (FPT_BL_Card[p_card].discQCount != 0)
						FPT_BL_Card[p_card].
						    discQCount--;
					FPT_BL_Card[p_card].discQ_Tbl[currSCCB->
								      Sccb_tag]
					    = NULL;
				} else {
					if (FPT_BL_Card[p_card].discQCount != 0)
						FPT_BL_Card[p_card].
						    discQCount--;
					FPT_BL_Card[p_card].
					    discQ_Tbl[FPT_sccbMgrTbl[p_card]
						      [currSCCB->TargID].
						      LunDiscQ_Idx[0]] = NULL;
				}
			}
			return;

		}

		if (currSCCB->Sccb_scsistat == SELECT_WN_ST) {

			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
			    (FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			     TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;

			FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &=
			    ~EE_WIDE_SCSI;
			FPT_BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;

			if (((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
			     ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
			       TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))) {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[currSCCB->Lun] = 1;
				if (FPT_BL_Card[p_card].discQCount != 0)
					FPT_BL_Card[p_card].discQCount--;
				FPT_BL_Card[p_card].
				    discQ_Tbl[FPT_sccbMgrTbl[p_card]
					      [currSCCB->TargID].
					      LunDiscQ_Idx[currSCCB->Lun]] =
				    NULL;
			} else {
				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUNBusy[0] = 1;
				if (currSCCB->Sccb_tag) {
					if (FPT_BL_Card[p_card].discQCount != 0)
						FPT_BL_Card[p_card].
						    discQCount--;
					FPT_BL_Card[p_card].discQ_Tbl[currSCCB->
								      Sccb_tag]
					    = NULL;
				} else {
					if (FPT_BL_Card[p_card].discQCount != 0)
						FPT_BL_Card[p_card].
						    discQCount--;
					FPT_BL_Card[p_card].
					    discQ_Tbl[FPT_sccbMgrTbl[p_card]
						      [currSCCB->TargID].
						      LunDiscQ_Idx[0]] = NULL;
				}
			}
			return;

		}

		if (status_byte == SAM_STAT_CHECK_CONDITION) {
			if (FPT_BL_Card[p_card].globalFlags & F_DO_RENEGO) {
				if (FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarEEValue & EE_SYNC_MASK) {
					FPT_sccbMgrTbl[p_card][currSCCB->
							       TargID].
					    TarStatus &= ~TAR_SYNC_MASK;
				}
				if (FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarEEValue & EE_WIDE_SCSI) {
					FPT_sccbMgrTbl[p_card][currSCCB->
							       TargID].
					    TarStatus &= ~TAR_WIDE_MASK;
				}
			}
		}

		if (!(currSCCB->Sccb_XferState & F_AUTO_SENSE)) {

			currSCCB->SccbStatus = SCCB_ERROR;
			currSCCB->TargetStatus = status_byte;

			if (status_byte == SAM_STAT_CHECK_CONDITION) {

				FPT_sccbMgrTbl[p_card][currSCCB->TargID].
				    TarLUN_CA = 1;

				if (currSCCB->RequestSenseLength !=
				    NO_AUTO_REQUEST_SENSE) {

					if (currSCCB->RequestSenseLength == 0)
						currSCCB->RequestSenseLength =
						    14;

					FPT_ssenss(&FPT_BL_Card[p_card]);
					FPT_BL_Card[p_card].globalFlags |=
					    F_NEW_SCCB_CMD;

					if (((FPT_BL_Card[p_card].
					      globalFlags & F_CONLUN_IO)
					     &&
					     ((FPT_sccbMgrTbl[p_card]
					       [currSCCB->TargID].
					       TarStatus & TAR_TAG_Q_MASK) !=
					      TAG_Q_TRYING))) {
						FPT_sccbMgrTbl[p_card]
						    [currSCCB->TargID].
						    TarLUNBusy[currSCCB->Lun] =
						    1;
						if (FPT_BL_Card[p_card].
						    discQCount != 0)
							FPT_BL_Card[p_card].
							    discQCount--;
						FPT_BL_Card[p_card].
						    discQ_Tbl[FPT_sccbMgrTbl
							      [p_card]
							      [currSCCB->
							       TargID].
							      LunDiscQ_Idx
							      [currSCCB->Lun]] =
						    NULL;
					} else {
						FPT_sccbMgrTbl[p_card]
						    [currSCCB->TargID].
						    TarLUNBusy[0] = 1;
						if (currSCCB->Sccb_tag) {
							if (FPT_BL_Card[p_card].
							    discQCount != 0)
								FPT_BL_Card
								    [p_card].
								    discQCount--;
							FPT_BL_Card[p_card].
							    discQ_Tbl[currSCCB->
								      Sccb_tag]
							    = NULL;
						} else {
							if (FPT_BL_Card[p_card].
							    discQCount != 0)
								FPT_BL_Card
								    [p_card].
								    discQCount--;
							FPT_BL_Card[p_card].
							    discQ_Tbl
							    [FPT_sccbMgrTbl
							     [p_card][currSCCB->
								      TargID].
							     LunDiscQ_Idx[0]] =
							    NULL;
						}
					}
					return;
				}
			}
		}
	}

	if ((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
	    ((FPT_sccbMgrTbl[p_card][currSCCB->TargID].
	      TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
		FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->
								    Lun] = 0;
	else
		FPT_sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = 0;

	FPT_queueCmdComplete(&FPT_BL_Card[p_card], currSCCB, p_card);
}

#define SHORT_WAIT   0x0000000F
#define LONG_WAIT    0x0000FFFFL

/*---------------------------------------------------------------------
 *
 * Function: Data Transfer Processor
 *
 * Description: This routine performs two tasks.
 *              (1) Start data transfer by calling HOST_DATA_XFER_START
 *              function.  Once data transfer is started, (2) Depends
 *              on the type of data transfer mode Scatter/Gather mode
 *              or NON Scatter/Gather mode.  In NON Scatter/Gather mode,
 *              this routine checks Sccb_MGRFlag (F_HOST_XFER_ACT bit) for
 *              data transfer done.  In Scatter/Gather mode, this routine
 *              checks bus master command complete and dual rank busy
 *              bit to keep chaining SC transfer command.  Similarly,
 *              in Scatter/Gather mode, it checks Sccb_MGRFlag
 *              (F_HOST_XFER_ACT bit) for data transfer done.
 *              
 *---------------------------------------------------------------------*/

static void FPT_dataXferProcessor(u32 port, struct sccb_card *pCurrCard)
{
	struct sccb *currSCCB;

	currSCCB = pCurrCard->currentSCCB;

	if (currSCCB->Sccb_XferState & F_SG_XFER) {
		if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
		{
			currSCCB->Sccb_sgseg += (unsigned char)SG_BUF_CNT;
			currSCCB->Sccb_SGoffset = 0x00;
		}
		pCurrCard->globalFlags |= F_HOST_XFER_ACT;

		FPT_busMstrSGDataXferStart(port, currSCCB);
	}

	else {
		if (!(pCurrCard->globalFlags & F_HOST_XFER_ACT)) {
			pCurrCard->globalFlags |= F_HOST_XFER_ACT;

			FPT_busMstrDataXferStart(port, currSCCB);
		}
	}
}

/*---------------------------------------------------------------------
 *
 * Function: BusMaster Scatter Gather Data Transfer Start
 *
 * Description:
 *
 *---------------------------------------------------------------------*/
static void FPT_busMstrSGDataXferStart(u32 p_port, struct sccb *pcurrSCCB)
{
	u32 count, addr, tmpSGCnt;
	unsigned int sg_index;
	unsigned char sg_count, i;
	u32 reg_offset;
	struct blogic_sg_seg *segp;

	if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR)
		count = ((u32)HOST_RD_CMD) << 24;
	else
		count = ((u32)HOST_WRT_CMD) << 24;

	sg_count = 0;
	tmpSGCnt = 0;
	sg_index = pcurrSCCB->Sccb_sgseg;
	reg_offset = hp_aramBase;

	i = (unsigned char)(RD_HARPOON(p_port + hp_page_ctrl) &
			    ~(SGRAM_ARAM | SCATTER_EN));

	WR_HARPOON(p_port + hp_page_ctrl, i);

	while ((sg_count < (unsigned char)SG_BUF_CNT) &&
			((sg_index * (unsigned int)SG_ELEMENT_SIZE) <
			pcurrSCCB->DataLength)) {

		segp = (struct blogic_sg_seg *)(pcurrSCCB->DataPointer) +
				sg_index;
		tmpSGCnt += segp->segbytes;
		count |= segp->segbytes;
		addr = segp->segdata;

		if ((!sg_count) && (pcurrSCCB->Sccb_SGoffset)) {
			addr +=
			    ((count & 0x00FFFFFFL) - pcurrSCCB->Sccb_SGoffset);
			count =
			    (count & 0xFF000000L) | pcurrSCCB->Sccb_SGoffset;
			tmpSGCnt = count & 0x00FFFFFFL;
		}

		WR_HARP32(p_port, reg_offset, addr);
		reg_offset += 4;

		WR_HARP32(p_port, reg_offset, count);
		reg_offset += 4;

		count &= 0xFF000000L;
		sg_index++;
		sg_count++;

	}			/*End While */

	pcurrSCCB->Sccb_XferCnt = tmpSGCnt;

	WR_HARPOON(p_port + hp_sg_addr, (sg_count << 4));

	if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {

		WR_HARP32(p_port, hp_xfercnt_0, tmpSGCnt);

		WR_HARPOON(p_port + hp_portctrl_0,
			   (DMA_PORT | SCSI_PORT | SCSI_INBIT));
		WR_HARPOON(p_port + hp_scsisig, S_DATAI_PH);
	}

	else {

		if ((!(RD_HARPOON(p_port + hp_synctarg_0) & NARROW_SCSI)) &&
		    (tmpSGCnt & 0x000000001)) {

			pcurrSCCB->Sccb_XferState |= F_ODD_BALL_CNT;
			tmpSGCnt--;
		}

		WR_HARP32(p_port, hp_xfercnt_0, tmpSGCnt);

		WR_HARPOON(p_port + hp_portctrl_0,
			   (SCSI_PORT | DMA_PORT | DMA_RD));
		WR_HARPOON(p_port + hp_scsisig, S_DATAO_PH);
	}

	WR_HARPOON(p_port + hp_page_ctrl, (unsigned char)(i | SCATTER_EN));

}

/*---------------------------------------------------------------------
 *
 * Function: BusMaster Data Transfer Start
 *
 * Description: 
 *
 *---------------------------------------------------------------------*/
static void FPT_busMstrDataXferStart(u32 p_port, struct sccb *pcurrSCCB)
{
	u32 addr, count;

	if (!(pcurrSCCB->Sccb_XferState & F_AUTO_SENSE)) {

		count = pcurrSCCB->Sccb_XferCnt;

		addr = (u32)(unsigned long)pcurrSCCB->DataPointer + pcurrSCCB->Sccb_ATC;
	}

	else {
		addr = pcurrSCCB->SensePointer;
		count = pcurrSCCB->RequestSenseLength;

	}

	HP_SETUP_ADDR_CNT(p_port, addr, count);

	if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {

		WR_HARPOON(p_port + hp_portctrl_0,
			   (DMA_PORT | SCSI_PORT | SCSI_INBIT));
		WR_HARPOON(p_port + hp_scsisig, S_DATAI_PH);

		WR_HARPOON(p_port + hp_xfer_cmd,
			   (XFER_DMA_HOST | XFER_HOST_AUTO | XFER_DMA_8BIT));
	}

	else {

		WR_HARPOON(p_port + hp_portctrl_0,
			   (SCSI_PORT | DMA_PORT | DMA_RD));
		WR_HARPOON(p_port + hp_scsisig, S_DATAO_PH);

		WR_HARPOON(p_port + hp_xfer_cmd,
			   (XFER_HOST_DMA | XFER_HOST_AUTO | XFER_DMA_8BIT));

	}
}

/*---------------------------------------------------------------------
 *
 * Function: BusMaster Timeout Handler
 *
 * Description: This function is called after a bus master command busy time
 *               out is detected.  This routines issue halt state machine
 *               with a software time out for command busy.  If command busy
 *               is still asserted at the end of the time out, it issues
 *               hard abort with another software time out.  It hard abort
 *               command busy is also time out, it'll just give up.
 *
 *---------------------------------------------------------------------*/
static unsigned char FPT_busMstrTimeOut(u32 p_port)
{
	unsigned long timeout;

	timeout = LONG_WAIT;

	WR_HARPOON(p_port + hp_sys_ctrl, HALT_MACH);

	while ((!(RD_HARPOON(p_port + hp_ext_status) & CMD_ABORTED))
	       && timeout--) {
	}

	if (RD_HARPOON(p_port + hp_ext_status) & BM_CMD_BUSY) {
		WR_HARPOON(p_port + hp_sys_ctrl, HARD_ABORT);

		timeout = LONG_WAIT;
		while ((RD_HARPOON(p_port + hp_ext_status) & BM_CMD_BUSY)
		       && timeout--) {
		}
	}

	RD_HARPOON(p_port + hp_int_status);	/*Clear command complete */

	if (RD_HARPOON(p_port + hp_ext_status) & BM_CMD_BUSY) {
		return 1;
	}

	else {
		return 0;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: Host Data Transfer Abort
 *
 * Description: Abort any in progress transfer.
 *
 *---------------------------------------------------------------------*/
static void FPT_hostDataXferAbort(u32 port, unsigned char p_card,
				  struct sccb *pCurrSCCB)
{

	unsigned long timeout;
	unsigned long remain_cnt;
	u32 sg_ptr;
	struct blogic_sg_seg *segp;

	FPT_BL_Card[p_card].globalFlags &= ~F_HOST_XFER_ACT;

	if (pCurrSCCB->Sccb_XferState & F_AUTO_SENSE) {

		if (!(RD_HARPOON(port + hp_int_status) & INT_CMD_COMPL)) {

			WR_HARPOON(port + hp_bm_ctrl,
				   (RD_HARPOON(port + hp_bm_ctrl) |
				    FLUSH_XFER_CNTR));
			timeout = LONG_WAIT;

			while ((RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY)
			       && timeout--) {
			}

			WR_HARPOON(port + hp_bm_ctrl,
				   (RD_HARPOON(port + hp_bm_ctrl) &
				    ~FLUSH_XFER_CNTR));

			if (RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY) {

				if (FPT_busMstrTimeOut(port)) {

					if (pCurrSCCB->HostStatus == 0x00)

						pCurrSCCB->HostStatus =
						    SCCB_BM_ERR;

				}

				if (RD_HARPOON(port + hp_int_status) &
				    INT_EXT_STATUS)

					if (RD_HARPOON(port + hp_ext_status) &
					    BAD_EXT_STATUS)

						if (pCurrSCCB->HostStatus ==
						    0x00)
						{
							pCurrSCCB->HostStatus =
							    SCCB_BM_ERR;
						}
			}
		}
	}

	else if (pCurrSCCB->Sccb_XferCnt) {

		if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {

			WR_HARPOON(port + hp_page_ctrl,
				   (RD_HARPOON(port + hp_page_ctrl) &
				    ~SCATTER_EN));

			WR_HARPOON(port + hp_sg_addr, 0x00);

			sg_ptr = pCurrSCCB->Sccb_sgseg + SG_BUF_CNT;

			if (sg_ptr >
			    (unsigned int)(pCurrSCCB->DataLength /
					   SG_ELEMENT_SIZE)) {

				sg_ptr = (u32)(pCurrSCCB->DataLength /
							SG_ELEMENT_SIZE);
			}

			remain_cnt = pCurrSCCB->Sccb_XferCnt;

			while (remain_cnt < 0x01000000L) {

				sg_ptr--;
				segp = (struct blogic_sg_seg *)(pCurrSCCB->
						DataPointer) + (sg_ptr * 2);
				if (remain_cnt > (unsigned long)segp->segbytes)
					remain_cnt -=
						(unsigned long)segp->segbytes;
				else
					break;
			}

			if (remain_cnt < 0x01000000L) {

				pCurrSCCB->Sccb_SGoffset = remain_cnt;

				pCurrSCCB->Sccb_sgseg = (unsigned short)sg_ptr;

				if ((unsigned long)(sg_ptr * SG_ELEMENT_SIZE) ==
				    pCurrSCCB->DataLength && (remain_cnt == 0))

					pCurrSCCB->Sccb_XferState |=
					    F_ALL_XFERRED;
			}

			else {

				if (pCurrSCCB->HostStatus == 0x00) {

					pCurrSCCB->HostStatus =
					    SCCB_GROSS_FW_ERR;
				}
			}
		}

		if (!(pCurrSCCB->Sccb_XferState & F_HOST_XFER_DIR)) {

			if (RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY) {

				FPT_busMstrTimeOut(port);
			}

			else {

				if (RD_HARPOON(port + hp_int_status) &
				    INT_EXT_STATUS) {

					if (RD_HARPOON(port + hp_ext_status) &
					    BAD_EXT_STATUS) {

						if (pCurrSCCB->HostStatus ==
						    0x00) {

							pCurrSCCB->HostStatus =
							    SCCB_BM_ERR;
						}
					}
				}

			}
		}

		else {

			if ((RD_HARPOON(port + hp_fifo_cnt)) >= BM_THRESHOLD) {

				timeout = SHORT_WAIT;

				while ((RD_HARPOON(port + hp_ext_status) &
					BM_CMD_BUSY)
				       && ((RD_HARPOON(port + hp_fifo_cnt)) >=
					   BM_THRESHOLD) && timeout--) {
				}
			}

			if (RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY) {

				WR_HARPOON(port + hp_bm_ctrl,
					   (RD_HARPOON(port + hp_bm_ctrl) |
					    FLUSH_XFER_CNTR));

				timeout = LONG_WAIT;

				while ((RD_HARPOON(port + hp_ext_status) &
					BM_CMD_BUSY) && timeout--) {
				}

				WR_HARPOON(port + hp_bm_ctrl,
					   (RD_HARPOON(port + hp_bm_ctrl) &
					    ~FLUSH_XFER_CNTR));

				if (RD_HARPOON(port + hp_ext_status) &
				    BM_CMD_BUSY) {

					if (pCurrSCCB->HostStatus == 0x00) {

						pCurrSCCB->HostStatus =
						    SCCB_BM_ERR;
					}

					FPT_busMstrTimeOut(port);
				}
			}

			if (RD_HARPOON(port + hp_int_status) & INT_EXT_STATUS) {

				if (RD_HARPOON(port + hp_ext_status) &
				    BAD_EXT_STATUS) {

					if (pCurrSCCB->HostStatus == 0x00) {

						pCurrSCCB->HostStatus =
						    SCCB_BM_ERR;
					}
				}
			}
		}

	}

	else {

		if (RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY) {

			timeout = LONG_WAIT;

			while ((RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY)
			       && timeout--) {
			}

			if (RD_HARPOON(port + hp_ext_status) & BM_CMD_BUSY) {

				if (pCurrSCCB->HostStatus == 0x00) {

					pCurrSCCB->HostStatus = SCCB_BM_ERR;
				}

				FPT_busMstrTimeOut(port);
			}
		}

		if (RD_HARPOON(port + hp_int_status) & INT_EXT_STATUS) {

			if (RD_HARPOON(port + hp_ext_status) & BAD_EXT_STATUS) {

				if (pCurrSCCB->HostStatus == 0x00) {

					pCurrSCCB->HostStatus = SCCB_BM_ERR;
				}
			}

		}

		if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {

			WR_HARPOON(port + hp_page_ctrl,
				   (RD_HARPOON(port + hp_page_ctrl) &
				    ~SCATTER_EN));

			WR_HARPOON(port + hp_sg_addr, 0x00);

			pCurrSCCB->Sccb_sgseg += SG_BUF_CNT;

			pCurrSCCB->Sccb_SGoffset = 0x00;

			if ((u32)(pCurrSCCB->Sccb_sgseg * SG_ELEMENT_SIZE) >=
					pCurrSCCB->DataLength) {

				pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
				pCurrSCCB->Sccb_sgseg =
				    (unsigned short)(pCurrSCCB->DataLength /
						     SG_ELEMENT_SIZE);
			}
		}

		else {
			if (!(pCurrSCCB->Sccb_XferState & F_AUTO_SENSE))
				pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
		}
	}

	WR_HARPOON(port + hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
}

/*---------------------------------------------------------------------
 *
 * Function: Host Data Transfer Restart
 *
 * Description: Reset the available count due to a restore data
 *              pointers message.
 *
 *---------------------------------------------------------------------*/
static void FPT_hostDataXferRestart(struct sccb *currSCCB)
{
	unsigned long data_count;
	unsigned int sg_index;
	struct blogic_sg_seg *segp;

	if (currSCCB->Sccb_XferState & F_SG_XFER) {

		currSCCB->Sccb_XferCnt = 0;

		sg_index = 0xffff;	/*Index by long words into sg list. */
		data_count = 0;		/*Running count of SG xfer counts. */


		while (data_count < currSCCB->Sccb_ATC) {

			sg_index++;
			segp = (struct blogic_sg_seg *)(currSCCB->DataPointer) +
						(sg_index * 2);
			data_count += segp->segbytes;
		}

		if (data_count == currSCCB->Sccb_ATC) {

			currSCCB->Sccb_SGoffset = 0;
			sg_index++;
		}

		else {
			currSCCB->Sccb_SGoffset =
			    data_count - currSCCB->Sccb_ATC;
		}

		currSCCB->Sccb_sgseg = (unsigned short)sg_index;
	}

	else {
		currSCCB->Sccb_XferCnt =
		    currSCCB->DataLength - currSCCB->Sccb_ATC;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scini
 *
 * Description: Setup all data structures necessary for SCAM selection.
 *
 *---------------------------------------------------------------------*/

static void FPT_scini(unsigned char p_card, unsigned char p_our_id,
		      unsigned char p_power_up)
{

	unsigned char loser, assigned_id;
	u32 p_port;

	unsigned char i, k, ScamFlg;
	struct sccb_card *currCard;
	struct nvram_info *pCurrNvRam;

	currCard = &FPT_BL_Card[p_card];
	p_port = currCard->ioPort;
	pCurrNvRam = currCard->pNvRamInfo;

	if (pCurrNvRam) {
		ScamFlg = pCurrNvRam->niScamConf;
		i = pCurrNvRam->niSysConf;
	} else {
		ScamFlg =
		    (unsigned char)FPT_utilEERead(p_port, SCAM_CONFIG / 2);
		i = (unsigned
		     char)(FPT_utilEERead(p_port, (SYSTEM_CONFIG / 2)));
	}
	if (!(i & 0x02))	/* check if reset bus in AutoSCSI parameter set */
		return;

	FPT_inisci(p_card, p_port, p_our_id);

	/* Force to wait 1 sec after SCSI bus reset. Some SCAM device FW
	   too slow to return to SCAM selection */

	/* if (p_power_up)
	   FPT_Wait1Second(p_port);
	   else
	   FPT_Wait(p_port, TO_250ms); */

	FPT_Wait1Second(p_port);

	if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2)) {
		while (!(FPT_scarb(p_port, INIT_SELTD))) {
		}

		FPT_scsel(p_port);

		do {
			FPT_scxferc(p_port, SYNC_PTRN);
			FPT_scxferc(p_port, DOM_MSTR);
			loser =
			    FPT_scsendi(p_port,
					&FPT_scamInfo[p_our_id].id_string[0]);
		} while (loser == 0xFF);

		FPT_scbusf(p_port);

		if ((p_power_up) && (!loser)) {
			FPT_sresb(p_port, p_card);
			FPT_Wait(p_port, TO_250ms);

			while (!(FPT_scarb(p_port, INIT_SELTD))) {
			}

			FPT_scsel(p_port);

			do {
				FPT_scxferc(p_port, SYNC_PTRN);
				FPT_scxferc(p_port, DOM_MSTR);
				loser =
				    FPT_scsendi(p_port,
						&FPT_scamInfo[p_our_id].
						id_string[0]);
			} while (loser == 0xFF);

			FPT_scbusf(p_port);
		}
	}

	else {
		loser = 0;
	}

	if (!loser) {

		FPT_scamInfo[p_our_id].state = ID_ASSIGNED;

		if (ScamFlg & SCAM_ENABLED) {

			for (i = 0; i < MAX_SCSI_TAR; i++) {
				if ((FPT_scamInfo[i].state == ID_UNASSIGNED) ||
				    (FPT_scamInfo[i].state == ID_UNUSED)) {
					if (FPT_scsell(p_port, i)) {
						FPT_scamInfo[i].state = LEGACY;
						if ((FPT_scamInfo[i].
						     id_string[0] != 0xFF)
						    || (FPT_scamInfo[i].
							id_string[1] != 0xFA)) {

							FPT_scamInfo[i].
							    id_string[0] = 0xFF;
							FPT_scamInfo[i].
							    id_string[1] = 0xFA;
							if (pCurrNvRam == NULL)
								currCard->
								    globalFlags
								    |=
								    F_UPDATE_EEPROM;
						}
					}
				}
			}

			FPT_sresb(p_port, p_card);
			FPT_Wait1Second(p_port);
			while (!(FPT_scarb(p_port, INIT_SELTD))) {
			}
			FPT_scsel(p_port);
			FPT_scasid(p_card, p_port);
		}

	}

	else if ((loser) && (ScamFlg & SCAM_ENABLED)) {
		FPT_scamInfo[p_our_id].id_string[0] = SLV_TYPE_CODE0;
		assigned_id = 0;
		FPT_scwtsel(p_port);

		do {
			while (FPT_scxferc(p_port, 0x00) != SYNC_PTRN) {
			}

			i = FPT_scxferc(p_port, 0x00);
			if (i == ASSIGN_ID) {
				if (!
				    (FPT_scsendi
				     (p_port,
				      &FPT_scamInfo[p_our_id].id_string[0]))) {
					i = FPT_scxferc(p_port, 0x00);
					if (FPT_scvalq(i)) {
						k = FPT_scxferc(p_port, 0x00);

						if (FPT_scvalq(k)) {
							currCard->ourId =
							    ((unsigned char)(i
									     <<
									     3)
							     +
							     (k &
							      (unsigned char)7))
							    & (unsigned char)
							    0x3F;
							FPT_inisci(p_card,
								   p_port,
								   p_our_id);
							FPT_scamInfo[currCard->
								     ourId].
							    state = ID_ASSIGNED;
							FPT_scamInfo[currCard->
								     ourId].
							    id_string[0]
							    = SLV_TYPE_CODE0;
							assigned_id = 1;
						}
					}
				}
			}

			else if (i == SET_P_FLAG) {
				if (!(FPT_scsendi(p_port,
						  &FPT_scamInfo[p_our_id].
						  id_string[0])))
					FPT_scamInfo[p_our_id].id_string[0] |=
					    0x80;
			}
		} while (!assigned_id);

		while (FPT_scxferc(p_port, 0x00) != CFG_CMPLT) {
		}
	}

	if (ScamFlg & SCAM_ENABLED) {
		FPT_scbusf(p_port);
		if (currCard->globalFlags & F_UPDATE_EEPROM) {
			FPT_scsavdi(p_card, p_port);
			currCard->globalFlags &= ~F_UPDATE_EEPROM;
		}
	}

/*
   for (i=0,k=0; i < MAX_SCSI_TAR; i++)
      {
      if ((FPT_scamInfo[i].state == ID_ASSIGNED) ||
         (FPT_scamInfo[i].state == LEGACY))
         k++;
      }

   if (k==2)
      currCard->globalFlags |= F_SINGLE_DEVICE;
   else
      currCard->globalFlags &= ~F_SINGLE_DEVICE;
*/
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scarb
 *
 * Description: Gain control of the bus and wait SCAM select time (250ms)
 *
 *---------------------------------------------------------------------*/

static int FPT_scarb(u32 p_port, unsigned char p_sel_type)
{
	if (p_sel_type == INIT_SELTD) {

		while (RD_HARPOON(p_port + hp_scsisig) & (SCSI_SEL | SCSI_BSY)) {
		}

		if (RD_HARPOON(p_port + hp_scsisig) & SCSI_SEL)
			return 0;

		if (RD_HARPOON(p_port + hp_scsidata_0) != 00)
			return 0;

		WR_HARPOON(p_port + hp_scsisig,
			   (RD_HARPOON(p_port + hp_scsisig) | SCSI_BSY));

		if (RD_HARPOON(p_port + hp_scsisig) & SCSI_SEL) {

			WR_HARPOON(p_port + hp_scsisig,
				   (RD_HARPOON(p_port + hp_scsisig) &
				    ~SCSI_BSY));
			return 0;
		}

		WR_HARPOON(p_port + hp_scsisig,
			   (RD_HARPOON(p_port + hp_scsisig) | SCSI_SEL));

		if (RD_HARPOON(p_port + hp_scsidata_0) != 00) {

			WR_HARPOON(p_port + hp_scsisig,
				   (RD_HARPOON(p_port + hp_scsisig) &
				    ~(SCSI_BSY | SCSI_SEL)));
			return 0;
		}
	}

	WR_HARPOON(p_port + hp_clkctrl_0, (RD_HARPOON(p_port + hp_clkctrl_0)
					   & ~ACTdeassert));
	WR_HARPOON(p_port + hp_scsireset, SCAM_EN);
	WR_HARPOON(p_port + hp_scsidata_0, 0x00);
	WR_HARPOON(p_port + hp_scsidata_1, 0x00);
	WR_HARPOON(p_port + hp_portctrl_0, SCSI_BUS_EN);

	WR_HARPOON(p_port + hp_scsisig,
		   (RD_HARPOON(p_port + hp_scsisig) | SCSI_MSG));

	WR_HARPOON(p_port + hp_scsisig, (RD_HARPOON(p_port + hp_scsisig)
					 & ~SCSI_BSY));

	FPT_Wait(p_port, TO_250ms);

	return 1;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scbusf
 *
 * Description: Release the SCSI bus and disable SCAM selection.
 *
 *---------------------------------------------------------------------*/

static void FPT_scbusf(u32 p_port)
{
	WR_HARPOON(p_port + hp_page_ctrl,
		   (RD_HARPOON(p_port + hp_page_ctrl) | G_INT_DISABLE));

	WR_HARPOON(p_port + hp_scsidata_0, 0x00);

	WR_HARPOON(p_port + hp_portctrl_0, (RD_HARPOON(p_port + hp_portctrl_0)
					    & ~SCSI_BUS_EN));

	WR_HARPOON(p_port + hp_scsisig, 0x00);

	WR_HARPOON(p_port + hp_scsireset, (RD_HARPOON(p_port + hp_scsireset)
					   & ~SCAM_EN));

	WR_HARPOON(p_port + hp_clkctrl_0, (RD_HARPOON(p_port + hp_clkctrl_0)
					   | ACTdeassert));

	WRW_HARPOON((p_port + hp_intstat), (BUS_FREE | AUTO_INT | SCAM_SEL));

	WR_HARPOON(p_port + hp_page_ctrl,
		   (RD_HARPOON(p_port + hp_page_ctrl) & ~G_INT_DISABLE));
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scasid
 *
 * Description: Assign an ID to all the SCAM devices.
 *
 *---------------------------------------------------------------------*/

static void FPT_scasid(unsigned char p_card, u32 p_port)
{
	unsigned char temp_id_string[ID_STRING_LENGTH];

	unsigned char i, k, scam_id;
	unsigned char crcBytes[3];
	struct nvram_info *pCurrNvRam;
	unsigned short *pCrcBytes;

	pCurrNvRam = FPT_BL_Card[p_card].pNvRamInfo;

	i = 0;

	while (!i) {

		for (k = 0; k < ID_STRING_LENGTH; k++) {
			temp_id_string[k] = (unsigned char)0x00;
		}

		FPT_scxferc(p_port, SYNC_PTRN);
		FPT_scxferc(p_port, ASSIGN_ID);

		if (!(FPT_sciso(p_port, &temp_id_string[0]))) {
			if (pCurrNvRam) {
				pCrcBytes = (unsigned short *)&crcBytes[0];
				*pCrcBytes = FPT_CalcCrc16(&temp_id_string[0]);
				crcBytes[2] = FPT_CalcLrc(&temp_id_string[0]);
				temp_id_string[1] = crcBytes[2];
				temp_id_string[2] = crcBytes[0];
				temp_id_string[3] = crcBytes[1];
				for (k = 4; k < ID_STRING_LENGTH; k++)
					temp_id_string[k] = (unsigned char)0x00;
			}
			i = FPT_scmachid(p_card, temp_id_string);

			if (i == CLR_PRIORITY) {
				FPT_scxferc(p_port, MISC_CODE);
				FPT_scxferc(p_port, CLR_P_FLAG);
				i = 0;	/*Not the last ID yet. */
			}

			else if (i != NO_ID_AVAIL) {
				if (i < 8)
					FPT_scxferc(p_port, ID_0_7);
				else
					FPT_scxferc(p_port, ID_8_F);

				scam_id = (i & (unsigned char)0x07);

				for (k = 1; k < 0x08; k <<= 1)
					if (!(k & i))
						scam_id += 0x08;	/*Count number of zeros in DB0-3. */

				FPT_scxferc(p_port, scam_id);

				i = 0;	/*Not the last ID yet. */
			}
		}

		else {
			i = 1;
		}

	}			/*End while */

	FPT_scxferc(p_port, SYNC_PTRN);
	FPT_scxferc(p_port, CFG_CMPLT);
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scsel
 *
 * Description: Select all the SCAM devices.
 *
 *---------------------------------------------------------------------*/

static void FPT_scsel(u32 p_port)
{

	WR_HARPOON(p_port + hp_scsisig, SCSI_SEL);
	FPT_scwiros(p_port, SCSI_MSG);

	WR_HARPOON(p_port + hp_scsisig, (SCSI_SEL | SCSI_BSY));

	WR_HARPOON(p_port + hp_scsisig,
		   (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
	WR_HARPOON(p_port + hp_scsidata_0,
		   (unsigned char)(RD_HARPOON(p_port + hp_scsidata_0) |
				   (unsigned char)(BIT(7) + BIT(6))));

	WR_HARPOON(p_port + hp_scsisig, (SCSI_BSY | SCSI_IOBIT | SCSI_CD));
	FPT_scwiros(p_port, SCSI_SEL);

	WR_HARPOON(p_port + hp_scsidata_0,
		   (unsigned char)(RD_HARPOON(p_port + hp_scsidata_0) &
				   ~(unsigned char)BIT(6)));
	FPT_scwirod(p_port, BIT(6));

	WR_HARPOON(p_port + hp_scsisig,
		   (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scxferc
 *
 * Description: Handshake the p_data (DB4-0) across the bus.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_scxferc(u32 p_port, unsigned char p_data)
{
	unsigned char curr_data, ret_data;

	curr_data = p_data | BIT(7) | BIT(5);	/*Start with DB7 & DB5 asserted. */

	WR_HARPOON(p_port + hp_scsidata_0, curr_data);

	curr_data &= ~BIT(7);

	WR_HARPOON(p_port + hp_scsidata_0, curr_data);

	FPT_scwirod(p_port, BIT(7));	/*Wait for DB7 to be released. */
	while (!(RD_HARPOON(p_port + hp_scsidata_0) & BIT(5))) ;

	ret_data = (RD_HARPOON(p_port + hp_scsidata_0) & (unsigned char)0x1F);

	curr_data |= BIT(6);

	WR_HARPOON(p_port + hp_scsidata_0, curr_data);

	curr_data &= ~BIT(5);

	WR_HARPOON(p_port + hp_scsidata_0, curr_data);

	FPT_scwirod(p_port, BIT(5));	/*Wait for DB5 to be released. */

	curr_data &= ~(BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0));	/*Release data bits */
	curr_data |= BIT(7);

	WR_HARPOON(p_port + hp_scsidata_0, curr_data);

	curr_data &= ~BIT(6);

	WR_HARPOON(p_port + hp_scsidata_0, curr_data);

	FPT_scwirod(p_port, BIT(6));	/*Wait for DB6 to be released. */

	return ret_data;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scsendi
 *
 * Description: Transfer our Identification string to determine if we
 *              will be the dominant master.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_scsendi(u32 p_port, unsigned char p_id_string[])
{
	unsigned char ret_data, byte_cnt, bit_cnt, defer;

	defer = 0;

	for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {

		for (bit_cnt = 0x80; bit_cnt != 0; bit_cnt >>= 1) {

			if (defer)
				ret_data = FPT_scxferc(p_port, 00);

			else if (p_id_string[byte_cnt] & bit_cnt)

				ret_data = FPT_scxferc(p_port, 02);

			else {

				ret_data = FPT_scxferc(p_port, 01);
				if (ret_data & 02)
					defer = 1;
			}

			if ((ret_data & 0x1C) == 0x10)
				return 0x00;	/*End of isolation stage, we won! */

			if (ret_data & 0x1C)
				return 0xFF;

			if ((defer) && (!(ret_data & 0x1F)))
				return 0x01;	/*End of isolation stage, we lost. */

		}		/*bit loop */

	}			/*byte loop */

	if (defer)
		return 0x01;	/*We lost */
	else
		return 0;	/*We WON! Yeeessss! */
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_sciso
 *
 * Description: Transfer the Identification string.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_sciso(u32 p_port, unsigned char p_id_string[])
{
	unsigned char ret_data, the_data, byte_cnt, bit_cnt;

	the_data = 0;

	for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {

		for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) {

			ret_data = FPT_scxferc(p_port, 0);

			if (ret_data & 0xFC)
				return 0xFF;

			else {

				the_data <<= 1;
				if (ret_data & BIT(1)) {
					the_data |= 1;
				}
			}

			if ((ret_data & 0x1F) == 0) {
/*
				if(bit_cnt != 0 || bit_cnt != 8)
				{
					byte_cnt = 0;
					bit_cnt = 0;
					FPT_scxferc(p_port, SYNC_PTRN);
					FPT_scxferc(p_port, ASSIGN_ID);
					continue;
				}
*/
				if (byte_cnt)
					return 0x00;
				else
					return 0xFF;
			}

		}		/*bit loop */

		p_id_string[byte_cnt] = the_data;

	}			/*byte loop */

	return 0;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scwirod
 *
 * Description: Sample the SCSI data bus making sure the signal has been
 *              deasserted for the correct number of consecutive samples.
 *
 *---------------------------------------------------------------------*/

static void FPT_scwirod(u32 p_port, unsigned char p_data_bit)
{
	unsigned char i;

	i = 0;
	while (i < MAX_SCSI_TAR) {

		if (RD_HARPOON(p_port + hp_scsidata_0) & p_data_bit)

			i = 0;

		else

			i++;

	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scwiros
 *
 * Description: Sample the SCSI Signal lines making sure the signal has been
 *              deasserted for the correct number of consecutive samples.
 *
 *---------------------------------------------------------------------*/

static void FPT_scwiros(u32 p_port, unsigned char p_data_bit)
{
	unsigned char i;

	i = 0;
	while (i < MAX_SCSI_TAR) {

		if (RD_HARPOON(p_port + hp_scsisig) & p_data_bit)

			i = 0;

		else

			i++;

	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scvalq
 *
 * Description: Make sure we received a valid data byte.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_scvalq(unsigned char p_quintet)
{
	unsigned char count;

	for (count = 1; count < 0x08; count <<= 1) {
		if (!(p_quintet & count))
			p_quintet -= 0x80;
	}

	if (p_quintet & 0x18)
		return 0;

	else
		return 1;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scsell
 *
 * Description: Select the specified device ID using a selection timeout
 *              less than 4ms.  If somebody responds then it is a legacy
 *              drive and this ID must be marked as such.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_scsell(u32 p_port, unsigned char targ_id)
{
	unsigned long i;

	WR_HARPOON(p_port + hp_page_ctrl,
		   (RD_HARPOON(p_port + hp_page_ctrl) | G_INT_DISABLE));

	ARAM_ACCESS(p_port);

	WR_HARPOON(p_port + hp_addstat,
		   (RD_HARPOON(p_port + hp_addstat) | SCAM_TIMER));
	WR_HARPOON(p_port + hp_seltimeout, TO_4ms);

	for (i = p_port + CMD_STRT; i < p_port + CMD_STRT + 12; i += 2) {
		WRW_HARPOON(i, (MPM_OP + ACOMMAND));
	}
	WRW_HARPOON(i, (BRH_OP + ALWAYS + NP));

	WRW_HARPOON((p_port + hp_intstat),
		    (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT));

	WR_HARPOON(p_port + hp_select_id, targ_id);

	WR_HARPOON(p_port + hp_portctrl_0, SCSI_PORT);
	WR_HARPOON(p_port + hp_autostart_3, (SELECT | CMD_ONLY_STRT));
	WR_HARPOON(p_port + hp_scsictrl_0, (SEL_TAR | ENA_RESEL));

	while (!(RDW_HARPOON((p_port + hp_intstat)) &
		 (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {
	}

	if (RDW_HARPOON((p_port + hp_intstat)) & RESET)
		FPT_Wait(p_port, TO_250ms);

	DISABLE_AUTO(p_port);

	WR_HARPOON(p_port + hp_addstat,
		   (RD_HARPOON(p_port + hp_addstat) & ~SCAM_TIMER));
	WR_HARPOON(p_port + hp_seltimeout, TO_290ms);

	SGRAM_ACCESS(p_port);

	if (RDW_HARPOON((p_port + hp_intstat)) & (RESET | TIMEOUT)) {

		WRW_HARPOON((p_port + hp_intstat),
			    (RESET | TIMEOUT | SEL | BUS_FREE | PHASE));

		WR_HARPOON(p_port + hp_page_ctrl,
			   (RD_HARPOON(p_port + hp_page_ctrl) &
			    ~G_INT_DISABLE));

		return 0;	/*No legacy device */
	}

	else {

		while (!(RDW_HARPOON((p_port + hp_intstat)) & BUS_FREE)) {
			if (RD_HARPOON(p_port + hp_scsisig) & SCSI_REQ) {
				WR_HARPOON(p_port + hp_scsisig,
					   (SCSI_ACK + S_ILL_PH));
				ACCEPT_MSG(p_port);
			}
		}

		WRW_HARPOON((p_port + hp_intstat), CLR_ALL_INT_1);

		WR_HARPOON(p_port + hp_page_ctrl,
			   (RD_HARPOON(p_port + hp_page_ctrl) &
			    ~G_INT_DISABLE));

		return 1;	/*Found one of them oldies! */
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scwtsel
 *
 * Description: Wait to be selected by another SCAM initiator.
 *
 *---------------------------------------------------------------------*/

static void FPT_scwtsel(u32 p_port)
{
	while (!(RDW_HARPOON((p_port + hp_intstat)) & SCAM_SEL)) {
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_inisci
 *
 * Description: Setup the data Structure with the info from the EEPROM.
 *
 *---------------------------------------------------------------------*/

static void FPT_inisci(unsigned char p_card, u32 p_port, unsigned char p_our_id)
{
	unsigned char i, k, max_id;
	unsigned short ee_data;
	struct nvram_info *pCurrNvRam;

	pCurrNvRam = FPT_BL_Card[p_card].pNvRamInfo;

	if (RD_HARPOON(p_port + hp_page_ctrl) & NARROW_SCSI_CARD)
		max_id = 0x08;

	else
		max_id = 0x10;

	if (pCurrNvRam) {
		for (i = 0; i < max_id; i++) {

			for (k = 0; k < 4; k++)
				FPT_scamInfo[i].id_string[k] =
				    pCurrNvRam->niScamTbl[i][k];
			for (k = 4; k < ID_STRING_LENGTH; k++)
				FPT_scamInfo[i].id_string[k] =
				    (unsigned char)0x00;

			if (FPT_scamInfo[i].id_string[0] == 0x00)
				FPT_scamInfo[i].state = ID_UNUSED;	/*Default to unused ID. */
			else
				FPT_scamInfo[i].state = ID_UNASSIGNED;	/*Default to unassigned ID. */

		}
	} else {
		for (i = 0; i < max_id; i++) {
			for (k = 0; k < ID_STRING_LENGTH; k += 2) {
				ee_data =
				    FPT_utilEERead(p_port,
						   (unsigned
						    short)((EE_SCAMBASE / 2) +
							   (unsigned short)(i *
									    ((unsigned short)ID_STRING_LENGTH / 2)) + (unsigned short)(k / 2)));
				FPT_scamInfo[i].id_string[k] =
				    (unsigned char)ee_data;
				ee_data >>= 8;
				FPT_scamInfo[i].id_string[k + 1] =
				    (unsigned char)ee_data;
			}

			if ((FPT_scamInfo[i].id_string[0] == 0x00) ||
			    (FPT_scamInfo[i].id_string[0] == 0xFF))

				FPT_scamInfo[i].state = ID_UNUSED;	/*Default to unused ID. */

			else
				FPT_scamInfo[i].state = ID_UNASSIGNED;	/*Default to unassigned ID. */

		}
	}
	for (k = 0; k < ID_STRING_LENGTH; k++)
		FPT_scamInfo[p_our_id].id_string[k] = FPT_scamHAString[k];

}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scmachid
 *
 * Description: Match the Device ID string with our values stored in
 *              the EEPROM.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_scmachid(unsigned char p_card,
				  unsigned char p_id_string[])
{

	unsigned char i, k, match;

	for (i = 0; i < MAX_SCSI_TAR; i++) {

		match = 1;

		for (k = 0; k < ID_STRING_LENGTH; k++) {
			if (p_id_string[k] != FPT_scamInfo[i].id_string[k])
				match = 0;
		}

		if (match) {
			FPT_scamInfo[i].state = ID_ASSIGNED;
			return i;
		}

	}

	if (p_id_string[0] & BIT(5))
		i = 8;
	else
		i = MAX_SCSI_TAR;

	if (((p_id_string[0] & 0x06) == 0x02)
	    || ((p_id_string[0] & 0x06) == 0x04))
		match = p_id_string[1] & (unsigned char)0x1F;
	else
		match = 7;

	while (i > 0) {
		i--;

		if (FPT_scamInfo[match].state == ID_UNUSED) {
			for (k = 0; k < ID_STRING_LENGTH; k++) {
				FPT_scamInfo[match].id_string[k] =
				    p_id_string[k];
			}

			FPT_scamInfo[match].state = ID_ASSIGNED;

			if (FPT_BL_Card[p_card].pNvRamInfo == NULL)
				FPT_BL_Card[p_card].globalFlags |=
				    F_UPDATE_EEPROM;
			return match;

		}

		match--;

		if (match == 0xFF) {
			if (p_id_string[0] & BIT(5))
				match = 7;
			else
				match = MAX_SCSI_TAR - 1;
		}
	}

	if (p_id_string[0] & BIT(7)) {
		return CLR_PRIORITY;
	}

	if (p_id_string[0] & BIT(5))
		i = 8;
	else
		i = MAX_SCSI_TAR;

	if (((p_id_string[0] & 0x06) == 0x02)
	    || ((p_id_string[0] & 0x06) == 0x04))
		match = p_id_string[1] & (unsigned char)0x1F;
	else
		match = 7;

	while (i > 0) {

		i--;

		if (FPT_scamInfo[match].state == ID_UNASSIGNED) {
			for (k = 0; k < ID_STRING_LENGTH; k++) {
				FPT_scamInfo[match].id_string[k] =
				    p_id_string[k];
			}

			FPT_scamInfo[match].id_string[0] |= BIT(7);
			FPT_scamInfo[match].state = ID_ASSIGNED;
			if (FPT_BL_Card[p_card].pNvRamInfo == NULL)
				FPT_BL_Card[p_card].globalFlags |=
				    F_UPDATE_EEPROM;
			return match;

		}

		match--;

		if (match == 0xFF) {
			if (p_id_string[0] & BIT(5))
				match = 7;
			else
				match = MAX_SCSI_TAR - 1;
		}
	}

	return NO_ID_AVAIL;
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_scsavdi
 *
 * Description: Save off the device SCAM ID strings.
 *
 *---------------------------------------------------------------------*/

static void FPT_scsavdi(unsigned char p_card, u32 p_port)
{
	unsigned char i, k, max_id;
	unsigned short ee_data, sum_data;

	sum_data = 0x0000;

	for (i = 1; i < EE_SCAMBASE / 2; i++) {
		sum_data += FPT_utilEERead(p_port, i);
	}

	FPT_utilEEWriteOnOff(p_port, 1);	/* Enable write access to the EEPROM */

	if (RD_HARPOON(p_port + hp_page_ctrl) & NARROW_SCSI_CARD)
		max_id = 0x08;

	else
		max_id = 0x10;

	for (i = 0; i < max_id; i++) {

		for (k = 0; k < ID_STRING_LENGTH; k += 2) {
			ee_data = FPT_scamInfo[i].id_string[k + 1];
			ee_data <<= 8;
			ee_data |= FPT_scamInfo[i].id_string[k];
			sum_data += ee_data;
			FPT_utilEEWrite(p_port, ee_data,
					(unsigned short)((EE_SCAMBASE / 2) +
							 (unsigned short)(i *
									  ((unsigned short)ID_STRING_LENGTH / 2)) + (unsigned short)(k / 2)));
		}
	}

	FPT_utilEEWrite(p_port, sum_data, EEPROM_CHECK_SUM / 2);
	FPT_utilEEWriteOnOff(p_port, 0);	/* Turn off write access */
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_XbowInit
 *
 * Description: Setup the Xbow for normal operation.
 *
 *---------------------------------------------------------------------*/

static void FPT_XbowInit(u32 port, unsigned char ScamFlg)
{
	unsigned char i;

	i = RD_HARPOON(port + hp_page_ctrl);
	WR_HARPOON(port + hp_page_ctrl, (unsigned char)(i | G_INT_DISABLE));

	WR_HARPOON(port + hp_scsireset, 0x00);
	WR_HARPOON(port + hp_portctrl_1, HOST_MODE8);

	WR_HARPOON(port + hp_scsireset, (DMA_RESET | HPSCSI_RESET | PROG_RESET |
					 FIFO_CLR));

	WR_HARPOON(port + hp_scsireset, SCSI_INI);

	WR_HARPOON(port + hp_clkctrl_0, CLKCTRL_DEFAULT);

	WR_HARPOON(port + hp_scsisig, 0x00);	/*  Clear any signals we might */
	WR_HARPOON(port + hp_scsictrl_0, ENA_SCAM_SEL);

	WRW_HARPOON((port + hp_intstat), CLR_ALL_INT);

	FPT_default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
	    BUS_FREE | XFER_CNT_0 | AUTO_INT;

	if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
		FPT_default_intena |= SCAM_SEL;

	WRW_HARPOON((port + hp_intena), FPT_default_intena);

	WR_HARPOON(port + hp_seltimeout, TO_290ms);

	/* Turn on SCSI_MODE8 for narrow cards to fix the
	   strapping issue with the DUAL CHANNEL card */
	if (RD_HARPOON(port + hp_page_ctrl) & NARROW_SCSI_CARD)
		WR_HARPOON(port + hp_addstat, SCSI_MODE8);

	WR_HARPOON(port + hp_page_ctrl, i);

}

/*---------------------------------------------------------------------
 *
 * Function: FPT_BusMasterInit
 *
 * Description: Initialize the BusMaster for normal operations.
 *
 *---------------------------------------------------------------------*/

static void FPT_BusMasterInit(u32 p_port)
{

	WR_HARPOON(p_port + hp_sys_ctrl, DRVR_RST);
	WR_HARPOON(p_port + hp_sys_ctrl, 0x00);

	WR_HARPOON(p_port + hp_host_blk_cnt, XFER_BLK64);

	WR_HARPOON(p_port + hp_bm_ctrl, (BMCTRL_DEFAULT));

	WR_HARPOON(p_port + hp_ee_ctrl, (SCSI_TERM_ENA_H));

	RD_HARPOON(p_port + hp_int_status);	/*Clear interrupts. */
	WR_HARPOON(p_port + hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
	WR_HARPOON(p_port + hp_page_ctrl, (RD_HARPOON(p_port + hp_page_ctrl) &
					   ~SCATTER_EN));
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_DiagEEPROM
 *
 * Description: Verfiy checksum and 'Key' and initialize the EEPROM if
 *              necessary.
 *
 *---------------------------------------------------------------------*/

static void FPT_DiagEEPROM(u32 p_port)
{
	unsigned short index, temp, max_wd_cnt;

	if (RD_HARPOON(p_port + hp_page_ctrl) & NARROW_SCSI_CARD)
		max_wd_cnt = EEPROM_WD_CNT;
	else
		max_wd_cnt = EEPROM_WD_CNT * 2;

	temp = FPT_utilEERead(p_port, FW_SIGNATURE / 2);

	if (temp == 0x4641) {

		for (index = 2; index < max_wd_cnt; index++) {

			temp += FPT_utilEERead(p_port, index);

		}

		if (temp == FPT_utilEERead(p_port, EEPROM_CHECK_SUM / 2)) {

			return;	/*EEPROM is Okay so return now! */
		}
	}

	FPT_utilEEWriteOnOff(p_port, (unsigned char)1);

	for (index = 0; index < max_wd_cnt; index++) {

		FPT_utilEEWrite(p_port, 0x0000, index);
	}

	temp = 0;

	FPT_utilEEWrite(p_port, 0x4641, FW_SIGNATURE / 2);
	temp += 0x4641;
	FPT_utilEEWrite(p_port, 0x3920, MODEL_NUMB_0 / 2);
	temp += 0x3920;
	FPT_utilEEWrite(p_port, 0x3033, MODEL_NUMB_2 / 2);
	temp += 0x3033;
	FPT_utilEEWrite(p_port, 0x2020, MODEL_NUMB_4 / 2);
	temp += 0x2020;
	FPT_utilEEWrite(p_port, 0x70D3, SYSTEM_CONFIG / 2);
	temp += 0x70D3;
	FPT_utilEEWrite(p_port, 0x0010, BIOS_CONFIG / 2);
	temp += 0x0010;
	FPT_utilEEWrite(p_port, 0x0003, SCAM_CONFIG / 2);
	temp += 0x0003;
	FPT_utilEEWrite(p_port, 0x0007, ADAPTER_SCSI_ID / 2);
	temp += 0x0007;

	FPT_utilEEWrite(p_port, 0x0000, IGNORE_B_SCAN / 2);
	temp += 0x0000;
	FPT_utilEEWrite(p_port, 0x0000, SEND_START_ENA / 2);
	temp += 0x0000;
	FPT_utilEEWrite(p_port, 0x0000, DEVICE_ENABLE / 2);
	temp += 0x0000;

	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL01 / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL23 / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL45 / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL67 / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL89 / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLab / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLcd / 2);
	temp += 0x4242;
	FPT_utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLef / 2);
	temp += 0x4242;

	FPT_utilEEWrite(p_port, 0x6C46, 64 / 2);	/*PRODUCT ID */
	temp += 0x6C46;
	FPT_utilEEWrite(p_port, 0x7361, 66 / 2);	/* FlashPoint LT   */
	temp += 0x7361;
	FPT_utilEEWrite(p_port, 0x5068, 68 / 2);
	temp += 0x5068;
	FPT_utilEEWrite(p_port, 0x696F, 70 / 2);
	temp += 0x696F;
	FPT_utilEEWrite(p_port, 0x746E, 72 / 2);
	temp += 0x746E;
	FPT_utilEEWrite(p_port, 0x4C20, 74 / 2);
	temp += 0x4C20;
	FPT_utilEEWrite(p_port, 0x2054, 76 / 2);
	temp += 0x2054;
	FPT_utilEEWrite(p_port, 0x2020, 78 / 2);
	temp += 0x2020;

	index = ((EE_SCAMBASE / 2) + (7 * 16));
	FPT_utilEEWrite(p_port, (0x0700 + TYPE_CODE0), index);
	temp += (0x0700 + TYPE_CODE0);
	index++;
	FPT_utilEEWrite(p_port, 0x5542, index);	/*Vendor ID code */
	temp += 0x5542;		/* BUSLOGIC      */
	index++;
	FPT_utilEEWrite(p_port, 0x4C53, index);
	temp += 0x4C53;
	index++;
	FPT_utilEEWrite(p_port, 0x474F, index);
	temp += 0x474F;
	index++;
	FPT_utilEEWrite(p_port, 0x4349, index);
	temp += 0x4349;
	index++;
	FPT_utilEEWrite(p_port, 0x5442, index);	/*Vendor unique code */
	temp += 0x5442;		/* BT- 930           */
	index++;
	FPT_utilEEWrite(p_port, 0x202D, index);
	temp += 0x202D;
	index++;
	FPT_utilEEWrite(p_port, 0x3339, index);
	temp += 0x3339;
	index++;		/*Serial #          */
	FPT_utilEEWrite(p_port, 0x2030, index);	/* 01234567         */
	temp += 0x2030;
	index++;
	FPT_utilEEWrite(p_port, 0x5453, index);
	temp += 0x5453;
	index++;
	FPT_utilEEWrite(p_port, 0x5645, index);
	temp += 0x5645;
	index++;
	FPT_utilEEWrite(p_port, 0x2045, index);
	temp += 0x2045;
	index++;
	FPT_utilEEWrite(p_port, 0x202F, index);
	temp += 0x202F;
	index++;
	FPT_utilEEWrite(p_port, 0x4F4A, index);
	temp += 0x4F4A;
	index++;
	FPT_utilEEWrite(p_port, 0x204E, index);
	temp += 0x204E;
	index++;
	FPT_utilEEWrite(p_port, 0x3539, index);
	temp += 0x3539;

	FPT_utilEEWrite(p_port, temp, EEPROM_CHECK_SUM / 2);

	FPT_utilEEWriteOnOff(p_port, (unsigned char)0);

}

/*---------------------------------------------------------------------
 *
 * Function: Queue Search Select
 *
 * Description: Try to find a new command to execute.
 *
 *---------------------------------------------------------------------*/

static void FPT_queueSearchSelect(struct sccb_card *pCurrCard,
				  unsigned char p_card)
{
	unsigned char scan_ptr, lun;
	struct sccb_mgr_tar_info *currTar_Info;
	struct sccb *pOldSccb;

	scan_ptr = pCurrCard->scanIndex;
	do {
		currTar_Info = &FPT_sccbMgrTbl[p_card][scan_ptr];
		if ((pCurrCard->globalFlags & F_CONLUN_IO) &&
		    ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) !=
		     TAG_Q_TRYING)) {
			if (currTar_Info->TarSelQ_Cnt != 0) {

				scan_ptr++;
				if (scan_ptr == MAX_SCSI_TAR)
					scan_ptr = 0;

				for (lun = 0; lun < MAX_LUN; lun++) {
					if (currTar_Info->TarLUNBusy[lun] == 0) {

						pCurrCard->currentSCCB =
						    currTar_Info->TarSelQ_Head;
						pOldSccb = NULL;

						while ((pCurrCard->
							currentSCCB != NULL)
						       && (lun !=
							   pCurrCard->
							   currentSCCB->Lun)) {
							pOldSccb =
							    pCurrCard->
							    currentSCCB;
							pCurrCard->currentSCCB =
							    (struct sccb
							     *)(pCurrCard->
								currentSCCB)->
							    Sccb_forwardlink;
						}
						if (pCurrCard->currentSCCB ==
						    NULL)
							continue;
						if (pOldSccb != NULL) {
							pOldSccb->
							    Sccb_forwardlink =
							    (struct sccb
							     *)(pCurrCard->
								currentSCCB)->
							    Sccb_forwardlink;
							pOldSccb->
							    Sccb_backlink =
							    (struct sccb
							     *)(pCurrCard->
								currentSCCB)->
							    Sccb_backlink;
							currTar_Info->
							    TarSelQ_Cnt--;
						} else {
							currTar_Info->
							    TarSelQ_Head =
							    (struct sccb
							     *)(pCurrCard->
								currentSCCB)->
							    Sccb_forwardlink;

							if (currTar_Info->
							    TarSelQ_Head ==
							    NULL) {
								currTar_Info->
								    TarSelQ_Tail
								    = NULL;
								currTar_Info->
								    TarSelQ_Cnt
								    = 0;
							} else {
								currTar_Info->
								    TarSelQ_Cnt--;
								currTar_Info->
								    TarSelQ_Head->
								    Sccb_backlink
								    =
								    (struct sccb
								     *)NULL;
							}
						}
						pCurrCard->scanIndex = scan_ptr;

						pCurrCard->globalFlags |=
						    F_NEW_SCCB_CMD;

						break;
					}
				}
			}

			else {
				scan_ptr++;
				if (scan_ptr == MAX_SCSI_TAR) {
					scan_ptr = 0;
				}
			}

		} else {
			if ((currTar_Info->TarSelQ_Cnt != 0) &&
			    (currTar_Info->TarLUNBusy[0] == 0)) {

				pCurrCard->currentSCCB =
				    currTar_Info->TarSelQ_Head;

				currTar_Info->TarSelQ_Head =
				    (struct sccb *)(pCurrCard->currentSCCB)->
				    Sccb_forwardlink;

				if (currTar_Info->TarSelQ_Head == NULL) {
					currTar_Info->TarSelQ_Tail = NULL;
					currTar_Info->TarSelQ_Cnt = 0;
				} else {
					currTar_Info->TarSelQ_Cnt--;
					currTar_Info->TarSelQ_Head->
					    Sccb_backlink = (struct sccb *)NULL;
				}

				scan_ptr++;
				if (scan_ptr == MAX_SCSI_TAR)
					scan_ptr = 0;

				pCurrCard->scanIndex = scan_ptr;

				pCurrCard->globalFlags |= F_NEW_SCCB_CMD;

				break;
			}

			else {
				scan_ptr++;
				if (scan_ptr == MAX_SCSI_TAR) {
					scan_ptr = 0;
				}
			}
		}
	} while (scan_ptr != pCurrCard->scanIndex);
}

/*---------------------------------------------------------------------
 *
 * Function: Queue Select Fail
 *
 * Description: Add the current SCCB to the head of the Queue.
 *
 *---------------------------------------------------------------------*/

static void FPT_queueSelectFail(struct sccb_card *pCurrCard,
				unsigned char p_card)
{
	unsigned char thisTarg;
	struct sccb_mgr_tar_info *currTar_Info;

	if (pCurrCard->currentSCCB != NULL) {
		thisTarg =
		    (unsigned char)(((struct sccb *)(pCurrCard->currentSCCB))->
				    TargID);
		currTar_Info = &FPT_sccbMgrTbl[p_card][thisTarg];

		pCurrCard->currentSCCB->Sccb_backlink = (struct sccb *)NULL;

		pCurrCard->currentSCCB->Sccb_forwardlink =
		    currTar_Info->TarSelQ_Head;

		if (currTar_Info->TarSelQ_Cnt == 0) {
			currTar_Info->TarSelQ_Tail = pCurrCard->currentSCCB;
		}

		else {
			currTar_Info->TarSelQ_Head->Sccb_backlink =
			    pCurrCard->currentSCCB;
		}

		currTar_Info->TarSelQ_Head = pCurrCard->currentSCCB;

		pCurrCard->currentSCCB = NULL;
		currTar_Info->TarSelQ_Cnt++;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: Queue Command Complete
 *
 * Description: Call the callback function with the current SCCB.
 *
 *---------------------------------------------------------------------*/

static void FPT_queueCmdComplete(struct sccb_card *pCurrCard,
				 struct sccb *p_sccb, unsigned char p_card)
{

	unsigned char i, SCSIcmd;
	CALL_BK_FN callback;
	struct sccb_mgr_tar_info *currTar_Info;

	SCSIcmd = p_sccb->Cdb[0];

	if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED)) {

		if ((p_sccb->
		     ControlByte & (SCCB_DATA_XFER_OUT | SCCB_DATA_XFER_IN))
		    && (p_sccb->HostStatus == SCCB_COMPLETE)
		    && (p_sccb->TargetStatus != SAM_STAT_CHECK_CONDITION))

			if ((SCSIcmd == READ_6) ||
			    (SCSIcmd == WRITE_6) ||
			    (SCSIcmd == READ_10) ||
			    (SCSIcmd == WRITE_10) ||
			    (SCSIcmd == WRITE_VERIFY) ||
			    (SCSIcmd == START_STOP) ||
			    (pCurrCard->globalFlags & F_NO_FILTER)
			    )
				p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
	}

	if (p_sccb->SccbStatus == SCCB_IN_PROCESS) {
		if (p_sccb->HostStatus || p_sccb->TargetStatus)
			p_sccb->SccbStatus = SCCB_ERROR;
		else
			p_sccb->SccbStatus = SCCB_SUCCESS;
	}

	if (p_sccb->Sccb_XferState & F_AUTO_SENSE) {

		p_sccb->CdbLength = p_sccb->Save_CdbLen;
		for (i = 0; i < 6; i++) {
			p_sccb->Cdb[i] = p_sccb->Save_Cdb[i];
		}
	}

	if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
	    (p_sccb->OperationCode == RESIDUAL_COMMAND)) {

		FPT_utilUpdateResidual(p_sccb);
	}

	pCurrCard->cmdCounter--;
	if (!pCurrCard->cmdCounter) {

		if (pCurrCard->globalFlags & F_GREEN_PC) {
			WR_HARPOON(pCurrCard->ioPort + hp_clkctrl_0,
				   (PWR_DWN | CLKCTRL_DEFAULT));
			WR_HARPOON(pCurrCard->ioPort + hp_sys_ctrl, STOP_CLK);
		}

		WR_HARPOON(pCurrCard->ioPort + hp_semaphore,
			   (RD_HARPOON(pCurrCard->ioPort + hp_semaphore) &
			    ~SCCB_MGR_ACTIVE));

	}

	if (pCurrCard->discQCount != 0) {
		currTar_Info = &FPT_sccbMgrTbl[p_card][p_sccb->TargID];
		if (((pCurrCard->globalFlags & F_CONLUN_IO) &&
		     ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) !=
		      TAG_Q_TRYING))) {
			pCurrCard->discQCount--;
			pCurrCard->discQ_Tbl[currTar_Info->
					     LunDiscQ_Idx[p_sccb->Lun]] = NULL;
		} else {
			if (p_sccb->Sccb_tag) {
				pCurrCard->discQCount--;
				pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
			} else {
				pCurrCard->discQCount--;
				pCurrCard->discQ_Tbl[currTar_Info->
						     LunDiscQ_Idx[0]] = NULL;
			}
		}

	}

	callback = (CALL_BK_FN) p_sccb->SccbCallback;
	callback(p_sccb);
	pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
	pCurrCard->currentSCCB = NULL;
}

/*---------------------------------------------------------------------
 *
 * Function: Queue Disconnect
 *
 * Description: Add SCCB to our disconnect array.
 *
 *---------------------------------------------------------------------*/
static void FPT_queueDisconnect(struct sccb *p_sccb, unsigned char p_card)
{
	struct sccb_mgr_tar_info *currTar_Info;

	currTar_Info = &FPT_sccbMgrTbl[p_card][p_sccb->TargID];

	if (((FPT_BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
	     ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))) {
		FPT_BL_Card[p_card].discQ_Tbl[currTar_Info->
					      LunDiscQ_Idx[p_sccb->Lun]] =
		    p_sccb;
	} else {
		if (p_sccb->Sccb_tag) {
			FPT_BL_Card[p_card].discQ_Tbl[p_sccb->Sccb_tag] =
			    p_sccb;
			FPT_sccbMgrTbl[p_card][p_sccb->TargID].TarLUNBusy[0] =
			    0;
			FPT_sccbMgrTbl[p_card][p_sccb->TargID].TarTagQ_Cnt++;
		} else {
			FPT_BL_Card[p_card].discQ_Tbl[currTar_Info->
						      LunDiscQ_Idx[0]] = p_sccb;
		}
	}
	FPT_BL_Card[p_card].currentSCCB = NULL;
}

/*---------------------------------------------------------------------
 *
 * Function: Queue Flush SCCB
 *
 * Description: Flush all SCCB's back to the host driver for this target.
 *
 *---------------------------------------------------------------------*/

static void FPT_queueFlushSccb(unsigned char p_card, unsigned char error_code)
{
	unsigned char qtag, thisTarg;
	struct sccb *currSCCB;
	struct sccb_mgr_tar_info *currTar_Info;

	currSCCB = FPT_BL_Card[p_card].currentSCCB;
	if (currSCCB != NULL) {
		thisTarg = (unsigned char)currSCCB->TargID;
		currTar_Info = &FPT_sccbMgrTbl[p_card][thisTarg];

		for (qtag = 0; qtag < QUEUE_DEPTH; qtag++) {

			if (FPT_BL_Card[p_card].discQ_Tbl[qtag] &&
			    (FPT_BL_Card[p_card].discQ_Tbl[qtag]->TargID ==
			     thisTarg)) {

				FPT_BL_Card[p_card].discQ_Tbl[qtag]->
				    HostStatus = (unsigned char)error_code;

				FPT_queueCmdComplete(&FPT_BL_Card[p_card],
						     FPT_BL_Card[p_card].
						     discQ_Tbl[qtag], p_card);

				FPT_BL_Card[p_card].discQ_Tbl[qtag] = NULL;
				currTar_Info->TarTagQ_Cnt--;

			}
		}
	}

}

/*---------------------------------------------------------------------
 *
 * Function: Queue Flush Target SCCB
 *
 * Description: Flush all SCCB's back to the host driver for this target.
 *
 *---------------------------------------------------------------------*/

static void FPT_queueFlushTargSccb(unsigned char p_card, unsigned char thisTarg,
				   unsigned char error_code)
{
	unsigned char qtag;
	struct sccb_mgr_tar_info *currTar_Info;

	currTar_Info = &FPT_sccbMgrTbl[p_card][thisTarg];

	for (qtag = 0; qtag < QUEUE_DEPTH; qtag++) {

		if (FPT_BL_Card[p_card].discQ_Tbl[qtag] &&
		    (FPT_BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg)) {

			FPT_BL_Card[p_card].discQ_Tbl[qtag]->HostStatus =
			    (unsigned char)error_code;

			FPT_queueCmdComplete(&FPT_BL_Card[p_card],
					     FPT_BL_Card[p_card].
					     discQ_Tbl[qtag], p_card);

			FPT_BL_Card[p_card].discQ_Tbl[qtag] = NULL;
			currTar_Info->TarTagQ_Cnt--;

		}
	}

}

static void FPT_queueAddSccb(struct sccb *p_SCCB, unsigned char p_card)
{
	struct sccb_mgr_tar_info *currTar_Info;
	currTar_Info = &FPT_sccbMgrTbl[p_card][p_SCCB->TargID];

	p_SCCB->Sccb_forwardlink = NULL;

	p_SCCB->Sccb_backlink = currTar_Info->TarSelQ_Tail;

	if (currTar_Info->TarSelQ_Cnt == 0) {

		currTar_Info->TarSelQ_Head = p_SCCB;
	}

	else {

		currTar_Info->TarSelQ_Tail->Sccb_forwardlink = p_SCCB;
	}

	currTar_Info->TarSelQ_Tail = p_SCCB;
	currTar_Info->TarSelQ_Cnt++;
}

/*---------------------------------------------------------------------
 *
 * Function: Queue Find SCCB
 *
 * Description: Search the target select Queue for this SCCB, and
 *              remove it if found.
 *
 *---------------------------------------------------------------------*/

static unsigned char FPT_queueFindSccb(struct sccb *p_SCCB,
				       unsigned char p_card)
{
	struct sccb *q_ptr;
	struct sccb_mgr_tar_info *currTar_Info;

	currTar_Info = &FPT_sccbMgrTbl[p_card][p_SCCB->TargID];

	q_ptr = currTar_Info->TarSelQ_Head;

	while (q_ptr != NULL) {

		if (q_ptr == p_SCCB) {

			if (currTar_Info->TarSelQ_Head == q_ptr) {

				currTar_Info->TarSelQ_Head =
				    q_ptr->Sccb_forwardlink;
			}

			if (currTar_Info->TarSelQ_Tail == q_ptr) {

				currTar_Info->TarSelQ_Tail =
				    q_ptr->Sccb_backlink;
			}

			if (q_ptr->Sccb_forwardlink != NULL) {
				q_ptr->Sccb_forwardlink->Sccb_backlink =
				    q_ptr->Sccb_backlink;
			}

			if (q_ptr->Sccb_backlink != NULL) {
				q_ptr->Sccb_backlink->Sccb_forwardlink =
				    q_ptr->Sccb_forwardlink;
			}

			currTar_Info->TarSelQ_Cnt--;

			return 1;
		}

		else {
			q_ptr = q_ptr->Sccb_forwardlink;
		}
	}

	return 0;

}

/*---------------------------------------------------------------------
 *
 * Function: Utility Update Residual Count
 *
 * Description: Update the XferCnt to the remaining byte count.
 *              If we transferred all the data then just write zero.
 *              If Non-SG transfer then report Total Cnt - Actual Transfer
 *              Cnt.  For SG transfers add the count fields of all
 *              remaining SG elements, as well as any partial remaining
 *              element.
 *
 *---------------------------------------------------------------------*/

static void FPT_utilUpdateResidual(struct sccb *p_SCCB)
{
	unsigned long partial_cnt;
	unsigned int sg_index;
	struct blogic_sg_seg *segp;

	if (p_SCCB->Sccb_XferState & F_ALL_XFERRED) {

		p_SCCB->DataLength = 0x0000;
	}

	else if (p_SCCB->Sccb_XferState & F_SG_XFER) {

		partial_cnt = 0x0000;

		sg_index = p_SCCB->Sccb_sgseg;


		if (p_SCCB->Sccb_SGoffset) {

			partial_cnt = p_SCCB->Sccb_SGoffset;
			sg_index++;
		}

		while (((unsigned long)sg_index *
			(unsigned long)SG_ELEMENT_SIZE) < p_SCCB->DataLength) {
			segp = (struct blogic_sg_seg *)(p_SCCB->DataPointer) +
					(sg_index * 2);
			partial_cnt += segp->segbytes;
			sg_index++;
		}

		p_SCCB->DataLength = partial_cnt;
	}

	else {

		p_SCCB->DataLength -= p_SCCB->Sccb_ATC;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: Wait 1 Second
 *
 * Description: Wait for 1 second.
 *
 *---------------------------------------------------------------------*/

static void FPT_Wait1Second(u32 p_port)
{
	unsigned char i;

	for (i = 0; i < 4; i++) {

		FPT_Wait(p_port, TO_250ms);

		if ((RD_HARPOON(p_port + hp_scsictrl_0) & SCSI_RST))
			break;

		if ((RDW_HARPOON((p_port + hp_intstat)) & SCAM_SEL))
			break;
	}
}

/*---------------------------------------------------------------------
 *
 * Function: FPT_Wait
 *
 * Description: Wait the desired delay.
 *
 *---------------------------------------------------------------------*/

static void FPT_Wait(u32 p_port, unsigned char p_delay)
{
	unsigned char old_timer;
	unsigned char green_flag;

	old_timer = RD_HARPOON(p_port + hp_seltimeout);

	green_flag = RD_HARPOON(p_port + hp_clkctrl_0);
	WR_HARPOON(p_port + hp_clkctrl_0, CLKCTRL_DEFAULT);

	WR_HARPOON(p_port + hp_seltimeout, p_delay);
	WRW_HARPOON((p_port + hp_intstat), TIMEOUT);
	WRW_HARPOON((p_port + hp_intena), (FPT_default_intena & ~TIMEOUT));

	WR_HARPOON(p_port + hp_portctrl_0,
		   (RD_HARPOON(p_port + hp_portctrl_0) | START_TO));

	while (!(RDW_HARPOON((p_port + hp_intstat)) & TIMEOUT)) {

		if ((RD_HARPOON(p_port + hp_scsictrl_0) & SCSI_RST))
			break;

		if ((RDW_HARPOON((p_port + hp_intstat)) & SCAM_SEL))
			break;
	}

	WR_HARPOON(p_port + hp_portctrl_0,
		   (RD_HARPOON(p_port + hp_portctrl_0) & ~START_TO));

	WRW_HARPOON((p_port + hp_intstat), TIMEOUT);
	WRW_HARPOON((p_port + hp_intena), FPT_default_intena);

	WR_HARPOON(p_port + hp_clkctrl_0, green_flag);

	WR_HARPOON(p_port + hp_seltimeout, old_timer);
}

/*---------------------------------------------------------------------
 *
 * Function: Enable/Disable Write to EEPROM
 *
 * Description: The EEPROM must first be enabled for writes
 *              A total of 9 clocks are needed.
 *
 *---------------------------------------------------------------------*/

static void FPT_utilEEWriteOnOff(u32 p_port, unsigned char p_mode)
{
	unsigned char ee_value;

	ee_value =
	    (unsigned char)(RD_HARPOON(p_port + hp_ee_ctrl) &
			    (EXT_ARB_ACK | SCSI_TERM_ENA_H));

	if (p_mode)

		FPT_utilEESendCmdAddr(p_port, EWEN, EWEN_ADDR);

	else

		FPT_utilEESendCmdAddr(p_port, EWDS, EWDS_ADDR);

	WR_HARPOON(p_port + hp_ee_ctrl, (ee_value | SEE_MS));	/*Turn off CS */
	WR_HARPOON(p_port + hp_ee_ctrl, ee_value);	/*Turn off Master Select */
}

/*---------------------------------------------------------------------
 *
 * Function: Write EEPROM
 *
 * Description: Write a word to the EEPROM at the specified
 *              address.
 *
 *---------------------------------------------------------------------*/

static void FPT_utilEEWrite(u32 p_port, unsigned short ee_data,
			    unsigned short ee_addr)
{

	unsigned char ee_value;
	unsigned short i;

	ee_value =
	    (unsigned
	     char)((RD_HARPOON(p_port + hp_ee_ctrl) &
		    (EXT_ARB_ACK | SCSI_TERM_ENA_H)) | (SEE_MS | SEE_CS));

	FPT_utilEESendCmdAddr(p_port, EE_WRITE, ee_addr);

	ee_value |= (SEE_MS + SEE_CS);

	for (i = 0x8000; i != 0; i >>= 1) {

		if (i & ee_data)
			ee_value |= SEE_DO;
		else
			ee_value &= ~SEE_DO;

		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value |= SEE_CLK;	/* Clock  data! */
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value &= ~SEE_CLK;
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
	}
	ee_value &= (EXT_ARB_ACK | SCSI_TERM_ENA_H);
	WR_HARPOON(p_port + hp_ee_ctrl, (ee_value | SEE_MS));

	FPT_Wait(p_port, TO_10ms);

	WR_HARPOON(p_port + hp_ee_ctrl, (ee_value | SEE_MS | SEE_CS));	/* Set CS to EEPROM */
	WR_HARPOON(p_port + hp_ee_ctrl, (ee_value | SEE_MS));	/* Turn off CS */
	WR_HARPOON(p_port + hp_ee_ctrl, ee_value);	/* Turn off Master Select */
}

/*---------------------------------------------------------------------
 *
 * Function: Read EEPROM
 *
 * Description: Read a word from the EEPROM at the desired
 *              address.
 *
 *---------------------------------------------------------------------*/

static unsigned short FPT_utilEERead(u32 p_port,
				     unsigned short ee_addr)
{
	unsigned short i, ee_data1, ee_data2;

	i = 0;
	ee_data1 = FPT_utilEEReadOrg(p_port, ee_addr);
	do {
		ee_data2 = FPT_utilEEReadOrg(p_port, ee_addr);

		if (ee_data1 == ee_data2)
			return ee_data1;

		ee_data1 = ee_data2;
		i++;

	} while (i < 4);

	return ee_data1;
}

/*---------------------------------------------------------------------
 *
 * Function: Read EEPROM Original 
 *
 * Description: Read a word from the EEPROM at the desired
 *              address.
 *
 *---------------------------------------------------------------------*/

static unsigned short FPT_utilEEReadOrg(u32 p_port, unsigned short ee_addr)
{

	unsigned char ee_value;
	unsigned short i, ee_data;

	ee_value =
	    (unsigned
	     char)((RD_HARPOON(p_port + hp_ee_ctrl) &
		    (EXT_ARB_ACK | SCSI_TERM_ENA_H)) | (SEE_MS | SEE_CS));

	FPT_utilEESendCmdAddr(p_port, EE_READ, ee_addr);

	ee_value |= (SEE_MS + SEE_CS);
	ee_data = 0;

	for (i = 1; i <= 16; i++) {

		ee_value |= SEE_CLK;	/* Clock  data! */
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value &= ~SEE_CLK;
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);

		ee_data <<= 1;

		if (RD_HARPOON(p_port + hp_ee_ctrl) & SEE_DI)
			ee_data |= 1;
	}

	ee_value &= ~(SEE_MS + SEE_CS);
	WR_HARPOON(p_port + hp_ee_ctrl, (ee_value | SEE_MS));	/*Turn off CS */
	WR_HARPOON(p_port + hp_ee_ctrl, ee_value);	/*Turn off Master Select */

	return ee_data;
}

/*---------------------------------------------------------------------
 *
 * Function: Send EE command and Address to the EEPROM
 *
 * Description: Transfers the correct command and sends the address
 *              to the eeprom.
 *
 *---------------------------------------------------------------------*/

static void FPT_utilEESendCmdAddr(u32 p_port, unsigned char ee_cmd,
				  unsigned short ee_addr)
{
	unsigned char ee_value;
	unsigned char narrow_flg;

	unsigned short i;

	narrow_flg =
	    (unsigned char)(RD_HARPOON(p_port + hp_page_ctrl) &
			    NARROW_SCSI_CARD);

	ee_value = SEE_MS;
	WR_HARPOON(p_port + hp_ee_ctrl, ee_value);

	ee_value |= SEE_CS;	/* Set CS to EEPROM */
	WR_HARPOON(p_port + hp_ee_ctrl, ee_value);

	for (i = 0x04; i != 0; i >>= 1) {

		if (i & ee_cmd)
			ee_value |= SEE_DO;
		else
			ee_value &= ~SEE_DO;

		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value |= SEE_CLK;	/* Clock  data! */
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value &= ~SEE_CLK;
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
	}

	if (narrow_flg)
		i = 0x0080;

	else
		i = 0x0200;

	while (i != 0) {

		if (i & ee_addr)
			ee_value |= SEE_DO;
		else
			ee_value &= ~SEE_DO;

		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value |= SEE_CLK;	/* Clock  data! */
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		ee_value &= ~SEE_CLK;
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);
		WR_HARPOON(p_port + hp_ee_ctrl, ee_value);

		i >>= 1;
	}
}

static unsigned short FPT_CalcCrc16(unsigned char buffer[])
{
	unsigned short crc = 0;
	int i, j;
	unsigned short ch;
	for (i = 0; i < ID_STRING_LENGTH; i++) {
		ch = (unsigned short)buffer[i];
		for (j = 0; j < 8; j++) {
			if ((crc ^ ch) & 1)
				crc = (crc >> 1) ^ CRCMASK;
			else
				crc >>= 1;
			ch >>= 1;
		}
	}
	return crc;
}

static unsigned char FPT_CalcLrc(unsigned char buffer[])
{
	int i;
	unsigned char lrc;
	lrc = 0;
	for (i = 0; i < ID_STRING_LENGTH; i++)
		lrc ^= buffer[i];
	return lrc;
}

/*
  The following inline definitions avoid type conflicts.
*/

static inline unsigned char
FlashPoint__ProbeHostAdapter(struct fpoint_info *FlashPointInfo)
{
	return FlashPoint_ProbeHostAdapter((struct sccb_mgr_info *)
					   FlashPointInfo);
}

static inline void *
FlashPoint__HardwareResetHostAdapter(struct fpoint_info *FlashPointInfo)
{
	return FlashPoint_HardwareResetHostAdapter((struct sccb_mgr_info *)
						   FlashPointInfo);
}

static inline void
FlashPoint__ReleaseHostAdapter(void *CardHandle)
{
	FlashPoint_ReleaseHostAdapter(CardHandle);
}

static inline void
FlashPoint__StartCCB(void *CardHandle, struct blogic_ccb *CCB)
{
	FlashPoint_StartCCB(CardHandle, (struct sccb *)CCB);
}

static inline void
FlashPoint__AbortCCB(void *CardHandle, struct blogic_ccb *CCB)
{
	FlashPoint_AbortCCB(CardHandle, (struct sccb *)CCB);
}

static inline bool
FlashPoint__InterruptPending(void *CardHandle)
{
	return FlashPoint_InterruptPending(CardHandle);
}

static inline int
FlashPoint__HandleInterrupt(void *CardHandle)
{
	return FlashPoint_HandleInterrupt(CardHandle);
}

#define FlashPoint_ProbeHostAdapter	    FlashPoint__ProbeHostAdapter
#define FlashPoint_HardwareResetHostAdapter FlashPoint__HardwareResetHostAdapter
#define FlashPoint_ReleaseHostAdapter	    FlashPoint__ReleaseHostAdapter
#define FlashPoint_StartCCB		    FlashPoint__StartCCB
#define FlashPoint_AbortCCB		    FlashPoint__AbortCCB
#define FlashPoint_InterruptPending	    FlashPoint__InterruptPending
#define FlashPoint_HandleInterrupt	    FlashPoint__HandleInterrupt

#else				/* !CONFIG_SCSI_FLASHPOINT */

/*
  Define prototypes for the FlashPoint SCCB Manager Functions.
*/

extern unsigned char FlashPoint_ProbeHostAdapter(struct fpoint_info *);
extern void *FlashPoint_HardwareResetHostAdapter(struct fpoint_info *);
extern void FlashPoint_StartCCB(void *, struct blogic_ccb *);
extern int FlashPoint_AbortCCB(void *, struct blogic_ccb *);
extern bool FlashPoint_InterruptPending(void *);
extern int FlashPoint_HandleInterrupt(void *);
extern void FlashPoint_ReleaseHostAdapter(void *);

#endif				/* CONFIG_SCSI_FLASHPOINT */