aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/fd_mcs.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/fd_mcs.c
downloadlinux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
linux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.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/fd_mcs.c')
-rw-r--r--drivers/scsi/fd_mcs.c1369
1 files changed, 1369 insertions, 0 deletions
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
new file mode 100644
index 000000000000..770930e2aec3
--- /dev/null
+++ b/drivers/scsi/fd_mcs.c
@@ -0,0 +1,1369 @@
+/* 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 sizeof(fd_mcs_adapters)/sizeof(struct fd_mcs_adapters_struct)
+
+static irqreturn_t fd_mcs_intr(int irq, void *dev_id, struct pt_regs *regs);
+
+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;
+
+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);
+
+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(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, SA_SHIRQ, "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, struct pt_regs *regs)
+{
+ 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 << current_SC->device->id), 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, current_SC->device->id)) {
+#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 = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ 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 = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ 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 ERRORS_ONLY
+ if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
+ if ((unsigned char) (*((char *) current_SC->request_buffer + 2)) & 0x0f) {
+ unsigned char key;
+ unsigned char code;
+ unsigned char qualifier;
+
+ key = (unsigned char) (*((char *) current_SC->request_buffer + 2)) & 0x0f;
+ code = (unsigned char) (*((char *) current_SC->request_buffer + 12));
+ qualifier = (unsigned char) (*((char *) current_SC->request_buffer + 13));
+
+ if (key != UNIT_ATTENTION && !(key == NOT_READY && code == 0x04 && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
+ && !(key == ILLEGAL_REQUEST && (code == 0x25 || code == 0x24 || !code)))
+
+ printk("fd_mcs: REQUEST SENSE " "Key = %x, Code = %x, Qualifier = %x\n", key, code, qualifier);
+ }
+ }
+#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, SCpnt->use_sg, SCpnt->request_bufflen);
+#endif
+
+ fd_mcs_make_bus_idle(shpnt);
+
+ SCpnt->scsi_done = done; /* Save this for the done function */
+ current_SC = SCpnt;
+
+ /* Initialize static data */
+
+ if (current_SC->use_sg) {
+ current_SC->SCp.buffer = (struct scatterlist *) current_SC->request_buffer;
+ current_SC->SCp.ptr = page_address(current_SC->SCp.buffer->page) + current_SC->SCp.buffer->offset;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
+ } else {
+ current_SC->SCp.ptr = (char *) current_SC->request_buffer;
+ current_SC->SCp.this_residual = current_SC->request_bufflen;
+ 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, SCpnt->use_sg, SCpnt->request_bufflen);
+ 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_host_reset(Scsi_Cmnd * SCpnt)
+{
+ return FAILED;
+}
+
+static int fd_mcs_device_reset(Scsi_Cmnd * SCpnt)
+{
+ return FAILED;
+}
+
+static int fd_mcs_bus_reset(Scsi_Cmnd * SCpnt) {
+ struct Scsi_Host *shpnt = SCpnt->device->host;
+
+#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
+
+ 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);
+
+ /* 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 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,
+ .eh_host_reset_handler = fd_mcs_host_reset,
+ .eh_device_reset_handler = fd_mcs_device_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"