1353 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1353 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver
 | 
						|
 *
 | 
						|
 * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net)
 | 
						|
 *
 | 
						|
 * This driver is cloned from fdomain.* to specifically support
 | 
						|
 * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s
 | 
						|
 * also equipped with IBM Fast SCSI Adapter/A which is an OEM
 | 
						|
 * of MCS 700.
 | 
						|
 *
 | 
						|
 * This driver also supports Reply SB16/SCSI card (the SCSI part).
 | 
						|
 *
 | 
						|
 * What makes this driver different is that this driver is MCA only
 | 
						|
 * and it supports multiple adapters in the same system, IRQ 
 | 
						|
 * sharing, some driver statistics, and maps highest SCSI id to sda.
 | 
						|
 * All cards are auto-detected.
 | 
						|
 *
 | 
						|
 * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4
 | 
						|
 *
 | 
						|
 * LILO command-line options:
 | 
						|
 *   fd_mcs=<FIFO_COUNT>[,<FIFO_SIZE>]
 | 
						|
 *
 | 
						|
 * ********************************************************
 | 
						|
 * Please see Copyrights/Comments in fdomain.* for credits.
 | 
						|
 * Following is from fdomain.c for acknowledgement:
 | 
						|
 *
 | 
						|
 * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
 | 
						|
 * Revised: Wed Oct  2 11:10:55 1996 by r.faith@ieee.org
 | 
						|
 * Author: Rickard E. Faith, faith@cs.unc.edu
 | 
						|
 * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
 | 
						|
 *
 | 
						|
 * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $
 | 
						|
 | 
						|
 * 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; if not, write to the Free Software Foundation, Inc.,
 | 
						|
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
 | 
						|
 **************************************************************************
 | 
						|
 | 
						|
 NOTES ON USER DEFINABLE OPTIONS:
 | 
						|
 | 
						|
 DEBUG: This turns on the printing of various debug information.
 | 
						|
 | 
						|
 ENABLE_PARITY: This turns on SCSI parity checking.  With the current
 | 
						|
 driver, all attached devices must support SCSI parity.  If none of your
 | 
						|
 devices support parity, then you can probably get the driver to work by
 | 
						|
 turning this option off.  I have no way of testing this, however, and it
 | 
						|
 would appear that no one ever uses this option.
 | 
						|
 | 
						|
 FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
 | 
						|
 18C30 chip have a 2k cache).  When this many 512 byte blocks are filled by
 | 
						|
 the SCSI device, an interrupt will be raised.  Therefore, this could be as
 | 
						|
 low as 0, or as high as 16.  Note, however, that values which are too high
 | 
						|
 or too low seem to prevent any interrupts from occurring, and thereby lock
 | 
						|
 up the machine.  I have found that 2 is a good number, but throughput may
 | 
						|
 be increased by changing this value to values which are close to 2.
 | 
						|
 Please let me know if you try any different values.
 | 
						|
 [*****Now a runtime option*****]
 | 
						|
 | 
						|
 RESELECTION: This is no longer an option, since I gave up trying to
 | 
						|
 implement it in version 4.x of this driver.  It did not improve
 | 
						|
 performance at all and made the driver unstable (because I never found one
 | 
						|
 of the two race conditions which were introduced by the multiple
 | 
						|
 outstanding command code).  The instability seems a very high price to pay
 | 
						|
 just so that you don't have to wait for the tape to rewind.  If you want
 | 
						|
 this feature implemented, send me patches.  I'll be happy to send a copy
 | 
						|
 of my (broken) driver to anyone who would like to see a copy.
 | 
						|
 | 
						|
 **************************************************************************/
 | 
						|
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/blkdev.h>
 | 
						|
#include <linux/errno.h>
 | 
						|
#include <linux/string.h>
 | 
						|
#include <linux/ioport.h>
 | 
						|
#include <linux/proc_fs.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/mca.h>
 | 
						|
#include <linux/spinlock.h>
 | 
						|
#include <scsi/scsicam.h>
 | 
						|
#include <linux/mca-legacy.h>
 | 
						|
 | 
						|
#include <asm/io.h>
 | 
						|
#include <asm/system.h>
 | 
						|
 | 
						|
#include "scsi.h"
 | 
						|
#include <scsi/scsi_host.h>
 | 
						|
 | 
						|
#define DRIVER_VERSION "v0.2 by ZP Gu<zpg@castle.net>"
 | 
						|
 | 
						|
/* START OF USER DEFINABLE OPTIONS */
 | 
						|
 | 
						|
#define DEBUG            0	/* Enable debugging output */
 | 
						|
#define ENABLE_PARITY    1	/* Enable SCSI Parity */
 | 
						|
 | 
						|
/* END OF USER DEFINABLE OPTIONS */
 | 
						|
 | 
						|
#if DEBUG
 | 
						|
#define EVERY_ACCESS     0	/* Write a line on every scsi access */
 | 
						|
#define ERRORS_ONLY      1	/* Only write a line if there is an error */
 | 
						|
#define DEBUG_MESSAGES   1	/* Debug MESSAGE IN phase */
 | 
						|
#define DEBUG_ABORT      1	/* Debug abort() routine */
 | 
						|
#define DEBUG_RESET      1	/* Debug reset() routine */
 | 
						|
#define DEBUG_RACE       1	/* Debug interrupt-driven race condition */
 | 
						|
#else
 | 
						|
#define EVERY_ACCESS     0	/* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
 | 
						|
#define ERRORS_ONLY      0
 | 
						|
#define DEBUG_MESSAGES   0
 | 
						|
#define DEBUG_ABORT      0
 | 
						|
#define DEBUG_RESET      0
 | 
						|
#define DEBUG_RACE       0
 | 
						|
#endif
 | 
						|
 | 
						|
/* Errors are reported on the line, so we don't need to report them again */
 | 
						|
#if EVERY_ACCESS
 | 
						|
#undef ERRORS_ONLY
 | 
						|
#define ERRORS_ONLY      0
 | 
						|
#endif
 | 
						|
 | 
						|
#if ENABLE_PARITY
 | 
						|
#define PARITY_MASK      0x08
 | 
						|
#else
 | 
						|
#define PARITY_MASK      0x00
 | 
						|
#endif
 | 
						|
 | 
						|
enum chip_type {
 | 
						|
	unknown = 0x00,
 | 
						|
	tmc1800 = 0x01,
 | 
						|
	tmc18c50 = 0x02,
 | 
						|
	tmc18c30 = 0x03,
 | 
						|
};
 | 
						|
 | 
						|
enum {
 | 
						|
	in_arbitration = 0x02,
 | 
						|
	in_selection = 0x04,
 | 
						|
	in_other = 0x08,
 | 
						|
	disconnect = 0x10,
 | 
						|
	aborted = 0x20,
 | 
						|
	sent_ident = 0x40,
 | 
						|
};
 | 
						|
 | 
						|
enum in_port_type {
 | 
						|
	Read_SCSI_Data = 0,
 | 
						|
	SCSI_Status = 1,
 | 
						|
	TMC_Status = 2,
 | 
						|
	FIFO_Status = 3,	/* tmc18c50/tmc18c30 only */
 | 
						|
	Interrupt_Cond = 4,	/* tmc18c50/tmc18c30 only */
 | 
						|
	LSB_ID_Code = 5,
 | 
						|
	MSB_ID_Code = 6,
 | 
						|
	Read_Loopback = 7,
 | 
						|
	SCSI_Data_NoACK = 8,
 | 
						|
	Interrupt_Status = 9,
 | 
						|
	Configuration1 = 10,
 | 
						|
	Configuration2 = 11,	/* tmc18c50/tmc18c30 only */
 | 
						|
	Read_FIFO = 12,
 | 
						|
	FIFO_Data_Count = 14
 | 
						|
};
 | 
						|
 | 
						|
enum out_port_type {
 | 
						|
	Write_SCSI_Data = 0,
 | 
						|
	SCSI_Cntl = 1,
 | 
						|
	Interrupt_Cntl = 2,
 | 
						|
	SCSI_Mode_Cntl = 3,
 | 
						|
	TMC_Cntl = 4,
 | 
						|
	Memory_Cntl = 5,	/* tmc18c50/tmc18c30 only */
 | 
						|
	Write_Loopback = 7,
 | 
						|
	IO_Control = 11,	/* tmc18c30 only */
 | 
						|
	Write_FIFO = 12
 | 
						|
};
 | 
						|
 | 
						|
struct fd_hostdata {
 | 
						|
	unsigned long _bios_base;
 | 
						|
	int _bios_major;
 | 
						|
	int _bios_minor;
 | 
						|
	volatile int _in_command;
 | 
						|
	Scsi_Cmnd *_current_SC;
 | 
						|
	enum chip_type _chip;
 | 
						|
	int _adapter_mask;
 | 
						|
	int _fifo_count;	/* Number of 512 byte blocks before INTR */
 | 
						|
 | 
						|
	char _adapter_name[64];
 | 
						|
#if DEBUG_RACE
 | 
						|
	volatile int _in_interrupt_flag;
 | 
						|
#endif
 | 
						|
 | 
						|
	int _SCSI_Mode_Cntl_port;
 | 
						|
	int _FIFO_Data_Count_port;
 | 
						|
	int _Interrupt_Cntl_port;
 | 
						|
	int _Interrupt_Status_port;
 | 
						|
	int _Interrupt_Cond_port;
 | 
						|
	int _Read_FIFO_port;
 | 
						|
	int _Read_SCSI_Data_port;
 | 
						|
	int _SCSI_Cntl_port;
 | 
						|
	int _SCSI_Data_NoACK_port;
 | 
						|
	int _SCSI_Status_port;
 | 
						|
	int _TMC_Cntl_port;
 | 
						|
	int _TMC_Status_port;
 | 
						|
	int _Write_FIFO_port;
 | 
						|
	int _Write_SCSI_Data_port;
 | 
						|
 | 
						|
	int _FIFO_Size;		/* = 0x2000;  8k FIFO for
 | 
						|
				   pre-tmc18c30 chips */
 | 
						|
	/* simple stats */
 | 
						|
	int _Bytes_Read;
 | 
						|
	int _Bytes_Written;
 | 
						|
	int _INTR_Processed;
 | 
						|
};
 | 
						|
 | 
						|
#define FD_MAX_HOSTS 3		/* enough? */
 | 
						|
 | 
						|
#define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata)
 | 
						|
#define bios_base             (HOSTDATA(shpnt)->_bios_base)
 | 
						|
#define bios_major            (HOSTDATA(shpnt)->_bios_major)
 | 
						|
#define bios_minor            (HOSTDATA(shpnt)->_bios_minor)
 | 
						|
#define in_command            (HOSTDATA(shpnt)->_in_command)
 | 
						|
#define current_SC            (HOSTDATA(shpnt)->_current_SC)
 | 
						|
#define chip                  (HOSTDATA(shpnt)->_chip)
 | 
						|
#define adapter_mask          (HOSTDATA(shpnt)->_adapter_mask)
 | 
						|
#define FIFO_COUNT            (HOSTDATA(shpnt)->_fifo_count)
 | 
						|
#define adapter_name          (HOSTDATA(shpnt)->_adapter_name)
 | 
						|
#if DEBUG_RACE
 | 
						|
#define in_interrupt_flag     (HOSTDATA(shpnt)->_in_interrupt_flag)
 | 
						|
#endif
 | 
						|
#define SCSI_Mode_Cntl_port   (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port)
 | 
						|
#define FIFO_Data_Count_port  (HOSTDATA(shpnt)->_FIFO_Data_Count_port)
 | 
						|
#define Interrupt_Cntl_port   (HOSTDATA(shpnt)->_Interrupt_Cntl_port)
 | 
						|
#define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port)
 | 
						|
#define Interrupt_Cond_port   (HOSTDATA(shpnt)->_Interrupt_Cond_port)
 | 
						|
#define Read_FIFO_port        (HOSTDATA(shpnt)->_Read_FIFO_port)
 | 
						|
#define Read_SCSI_Data_port   (HOSTDATA(shpnt)->_Read_SCSI_Data_port)
 | 
						|
#define SCSI_Cntl_port        (HOSTDATA(shpnt)->_SCSI_Cntl_port)
 | 
						|
#define SCSI_Data_NoACK_port  (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port)
 | 
						|
#define SCSI_Status_port      (HOSTDATA(shpnt)->_SCSI_Status_port)
 | 
						|
#define TMC_Cntl_port         (HOSTDATA(shpnt)->_TMC_Cntl_port)
 | 
						|
#define TMC_Status_port       (HOSTDATA(shpnt)->_TMC_Status_port)
 | 
						|
#define Write_FIFO_port       (HOSTDATA(shpnt)->_Write_FIFO_port)
 | 
						|
#define Write_SCSI_Data_port  (HOSTDATA(shpnt)->_Write_SCSI_Data_port)
 | 
						|
#define FIFO_Size             (HOSTDATA(shpnt)->_FIFO_Size)
 | 
						|
#define Bytes_Read            (HOSTDATA(shpnt)->_Bytes_Read)
 | 
						|
#define Bytes_Written         (HOSTDATA(shpnt)->_Bytes_Written)
 | 
						|
#define INTR_Processed        (HOSTDATA(shpnt)->_INTR_Processed)
 | 
						|
 | 
						|
struct fd_mcs_adapters_struct {
 | 
						|
	char *name;
 | 
						|
	int id;
 | 
						|
	enum chip_type fd_chip;
 | 
						|
	int fifo_size;
 | 
						|
	int fifo_count;
 | 
						|
};
 | 
						|
 | 
						|
#define REPLY_ID 0x5137
 | 
						|
 | 
						|
static struct fd_mcs_adapters_struct fd_mcs_adapters[] = {
 | 
						|
	{"Future Domain SCSI Adapter MCS-700(18C50)",
 | 
						|
	 0x60e9,
 | 
						|
	 tmc18c50,
 | 
						|
	 0x2000,
 | 
						|
	 4},
 | 
						|
	{"Future Domain SCSI Adapter MCS-600/700(TMC-1800)",
 | 
						|
	 0x6127,
 | 
						|
	 tmc1800,
 | 
						|
	 0x2000,
 | 
						|
	 4},
 | 
						|
	{"Reply Sound Blaster/SCSI Adapter",
 | 
						|
	 REPLY_ID,
 | 
						|
	 tmc18c30,
 | 
						|
	 0x800,
 | 
						|
	 2},
 | 
						|
};
 | 
						|
 | 
						|
#define FD_BRDS ARRAY_SIZE(fd_mcs_adapters)
 | 
						|
 | 
						|
static irqreturn_t fd_mcs_intr(int irq, void *dev_id);
 | 
						|
 | 
						|
static unsigned long addresses[] = { 0xc8000, 0xca000, 0xce000, 0xde000 };
 | 
						|
static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
 | 
						|
static unsigned short interrupts[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
 | 
						|
 | 
						|
/* host information */
 | 
						|
static int found = 0;
 | 
						|
static struct Scsi_Host *hosts[FD_MAX_HOSTS + 1] = { NULL };
 | 
						|
 | 
						|
static int user_fifo_count = 0;
 | 
						|
static int user_fifo_size = 0;
 | 
						|
 | 
						|
#ifndef MODULE
 | 
						|
static int __init fd_mcs_setup(char *str)
 | 
						|
{
 | 
						|
	static int done_setup = 0;
 | 
						|
	int ints[3];
 | 
						|
 | 
						|
	get_options(str, 3, ints);
 | 
						|
	if (done_setup++ || ints[0] < 1 || ints[0] > 2 || ints[1] < 1 || ints[1] > 16) {
 | 
						|
		printk("fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n");
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	user_fifo_count = ints[0] >= 1 ? ints[1] : 0;
 | 
						|
	user_fifo_size = ints[0] >= 2 ? ints[2] : 0;
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
__setup("fd_mcs=", fd_mcs_setup);
 | 
						|
#endif /* !MODULE */
 | 
						|
 | 
						|
static void print_banner(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	printk("scsi%d <fd_mcs>: ", shpnt->host_no);
 | 
						|
 | 
						|
	if (bios_base) {
 | 
						|
		printk("BIOS at 0x%lX", bios_base);
 | 
						|
	} else {
 | 
						|
		printk("No BIOS");
 | 
						|
	}
 | 
						|
 | 
						|
	printk(", HostID %d, %s Chip, IRQ %d, IO 0x%lX\n", shpnt->this_id, chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? "TMC-18C30" : (chip == tmc1800 ? "TMC-1800" : "Unknown")), shpnt->irq, shpnt->io_port);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void do_pause(unsigned amount)
 | 
						|
{				/* Pause for amount*10 milliseconds */
 | 
						|
	do {
 | 
						|
		mdelay(10);
 | 
						|
	} while (--amount);
 | 
						|
}
 | 
						|
 | 
						|
static void fd_mcs_make_bus_idle(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	outb(0, SCSI_Cntl_port);
 | 
						|
	outb(0, SCSI_Mode_Cntl_port);
 | 
						|
	if (chip == tmc18c50 || chip == tmc18c30)
 | 
						|
		outb(0x21 | PARITY_MASK, TMC_Cntl_port);	/* Clear forced intr. */
 | 
						|
	else
 | 
						|
		outb(0x01 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
}
 | 
						|
 | 
						|
static int fd_mcs_detect(struct scsi_host_template * tpnt)
 | 
						|
{
 | 
						|
	int loop;
 | 
						|
	struct Scsi_Host *shpnt;
 | 
						|
 | 
						|
	/* get id, port, bios, irq */
 | 
						|
	int slot;
 | 
						|
	u_char pos2, pos3, pos4;
 | 
						|
	int id, port, irq;
 | 
						|
	unsigned long bios;
 | 
						|
 | 
						|
	/* if not MCA machine, return */
 | 
						|
	if (!MCA_bus)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	/* changeable? */
 | 
						|
	id = 7;
 | 
						|
 | 
						|
	for (loop = 0; loop < FD_BRDS; loop++) {
 | 
						|
		slot = 0;
 | 
						|
		while (MCA_NOTFOUND != (slot = mca_find_adapter(fd_mcs_adapters[loop].id, slot))) {
 | 
						|
 | 
						|
			/* if we get this far, an adapter has been detected and is
 | 
						|
			   enabled */
 | 
						|
 | 
						|
			printk(KERN_INFO "scsi  <fd_mcs>: %s at slot %d\n", fd_mcs_adapters[loop].name, slot + 1);
 | 
						|
 | 
						|
			pos2 = mca_read_stored_pos(slot, 2);
 | 
						|
			pos3 = mca_read_stored_pos(slot, 3);
 | 
						|
			pos4 = mca_read_stored_pos(slot, 4);
 | 
						|
 | 
						|
			/* ready for next probe */
 | 
						|
			slot++;
 | 
						|
 | 
						|
			if (fd_mcs_adapters[loop].id == REPLY_ID) {	/* reply card */
 | 
						|
				static int reply_irq[] = { 10, 11, 14, 15 };
 | 
						|
 | 
						|
				bios = 0;	/* no bios */
 | 
						|
 | 
						|
				if (pos2 & 0x2)
 | 
						|
					port = ports[pos4 & 0x3];
 | 
						|
				else
 | 
						|
					continue;
 | 
						|
 | 
						|
				/* can't really disable it, same as irq=10 */
 | 
						|
				irq = reply_irq[((pos4 >> 2) & 0x1) + 2 * ((pos4 >> 4) & 0x1)];
 | 
						|
			} else {
 | 
						|
				bios = addresses[pos2 >> 6];
 | 
						|
				port = ports[(pos2 >> 4) & 0x03];
 | 
						|
				irq = interrupts[(pos2 >> 1) & 0x07];
 | 
						|
			}
 | 
						|
 | 
						|
			if (irq) {
 | 
						|
				/* claim the slot */
 | 
						|
				mca_set_adapter_name(slot - 1, fd_mcs_adapters[loop].name);
 | 
						|
 | 
						|
				/* check irq/region */
 | 
						|
				if (request_irq(irq, fd_mcs_intr, IRQF_SHARED, "fd_mcs", hosts)) {
 | 
						|
					printk(KERN_ERR "fd_mcs: interrupt is not available, skipping...\n");
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
 | 
						|
				/* request I/O region */
 | 
						|
				if (request_region(port, 0x10, "fd_mcs")) {
 | 
						|
					printk(KERN_ERR "fd_mcs: I/O region is already in use, skipping...\n");
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				/* register */
 | 
						|
				if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) {
 | 
						|
					printk(KERN_ERR "fd_mcs: scsi_register() failed\n");
 | 
						|
					release_region(port, 0x10);
 | 
						|
					free_irq(irq, hosts);
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
 | 
						|
 | 
						|
				/* save name */
 | 
						|
				strcpy(adapter_name, fd_mcs_adapters[loop].name);
 | 
						|
 | 
						|
				/* chip/fifo */
 | 
						|
				chip = fd_mcs_adapters[loop].fd_chip;
 | 
						|
				/* use boot time value if available */
 | 
						|
				FIFO_COUNT = user_fifo_count ? user_fifo_count : fd_mcs_adapters[loop].fifo_count;
 | 
						|
				FIFO_Size = user_fifo_size ? user_fifo_size : fd_mcs_adapters[loop].fifo_size;
 | 
						|
 | 
						|
/* FIXME: Do we need to keep this bit of code inside NOT_USED around at all? */
 | 
						|
#ifdef NOT_USED
 | 
						|
				/* *************************************************** */
 | 
						|
				/* Try to toggle 32-bit mode.  This only
 | 
						|
				   works on an 18c30 chip.  (User reports
 | 
						|
				   say this works, so we should switch to
 | 
						|
				   it in the near future.) */
 | 
						|
				outb(0x80, port + IO_Control);
 | 
						|
				if ((inb(port + Configuration2) & 0x80) == 0x80) {
 | 
						|
					outb(0x00, port + IO_Control);
 | 
						|
					if ((inb(port + Configuration2) & 0x80) == 0x00) {
 | 
						|
						chip = tmc18c30;
 | 
						|
						FIFO_Size = 0x800;	/* 2k FIFO */
 | 
						|
 | 
						|
						printk("FIRST: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				/* That should have worked, but appears to
 | 
						|
				   have problems.  Let's assume it is an
 | 
						|
				   18c30 if the RAM is disabled. */
 | 
						|
 | 
						|
				if (inb(port + Configuration2) & 0x02) {
 | 
						|
					chip = tmc18c30;
 | 
						|
					FIFO_Size = 0x800;	/* 2k FIFO */
 | 
						|
 | 
						|
					printk("SECOND: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
 | 
						|
				}
 | 
						|
				/* *************************************************** */
 | 
						|
#endif
 | 
						|
 | 
						|
				/* IBM/ANSI scsi scan ordering */
 | 
						|
				/* Stick this back in when the scsi.c changes are there */
 | 
						|
				shpnt->reverse_ordering = 1;
 | 
						|
 | 
						|
 | 
						|
				/* saving info */
 | 
						|
				hosts[found++] = shpnt;
 | 
						|
 | 
						|
				shpnt->this_id = id;
 | 
						|
				shpnt->irq = irq;
 | 
						|
				shpnt->io_port = port;
 | 
						|
				shpnt->n_io_port = 0x10;
 | 
						|
 | 
						|
				/* save */
 | 
						|
				bios_base = bios;
 | 
						|
				adapter_mask = (1 << id);
 | 
						|
 | 
						|
				/* save more */
 | 
						|
				SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl;
 | 
						|
				FIFO_Data_Count_port = port + FIFO_Data_Count;
 | 
						|
				Interrupt_Cntl_port = port + Interrupt_Cntl;
 | 
						|
				Interrupt_Status_port = port + Interrupt_Status;
 | 
						|
				Interrupt_Cond_port = port + Interrupt_Cond;
 | 
						|
				Read_FIFO_port = port + Read_FIFO;
 | 
						|
				Read_SCSI_Data_port = port + Read_SCSI_Data;
 | 
						|
				SCSI_Cntl_port = port + SCSI_Cntl;
 | 
						|
				SCSI_Data_NoACK_port = port + SCSI_Data_NoACK;
 | 
						|
				SCSI_Status_port = port + SCSI_Status;
 | 
						|
				TMC_Cntl_port = port + TMC_Cntl;
 | 
						|
				TMC_Status_port = port + TMC_Status;
 | 
						|
				Write_FIFO_port = port + Write_FIFO;
 | 
						|
				Write_SCSI_Data_port = port + Write_SCSI_Data;
 | 
						|
 | 
						|
				Bytes_Read = 0;
 | 
						|
				Bytes_Written = 0;
 | 
						|
				INTR_Processed = 0;
 | 
						|
 | 
						|
				/* say something */
 | 
						|
				print_banner(shpnt);
 | 
						|
 | 
						|
				/* reset */
 | 
						|
				outb(1, SCSI_Cntl_port);
 | 
						|
				do_pause(2);
 | 
						|
				outb(0, SCSI_Cntl_port);
 | 
						|
				do_pause(115);
 | 
						|
				outb(0, SCSI_Mode_Cntl_port);
 | 
						|
				outb(PARITY_MASK, TMC_Cntl_port);
 | 
						|
				/* done reset */
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (found == FD_MAX_HOSTS) {
 | 
						|
			printk("fd_mcs: detecting reached max=%d host adapters.\n", FD_MAX_HOSTS);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return found;
 | 
						|
}
 | 
						|
 | 
						|
static const char *fd_mcs_info(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	return adapter_name;
 | 
						|
}
 | 
						|
 | 
						|
static int TOTAL_INTR = 0;
 | 
						|
 | 
						|
/*
 | 
						|
 * inout : decides on the direction of the dataflow and the meaning of the 
 | 
						|
 *         variables
 | 
						|
 * buffer: If inout==FALSE data is being written to it else read from it
 | 
						|
 * *start: If inout==FALSE start of the valid data in the buffer
 | 
						|
 * offset: If inout==FALSE offset from the beginning of the imaginary file 
 | 
						|
 *         from which we start writing into the buffer
 | 
						|
 * length: If inout==FALSE max number of bytes to be written into the buffer 
 | 
						|
 *         else number of bytes in the buffer
 | 
						|
 */
 | 
						|
static int fd_mcs_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
 | 
						|
{
 | 
						|
	int len = 0;
 | 
						|
 | 
						|
	if (inout)
 | 
						|
		return (-ENOSYS);
 | 
						|
 | 
						|
	*start = buffer + offset;
 | 
						|
 | 
						|
	len += sprintf(buffer + len, "Future Domain MCS-600/700 Driver %s\n", DRIVER_VERSION);
 | 
						|
	len += sprintf(buffer + len, "HOST #%d: %s\n", shpnt->host_no, adapter_name);
 | 
						|
	len += sprintf(buffer + len, "FIFO Size=0x%x, FIFO Count=%d\n", FIFO_Size, FIFO_COUNT);
 | 
						|
	len += sprintf(buffer + len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d\n\n", TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written);
 | 
						|
 | 
						|
	if ((len -= offset) <= 0)
 | 
						|
		return 0;
 | 
						|
	if (len > length)
 | 
						|
		len = length;
 | 
						|
	return len;
 | 
						|
}
 | 
						|
 | 
						|
static int fd_mcs_select(struct Scsi_Host *shpnt, int target)
 | 
						|
{
 | 
						|
	int status;
 | 
						|
	unsigned long timeout;
 | 
						|
 | 
						|
	outb(0x82, SCSI_Cntl_port);	/* Bus Enable + Select */
 | 
						|
	outb(adapter_mask | (1 << target), SCSI_Data_NoACK_port);
 | 
						|
 | 
						|
	/* Stop arbitration and enable parity */
 | 
						|
	outb(PARITY_MASK, TMC_Cntl_port);
 | 
						|
 | 
						|
	timeout = 350;		/* 350mS -- because of timeouts
 | 
						|
				   (was 250mS) */
 | 
						|
 | 
						|
	do {
 | 
						|
		status = inb(SCSI_Status_port);	/* Read adapter status */
 | 
						|
		if (status & 1) {	/* Busy asserted */
 | 
						|
			/* Enable SCSI Bus (on error, should make bus idle with 0) */
 | 
						|
			outb(0x80, SCSI_Cntl_port);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
		udelay(1000);	/* wait one msec */
 | 
						|
	} while (--timeout);
 | 
						|
 | 
						|
	/* Make bus idle */
 | 
						|
	fd_mcs_make_bus_idle(shpnt);
 | 
						|
#if EVERY_ACCESS
 | 
						|
	if (!target)
 | 
						|
		printk("Selection failed\n");
 | 
						|
#endif
 | 
						|
#if ERRORS_ONLY
 | 
						|
	if (!target) {
 | 
						|
		static int flag = 0;
 | 
						|
 | 
						|
		if (!flag)	/* Skip first failure for all chips. */
 | 
						|
			++flag;
 | 
						|
		else
 | 
						|
			printk("fd_mcs: Selection failed\n");
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void my_done(struct Scsi_Host *shpnt, int error)
 | 
						|
{
 | 
						|
	if (in_command) {
 | 
						|
		in_command = 0;
 | 
						|
		outb(0x00, Interrupt_Cntl_port);
 | 
						|
		fd_mcs_make_bus_idle(shpnt);
 | 
						|
		current_SC->result = error;
 | 
						|
		current_SC->scsi_done(current_SC);
 | 
						|
	} else {
 | 
						|
		panic("fd_mcs: my_done() called outside of command\n");
 | 
						|
	}
 | 
						|
#if DEBUG_RACE
 | 
						|
	in_interrupt_flag = 0;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* only my_done needs to be protected  */
 | 
						|
static irqreturn_t fd_mcs_intr(int irq, void *dev_id)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	int status;
 | 
						|
	int done = 0;
 | 
						|
	unsigned data_count, tmp_count;
 | 
						|
 | 
						|
	int i = 0;
 | 
						|
	struct Scsi_Host *shpnt;
 | 
						|
 | 
						|
	TOTAL_INTR++;
 | 
						|
 | 
						|
	/* search for one adapter-response on shared interrupt */
 | 
						|
	while ((shpnt = hosts[i++])) {
 | 
						|
		if ((inb(TMC_Status_port)) & 1)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	/* return if some other device on this IRQ caused the interrupt */
 | 
						|
	if (!shpnt) {
 | 
						|
		return IRQ_NONE;
 | 
						|
	}
 | 
						|
 | 
						|
	INTR_Processed++;
 | 
						|
 | 
						|
	outb(0x00, Interrupt_Cntl_port);
 | 
						|
 | 
						|
	/* Abort calls my_done, so we do nothing here. */
 | 
						|
	if (current_SC->SCp.phase & aborted) {
 | 
						|
#if DEBUG_ABORT
 | 
						|
		printk("Interrupt after abort, ignoring\n");
 | 
						|
#endif
 | 
						|
		/* return IRQ_HANDLED; */
 | 
						|
	}
 | 
						|
#if DEBUG_RACE
 | 
						|
	++in_interrupt_flag;
 | 
						|
#endif
 | 
						|
 | 
						|
	if (current_SC->SCp.phase & in_arbitration) {
 | 
						|
		status = inb(TMC_Status_port);	/* Read adapter status */
 | 
						|
		if (!(status & 0x02)) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
			printk(" AFAIL ");
 | 
						|
#endif
 | 
						|
			spin_lock_irqsave(shpnt->host_lock, flags);
 | 
						|
			my_done(shpnt, DID_BUS_BUSY << 16);
 | 
						|
			spin_unlock_irqrestore(shpnt->host_lock, flags);
 | 
						|
			return IRQ_HANDLED;
 | 
						|
		}
 | 
						|
		current_SC->SCp.phase = in_selection;
 | 
						|
 | 
						|
		outb(0x40 | FIFO_COUNT, Interrupt_Cntl_port);
 | 
						|
 | 
						|
		outb(0x82, SCSI_Cntl_port);	/* Bus Enable + Select */
 | 
						|
		outb(adapter_mask | (1 << scmd_id(current_SC)), SCSI_Data_NoACK_port);
 | 
						|
 | 
						|
		/* Stop arbitration and enable parity */
 | 
						|
		outb(0x10 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
#if DEBUG_RACE
 | 
						|
		in_interrupt_flag = 0;
 | 
						|
#endif
 | 
						|
		return IRQ_HANDLED;
 | 
						|
	} else if (current_SC->SCp.phase & in_selection) {
 | 
						|
		status = inb(SCSI_Status_port);
 | 
						|
		if (!(status & 0x01)) {
 | 
						|
			/* Try again, for slow devices */
 | 
						|
			if (fd_mcs_select(shpnt, scmd_id(current_SC))) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
				printk(" SFAIL ");
 | 
						|
#endif
 | 
						|
				spin_lock_irqsave(shpnt->host_lock, flags);
 | 
						|
				my_done(shpnt, DID_NO_CONNECT << 16);
 | 
						|
				spin_unlock_irqrestore(shpnt->host_lock, flags);
 | 
						|
				return IRQ_HANDLED;
 | 
						|
			} else {
 | 
						|
#if EVERY_ACCESS
 | 
						|
				printk(" AltSel ");
 | 
						|
#endif
 | 
						|
				/* Stop arbitration and enable parity */
 | 
						|
				outb(0x10 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		current_SC->SCp.phase = in_other;
 | 
						|
		outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
 | 
						|
		outb(0x80, SCSI_Cntl_port);
 | 
						|
#if DEBUG_RACE
 | 
						|
		in_interrupt_flag = 0;
 | 
						|
#endif
 | 
						|
		return IRQ_HANDLED;
 | 
						|
	}
 | 
						|
 | 
						|
	/* current_SC->SCp.phase == in_other: this is the body of the routine */
 | 
						|
 | 
						|
	status = inb(SCSI_Status_port);
 | 
						|
 | 
						|
	if (status & 0x10) {	/* REQ */
 | 
						|
 | 
						|
		switch (status & 0x0e) {
 | 
						|
 | 
						|
		case 0x08:	/* COMMAND OUT */
 | 
						|
			outb(current_SC->cmnd[current_SC->SCp.sent_command++], Write_SCSI_Data_port);
 | 
						|
#if EVERY_ACCESS
 | 
						|
			printk("CMD = %x,", current_SC->cmnd[current_SC->SCp.sent_command - 1]);
 | 
						|
#endif
 | 
						|
			break;
 | 
						|
		case 0x00:	/* DATA OUT -- tmc18c50/tmc18c30 only */
 | 
						|
			if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
 | 
						|
				current_SC->SCp.have_data_in = -1;
 | 
						|
				outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case 0x04:	/* DATA IN -- tmc18c50/tmc18c30 only */
 | 
						|
			if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
 | 
						|
				current_SC->SCp.have_data_in = 1;
 | 
						|
				outb(0x90 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case 0x0c:	/* STATUS IN */
 | 
						|
			current_SC->SCp.Status = inb(Read_SCSI_Data_port);
 | 
						|
#if EVERY_ACCESS
 | 
						|
			printk("Status = %x, ", current_SC->SCp.Status);
 | 
						|
#endif
 | 
						|
#if ERRORS_ONLY
 | 
						|
			if (current_SC->SCp.Status && current_SC->SCp.Status != 2 && current_SC->SCp.Status != 8) {
 | 
						|
				printk("ERROR fd_mcs: target = %d, command = %x, status = %x\n", current_SC->device->id, current_SC->cmnd[0], current_SC->SCp.Status);
 | 
						|
			}
 | 
						|
#endif
 | 
						|
			break;
 | 
						|
		case 0x0a:	/* MESSAGE OUT */
 | 
						|
			outb(MESSAGE_REJECT, Write_SCSI_Data_port);	/* Reject */
 | 
						|
			break;
 | 
						|
		case 0x0e:	/* MESSAGE IN */
 | 
						|
			current_SC->SCp.Message = inb(Read_SCSI_Data_port);
 | 
						|
#if EVERY_ACCESS
 | 
						|
			printk("Message = %x, ", current_SC->SCp.Message);
 | 
						|
#endif
 | 
						|
			if (!current_SC->SCp.Message)
 | 
						|
				++done;
 | 
						|
#if DEBUG_MESSAGES || EVERY_ACCESS
 | 
						|
			if (current_SC->SCp.Message) {
 | 
						|
				printk("fd_mcs: message = %x\n", current_SC->SCp.Message);
 | 
						|
			}
 | 
						|
#endif
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (chip == tmc1800 && !current_SC->SCp.have_data_in && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
 | 
						|
		/* We have to get the FIFO direction
 | 
						|
		   correct, so I've made a table based
 | 
						|
		   on the SCSI Standard of which commands
 | 
						|
		   appear to require a DATA OUT phase.
 | 
						|
		 */
 | 
						|
		/*
 | 
						|
		   p. 94: Command for all device types
 | 
						|
		   CHANGE DEFINITION            40 DATA OUT
 | 
						|
		   COMPARE                      39 DATA OUT
 | 
						|
		   COPY                         18 DATA OUT
 | 
						|
		   COPY AND VERIFY              3a DATA OUT
 | 
						|
		   INQUIRY                      12 
 | 
						|
		   LOG SELECT                   4c DATA OUT
 | 
						|
		   LOG SENSE                    4d
 | 
						|
		   MODE SELECT (6)              15 DATA OUT
 | 
						|
		   MODE SELECT (10)             55 DATA OUT
 | 
						|
		   MODE SENSE (6)               1a
 | 
						|
		   MODE SENSE (10)              5a
 | 
						|
		   READ BUFFER                  3c
 | 
						|
		   RECEIVE DIAGNOSTIC RESULTS   1c
 | 
						|
		   REQUEST SENSE                03
 | 
						|
		   SEND DIAGNOSTIC              1d DATA OUT
 | 
						|
		   TEST UNIT READY              00
 | 
						|
		   WRITE BUFFER                 3b DATA OUT
 | 
						|
 | 
						|
		   p.178: Commands for direct-access devices (not listed on p. 94)
 | 
						|
		   FORMAT UNIT                  04 DATA OUT
 | 
						|
		   LOCK-UNLOCK CACHE            36
 | 
						|
		   PRE-FETCH                    34
 | 
						|
		   PREVENT-ALLOW MEDIUM REMOVAL 1e
 | 
						|
		   READ (6)/RECEIVE             08
 | 
						|
		   READ (10)                    3c
 | 
						|
		   READ CAPACITY                25
 | 
						|
		   READ DEFECT DATA (10)        37
 | 
						|
		   READ LONG                    3e
 | 
						|
		   REASSIGN BLOCKS              07 DATA OUT
 | 
						|
		   RELEASE                      17
 | 
						|
		   RESERVE                      16 DATA OUT
 | 
						|
		   REZERO UNIT/REWIND           01
 | 
						|
		   SEARCH DATA EQUAL (10)       31 DATA OUT
 | 
						|
		   SEARCH DATA HIGH (10)        30 DATA OUT
 | 
						|
		   SEARCH DATA LOW (10)         32 DATA OUT
 | 
						|
		   SEEK (6)                     0b
 | 
						|
		   SEEK (10)                    2b
 | 
						|
		   SET LIMITS (10)              33
 | 
						|
		   START STOP UNIT              1b
 | 
						|
		   SYNCHRONIZE CACHE            35
 | 
						|
		   VERIFY (10)                  2f
 | 
						|
		   WRITE (6)/PRINT/SEND         0a DATA OUT
 | 
						|
		   WRITE (10)/SEND              2a DATA OUT
 | 
						|
		   WRITE AND VERIFY (10)        2e DATA OUT
 | 
						|
		   WRITE LONG                   3f DATA OUT
 | 
						|
		   WRITE SAME                   41 DATA OUT ?
 | 
						|
 | 
						|
		   p. 261: Commands for sequential-access devices (not previously listed)
 | 
						|
		   ERASE                        19
 | 
						|
		   LOAD UNLOAD                  1b
 | 
						|
		   LOCATE                       2b
 | 
						|
		   READ BLOCK LIMITS            05
 | 
						|
		   READ POSITION                34
 | 
						|
		   READ REVERSE                 0f
 | 
						|
		   RECOVER BUFFERED DATA        14
 | 
						|
		   SPACE                        11
 | 
						|
		   WRITE FILEMARKS              10 ?
 | 
						|
 | 
						|
		   p. 298: Commands for printer devices (not previously listed)
 | 
						|
		   ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
 | 
						|
		   SLEW AND PRINT               0b DATA OUT  -- same as seek
 | 
						|
		   STOP PRINT                   1b
 | 
						|
		   SYNCHRONIZE BUFFER           10
 | 
						|
 | 
						|
		   p. 315: Commands for processor devices (not previously listed)
 | 
						|
 | 
						|
		   p. 321: Commands for write-once devices (not previously listed)
 | 
						|
		   MEDIUM SCAN                  38
 | 
						|
		   READ (12)                    a8
 | 
						|
		   SEARCH DATA EQUAL (12)       b1 DATA OUT
 | 
						|
		   SEARCH DATA HIGH (12)        b0 DATA OUT
 | 
						|
		   SEARCH DATA LOW (12)         b2 DATA OUT
 | 
						|
		   SET LIMITS (12)              b3
 | 
						|
		   VERIFY (12)                  af
 | 
						|
		   WRITE (12)                   aa DATA OUT
 | 
						|
		   WRITE AND VERIFY (12)        ae DATA OUT
 | 
						|
 | 
						|
		   p. 332: Commands for CD-ROM devices (not previously listed)
 | 
						|
		   PAUSE/RESUME                 4b
 | 
						|
		   PLAY AUDIO (10)              45
 | 
						|
		   PLAY AUDIO (12)              a5
 | 
						|
		   PLAY AUDIO MSF               47
 | 
						|
		   PLAY TRACK RELATIVE (10)     49
 | 
						|
		   PLAY TRACK RELATIVE (12)     a9
 | 
						|
		   READ HEADER                  44
 | 
						|
		   READ SUB-CHANNEL             42
 | 
						|
		   READ TOC                     43
 | 
						|
 | 
						|
		   p. 370: Commands for scanner devices (not previously listed)
 | 
						|
		   GET DATA BUFFER STATUS       34
 | 
						|
		   GET WINDOW                   25
 | 
						|
		   OBJECT POSITION              31
 | 
						|
		   SCAN                         1b
 | 
						|
		   SET WINDOW                   24 DATA OUT
 | 
						|
 | 
						|
		   p. 391: Commands for optical memory devices (not listed)
 | 
						|
		   ERASE (10)                   2c
 | 
						|
		   ERASE (12)                   ac
 | 
						|
		   MEDIUM SCAN                  38 DATA OUT
 | 
						|
		   READ DEFECT DATA (12)        b7
 | 
						|
		   READ GENERATION              29
 | 
						|
		   READ UPDATED BLOCK           2d
 | 
						|
		   UPDATE BLOCK                 3d DATA OUT
 | 
						|
 | 
						|
		   p. 419: Commands for medium changer devices (not listed)
 | 
						|
		   EXCHANGE MEDIUM              46
 | 
						|
		   INITIALIZE ELEMENT STATUS    07
 | 
						|
		   MOVE MEDIUM                  a5
 | 
						|
		   POSITION TO ELEMENT          2b
 | 
						|
		   READ ELEMENT STATUS          b8
 | 
						|
		   REQUEST VOL. ELEMENT ADDRESS b5
 | 
						|
		   SEND VOLUME TAG              b6 DATA OUT
 | 
						|
 | 
						|
		   p. 454: Commands for communications devices (not listed previously)
 | 
						|
		   GET MESSAGE (6)              08
 | 
						|
		   GET MESSAGE (10)             28
 | 
						|
		   GET MESSAGE (12)             a8
 | 
						|
		 */
 | 
						|
 | 
						|
		switch (current_SC->cmnd[0]) {
 | 
						|
		case CHANGE_DEFINITION:
 | 
						|
		case COMPARE:
 | 
						|
		case COPY:
 | 
						|
		case COPY_VERIFY:
 | 
						|
		case LOG_SELECT:
 | 
						|
		case MODE_SELECT:
 | 
						|
		case MODE_SELECT_10:
 | 
						|
		case SEND_DIAGNOSTIC:
 | 
						|
		case WRITE_BUFFER:
 | 
						|
 | 
						|
		case FORMAT_UNIT:
 | 
						|
		case REASSIGN_BLOCKS:
 | 
						|
		case RESERVE:
 | 
						|
		case SEARCH_EQUAL:
 | 
						|
		case SEARCH_HIGH:
 | 
						|
		case SEARCH_LOW:
 | 
						|
		case WRITE_6:
 | 
						|
		case WRITE_10:
 | 
						|
		case WRITE_VERIFY:
 | 
						|
		case 0x3f:
 | 
						|
		case 0x41:
 | 
						|
 | 
						|
		case 0xb1:
 | 
						|
		case 0xb0:
 | 
						|
		case 0xb2:
 | 
						|
		case 0xaa:
 | 
						|
		case 0xae:
 | 
						|
 | 
						|
		case 0x24:
 | 
						|
 | 
						|
		case 0x38:
 | 
						|
		case 0x3d:
 | 
						|
 | 
						|
		case 0xb6:
 | 
						|
 | 
						|
		case 0xea:	/* alternate number for WRITE LONG */
 | 
						|
 | 
						|
			current_SC->SCp.have_data_in = -1;
 | 
						|
			outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
			break;
 | 
						|
 | 
						|
		case 0x00:
 | 
						|
		default:
 | 
						|
 | 
						|
			current_SC->SCp.have_data_in = 1;
 | 
						|
			outb(0x90 | PARITY_MASK, TMC_Cntl_port);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (current_SC->SCp.have_data_in == -1) {	/* DATA OUT */
 | 
						|
		while ((data_count = FIFO_Size - inw(FIFO_Data_Count_port)) > 512) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
			printk("DC=%d, ", data_count);
 | 
						|
#endif
 | 
						|
			if (data_count > current_SC->SCp.this_residual)
 | 
						|
				data_count = current_SC->SCp.this_residual;
 | 
						|
			if (data_count > 0) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
				printk("%d OUT, ", data_count);
 | 
						|
#endif
 | 
						|
				if (data_count == 1) {
 | 
						|
					Bytes_Written++;
 | 
						|
 | 
						|
					outb(*current_SC->SCp.ptr++, Write_FIFO_port);
 | 
						|
					--current_SC->SCp.this_residual;
 | 
						|
				} else {
 | 
						|
					data_count >>= 1;
 | 
						|
					tmp_count = data_count << 1;
 | 
						|
					outsw(Write_FIFO_port, current_SC->SCp.ptr, data_count);
 | 
						|
					current_SC->SCp.ptr += tmp_count;
 | 
						|
					Bytes_Written += tmp_count;
 | 
						|
					current_SC->SCp.this_residual -= tmp_count;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (!current_SC->SCp.this_residual) {
 | 
						|
				if (current_SC->SCp.buffers_residual) {
 | 
						|
					--current_SC->SCp.buffers_residual;
 | 
						|
					++current_SC->SCp.buffer;
 | 
						|
					current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
 | 
						|
					current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
 | 
						|
				} else
 | 
						|
					break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else if (current_SC->SCp.have_data_in == 1) {	/* DATA IN */
 | 
						|
		while ((data_count = inw(FIFO_Data_Count_port)) > 0) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
			printk("DC=%d, ", data_count);
 | 
						|
#endif
 | 
						|
			if (data_count > current_SC->SCp.this_residual)
 | 
						|
				data_count = current_SC->SCp.this_residual;
 | 
						|
			if (data_count) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
				printk("%d IN, ", data_count);
 | 
						|
#endif
 | 
						|
				if (data_count == 1) {
 | 
						|
					Bytes_Read++;
 | 
						|
					*current_SC->SCp.ptr++ = inb(Read_FIFO_port);
 | 
						|
					--current_SC->SCp.this_residual;
 | 
						|
				} else {
 | 
						|
					data_count >>= 1;	/* Number of words */
 | 
						|
					tmp_count = data_count << 1;
 | 
						|
					insw(Read_FIFO_port, current_SC->SCp.ptr, data_count);
 | 
						|
					current_SC->SCp.ptr += tmp_count;
 | 
						|
					Bytes_Read += tmp_count;
 | 
						|
					current_SC->SCp.this_residual -= tmp_count;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (!current_SC->SCp.this_residual && current_SC->SCp.buffers_residual) {
 | 
						|
				--current_SC->SCp.buffers_residual;
 | 
						|
				++current_SC->SCp.buffer;
 | 
						|
				current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
 | 
						|
				current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (done) {
 | 
						|
#if EVERY_ACCESS
 | 
						|
		printk(" ** IN DONE %d ** ", current_SC->SCp.have_data_in);
 | 
						|
#endif
 | 
						|
 | 
						|
#if EVERY_ACCESS
 | 
						|
		printk("BEFORE MY_DONE. . .");
 | 
						|
#endif
 | 
						|
		spin_lock_irqsave(shpnt->host_lock, flags);
 | 
						|
		my_done(shpnt, (current_SC->SCp.Status & 0xff)
 | 
						|
			| ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
 | 
						|
		spin_unlock_irqrestore(shpnt->host_lock, flags);
 | 
						|
#if EVERY_ACCESS
 | 
						|
		printk("RETURNING.\n");
 | 
						|
#endif
 | 
						|
 | 
						|
	} else {
 | 
						|
		if (current_SC->SCp.phase & disconnect) {
 | 
						|
			outb(0xd0 | FIFO_COUNT, Interrupt_Cntl_port);
 | 
						|
			outb(0x00, SCSI_Cntl_port);
 | 
						|
		} else {
 | 
						|
			outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
 | 
						|
		}
 | 
						|
	}
 | 
						|
#if DEBUG_RACE
 | 
						|
	in_interrupt_flag = 0;
 | 
						|
#endif
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
static int fd_mcs_release(struct Scsi_Host *shpnt)
 | 
						|
{
 | 
						|
	int i, this_host, irq_usage;
 | 
						|
 | 
						|
	release_region(shpnt->io_port, shpnt->n_io_port);
 | 
						|
 | 
						|
	this_host = -1;
 | 
						|
	irq_usage = 0;
 | 
						|
	for (i = 0; i < found; i++) {
 | 
						|
		if (shpnt == hosts[i])
 | 
						|
			this_host = i;
 | 
						|
		if (shpnt->irq == hosts[i]->irq)
 | 
						|
			irq_usage++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* only for the last one */
 | 
						|
	if (1 == irq_usage)
 | 
						|
		free_irq(shpnt->irq, hosts);
 | 
						|
 | 
						|
	found--;
 | 
						|
 | 
						|
	for (i = this_host; i < found; i++)
 | 
						|
		hosts[i] = hosts[i + 1];
 | 
						|
 | 
						|
	hosts[found] = NULL;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fd_mcs_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->device->host;
 | 
						|
 | 
						|
	if (in_command) {
 | 
						|
		panic("fd_mcs: fd_mcs_queue() NOT REENTRANT!\n");
 | 
						|
	}
 | 
						|
#if EVERY_ACCESS
 | 
						|
	printk("queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
 | 
						|
		SCpnt->target, *(unsigned char *) SCpnt->cmnd,
 | 
						|
		scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
 | 
						|
#endif
 | 
						|
 | 
						|
	fd_mcs_make_bus_idle(shpnt);
 | 
						|
 | 
						|
	SCpnt->scsi_done = done;	/* Save this for the done function */
 | 
						|
	current_SC = SCpnt;
 | 
						|
 | 
						|
	/* Initialize static data */
 | 
						|
 | 
						|
	if (scsi_bufflen(current_SC)) {
 | 
						|
		current_SC->SCp.buffer = scsi_sglist(current_SC);
 | 
						|
		current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
 | 
						|
		current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
 | 
						|
		current_SC->SCp.buffers_residual = scsi_sg_count(current_SC) - 1;
 | 
						|
	} else {
 | 
						|
		current_SC->SCp.ptr = NULL;
 | 
						|
		current_SC->SCp.this_residual = 0;
 | 
						|
		current_SC->SCp.buffer = NULL;
 | 
						|
		current_SC->SCp.buffers_residual = 0;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	current_SC->SCp.Status = 0;
 | 
						|
	current_SC->SCp.Message = 0;
 | 
						|
	current_SC->SCp.have_data_in = 0;
 | 
						|
	current_SC->SCp.sent_command = 0;
 | 
						|
	current_SC->SCp.phase = in_arbitration;
 | 
						|
 | 
						|
	/* Start arbitration */
 | 
						|
	outb(0x00, Interrupt_Cntl_port);
 | 
						|
	outb(0x00, SCSI_Cntl_port);	/* Disable data drivers */
 | 
						|
	outb(adapter_mask, SCSI_Data_NoACK_port);	/* Set our id bit */
 | 
						|
	in_command = 1;
 | 
						|
	outb(0x20, Interrupt_Cntl_port);
 | 
						|
	outb(0x14 | PARITY_MASK, TMC_Cntl_port);	/* Start arbitration */
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#if DEBUG_ABORT || DEBUG_RESET
 | 
						|
static void fd_mcs_print_info(Scsi_Cmnd * SCpnt)
 | 
						|
{
 | 
						|
	unsigned int imr;
 | 
						|
	unsigned int irr;
 | 
						|
	unsigned int isr;
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->host;
 | 
						|
 | 
						|
	if (!SCpnt || !SCpnt->host) {
 | 
						|
		printk("fd_mcs: cannot provide detailed information\n");
 | 
						|
	}
 | 
						|
 | 
						|
	printk("%s\n", fd_mcs_info(SCpnt->host));
 | 
						|
	print_banner(SCpnt->host);
 | 
						|
	switch (SCpnt->SCp.phase) {
 | 
						|
	case in_arbitration:
 | 
						|
		printk("arbitration ");
 | 
						|
		break;
 | 
						|
	case in_selection:
 | 
						|
		printk("selection ");
 | 
						|
		break;
 | 
						|
	case in_other:
 | 
						|
		printk("other ");
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		printk("unknown ");
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	printk("(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
 | 
						|
		SCpnt->SCp.phase, SCpnt->device->id, *(unsigned char *) SCpnt->cmnd,
 | 
						|
		scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
 | 
						|
	printk("sent_command = %d, have_data_in = %d, timeout = %d\n", SCpnt->SCp.sent_command, SCpnt->SCp.have_data_in, SCpnt->timeout);
 | 
						|
#if DEBUG_RACE
 | 
						|
	printk("in_interrupt_flag = %d\n", in_interrupt_flag);
 | 
						|
#endif
 | 
						|
 | 
						|
	imr = (inb(0x0a1) << 8) + inb(0x21);
 | 
						|
	outb(0x0a, 0xa0);
 | 
						|
	irr = inb(0xa0) << 8;
 | 
						|
	outb(0x0a, 0x20);
 | 
						|
	irr += inb(0x20);
 | 
						|
	outb(0x0b, 0xa0);
 | 
						|
	isr = inb(0xa0) << 8;
 | 
						|
	outb(0x0b, 0x20);
 | 
						|
	isr += inb(0x20);
 | 
						|
 | 
						|
	/* Print out interesting information */
 | 
						|
	printk("IMR = 0x%04x", imr);
 | 
						|
	if (imr & (1 << shpnt->irq))
 | 
						|
		printk(" (masked)");
 | 
						|
	printk(", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr);
 | 
						|
 | 
						|
	printk("SCSI Status      = 0x%02x\n", inb(SCSI_Status_port));
 | 
						|
	printk("TMC Status       = 0x%02x", inb(TMC_Status_port));
 | 
						|
	if (inb(TMC_Status_port) & 1)
 | 
						|
		printk(" (interrupt)");
 | 
						|
	printk("\n");
 | 
						|
	printk("Interrupt Status = 0x%02x", inb(Interrupt_Status_port));
 | 
						|
	if (inb(Interrupt_Status_port) & 0x08)
 | 
						|
		printk(" (enabled)");
 | 
						|
	printk("\n");
 | 
						|
	if (chip == tmc18c50 || chip == tmc18c30) {
 | 
						|
		printk("FIFO Status      = 0x%02x\n", inb(shpnt->io_port + FIFO_Status));
 | 
						|
		printk("Int. Condition   = 0x%02x\n", inb(shpnt->io_port + Interrupt_Cond));
 | 
						|
	}
 | 
						|
	printk("Configuration 1  = 0x%02x\n", inb(shpnt->io_port + Configuration1));
 | 
						|
	if (chip == tmc18c50 || chip == tmc18c30)
 | 
						|
		printk("Configuration 2  = 0x%02x\n", inb(shpnt->io_port + Configuration2));
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static int fd_mcs_abort(Scsi_Cmnd * SCpnt)
 | 
						|
{
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->device->host;
 | 
						|
 | 
						|
	unsigned long flags;
 | 
						|
#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
 | 
						|
	printk("fd_mcs: abort ");
 | 
						|
#endif
 | 
						|
 | 
						|
	spin_lock_irqsave(shpnt->host_lock, flags);
 | 
						|
	if (!in_command) {
 | 
						|
#if EVERY_ACCESS || ERRORS_ONLY
 | 
						|
		printk(" (not in command)\n");
 | 
						|
#endif
 | 
						|
		spin_unlock_irqrestore(shpnt->host_lock, flags);
 | 
						|
		return FAILED;
 | 
						|
	} else
 | 
						|
		printk("\n");
 | 
						|
 | 
						|
#if DEBUG_ABORT
 | 
						|
	fd_mcs_print_info(SCpnt);
 | 
						|
#endif
 | 
						|
 | 
						|
	fd_mcs_make_bus_idle(shpnt);
 | 
						|
 | 
						|
	current_SC->SCp.phase |= aborted;
 | 
						|
 | 
						|
	current_SC->result = DID_ABORT << 16;
 | 
						|
 | 
						|
	/* Aborts are not done well. . . */
 | 
						|
	my_done(shpnt, DID_ABORT << 16);
 | 
						|
 | 
						|
	spin_unlock_irqrestore(shpnt->host_lock, flags);
 | 
						|
	return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static int fd_mcs_bus_reset(Scsi_Cmnd * SCpnt) {
 | 
						|
	struct Scsi_Host *shpnt = SCpnt->device->host;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
#if DEBUG_RESET
 | 
						|
	static int called_once = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
#if ERRORS_ONLY
 | 
						|
	if (SCpnt)
 | 
						|
		printk("fd_mcs: SCSI Bus Reset\n");
 | 
						|
#endif
 | 
						|
 | 
						|
#if DEBUG_RESET
 | 
						|
	if (called_once)
 | 
						|
		fd_mcs_print_info(current_SC);
 | 
						|
	called_once = 1;
 | 
						|
#endif
 | 
						|
 | 
						|
	spin_lock_irqsave(shpnt->host_lock, flags);
 | 
						|
 | 
						|
	outb(1, SCSI_Cntl_port);
 | 
						|
	do_pause(2);
 | 
						|
	outb(0, SCSI_Cntl_port);
 | 
						|
	do_pause(115);
 | 
						|
	outb(0, SCSI_Mode_Cntl_port);
 | 
						|
	outb(PARITY_MASK, TMC_Cntl_port);
 | 
						|
 | 
						|
	spin_unlock_irqrestore(shpnt->host_lock, flags);
 | 
						|
 | 
						|
	/* Unless this is the very first call (i.e., SCPnt == NULL), everything
 | 
						|
	   is probably hosed at this point.  We will, however, try to keep
 | 
						|
	   things going by informing the high-level code that we need help. */
 | 
						|
		return SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
#include <scsi/scsi_ioctl.h>
 | 
						|
 | 
						|
static int fd_mcs_biosparam(struct scsi_device * disk, struct block_device *bdev,
 | 
						|
			    sector_t capacity, int *info_array) 
 | 
						|
{
 | 
						|
	unsigned char *p = scsi_bios_ptable(bdev);
 | 
						|
	int size = capacity;
 | 
						|
 | 
						|
	/* BIOS >= 3.4 for MCA cards */
 | 
						|
	/* This algorithm was provided by Future Domain (much thanks!). */
 | 
						|
 | 
						|
	if (p && p[65] == 0xaa && p[64] == 0x55	/* Partition table valid */
 | 
						|
	    && p[4]) {	/* Partition type */
 | 
						|
		/* The partition table layout is as follows:
 | 
						|
 | 
						|
		   Start: 0x1b3h
 | 
						|
		   Offset: 0 = partition status
 | 
						|
		   1 = starting head
 | 
						|
		   2 = starting sector and cylinder (word, encoded)
 | 
						|
		   4 = partition type
 | 
						|
		   5 = ending head
 | 
						|
		   6 = ending sector and cylinder (word, encoded)
 | 
						|
		   8 = starting absolute sector (double word)
 | 
						|
		   c = number of sectors (double word)
 | 
						|
		   Signature: 0x1fe = 0x55aa
 | 
						|
 | 
						|
		   So, this algorithm assumes:
 | 
						|
		   1) the first partition table is in use,
 | 
						|
		   2) the data in the first entry is correct, and
 | 
						|
		   3) partitions never divide cylinders
 | 
						|
 | 
						|
		   Note that (1) may be FALSE for NetBSD (and other BSD flavors),
 | 
						|
		   as well as for Linux.  Note also, that Linux doesn't pay any
 | 
						|
		   attention to the fields that are used by this algorithm -- it
 | 
						|
		   only uses the absolute sector data.  Recent versions of Linux's
 | 
						|
		   fdisk(1) will fill this data in correctly, and forthcoming
 | 
						|
		   versions will check for consistency.
 | 
						|
 | 
						|
		   Checking for a non-zero partition type is not part of the
 | 
						|
		   Future Domain algorithm, but it seemed to be a reasonable thing
 | 
						|
		   to do, especially in the Linux and BSD worlds. */
 | 
						|
 | 
						|
		info_array[0] = p[5] + 1;	/* heads */
 | 
						|
		info_array[1] = p[6] & 0x3f;	/* sectors */
 | 
						|
	} else {
 | 
						|
		/* Note that this new method guarantees that there will always be
 | 
						|
		   less than 1024 cylinders on a platter.  This is good for drives
 | 
						|
		   up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
 | 
						|
		if ((unsigned int) size >= 0x7e0000U) 
 | 
						|
		{
 | 
						|
			info_array[0] = 0xff;	/* heads   = 255 */
 | 
						|
			info_array[1] = 0x3f;	/* sectors =  63 */
 | 
						|
		} else if ((unsigned int) size >= 0x200000U) {
 | 
						|
			info_array[0] = 0x80;	/* heads   = 128 */
 | 
						|
			info_array[1] = 0x3f;	/* sectors =  63 */
 | 
						|
		} else {
 | 
						|
			info_array[0] = 0x40;	/* heads   =  64 */
 | 
						|
			info_array[1] = 0x20;	/* sectors =  32 */
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* For both methods, compute the cylinders */
 | 
						|
	info_array[2] = (unsigned int) size / (info_array[0] * info_array[1]);
 | 
						|
	kfree(p);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct scsi_host_template driver_template = {
 | 
						|
	.proc_name			= "fd_mcs",
 | 
						|
	.proc_info			= fd_mcs_proc_info,
 | 
						|
	.detect				= fd_mcs_detect,
 | 
						|
	.release			= fd_mcs_release,
 | 
						|
	.info				= fd_mcs_info,
 | 
						|
	.queuecommand   		= fd_mcs_queue, 
 | 
						|
	.eh_abort_handler		= fd_mcs_abort,
 | 
						|
	.eh_bus_reset_handler		= fd_mcs_bus_reset,
 | 
						|
	.bios_param     		= fd_mcs_biosparam,
 | 
						|
	.can_queue      		= 1,
 | 
						|
	.this_id        		= 7,
 | 
						|
	.sg_tablesize   		= 64,
 | 
						|
	.cmd_per_lun    		= 1,
 | 
						|
	.use_clustering 		= DISABLE_CLUSTERING,
 | 
						|
};
 | 
						|
#include "scsi_module.c"
 | 
						|
 | 
						|
MODULE_LICENSE("GPL");
 |