aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/initio.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/initio.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi/initio.c')
-rw-r--r--drivers/scsi/initio.c3184
1 files changed, 3184 insertions, 0 deletions
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
new file mode 100644
index 00000000000..a7b74d8c53b
--- /dev/null
+++ b/drivers/scsi/initio.c
@@ -0,0 +1,3184 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
+ * adapters
+ *
+ * 08/06/97 hc - v1.01h
+ * - Support inic-940 and inic-935
+ * 09/26/97 hc - v1.01i
+ * - Make correction from J.W. Schultz suggestion
+ * 10/13/97 hc - Support reset function
+ * 10/21/97 hc - v1.01j
+ * - Support 32 LUN (SCSI 3)
+ * 01/14/98 hc - v1.01k
+ * - Fix memory allocation problem
+ * 03/04/98 hc - v1.01l
+ * - Fix tape rewind which will hang the system problem
+ * - Set can_queue to tul_num_scb
+ * 06/25/98 hc - v1.01m
+ * - Get it work for kernel version >= 2.1.75
+ * - Dynamic assign SCSI bus reset holding time in init_tulip()
+ * 07/02/98 hc - v1.01n
+ * - Support 0002134A
+ * 08/07/98 hc - v1.01o
+ * - Change the tul_abort_srb routine to use scsi_done. <01>
+ * 09/07/98 hl - v1.02
+ * - Change the INI9100U define and proc_dir_entry to
+ * reflect the newer Kernel 2.1.118, but the v1.o1o
+ * should work with Kernel 2.1.118.
+ * 09/20/98 wh - v1.02a
+ * - Support Abort command.
+ * - Handle reset routine.
+ * 09/21/98 hl - v1.03
+ * - remove comments.
+ * 12/09/98 bv - v1.03a
+ * - Removed unused code
+ * 12/13/98 bv - v1.03b
+ * - Remove cli() locking for kernels >= 2.1.95. This uses
+ * spinlocks to serialize access to the pSRB_head and
+ * pSRB_tail members of the HCS structure.
+ * 09/01/99 bv - v1.03d
+ * - Fixed a deadlock problem in SMP.
+ * 21/01/99 bv - v1.03e
+ * - Add support for the Domex 3192U PCI SCSI
+ * This is a slightly modified patch by
+ * Brian Macy <bmacy@sunshinecomputing.com>
+ * 22/02/99 bv - v1.03f
+ * - Didn't detect the INIC-950 in 2.0.x correctly.
+ * Now fixed.
+ * 05/07/99 bv - v1.03g
+ * - Changed the assumption that HZ = 100
+ * 10/17/03 mc - v1.04
+ * - added new DMA API support
+ * 06/01/04 jmd - v1.04a
+ * - Re-add reset_bus support
+ **************************************************************************/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "initio.h"
+
+#define SENSE_SIZE 14
+
+#define i91u_MAXQUEUE 2
+#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"
+
+#define INI_VENDOR_ID 0x1101 /* Initio's PCI vendor ID */
+#define DMX_VENDOR_ID 0x134a /* Domex's PCI vendor ID */
+#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */
+#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */
+#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */
+#define I920_DEVICE_ID 0x0002 /* Initio's other product ID */
+
+#ifdef DEBUG_i91u
+static unsigned int i91u_debug = DEBUG_DEFAULT;
+#endif
+
+#define TULSZ(sz) (sizeof(sz) / sizeof(sz[0]))
+#define TUL_RDWORD(x,y) (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+typedef struct PCI_ID_Struc {
+ unsigned short vendor_id;
+ unsigned short device_id;
+} PCI_ID;
+
+static int tul_num_ch = 4; /* Maximum 4 adapters */
+static int tul_num_scb;
+static int tul_tag_enable = 1;
+static SCB *tul_scb;
+
+#ifdef DEBUG_i91u
+static int setup_debug = 0;
+#endif
+
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb);
+
+static const PCI_ID i91u_pci_devices[] = {
+ { INI_VENDOR_ID, I950_DEVICE_ID },
+ { INI_VENDOR_ID, I940_DEVICE_ID },
+ { INI_VENDOR_ID, I935_DEVICE_ID },
+ { INI_VENDOR_ID, I920_DEVICE_ID },
+ { DMX_VENDOR_ID, I920_DEVICE_ID },
+};
+
+#define DEBUG_INTERRUPT 0
+#define DEBUG_QUEUE 0
+#define DEBUG_STATE 0
+#define INT_DISC 0
+
+/*--- external functions --*/
+static void tul_se2_wait(void);
+
+/*--- forward refrence ---*/
+static SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun);
+static SCB *tul_find_done_scb(HCS * pCurHcb);
+
+static int tulip_main(HCS * pCurHcb);
+
+static int tul_next_state(HCS * pCurHcb);
+static int tul_state_1(HCS * pCurHcb);
+static int tul_state_2(HCS * pCurHcb);
+static int tul_state_3(HCS * pCurHcb);
+static int tul_state_4(HCS * pCurHcb);
+static int tul_state_5(HCS * pCurHcb);
+static int tul_state_6(HCS * pCurHcb);
+static int tul_state_7(HCS * pCurHcb);
+static int tul_xfer_data_in(HCS * pCurHcb);
+static int tul_xfer_data_out(HCS * pCurHcb);
+static int tul_xpad_in(HCS * pCurHcb);
+static int tul_xpad_out(HCS * pCurHcb);
+static int tul_status_msg(HCS * pCurHcb);
+
+static int tul_msgin(HCS * pCurHcb);
+static int tul_msgin_sync(HCS * pCurHcb);
+static int tul_msgin_accept(HCS * pCurHcb);
+static int tul_msgout_reject(HCS * pCurHcb);
+static int tul_msgin_extend(HCS * pCurHcb);
+
+static int tul_msgout_ide(HCS * pCurHcb);
+static int tul_msgout_abort_targ(HCS * pCurHcb);
+static int tul_msgout_abort_tag(HCS * pCurHcb);
+
+static int tul_bus_device_reset(HCS * pCurHcb);
+static void tul_select_atn(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb);
+static int int_tul_busfree(HCS * pCurHcb);
+int int_tul_scsi_rst(HCS * pCurHcb);
+static int int_tul_bad_seq(HCS * pCurHcb);
+static int int_tul_resel(HCS * pCurHcb);
+static int tul_sync_done(HCS * pCurHcb);
+static int wdtr_done(HCS * pCurHcb);
+static int wait_tulip(HCS * pCurHcb);
+static int tul_wait_done_disc(HCS * pCurHcb);
+static int tul_wait_disc(HCS * pCurHcb);
+static void tulip_scsi(HCS * pCurHcb);
+static int tul_post_scsi_rst(HCS * pCurHcb);
+
+static void tul_se2_ew_en(WORD CurBase);
+static void tul_se2_ew_ds(WORD CurBase);
+static int tul_se2_rd_all(WORD CurBase);
+static void tul_se2_update_all(WORD CurBase); /* setup default pattern */
+static void tul_read_eeprom(WORD CurBase);
+
+ /* ---- EXTERNAL VARIABLES ---- */
+HCS tul_hcs[MAX_SUPPORTED_ADAPTERS];
+ /* ---- INTERNAL VARIABLES ---- */
+static INI_ADPT_STRUCT i91u_adpt[MAX_SUPPORTED_ADAPTERS];
+
+/*NVRAM nvram, *nvramp = &nvram; */
+static NVRAM i91unvram;
+static NVRAM *i91unvramp;
+
+
+
+static UCHAR i91udftNvRam[64] =
+{
+/*----------- header -----------*/
+ 0x25, 0xc9, /* Signature */
+ 0x40, /* Size */
+ 0x01, /* Revision */
+ /* -- Host Adapter Structure -- */
+ 0x95, /* ModelByte0 */
+ 0x00, /* ModelByte1 */
+ 0x00, /* ModelInfo */
+ 0x01, /* NumOfCh */
+ NBC1_DEFAULT, /* BIOSConfig1 */
+ 0, /* BIOSConfig2 */
+ 0, /* HAConfig1 */
+ 0, /* HAConfig2 */
+ /* SCSI channel 0 and target Structure */
+ 7, /* SCSIid */
+ NCC1_DEFAULT, /* SCSIconfig1 */
+ 0, /* SCSIconfig2 */
+ 0x10, /* NumSCSItarget */
+
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+
+ /* SCSI channel 1 and target Structure */
+ 7, /* SCSIid */
+ NCC1_DEFAULT, /* SCSIconfig1 */
+ 0, /* SCSIconfig2 */
+ 0x10, /* NumSCSItarget */
+
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0}; /* - CheckSum - */
+
+
+static UCHAR tul_rate_tbl[8] = /* fast 20 */
+{
+ /* nanosecond devide by 4 */
+ 12, /* 50ns, 20M */
+ 18, /* 75ns, 13.3M */
+ 25, /* 100ns, 10M */
+ 31, /* 125ns, 8M */
+ 37, /* 150ns, 6.6M */
+ 43, /* 175ns, 5.7M */
+ 50, /* 200ns, 5M */
+ 62 /* 250ns, 4M */
+};
+
+static void tul_do_pause(unsigned amount)
+{ /* Pause for amount jiffies */
+ unsigned long the_time = jiffies + amount;
+
+ while (time_before_eq(jiffies, the_time));
+}
+
+/*-- forward reference --*/
+
+/*******************************************************************
+ Use memeory refresh time ~ 15us * 2
+********************************************************************/
+void tul_se2_wait(void)
+{
+#if 1
+ udelay(30);
+#else
+ UCHAR readByte;
+
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10) {
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10)
+ break;
+ }
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) != 0x10)
+ break;
+ }
+ } else {
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) == 0x10)
+ break;
+ }
+ for (;;) {
+ readByte = TUL_RD(0, 0x61);
+ if ((readByte & 0x10) != 0x10)
+ break;
+ }
+ }
+#endif
+}
+
+
+/******************************************************************
+ Input: instruction for Serial E2PROM
+
+ EX: se2_rd(0 call se2_instr() to send address and read command
+
+ StartBit OP_Code Address Data
+ --------- -------- ------------------ -------
+ 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0
+
+ +-----------------------------------------------------
+ |
+ CS -----+
+ +--+ +--+ +--+ +--+ +--+
+ ^ | ^ | ^ | ^ | ^ |
+ | | | | | | | | | |
+ CLK -------+ +--+ +--+ +--+ +--+ +--
+ (leading edge trigger)
+
+ +--1-----1--+
+ | SB OP | OP A5 A4
+ DI ----+ +--0------------------
+ (address and cmd sent to nvram)
+
+ -------------------------------------------+
+ |
+ DO +---
+ (data sent from nvram)
+
+
+******************************************************************/
+void tul_se2_instr(WORD CurBase, UCHAR instr)
+{
+ int i;
+ UCHAR b;
+
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* cs+start bit */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK | SE2DO); /* +CLK */
+ tul_se2_wait();
+
+ for (i = 0; i < 8; i++) {
+ if (instr & 0x80)
+ b = SE2CS | SE2DO; /* -CLK+dataBit */
+ else
+ b = SE2CS; /* -CLK */
+ TUL_WR(CurBase + TUL_NVRAM, b);
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, b | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ instr <<= 1;
+ }
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ return;
+}
+
+
+/******************************************************************
+ Function name : tul_se2_ew_en
+ Description : Enable erase/write state of serial EEPROM
+******************************************************************/
+void tul_se2_ew_en(WORD CurBase)
+{
+ tul_se2_instr(CurBase, 0x30); /* EWEN */
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+ return;
+}
+
+
+/************************************************************************
+ Disable erase/write state of serial EEPROM
+*************************************************************************/
+void tul_se2_ew_ds(WORD CurBase)
+{
+ tul_se2_instr(CurBase, 0); /* EWDS */
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+ return;
+}
+
+
+/******************************************************************
+ Input :address of Serial E2PROM
+ Output :value stored in Serial E2PROM
+*******************************************************************/
+USHORT tul_se2_rd(WORD CurBase, ULONG adr)
+{
+ UCHAR instr, readByte;
+ USHORT readWord;
+ int i;
+
+ instr = (UCHAR) (adr | 0x80);
+ tul_se2_instr(CurBase, instr); /* READ INSTR */
+ readWord = 0;
+
+ for (i = 15; i >= 0; i--) {
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+
+ /* sample data after the following edge of clock */
+ readByte = TUL_RD(CurBase, TUL_NVRAM);
+ readByte &= SE2DI;
+ readWord += (readByte << i);
+ tul_se2_wait(); /* 6/20/95 */
+ }
+
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* no chip select */
+ tul_se2_wait();
+ return readWord;
+}
+
+
+/******************************************************************
+ Input: new value in Serial E2PROM, address of Serial E2PROM
+*******************************************************************/
+void tul_se2_wr(WORD CurBase, UCHAR adr, USHORT writeWord)
+{
+ UCHAR readByte;
+ UCHAR instr;
+ int i;
+
+ instr = (UCHAR) (adr | 0x40);
+ tul_se2_instr(CurBase, instr); /* WRITE INSTR */
+ for (i = 15; i >= 0; i--) {
+ if (writeWord & 0x8000)
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* -CLK+dataBit 1 */
+ else
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK+dataBit 0 */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ writeWord <<= 1;
+ }
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ tul_se2_wait();
+
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* +CS */
+ tul_se2_wait();
+
+ for (;;) {
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */
+ tul_se2_wait();
+ TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */
+ tul_se2_wait();
+ if ((readByte = TUL_RD(CurBase, TUL_NVRAM)) & SE2DI)
+ break; /* write complete */
+ }
+ TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */
+ return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+int tul_se2_rd_all(WORD CurBase)
+{
+ int i;
+ ULONG chksum = 0;
+ USHORT *np;
+
+ i91unvramp = &i91unvram;
+ np = (USHORT *) i91unvramp;
+ for (i = 0; i < 32; i++) {
+ *np++ = tul_se2_rd(CurBase, i);
+ }
+
+/*--------------------Is signature "ini" ok ? ----------------*/
+ if (i91unvramp->NVM_Signature != INI_SIGNATURE)
+ return -1;
+/*---------------------- Is ckecksum ok ? ----------------------*/
+ np = (USHORT *) i91unvramp;
+ for (i = 0; i < 31; i++)
+ chksum += *np++;
+ if (i91unvramp->NVM_CheckSum != (USHORT) chksum)
+ return -1;
+ return 1;
+}
+
+
+/***********************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+void tul_se2_update_all(WORD CurBase)
+{ /* setup default pattern */
+ int i;
+ ULONG chksum = 0;
+ USHORT *np, *np1;
+
+ i91unvramp = &i91unvram;
+ /* Calculate checksum first */
+ np = (USHORT *) i91udftNvRam;
+ for (i = 0; i < 31; i++)
+ chksum += *np++;
+ *np = (USHORT) chksum;
+ tul_se2_ew_en(CurBase); /* Enable write */
+
+ np = (USHORT *) i91udftNvRam;
+ np1 = (USHORT *) i91unvramp;
+ for (i = 0; i < 32; i++, np++, np1++) {
+ if (*np != *np1) {
+ tul_se2_wr(CurBase, i, *np);
+ }
+ }
+
+ tul_se2_ew_ds(CurBase); /* Disable write */
+ return;
+}
+
+/*************************************************************************
+ Function name : read_eeprom
+**************************************************************************/
+void tul_read_eeprom(WORD CurBase)
+{
+ UCHAR gctrl;
+
+ i91unvramp = &i91unvram;
+/*------Enable EEProm programming ---*/
+ gctrl = TUL_RD(CurBase, TUL_GCTRL);
+ TUL_WR(CurBase + TUL_GCTRL, gctrl | TUL_GCTRL_EEPROM_BIT);
+ if (tul_se2_rd_all(CurBase) != 1) {
+ tul_se2_update_all(CurBase); /* setup default pattern */
+ tul_se2_rd_all(CurBase); /* load again */
+ }
+/*------ Disable EEProm programming ---*/
+ gctrl = TUL_RD(CurBase, TUL_GCTRL);
+ TUL_WR(CurBase + TUL_GCTRL, gctrl & ~TUL_GCTRL_EEPROM_BIT);
+} /* read_eeprom */
+
+int Addi91u_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt,
+ BYTE bBus, BYTE bDevice)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {
+ if (i91u_adpt[i].ADPT_BIOS < wBIOS)
+ continue;
+ if (i91u_adpt[i].ADPT_BIOS == wBIOS) {
+ if (i91u_adpt[i].ADPT_BASE == wBASE) {
+ if (i91u_adpt[i].ADPT_Bus != 0xFF)
+ return 1;
+ } else if (i91u_adpt[i].ADPT_BASE < wBASE)
+ continue;
+ }
+ for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
+ i91u_adpt[j].ADPT_BASE = i91u_adpt[j - 1].ADPT_BASE;
+ i91u_adpt[j].ADPT_INTR = i91u_adpt[j - 1].ADPT_INTR;
+ i91u_adpt[j].ADPT_BIOS = i91u_adpt[j - 1].ADPT_BIOS;
+ i91u_adpt[j].ADPT_Bus = i91u_adpt[j - 1].ADPT_Bus;
+ i91u_adpt[j].ADPT_Device = i91u_adpt[j - 1].ADPT_Device;
+ }
+ i91u_adpt[i].ADPT_BASE = wBASE;
+ i91u_adpt[i].ADPT_INTR = bInterrupt;
+ i91u_adpt[i].ADPT_BIOS = wBIOS;
+ i91u_adpt[i].ADPT_Bus = bBus;
+ i91u_adpt[i].ADPT_Device = bDevice;
+ return 0;
+ }
+ return 1;
+}
+
+void init_i91uAdapter_table(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) { /* Initialize adapter structure */
+ i91u_adpt[i].ADPT_BIOS = 0xffff;
+ i91u_adpt[i].ADPT_BASE = 0xffff;
+ i91u_adpt[i].ADPT_INTR = 0xff;
+ i91u_adpt[i].ADPT_Bus = 0xff;
+ i91u_adpt[i].ADPT_Device = 0xff;
+ }
+ return;
+}
+
+void tul_stop_bm(HCS * pCurHcb)
+{
+
+ if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+ /* wait Abort DMA xfer done */
+ while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+}
+
+/***************************************************************************/
+void get_tulipPCIConfig(HCS * pCurHcb, int ch_idx)
+{
+ pCurHcb->HCS_Base = i91u_adpt[ch_idx].ADPT_BASE; /* Supply base address */
+ pCurHcb->HCS_BIOS = i91u_adpt[ch_idx].ADPT_BIOS; /* Supply BIOS address */
+ pCurHcb->HCS_Intr = i91u_adpt[ch_idx].ADPT_INTR; /* Supply interrupt line */
+ return;
+}
+
+/***************************************************************************/
+int tul_reset_scsi(HCS * pCurHcb, int seconds)
+{
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_BUS);
+
+ while (!((pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt)) & TSS_SCSIRST_INT));
+ /* reset tulip chip */
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, 0);
+
+ /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
+ /* SONY 5200 tape drive won't work if only stall for 1 sec */
+ tul_do_pause(seconds * HZ);
+
+ TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+ return (SCSI_RESET_SUCCESS);
+}
+
+/***************************************************************************/
+int init_tulip(HCS * pCurHcb, SCB * scbp, int tul_num_scb, BYTE * pbBiosAdr, int seconds)
+{
+ int i;
+ BYTE *pwFlags;
+ BYTE *pbHeads;
+ SCB *pTmpScb, *pPrevScb = NULL;
+
+ pCurHcb->HCS_NumScbs = tul_num_scb;
+ pCurHcb->HCS_Semaph = 1;
+ spin_lock_init(&pCurHcb->HCS_SemaphLock);
+ pCurHcb->HCS_JSStatus0 = 0;
+ pCurHcb->HCS_Scb = scbp;
+ pCurHcb->HCS_NxtPend = scbp;
+ pCurHcb->HCS_NxtAvail = scbp;
+ for (i = 0, pTmpScb = scbp; i < tul_num_scb; i++, pTmpScb++) {
+ pTmpScb->SCB_TagId = i;
+ if (i != 0)
+ pPrevScb->SCB_NxtScb = pTmpScb;
+ pPrevScb = pTmpScb;
+ }
+ pPrevScb->SCB_NxtScb = NULL;
+ pCurHcb->HCS_ScbEnd = pTmpScb;
+ pCurHcb->HCS_FirstAvail = scbp;
+ pCurHcb->HCS_LastAvail = pPrevScb;
+ spin_lock_init(&pCurHcb->HCS_AvailLock);
+ pCurHcb->HCS_FirstPend = NULL;
+ pCurHcb->HCS_LastPend = NULL;
+ pCurHcb->HCS_FirstBusy = NULL;
+ pCurHcb->HCS_LastBusy = NULL;
+ pCurHcb->HCS_FirstDone = NULL;
+ pCurHcb->HCS_LastDone = NULL;
+ pCurHcb->HCS_ActScb = NULL;
+ pCurHcb->HCS_ActTcs = NULL;
+
+ tul_read_eeprom(pCurHcb->HCS_Base);
+/*---------- get H/A configuration -------------*/
+ if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
+ pCurHcb->HCS_MaxTar = 8;
+ else
+ pCurHcb->HCS_MaxTar = 16;
+
+ pCurHcb->HCS_Config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;
+
+ pCurHcb->HCS_SCSI_ID = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
+ pCurHcb->HCS_IdMask = ~(1 << pCurHcb->HCS_SCSI_ID);
+
+#if CHK_PARITY
+ /* Enable parity error response */
+ TUL_WR(pCurHcb->HCS_Base + TUL_PCMD, TUL_RD(pCurHcb->HCS_Base, TUL_PCMD) | 0x40);
+#endif
+
+ /* Mask all the interrupt */
+ TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+
+ tul_stop_bm(pCurHcb);
+ /* --- Initialize the tulip --- */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_CHIP);
+
+ /* program HBA's SCSI ID */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId, pCurHcb->HCS_SCSI_ID << 4);
+
+ /* Enable Initiator Mode ,phase latch,alternate sync period mode,
+ disable SCSI reset */
+ if (pCurHcb->HCS_Config & HCC_EN_PAR)
+ pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
+ else
+ pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_SConf1);
+
+ /* Enable HW reselect */
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+
+ TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, 0);
+
+ /* selection time out = 250 ms */
+ TUL_WR(pCurHcb->HCS_Base + TUL_STimeOut, 153);
+
+/*--------- Enable SCSI terminator -----*/
+ TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, (pCurHcb->HCS_Config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)));
+ TUL_WR(pCurHcb->HCS_Base + TUL_GCTRL1,
+ ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE));
+
+ for (i = 0,
+ pwFlags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
+ pbHeads = pbBiosAdr + 0x180;
+ i < pCurHcb->HCS_MaxTar;
+ i++, pwFlags++) {
+ pCurHcb->HCS_Tcs[i].TCS_Flags = *pwFlags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+ if (pCurHcb->HCS_Tcs[i].TCS_Flags & TCF_EN_255)
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+ else
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+ pCurHcb->HCS_Tcs[i].TCS_JS_Period = 0;
+ pCurHcb->HCS_Tcs[i].TCS_SConfig0 = pCurHcb->HCS_SConf1;
+ pCurHcb->HCS_Tcs[i].TCS_DrvHead = *pbHeads++;
+ if (pCurHcb->HCS_Tcs[i].TCS_DrvHead == 255)
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+ else
+ pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+ pCurHcb->HCS_Tcs[i].TCS_DrvSector = *pbHeads++;
+ pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;
+ pCurHcb->HCS_ActTags[i] = 0;
+ pCurHcb->HCS_MaxTags[i] = 0xFF;
+ } /* for */
+ printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
+ pCurHcb->HCS_Base, pCurHcb->HCS_Intr,
+ pCurHcb->HCS_BIOS, pCurHcb->HCS_SCSI_ID);
+/*------------------- reset SCSI Bus ---------------------------*/
+ if (pCurHcb->HCS_Config & HCC_SCSI_RESET) {
+ printk("i91u: Reset SCSI Bus ... \n");
+ tul_reset_scsi(pCurHcb, seconds);
+ }
+ TUL_WR(pCurHcb->HCS_Base + TUL_SCFG1, 0x17);
+ TUL_WR(pCurHcb->HCS_Base + TUL_SIntEnable, 0xE9);
+ return (0);
+}
+
+/***************************************************************************/
+SCB *tul_alloc_scb(HCS * hcsp)
+{
+ SCB *pTmpScb;
+ ULONG flags;
+ spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+ if ((pTmpScb = hcsp->HCS_FirstAvail) != NULL) {
+#if DEBUG_QUEUE
+ printk("find scb at %08lx\n", (ULONG) pTmpScb);
+#endif
+ if ((hcsp->HCS_FirstAvail = pTmpScb->SCB_NxtScb) == NULL)
+ hcsp->HCS_LastAvail = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ pTmpScb->SCB_Status = SCB_RENT;
+ }
+ spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_release_scb(HCS * hcsp, SCB * scbp)
+{
+ ULONG flags;
+
+#if DEBUG_QUEUE
+ printk("Release SCB %lx; ", (ULONG) scbp);
+#endif
+ spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+ scbp->SCB_Srb = NULL;
+ scbp->SCB_Status = 0;
+ scbp->SCB_NxtScb = NULL;
+ if (hcsp->HCS_LastAvail != NULL) {
+ hcsp->HCS_LastAvail->SCB_NxtScb = scbp;
+ hcsp->HCS_LastAvail = scbp;
+ } else {
+ hcsp->HCS_FirstAvail = scbp;
+ hcsp->HCS_LastAvail = scbp;
+ }
+ spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+}
+
+/***************************************************************************/
+void tul_append_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("Append pend SCB %lx; ", (ULONG) scbp);
+#endif
+ scbp->SCB_Status = SCB_PEND;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastPend != NULL) {
+ pCurHcb->HCS_LastPend->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ } else {
+ pCurHcb->HCS_FirstPend = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ }
+}
+
+/***************************************************************************/
+void tul_push_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("Push pend SCB %lx; ", (ULONG) scbp);
+#endif
+ scbp->SCB_Status = SCB_PEND;
+ if ((scbp->SCB_NxtScb = pCurHcb->HCS_FirstPend) != NULL) {
+ pCurHcb->HCS_FirstPend = scbp;
+ } else {
+ pCurHcb->HCS_FirstPend = scbp;
+ pCurHcb->HCS_LastPend = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_find_first_pend_scb(HCS * pCurHcb)
+{
+ SCB *pFirstPend;
+
+
+ pFirstPend = pCurHcb->HCS_FirstPend;
+ while (pFirstPend != NULL) {
+ if (pFirstPend->SCB_Opcode != ExecSCSI) {
+ return (pFirstPend);
+ }
+ if (pFirstPend->SCB_TagMsg == 0) {
+ if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] == 0) &&
+ !(pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+ return (pFirstPend);
+ }
+ } else {
+ if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] >=
+ pCurHcb->HCS_MaxTags[pFirstPend->SCB_Target]) |
+ (pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+ pFirstPend = pFirstPend->SCB_NxtScb;
+ continue;
+ }
+ return (pFirstPend);
+ }
+ pFirstPend = pFirstPend->SCB_NxtScb;
+ }
+
+
+ return (pFirstPend);
+}
+/***************************************************************************/
+SCB *tul_pop_pend_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+ if ((pTmpScb = pCurHcb->HCS_FirstPend) != NULL) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ }
+#if DEBUG_QUEUE
+ printk("Pop pend SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+
+/***************************************************************************/
+void tul_unlink_pend_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+ printk("unlink pend SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;
+ while (pTmpScb != NULL) {
+ if (pCurScb == pTmpScb) { /* Unlink this SCB */
+ if (pTmpScb == pCurHcb->HCS_FirstPend) {
+ if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastPend = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastPend)
+ pCurHcb->HCS_LastPend = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ return;
+}
+/***************************************************************************/
+void tul_append_busy_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("append busy SCB %lx; ", (ULONG) scbp);
+#endif
+ if (scbp->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[scbp->SCB_Target]++;
+ else
+ pCurHcb->HCS_Tcs[scbp->SCB_Target].TCS_Flags |= TCF_BUSY;
+ scbp->SCB_Status = SCB_BUSY;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastBusy != NULL) {
+ pCurHcb->HCS_LastBusy->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastBusy = scbp;
+ } else {
+ pCurHcb->HCS_FirstBusy = scbp;
+ pCurHcb->HCS_LastBusy = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_pop_busy_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+
+ if ((pTmpScb = pCurHcb->HCS_FirstBusy) != NULL) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ if (pTmpScb->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ else
+ pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+ }
+#if DEBUG_QUEUE
+ printk("Pop busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_unlink_busy_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+ SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+ printk("unlink busy SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+ while (pTmpScb != NULL) {
+ if (pCurScb == pTmpScb) { /* Unlink this SCB */
+ if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+ if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastBusy = NULL;
+ } else {
+ pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+ if (pTmpScb == pCurHcb->HCS_LastBusy)
+ pCurHcb->HCS_LastBusy = pPrevScb;
+ }
+ pTmpScb->SCB_NxtScb = NULL;
+ if (pTmpScb->SCB_TagMsg)
+ pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+ else
+ pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+ return;
+}
+
+/***************************************************************************/
+SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun)
+{
+ SCB *pTmpScb, *pPrevScb;
+ WORD scbp_tarlun;
+
+
+ pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+ while (pTmpScb != NULL) {
+ scbp_tarlun = (pTmpScb->SCB_Lun << 8) | (pTmpScb->SCB_Target);
+ if (scbp_tarlun == tarlun) { /* Unlink this SCB */
+ break;
+ }
+ pPrevScb = pTmpScb;
+ pTmpScb = pTmpScb->SCB_NxtScb;
+ }
+#if DEBUG_QUEUE
+ printk("find busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_append_done_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+ printk("append done SCB %lx; ", (ULONG) scbp);
+#endif
+
+ scbp->SCB_Status = SCB_DONE;
+ scbp->SCB_NxtScb = NULL;
+ if (pCurHcb->HCS_LastDone != NULL) {
+ pCurHcb->HCS_LastDone->SCB_NxtScb = scbp;
+ pCurHcb->HCS_LastDone = scbp;
+ } else {
+ pCurHcb->HCS_FirstDone = scbp;
+ pCurHcb->HCS_LastDone = scbp;
+ }
+}
+
+/***************************************************************************/
+SCB *tul_find_done_scb(HCS * pCurHcb)
+{
+ SCB *pTmpScb;
+
+
+ if ((pTmpScb = pCurHcb->HCS_FirstDone) != NULL) {
+ if ((pCurHcb->HCS_FirstDone = pTmpScb->SCB_NxtScb) == NULL)
+ pCurHcb->HCS_LastDone = NULL;
+ pTmpScb->SCB_NxtScb = NULL;
+ }
+#if DEBUG_QUEUE
+ printk("find done SCB %lx; ", (ULONG) pTmpScb);
+#endif
+ return (pTmpScb);
+}
+
+/***************************************************************************/
+int tul_abort_srb(HCS * pCurHcb, struct scsi_cmnd *srbp)
+{
+ ULONG flags;
+ SCB *pTmpScb, *pPrevScb;
+
+ spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+