From patchwork Sun Apr 7 18:56:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ondrej Zary X-Patchwork-Id: 10888601 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 024481575 for ; Sun, 7 Apr 2019 19:02:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D2CD7283E7 for ; Sun, 7 Apr 2019 19:02:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C669728455; Sun, 7 Apr 2019 19:02:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DBFDE2861C for ; Sun, 7 Apr 2019 19:02:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726532AbfDGTBv (ORCPT ); Sun, 7 Apr 2019 15:01:51 -0400 Received: from hosting.gsystem.sk ([212.5.213.30]:55902 "EHLO hosting.gsystem.sk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726403AbfDGTBu (ORCPT ); Sun, 7 Apr 2019 15:01:50 -0400 Received: from gsql.ggedos.sk (off-20.infotel.telecom.sk [212.5.213.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by hosting.gsystem.sk (Postfix) with ESMTPSA id 853517A05CC; Sun, 7 Apr 2019 20:56:24 +0200 (CEST) From: Ondrej Zary To: Rik Faith Cc: linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 1/2] fdomain: Resurrect fdomain and fdomain_cs drivers Date: Sun, 7 Apr 2019 20:56:16 +0200 Message-Id: <20190407185617.8467-2-linux@zary.sk> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190407185617.8467-1-linux@zary.sk> References: <20190407185617.8467-1-linux@zary.sk> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This reverts commit e184f2bf4d9f1a3c612a8c1d67e73e9cf8ab5ab9. It does not compile but following patches will fix that. Signed-off-by: Ondrej Zary --- MAINTAINERS | 6 + drivers/scsi/Kconfig | 20 + drivers/scsi/Makefile | 1 + drivers/scsi/fdomain.c | 1783 ++++++++++++++++++++++++++++++++++++ drivers/scsi/fdomain.h | 24 + drivers/scsi/pcmcia/Kconfig | 9 + drivers/scsi/pcmcia/Makefile | 2 + drivers/scsi/pcmcia/fdomain_core.c | 2 + drivers/scsi/pcmcia/fdomain_stub.c | 209 +++++ 9 files changed, 2056 insertions(+) create mode 100644 drivers/scsi/fdomain.c create mode 100644 drivers/scsi/fdomain.h create mode 100644 drivers/scsi/pcmcia/fdomain_core.c create mode 100644 drivers/scsi/pcmcia/fdomain_stub.c diff --git a/MAINTAINERS b/MAINTAINERS index 2359e12e4c41..4ff767f79454 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6416,6 +6416,12 @@ F: tools/testing/selftests/futex/ F: tools/perf/bench/futex* F: Documentation/*futex* +FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit) +M: Rik Faith +L: linux-scsi@vger.kernel.org +S: Odd Fixes (e.g., new signatures) +F: drivers/scsi/fdomain.* + GCC PLUGINS M: Kees Cook R: Emese Revfy diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d528018e6fa8..58690189c8cf 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -663,6 +663,26 @@ config SCSI_DMX3191D To compile this driver as a module, choose M here: the module will be called dmx3191d. +config SCSI_FUTURE_DOMAIN + tristate "Future Domain 16xx SCSI/AHA-2920A support" + depends on (ISA || PCI) && SCSI + select CHECK_SIGNATURE + ---help--- + This is support for Future Domain's 16-bit SCSI host adapters + (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and + other adapters based on the Future Domain chipsets (Quantum + ISA-200S, ISA-250MG; Adaptec AHA-2920A; and at least one IBM board). + It is explained in section 3.7 of the SCSI-HOWTO, available from + . + + NOTE: Newer Adaptec AHA-2920C boards use the Adaptec AIC-7850 chip + and should use the aic7xxx driver ("Adaptec AIC7xxx chipset SCSI + controller support"). This Future Domain driver works with the older + Adaptec AHA-2920A boards with a Future Domain chip on them. + + To compile this driver as a module, choose M here: the + module will be called fdomain. + config SCSI_GDTH tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 8826111fdf4a..a9f80770d989 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/ obj-$(CONFIG_SCSI_PM8001) += pm8001/ obj-$(CONFIG_SCSI_ISCI) += isci/ obj-$(CONFIG_SCSI_IPS) += ips.o +obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas408.o qlogicfas.o obj-$(CONFIG_PCMCIA_QLOGIC) += qlogicfas408.o diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c new file mode 100644 index 000000000000..ebbe5a3e665d --- /dev/null +++ b/drivers/scsi/fdomain.c @@ -0,0 +1,1783 @@ +/* fdomain.c -- Future Domain TMC-16x0 SCSI driver + * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu + * Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org + * Author: Rickard E. Faith, faith@cs.unc.edu + * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org) + * Shared IRQ supported added 7/7/2001 Alan Cox + + * 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. + + ************************************************************************** + + SUMMARY: + + Future Domain BIOS versions supported for autodetect: + 2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61 + Chips are supported: + TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70 + Boards supported: + Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX + Future Domain TMC-3260 (PCI) + Quantum ISA-200S, ISA-250MG + Adaptec AHA-2920A (PCI) [BUT *NOT* AHA-2920C -- use aic7xxx instead] + IBM ? + LILO/INSMOD command-line options: + fdomain=,[,] + + + + NOTE: + + The Adaptec AHA-2920C has an Adaptec AIC-7850 chip on it. + Use the aic7xxx driver for this board. + + The Adaptec AHA-2920A has a Future Domain chip on it, so this is the right + driver for that card. Unfortunately, the boxes will probably just say + "2920", so you'll have to look on the card for a Future Domain logo, or a + letter after the 2920. + + + + THANKS: + + Thanks to Adaptec for providing PCI boards for testing. This finally + enabled me to test the PCI detection and correct it for PCI boards that do + not have a BIOS at a standard ISA location. For PCI boards, LILO/INSMOD + command-line options should no longer be needed. --RF 18Nov98 + + + + DESCRIPTION: + + This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680 + TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a + 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin + high-density external connector. The 1670 and 1680 have floppy disk + controllers built in. The TMC-3260 is a PCI bus card. + + Future Domain's older boards are based on the TMC-1800 chip, and this + driver was originally written for a TMC-1680 board with the TMC-1800 chip. + More recently, boards are being produced with the TMC-18C50 and TMC-18C30 + chips. The latest and greatest board may not work with this driver. If + you have to patch this driver so that it will recognize your board's BIOS + signature, then the driver may fail to function after the board is + detected. + + Please note that the drive ordering that Future Domain implemented in BIOS + versions 3.4 and 3.5 is the opposite of the order (currently) used by the + rest of the SCSI industry. If you have BIOS version 3.4 or 3.5, and have + more than one drive, then the drive ordering will be the reverse of that + which you see under DOS. For example, under DOS SCSI ID 0 will be D: and + SCSI ID 1 will be C: (the boot device). Under Linux, SCSI ID 0 will be + /dev/sda and SCSI ID 1 will be /dev/sdb. The Linux ordering is consistent + with that provided by all the other SCSI drivers for Linux. If you want + this changed, you will probably have to patch the higher level SCSI code. + If you do so, please send me patches that are protected by #ifdefs. + + If you have a TMC-8xx or TMC-9xx board, then this is not the driver for + your board. Please refer to the Seagate driver for more information and + possible support. + + + + HISTORY: + + Linux Driver Driver + Version Version Date Support/Notes + + 0.0 3 May 1992 V2.0 BIOS; 1800 chip + 0.97 1.9 28 Jul 1992 + 0.98.6 3.1 27 Nov 1992 + 0.99 3.2 9 Dec 1992 + + 0.99.3 3.3 10 Jan 1993 V3.0 BIOS + 0.99.5 3.5 18 Feb 1993 + 0.99.10 3.6 15 May 1993 V3.2 BIOS; 18C50 chip + 0.99.11 3.17 3 Jul 1993 (now under RCS) + 0.99.12 3.18 13 Aug 1993 + 0.99.14 5.6 31 Oct 1993 (reselection code removed) + + 0.99.15 5.9 23 Jan 1994 V3.4 BIOS (preliminary) + 1.0.8/1.1.1 5.15 1 Apr 1994 V3.4 BIOS; 18C30 chip (preliminary) + 1.0.9/1.1.3 5.16 7 Apr 1994 V3.4 BIOS; 18C30 chip + 1.1.38 5.18 30 Jul 1994 36C70 chip (PCI version of 18C30) + 1.1.62 5.20 2 Nov 1994 V3.5 BIOS + 1.1.73 5.22 7 Dec 1994 Quantum ISA-200S board; V2.0 BIOS + + 1.1.82 5.26 14 Jan 1995 V3.5 BIOS; TMC-1610M/MER/MEX board + 1.2.10 5.28 5 Jun 1995 Quantum ISA-250MG board; V2.0, V2.01 BIOS + 1.3.4 5.31 23 Jun 1995 PCI BIOS-32 detection (preliminary) + 1.3.7 5.33 4 Jul 1995 PCI BIOS-32 detection + 1.3.28 5.36 17 Sep 1995 V3.61 BIOS; LILO command-line support + 1.3.34 5.39 12 Oct 1995 V3.60 BIOS; /proc + 1.3.72 5.39 8 Feb 1996 Adaptec AHA-2920 board + 1.3.85 5.41 4 Apr 1996 + 2.0.12 5.44 8 Aug 1996 Use ID 7 for all PCI cards + 2.1.1 5.45 2 Oct 1996 Update ROM accesses for 2.1.x + 2.1.97 5.46 23 Apr 1998 Rewritten PCI detection routines [mj] + 2.1.11x 5.47 9 Aug 1998 Touched for 8 SCSI disk majors support + 5.48 18 Nov 1998 BIOS no longer needed for PCI detection + 2.2.0 5.50 28 Dec 1998 Support insmod parameters + + + REFERENCES USED: + + "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation, + 1990. + + "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain + Corporation, January 1992. + + "LXT SCSI Products: Specifications and OEM Technical Manual (Revision + B/September 1991)", Maxtor Corporation, 1991. + + "7213S product Manual (Revision P3)", Maxtor Corporation, 1992. + + "Draft Proposed American National Standard: Small Computer System + Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109, + revision 10h, October 17, 1991) + + Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric + Youngdale (ericy@cais.com), 1992. + + Private communication, Tuong Le (Future Domain Engineering department), + 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and + TMC-18C30 detection.) + + Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page + 60 (2.39: Disk Partition Table Layout). + + "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page + 6-1. + + + + NOTES ON REFERENCES: + + The Maxtor manuals were free. Maxtor telephone technical support is + great! + + The Future Domain manuals were $25 and $35. They document the chip, not + the TMC-16x0 boards, so some information I had to guess at. In 1992, + Future Domain sold DOS BIOS source for $250 and the UN*X driver source was + $750, but these required a non-disclosure agreement, so even if I could + have afforded them, they would *not* have been useful for writing this + publicly distributable driver. Future Domain technical support has + provided some information on the phone and have sent a few useful FAXs. + They have been much more helpful since they started to recognize that the + word "Linux" refers to an operating system :-). + + + + ALPHA TESTERS: + + There are many other alpha testers that come and go as the driver + develops. The people listed here were most helpful in times of greatest + need (mostly early on -- I've probably left out a few worthy people in + more recent times): + + Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken + Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@bruin@sterbbs.nl), Sakari + Aaltonen (sakaria@vipunen.hit.fi), John Rice (rice@xanth.cs.odu.edu), Brad + Yearwood (brad@optilink.com), and Ray Toy (toy@soho.crd.ge.com). + + Special thanks to Tien-Wan Yang (twyang@cs.uh.edu), who graciously lent me + his 18C50-based card for debugging. He is the sole reason that this + driver works with the 18C50 chip. + + Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for + the version 3.4 BIOS. + + Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing + patches that support the TMC-3260, a PCI bus card with the 36C70 chip. + The 36C70 chip appears to be "completely compatible" with the 18C30 chip. + + Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the + patch for the version 3.5 BIOS. + + Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the + patch for the Quantum ISA-200S SCSI adapter. + + Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards, to + Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to some + random TMC-1680 repackaged by IBM; and to Mintak Ng (mintak@panix.com) for + the version 3.61 BIOS signature. + + Thanks for Mark Singer (elf@netcom.com) and Richard Simpson + (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective + work on the Quantum RAM layout. + + Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for + providing patches for proper PCI BIOS32-mediated detection of the TMC-3260 + card (a PCI bus card with the 36C70 chip). Please send James PCI-related + bug reports. + + Thanks to Tom Cavin (tec@usa1.com) for preliminary command-line option + patches. + + New PCI detection code written by Martin Mares + + Insmod parameter code based on patches from Daniel Graham + . + + All of the alpha testers deserve much thanks. + + + + 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. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include "fdomain.h" + +#ifndef PCMCIA +MODULE_AUTHOR("Rickard E. Faith"); +MODULE_DESCRIPTION("Future domain SCSI driver"); +MODULE_LICENSE("GPL"); +#endif + + +#define VERSION "$Revision: 5.51 $" + +/* START OF USER DEFINABLE OPTIONS */ + +#define DEBUG 0 /* Enable debugging output */ +#define ENABLE_PARITY 1 /* Enable SCSI Parity */ +#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */ + +/* 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_DETECT 0 /* Debug fdomain_16x0_detect() */ +#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_DETECT 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 +}; + +/* .bss will zero all the static variables below */ +static int port_base; +static unsigned long bios_base; +static void __iomem * bios_mem; +static int bios_major; +static int bios_minor; +static int PCI_bus; +#ifdef CONFIG_PCI +static struct pci_dev *PCI_dev; +#endif +static int Quantum; /* Quantum board variant */ +static int interrupt_level; +static volatile int in_command; +static struct scsi_cmnd *current_SC; +static enum chip_type chip = unknown; +static int adapter_mask; +static int this_id; +static int setup_called; + +#if DEBUG_RACE +static volatile int in_interrupt_flag; +#endif + +static int FIFO_Size = 0x2000; /* 8k FIFO for + pre-tmc18c30 chips */ + +static irqreturn_t do_fdomain_16x0_intr( int irq, void *dev_id ); +/* Allow insmod parameters to be like LILO parameters. For example: + insmod fdomain fdomain=0x140,11 */ +static char * fdomain = NULL; +module_param(fdomain, charp, 0); + +#ifndef PCMCIA + +static unsigned long addresses[] = { + 0xc8000, + 0xca000, + 0xce000, + 0xde000, + 0xcc000, /* Extra addresses for PCI boards */ + 0xd0000, + 0xe0000, +}; +#define ADDRESS_COUNT ARRAY_SIZE(addresses) + +static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 }; +#define PORT_COUNT ARRAY_SIZE(ports) + +static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 }; + +#endif /* !PCMCIA */ + +/* + + READ THIS BEFORE YOU ADD A SIGNATURE! + + READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME! + + READ EVERY WORD, ESPECIALLY THE WORD *NOT* + + This driver works *ONLY* for Future Domain cards using the TMC-1800, + TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670, + and 1680. These are all 16-bit cards. + + The following BIOS signature signatures are for boards which do *NOT* + work with this driver (these TMC-8xx and TMC-9xx boards may work with the + Seagate driver): + + FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88 + FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89 + FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89 + FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90 + FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90 + FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90 + FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92 + + (The cards which do *NOT* work are all 8-bit cards -- although some of + them have a 16-bit form-factor, the upper 8-bits are used only for IRQs + and are *NOT* used for data. You can tell the difference by following + the tracings on the circuit board -- if only the IRQ lines are involved, + you have a "8-bit" card, and should *NOT* use this driver.) + +*/ + +#ifndef PCMCIA + +static struct signature { + const char *signature; + int sig_offset; + int sig_length; + int major_bios_version; + int minor_bios_version; + int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */ +} signatures[] = { + /* 1 2 3 4 5 6 */ + /* 123456789012345678901234567890123456789012345678901234567890 */ + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0, 0 }, + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 2, 0, 0 }, + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 }, + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 }, + { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 }, + { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0, 0 }, + { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2, 0 }, + { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 }, + { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 }, + { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 }, + { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 }, + { "IBM F1 P264/32", 5, 14, 3, -1, 1 }, + /* This next signature may not be a 3.5 bios */ + { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 }, + { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 }, + { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 }, + { "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 3, 6, 0 }, + { "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 3, 6, 0 }, + { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 }, + + /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE + Also, fix the disk geometry code for your signature and send your + changes for faith@cs.unc.edu. Above all, do *NOT* change any old + signatures! + + Note that the last line will match a "generic" 18XX bios. Because + Future Domain has changed the host SCSI ID and/or the location of the + geometry information in the on-board RAM area for each of the first + three BIOS's, it is still important to enter a fully qualified + signature in the table for any new BIOS's (after the host SCSI ID and + geometry location are verified). */ +}; + +#define SIGNATURE_COUNT ARRAY_SIZE(signatures) + +#endif /* !PCMCIA */ + +static void print_banner( struct Scsi_Host *shpnt ) +{ + if (!shpnt) return; /* This won't ever happen */ + + if (bios_major < 0 && bios_minor < 0) { + printk(KERN_INFO "scsi%d: No BIOS; using scsi id %d\n", + shpnt->host_no, shpnt->this_id); + } else { + printk(KERN_INFO "scsi%d: BIOS version ", shpnt->host_no); + + if (bios_major >= 0) printk("%d.", bios_major); + else printk("?."); + + if (bios_minor >= 0) printk("%d", bios_minor); + else printk("?."); + + printk( " at 0x%lx using scsi id %d\n", + bios_base, shpnt->this_id ); + } + + /* If this driver works for later FD PCI + boards, we will have to modify banner + for additional PCI cards, but for now if + it's PCI it's a TMC-3260 - JTM */ + printk(KERN_INFO "scsi%d: %s chip at 0x%x irq ", + shpnt->host_no, + chip == tmc1800 ? "TMC-1800" : (chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30") : "Unknown")), + port_base); + + if (interrupt_level) + printk("%d", interrupt_level); + else + printk(""); + + printk( "\n" ); +} + +int fdomain_setup(char *str) +{ + int ints[4]; + + (void)get_options(str, ARRAY_SIZE(ints), ints); + + if (setup_called++ || ints[0] < 2 || ints[0] > 3) { + printk(KERN_INFO "scsi: Usage: fdomain=,[,]\n"); + printk(KERN_ERR "scsi: Bad LILO/INSMOD parameters?\n"); + return 0; + } + + port_base = ints[0] >= 1 ? ints[1] : 0; + interrupt_level = ints[0] >= 2 ? ints[2] : 0; + this_id = ints[0] >= 3 ? ints[3] : 0; + + bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */ + ++setup_called; + return 1; +} + +__setup("fdomain=", fdomain_setup); + + +static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */ +{ + mdelay(10*amount); +} + +static inline void fdomain_make_bus_idle( void ) +{ + outb(0, port_base + SCSI_Cntl); + outb(0, port_base + SCSI_Mode_Cntl); + if (chip == tmc18c50 || chip == tmc18c30) + outb(0x21 | PARITY_MASK, port_base + TMC_Cntl); /* Clear forced intr. */ + else + outb(0x01 | PARITY_MASK, port_base + TMC_Cntl); +} + +static int fdomain_is_valid_port( int port ) +{ +#if DEBUG_DETECT + printk( " (%x%x),", + inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) ); +#endif + + /* The MCA ID is a unique id for each MCA compatible board. We + are using ISA boards, but Future Domain provides the MCA ID + anyway. We can use this ID to ensure that this is a Future + Domain TMC-1660/TMC-1680. + */ + + if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */ + if (inb( port + LSB_ID_Code ) != 0x27) return 0; + if (inb( port + MSB_ID_Code ) != 0x61) return 0; + chip = tmc1800; + } else { /* test for 0xe960 id */ + if (inb( port + MSB_ID_Code ) != 0x60) return 0; + chip = tmc18c50; + + /* 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 */ + } + } + /* If that failed, we are an 18c50. */ + } + + return 1; +} + +static int fdomain_test_loopback( void ) +{ + int i; + int result; + + for (i = 0; i < 255; i++) { + outb( i, port_base + Write_Loopback ); + result = inb( port_base + Read_Loopback ); + if (i != result) + return 1; + } + return 0; +} + +#ifndef PCMCIA + +/* fdomain_get_irq assumes that we have a valid MCA ID for a + TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the + bios_base matches these ports. If someone was unlucky enough to have + purchased more than one Future Domain board, then they will have to + modify this code, as we only detect one board here. [The one with the + lowest bios_base.] + + Note that this routine is only used for systems without a PCI BIOS32 + (e.g., ISA bus). For PCI bus systems, this routine will likely fail + unless one of the IRQs listed in the ints array is used by the board. + Sometimes it is possible to use the computer's BIOS setup screen to + configure a PCI system so that one of these IRQs will be used by the + Future Domain card. */ + +static int fdomain_get_irq( int base ) +{ + int options = inb(base + Configuration1); + +#if DEBUG_DETECT + printk("scsi: Options = %x\n", options); +#endif + + /* Check for board with lowest bios_base -- + this isn't valid for the 18c30 or for + boards on the PCI bus, so just assume we + have the right board. */ + + if (chip != tmc18c30 && !PCI_bus && addresses[(options & 0xc0) >> 6 ] != bios_base) + return 0; + return ints[(options & 0x0e) >> 1]; +} + +static int fdomain_isa_detect( int *irq, int *iobase ) +{ + int i, j; + int base = 0xdeadbeef; + int flag = 0; + +#if DEBUG_DETECT + printk( "scsi: fdomain_isa_detect:" ); +#endif + + for (i = 0; i < ADDRESS_COUNT; i++) { + void __iomem *p = ioremap(addresses[i], 0x2000); + if (!p) + continue; +#if DEBUG_DETECT + printk( " %lx(%lx),", addresses[i], bios_base ); +#endif + for (j = 0; j < SIGNATURE_COUNT; j++) { + if (check_signature(p + signatures[j].sig_offset, + signatures[j].signature, + signatures[j].sig_length )) { + bios_major = signatures[j].major_bios_version; + bios_minor = signatures[j].minor_bios_version; + PCI_bus = (signatures[j].flag == 1); + Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0; + bios_base = addresses[i]; + bios_mem = p; + goto found; + } + } + iounmap(p); + } + +found: + if (bios_major == 2) { + /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM. + Assuming the ROM is enabled (otherwise we wouldn't have been + able to read the ROM signature :-), then the ROM sets up the + RAM area with some magic numbers, such as a list of port + base addresses and a list of the disk "geometry" reported to + DOS (this geometry has nothing to do with physical geometry). + */ + + switch (Quantum) { + case 2: /* ISA_200S */ + case 3: /* ISA_250MG */ + base = readb(bios_mem + 0x1fa2) + (readb(bios_mem + 0x1fa3) << 8); + break; + case 4: /* ISA_200S (another one) */ + base = readb(bios_mem + 0x1fa3) + (readb(bios_mem + 0x1fa4) << 8); + break; + default: + base = readb(bios_mem + 0x1fcc) + (readb(bios_mem + 0x1fcd) << 8); + break; + } + +#if DEBUG_DETECT + printk( " %x,", base ); +#endif + + for (i = 0; i < PORT_COUNT; i++) { + if (base == ports[i]) { + if (!request_region(base, 0x10, "fdomain")) + break; + if (!fdomain_is_valid_port(base)) { + release_region(base, 0x10); + break; + } + *irq = fdomain_get_irq( base ); + *iobase = base; + return 1; + } + } + + /* This is a bad sign. It usually means that someone patched the + BIOS signature list (the signatures variable) to contain a BIOS + signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */ + +#if DEBUG_DETECT + printk( " RAM FAILED, " ); +#endif + } + + /* Anyway, the alternative to finding the address in the RAM is to just + search through every possible port address for one that is attached + to the Future Domain card. Don't panic, though, about reading all + these random port addresses -- there are rumors that the Future + Domain BIOS does something very similar. + + Do not, however, check ports which the kernel knows are being used by + another driver. */ + + for (i = 0; i < PORT_COUNT; i++) { + base = ports[i]; + if (!request_region(base, 0x10, "fdomain")) { +#if DEBUG_DETECT + printk( " (%x inuse),", base ); +#endif + continue; + } +#if DEBUG_DETECT + printk( " %x,", base ); +#endif + flag = fdomain_is_valid_port(base); + if (flag) + break; + release_region(base, 0x10); + } + +#if DEBUG_DETECT + if (flag) printk( " SUCCESS\n" ); + else printk( " FAILURE\n" ); +#endif + + if (!flag) return 0; /* iobase not found */ + + *irq = fdomain_get_irq( base ); + *iobase = base; + + return 1; /* success */ +} + +#else /* PCMCIA */ + +static int fdomain_isa_detect( int *irq, int *iobase ) +{ + if (irq) + *irq = 0; + if (iobase) + *iobase = 0; + return 0; +} + +#endif /* !PCMCIA */ + + +/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int* + iobase) This function gets the Interrupt Level and I/O base address from + the PCI configuration registers. */ + +#ifdef CONFIG_PCI +static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_pdev ) +{ + unsigned int pci_irq; /* PCI interrupt line */ + unsigned long pci_base; /* PCI I/O base address */ + struct pci_dev *pdev = NULL; + +#if DEBUG_DETECT + /* Tell how to print a list of the known PCI devices from bios32 and + list vendor and device IDs being used if in debug mode. */ + + printk( "scsi: INFO: use lspci -v to see list of PCI devices\n" ); + printk( "scsi: TMC-3260 detect:" + " Using Vendor ID: 0x%x and Device ID: 0x%x\n", + PCI_VENDOR_ID_FD, + PCI_DEVICE_ID_FD_36C70 ); +#endif + + if ((pdev = pci_get_device(PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, pdev)) == NULL) + return 0; + if (pci_enable_device(pdev)) + goto fail; + +#if DEBUG_DETECT + printk( "scsi: TMC-3260 detect:" + " PCI bus %u, device %u, function %u\n", + pdev->bus->number, + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); +#endif + + /* We now have the appropriate device function for the FD board so we + just read the PCI config info from the registers. */ + + pci_base = pci_resource_start(pdev, 0); + pci_irq = pdev->irq; + + if (!request_region( pci_base, 0x10, "fdomain" )) + goto fail; + + /* Now we have the I/O base address and interrupt from the PCI + configuration registers. */ + + *irq = pci_irq; + *iobase = pci_base; + *ret_pdev = pdev; + +#if DEBUG_DETECT + printk( "scsi: TMC-3260 detect:" + " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base ); +#endif + + if (!fdomain_is_valid_port(pci_base)) { + printk(KERN_ERR "scsi: PCI card detected, but driver not loaded (invalid port)\n" ); + release_region(pci_base, 0x10); + goto fail; + } + + /* Fill in a few global variables. Ugh. */ + bios_major = bios_minor = -1; + PCI_bus = 1; + PCI_dev = pdev; + Quantum = 0; + bios_base = 0; + + return 1; +fail: + pci_dev_put(pdev); + return 0; +} + +#endif + +struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) +{ + int retcode; + struct Scsi_Host *shpnt; + struct pci_dev *pdev = NULL; + + if (setup_called) { +#if DEBUG_DETECT + printk( "scsi: No BIOS, using port_base = 0x%x, irq = %d\n", + port_base, interrupt_level ); +#endif + if (!request_region(port_base, 0x10, "fdomain")) { + printk( "scsi: port 0x%x is busy\n", port_base ); + printk( "scsi: Bad LILO/INSMOD parameters?\n" ); + return NULL; + } + if (!fdomain_is_valid_port( port_base )) { + printk( "scsi: Cannot locate chip at port base 0x%x\n", + port_base ); + printk( "scsi: Bad LILO/INSMOD parameters?\n" ); + release_region(port_base, 0x10); + return NULL; + } + } else { + int flag = 0; + +#ifdef CONFIG_PCI + /* Try PCI detection first */ + flag = fdomain_pci_bios_detect( &interrupt_level, &port_base, &pdev ); +#endif + if (!flag) { + /* Then try ISA bus detection */ + flag = fdomain_isa_detect( &interrupt_level, &port_base ); + + if (!flag) { + printk( "scsi: Detection failed (no card)\n" ); + return NULL; + } + } + } + + fdomain_16x0_host_reset(NULL); + + if (fdomain_test_loopback()) { + printk(KERN_ERR "scsi: Detection failed (loopback test failed at port base 0x%x)\n", port_base); + if (setup_called) { + printk(KERN_ERR "scsi: Bad LILO/INSMOD parameters?\n"); + } + goto fail; + } + + if (this_id) { + tpnt->this_id = (this_id & 0x07); + adapter_mask = (1 << tpnt->this_id); + } else { + if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) { + tpnt->this_id = 7; + adapter_mask = 0x80; + } else { + tpnt->this_id = 6; + adapter_mask = 0x40; + } + } + +/* Print out a banner here in case we can't + get resources. */ + + shpnt = scsi_register( tpnt, 0 ); + if(shpnt == NULL) { + release_region(port_base, 0x10); + return NULL; + } + shpnt->irq = interrupt_level; + shpnt->io_port = port_base; + shpnt->n_io_port = 0x10; + print_banner( shpnt ); + + /* Log IRQ with kernel */ + if (!interrupt_level) { + printk(KERN_ERR "scsi: Card Detected, but driver not loaded (no IRQ)\n" ); + goto fail; + } else { + /* Register the IRQ with the kernel */ + + retcode = request_irq( interrupt_level, + do_fdomain_16x0_intr, pdev?IRQF_SHARED:0, "fdomain", shpnt); + + if (retcode < 0) { + if (retcode == -EINVAL) { + printk(KERN_ERR "scsi: IRQ %d is bad!\n", interrupt_level ); + printk(KERN_ERR " This shouldn't happen!\n" ); + printk(KERN_ERR " Send mail to faith@acm.org\n" ); + } else if (retcode == -EBUSY) { + printk(KERN_ERR "scsi: IRQ %d is already in use!\n", interrupt_level ); + printk(KERN_ERR " Please use another IRQ!\n" ); + } else { + printk(KERN_ERR "scsi: Error getting IRQ %d\n", interrupt_level ); + printk(KERN_ERR " This shouldn't happen!\n" ); + printk(KERN_ERR " Send mail to faith@acm.org\n" ); + } + printk(KERN_ERR "scsi: Detected, but driver not loaded (IRQ)\n" ); + goto fail; + } + } + return shpnt; +fail: + pci_dev_put(pdev); + release_region(port_base, 0x10); + return NULL; +} + +static int fdomain_16x0_detect(struct scsi_host_template *tpnt) +{ + if (fdomain) + fdomain_setup(fdomain); + return (__fdomain_16x0_detect(tpnt) != NULL); +} + +static const char *fdomain_16x0_info( struct Scsi_Host *ignore ) +{ + static char buffer[128]; + char *pt; + + strcpy( buffer, "Future Domain 16-bit SCSI Driver Version" ); + if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */ + strcat( buffer, strchr( VERSION, ':' ) + 1 ); + pt = strrchr( buffer, '$') - 1; + if (!pt) /* Stripped RCS Revision string? */ + pt = buffer + strlen( buffer ) - 1; + if (*pt != ' ') + ++pt; + *pt = '\0'; + } else { /* Assume VERSION is a number */ + strcat( buffer, " " VERSION ); + } + + return buffer; +} + +#if 0 +static int fdomain_arbitrate( void ) +{ + int status = 0; + unsigned long timeout; + +#if EVERY_ACCESS + printk( "fdomain_arbitrate()\n" ); +#endif + + outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */ + outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */ + outb(0x04 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */ + + timeout = 500; + do { + status = inb(port_base + TMC_Status); /* Read adapter status */ + if (status & 0x02) /* Arbitration complete */ + return 0; + mdelay(1); /* Wait one millisecond */ + } while (--timeout); + + /* Make bus idle */ + fdomain_make_bus_idle(); + +#if EVERY_ACCESS + printk( "Arbitration failed, status = %x\n", status ); +#endif +#if ERRORS_ONLY + printk( "scsi: Arbitration failed, status = %x\n", status ); +#endif + return 1; +} +#endif + +static int fdomain_select( int target ) +{ + int status; + unsigned long timeout; +#if ERRORS_ONLY + static int flag = 0; +#endif + + outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */ + outb(adapter_mask | (1 << target), port_base + SCSI_Data_NoACK); + + /* Stop arbitration and enable parity */ + outb(PARITY_MASK, port_base + TMC_Cntl); + + timeout = 350; /* 350 msec */ + + do { + status = inb(port_base + SCSI_Status); /* Read adapter status */ + if (status & 1) { /* Busy asserted */ + /* Enable SCSI Bus (on error, should make bus idle with 0) */ + outb(0x80, port_base + SCSI_Cntl); + return 0; + } + mdelay(1); /* wait one msec */ + } while (--timeout); + /* Make bus idle */ + fdomain_make_bus_idle(); +#if EVERY_ACCESS + if (!target) printk( "Selection failed\n" ); +#endif +#if ERRORS_ONLY + if (!target) { + if (!flag) /* Skip first failure for all chips. */ + ++flag; + else + printk( "scsi: Selection failed\n" ); + } +#endif + return 1; +} + +static void my_done(int error) +{ + if (in_command) { + in_command = 0; + outb(0x00, port_base + Interrupt_Cntl); + fdomain_make_bus_idle(); + current_SC->result = error; + if (current_SC->scsi_done) + current_SC->scsi_done( current_SC ); + else panic( "scsi: current_SC->scsi_done() == NULL" ); + } else { + panic( "scsi: my_done() called outside of command\n" ); + } +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif +} + +static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id) +{ + unsigned long flags; + int status; + int done = 0; + unsigned data_count; + + /* The fdomain_16x0_intr is only called via + the interrupt handler. The goal of the + sti() here is to allow other + interruptions while this routine is + running. */ + + /* Check for other IRQ sources */ + if ((inb(port_base + TMC_Status) & 0x01) == 0) + return IRQ_NONE; + + /* It is our IRQ */ + outb(0x00, port_base + Interrupt_Cntl); + + /* We usually have one spurious interrupt after each command. Ignore it. */ + if (!in_command || !current_SC) { /* Spurious interrupt */ +#if EVERY_ACCESS + printk( "Spurious interrupt, in_command = %d, current_SC = %x\n", + in_command, current_SC ); +#endif + return IRQ_NONE; + } + + /* Abort calls my_done, so we do nothing here. */ + if (current_SC->SCp.phase & aborted) { +#if DEBUG_ABORT + printk( "scsi: 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(port_base + TMC_Status); /* Read adapter status */ + if (!(status & 0x02)) { +#if EVERY_ACCESS + printk( " AFAIL " ); +#endif + spin_lock_irqsave(current_SC->device->host->host_lock, flags); + my_done( DID_BUS_BUSY << 16 ); + spin_unlock_irqrestore(current_SC->device->host->host_lock, flags); + return IRQ_HANDLED; + } + current_SC->SCp.phase = in_selection; + + outb(0x40 | FIFO_COUNT, port_base + Interrupt_Cntl); + + outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */ + outb(adapter_mask | (1 << scmd_id(current_SC)), port_base + SCSI_Data_NoACK); + + /* Stop arbitration and enable parity */ + outb(0x10 | PARITY_MASK, port_base + TMC_Cntl); +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif + return IRQ_HANDLED; + } else if (current_SC->SCp.phase & in_selection) { + status = inb(port_base + SCSI_Status); + if (!(status & 0x01)) { + /* Try again, for slow devices */ + if (fdomain_select( scmd_id(current_SC) )) { +#if EVERY_ACCESS + printk( " SFAIL " ); +#endif + spin_lock_irqsave(current_SC->device->host->host_lock, flags); + my_done( DID_NO_CONNECT << 16 ); + spin_unlock_irqrestore(current_SC->device->host->host_lock, flags); + return IRQ_HANDLED; + } else { +#if EVERY_ACCESS + printk( " AltSel " ); +#endif + /* Stop arbitration and enable parity */ + outb(0x10 | PARITY_MASK, port_base + TMC_Cntl); + } + } + current_SC->SCp.phase = in_other; + outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl); + outb(0x80, port_base + SCSI_Cntl); +#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(port_base + SCSI_Status); + + if (status & 0x10) { /* REQ */ + + switch (status & 0x0e) { + + case 0x08: /* COMMAND OUT */ + outb(current_SC->cmnd[current_SC->SCp.sent_command++], + port_base + Write_SCSI_Data); +#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, port_base + TMC_Cntl); + } + 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, port_base + TMC_Cntl); + } + break; + case 0x0c: /* STATUS IN */ + current_SC->SCp.Status = inb(port_base + Read_SCSI_Data); +#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( "scsi: 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, port_base + Write_SCSI_Data); /* Reject */ + break; + case 0x0e: /* MESSAGE IN */ + current_SC->SCp.Message = inb(port_base + Read_SCSI_Data); +#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( "scsi: 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)) { + + if(current_SC->sc_data_direction == DMA_TO_DEVICE) + { + current_SC->SCp.have_data_in = -1; + outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl); + } + else + { + current_SC->SCp.have_data_in = 1; + outb(0x90 | PARITY_MASK, port_base + TMC_Cntl); + } + } + + if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */ + while ((data_count = FIFO_Size - inw(port_base + FIFO_Data_Count)) > 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) { + outb(*current_SC->SCp.ptr++, port_base + Write_FIFO); + --current_SC->SCp.this_residual; + } else { + data_count >>= 1; + outsw(port_base + Write_FIFO, current_SC->SCp.ptr, data_count); + current_SC->SCp.ptr += 2 * data_count; + current_SC->SCp.this_residual -= 2 * data_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; + } + } + } + + if (current_SC->SCp.have_data_in == 1) { /* DATA IN */ + while ((data_count = inw(port_base + FIFO_Data_Count)) > 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) { + *current_SC->SCp.ptr++ = inb(port_base + Read_FIFO); + --current_SC->SCp.this_residual; + } else { + data_count >>= 1; /* Number of words */ + insw(port_base + Read_FIFO, current_SC->SCp.ptr, data_count); + current_SC->SCp.ptr += 2 * data_count; + current_SC->SCp.this_residual -= 2 * data_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 ERRORS_ONLY + if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) { + char *buf = scsi_sglist(current_SC); + if ((unsigned char)(*(buf + 2)) & 0x0f) { + unsigned char key; + unsigned char code; + unsigned char qualifier; + + key = (unsigned char)(*(buf + 2)) & 0x0f; + code = (unsigned char)(*(buf + 12)); + qualifier = (unsigned char)(*(buf + 13)); + + if (key != UNIT_ATTENTION + && !(key == NOT_READY + && code == 0x04 + && (!qualifier || qualifier == 0x02 || qualifier == 0x01)) + && !(key == ILLEGAL_REQUEST && (code == 0x25 + || code == 0x24 + || !code))) + + printk( "scsi: REQUEST SENSE" + " Key = %x, Code = %x, Qualifier = %x\n", + key, code, qualifier ); + } + } +#endif +#if EVERY_ACCESS + printk( "BEFORE MY_DONE. . ." ); +#endif + spin_lock_irqsave(current_SC->device->host->host_lock, flags); + my_done( (current_SC->SCp.Status & 0xff) + | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) ); + spin_unlock_irqrestore(current_SC->device->host->host_lock, flags); +#if EVERY_ACCESS + printk( "RETURNING.\n" ); +#endif + + } else { + if (current_SC->SCp.phase & disconnect) { + outb(0xd0 | FIFO_COUNT, port_base + Interrupt_Cntl); + outb(0x00, port_base + SCSI_Cntl); + } else { + outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl); + } + } +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif + return IRQ_HANDLED; +} + +static int fdomain_16x0_queue_lck(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + if (in_command) { + panic( "scsi: fdomain_16x0_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 + + fdomain_make_bus_idle(); + + current_SC = SCpnt; /* Save this for the done function */ + current_SC->scsi_done = done; + + /* Initialize static data */ + + if (scsi_sg_count(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, port_base + Interrupt_Cntl); + outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */ + outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */ + ++in_command; + outb(0x20, port_base + Interrupt_Cntl); + outb(0x14 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */ + + return 0; +} + +static DEF_SCSI_QCMD(fdomain_16x0_queue) + +#if DEBUG_ABORT +static void print_info(struct scsi_cmnd *SCpnt) +{ + unsigned int imr; + unsigned int irr; + unsigned int isr; + + if (!SCpnt || !SCpnt->device || !SCpnt->device->host) { + printk(KERN_WARNING "scsi: Cannot provide detailed information\n"); + return; + } + + printk(KERN_INFO "%s\n", fdomain_16x0_info( SCpnt->device->host ) ); + print_banner(SCpnt->device->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 << interrupt_level)) + printk( " (masked)" ); + printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr ); + + printk( "SCSI Status = 0x%02x\n", inb(port_base + SCSI_Status)); + printk( "TMC Status = 0x%02x", inb(port_base + TMC_Status)); + if (inb((port_base + TMC_Status) & 1)) + printk( " (interrupt)" ); + printk( "\n" ); + printk("Interrupt Status = 0x%02x", inb(port_base + Interrupt_Status)); + if (inb(port_base + Interrupt_Status) & 0x08) + printk( " (enabled)" ); + printk( "\n" ); + if (chip == tmc18c50 || chip == tmc18c30) { + printk("FIFO Status = 0x%02x\n", inb(port_base + FIFO_Status)); + printk( "Int. Condition = 0x%02x\n", + inb( port_base + Interrupt_Cond ) ); + } + printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) ); + if (chip == tmc18c50 || chip == tmc18c30) + printk( "Configuration 2 = 0x%02x\n", + inb( port_base + Configuration2 ) ); +} +#endif + +static int fdomain_16x0_abort(struct scsi_cmnd *SCpnt) +{ +#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT + printk( "scsi: abort " ); +#endif + + if (!in_command) { +#if EVERY_ACCESS || ERRORS_ONLY + printk( " (not in command)\n" ); +#endif + return FAILED; + } else printk( "\n" ); + +#if DEBUG_ABORT + print_info( SCpnt ); +#endif + + fdomain_make_bus_idle(); + current_SC->SCp.phase |= aborted; + current_SC->result = DID_ABORT << 16; + + /* Aborts are not done well. . . */ + my_done(DID_ABORT << 16); + return SUCCESS; +} + +int fdomain_16x0_host_reset(struct scsi_cmnd *SCpnt) +{ + unsigned long flags; + + local_irq_save(flags); + + outb(1, port_base + SCSI_Cntl); + do_pause( 2 ); + outb(0, port_base + SCSI_Cntl); + do_pause( 115 ); + outb(0, port_base + SCSI_Mode_Cntl); + outb(PARITY_MASK, port_base + TMC_Cntl); + + local_irq_restore(flags); + return SUCCESS; +} + +static int fdomain_16x0_biosparam(struct scsi_device *sdev, + struct block_device *bdev, + sector_t capacity, int *info_array) +{ + int drive; + int size = capacity; + unsigned long offset; + struct drive_info { + unsigned short cylinders; + unsigned char heads; + unsigned char sectors; + } i; + + /* NOTES: + The RAM area starts at 0x1f00 from the bios_base address. + + For BIOS Version 2.0: + + The drive parameter table seems to start at 0x1f30. + The first byte's purpose is not known. + Next is the cylinder, head, and sector information. + The last 4 bytes appear to be the drive's size in sectors. + The other bytes in the drive parameter table are unknown. + If anyone figures them out, please send me mail, and I will + update these notes. + + Tape drives do not get placed in this table. + + There is another table at 0x1fea: + If the byte is 0x01, then the SCSI ID is not in use. + If the byte is 0x18 or 0x48, then the SCSI ID is in use, + although tapes don't seem to be in this table. I haven't + seen any other numbers (in a limited sample). + + 0x1f2d is a drive count (i.e., not including tapes) + + The table at 0x1fcc are I/O ports addresses for the various + operations. I calculate these by hand in this driver code. + + + + For the ISA-200S version of BIOS Version 2.0: + + The drive parameter table starts at 0x1f33. + + WARNING: Assume that the table entry is 25 bytes long. Someone needs + to check this for the Quantum ISA-200S card. + + + + For BIOS Version 3.2: + + The drive parameter table starts at 0x1f70. Each entry is + 0x0a bytes long. Heads are one less than we need to report. + */ + + if (MAJOR(bdev->bd_dev) != SCSI_DISK0_MAJOR) { + printk("scsi: fdomain_16x0_biosparam: too many disks"); + return 0; + } + drive = MINOR(bdev->bd_dev) >> 4; + + if (bios_major == 2) { + switch (Quantum) { + case 2: /* ISA_200S */ + /* The value of 25 has never been verified. + It should probably be 15. */ + offset = 0x1f33 + drive * 25; + break; + case 3: /* ISA_250MG */ + offset = 0x1f36 + drive * 15; + break; + case 4: /* ISA_200S (another one) */ + offset = 0x1f34 + drive * 15; + break; + default: + offset = 0x1f31 + drive * 25; + break; + } + memcpy_fromio( &i, bios_mem + offset, sizeof( struct drive_info ) ); + info_array[0] = i.heads; + info_array[1] = i.sectors; + info_array[2] = i.cylinders; + } else if (bios_major == 3 + && bios_minor >= 0 + && bios_minor < 4) { /* 3.0 and 3.2 BIOS */ + memcpy_fromio( &i, bios_mem + 0x1f71 + drive * 10, + sizeof( struct drive_info ) ); + info_array[0] = i.heads + 1; + info_array[1] = i.sectors; + info_array[2] = i.cylinders; + } else { /* 3.4 BIOS (and up?) */ + /* This algorithm was provided by Future Domain (much thanks!). */ + unsigned char *p = scsi_bios_ptable(bdev); + + 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 int fdomain_16x0_release(struct Scsi_Host *shpnt) +{ + if (shpnt->irq) + free_irq(shpnt->irq, shpnt); + if (shpnt->io_port && shpnt->n_io_port) + release_region(shpnt->io_port, shpnt->n_io_port); + if (PCI_bus) + pci_dev_put(PCI_dev); + return 0; +} + +struct scsi_host_template fdomain_driver_template = { + .module = THIS_MODULE, + .name = "fdomain", + .proc_name = "fdomain", + .detect = fdomain_16x0_detect, + .info = fdomain_16x0_info, + .queuecommand = fdomain_16x0_queue, + .eh_abort_handler = fdomain_16x0_abort, + .eh_host_reset_handler = fdomain_16x0_host_reset, + .bios_param = fdomain_16x0_biosparam, + .release = fdomain_16x0_release, + .can_queue = 1, + .this_id = 6, + .sg_tablesize = 64, + .use_clustering = DISABLE_CLUSTERING, +}; + +#ifndef PCMCIA +#if defined(CONFIG_PCI) && defined(MODULE) + +static struct pci_device_id fdomain_pci_tbl[] = { + { PCI_VENDOR_ID_FD, PCI_DEVICE_ID_FD_36C70, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { } +}; +MODULE_DEVICE_TABLE(pci, fdomain_pci_tbl); +#endif +#define driver_template fdomain_driver_template +#include "scsi_module.c" + +#endif diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h new file mode 100644 index 000000000000..5cbe86b573ae --- /dev/null +++ b/drivers/scsi/fdomain.h @@ -0,0 +1,24 @@ +/* + * fdomain.c -- Future Domain TMC-16x0 SCSI driver + * Author: Rickard E. Faith, faith@cs.unc.edu + * Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org) + * + * 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. + */ + +extern struct scsi_host_template fdomain_driver_template; +extern int fdomain_setup(char *str); +extern struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ); +extern int fdomain_16x0_host_reset(struct scsi_cmnd *SCpnt); diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig index 2d435f105b16..ecc855c550aa 100644 --- a/drivers/scsi/pcmcia/Kconfig +++ b/drivers/scsi/pcmcia/Kconfig @@ -19,6 +19,15 @@ config PCMCIA_AHA152X To compile this driver as a module, choose M here: the module will be called aha152x_cs. +config PCMCIA_FDOMAIN + tristate "Future Domain PCMCIA support" + help + Say Y here if you intend to attach this type of PCMCIA SCSI host + adapter to your computer. + + To compile this driver as a module, choose M here: the + module will be called fdomain_cs. + config PCMCIA_NINJA_SCSI tristate "NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support" depends on !64BIT diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile index a5a24dd44e7e..ba1f126f9b17 100644 --- a/drivers/scsi/pcmcia/Makefile +++ b/drivers/scsi/pcmcia/Makefile @@ -4,9 +4,11 @@ ccflags-y := -I $(srctree)/drivers/scsi # 16-bit client drivers obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o +obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o aha152x_cs-objs := aha152x_stub.o aha152x_core.o +fdomain_cs-objs := fdomain_stub.o fdomain_core.o qlogic_cs-objs := qlogic_stub.o diff --git a/drivers/scsi/pcmcia/fdomain_core.c b/drivers/scsi/pcmcia/fdomain_core.c new file mode 100644 index 000000000000..a48913791868 --- /dev/null +++ b/drivers/scsi/pcmcia/fdomain_core.c @@ -0,0 +1,2 @@ +#define PCMCIA 1 +#include "fdomain.c" diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c new file mode 100644 index 000000000000..953a792150ae --- /dev/null +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -0,0 +1,209 @@ +/*====================================================================== + + A driver for Future Domain-compatible PCMCIA SCSI cards + + fdomain_cs.c 1.47 2001/10/13 00:08:52 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in + which case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include +#include "fdomain.h" + +#include +#include + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds "); +MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver"); +MODULE_LICENSE("Dual MPL/GPL"); + +/*====================================================================*/ + +typedef struct scsi_info_t { + struct pcmcia_device *p_dev; + struct Scsi_Host *host; +} scsi_info_t; + + +static void fdomain_release(struct pcmcia_device *link); +static void fdomain_detach(struct pcmcia_device *p_dev); +static int fdomain_config(struct pcmcia_device *link); + +static int fdomain_probe(struct pcmcia_device *link) +{ + scsi_info_t *info; + + dev_dbg(&link->dev, "fdomain_attach()\n"); + + /* Create new SCSI device */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + link->config_regs = PRESENT_OPTION; + + return fdomain_config(link); +} /* fdomain_attach */ + +/*====================================================================*/ + +static void fdomain_detach(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "fdomain_detach\n"); + + fdomain_release(link); + + kfree(link->priv); +} /* fdomain_detach */ + +/*====================================================================*/ + +static int fdomain_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + p_dev->io_lines = 10; + p_dev->resource[0]->end = 0x10; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + return pcmcia_request_io(p_dev); +} + + +static int fdomain_config(struct pcmcia_device *link) +{ + scsi_info_t *info = link->priv; + int ret; + char str[22]; + struct Scsi_Host *host; + + dev_dbg(&link->dev, "fdomain_config\n"); + + ret = pcmcia_loop_config(link, fdomain_config_check, NULL); + if (ret) + goto failed; + + if (!link->irq) + goto failed; + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + /* A bad hack... */ + release_region(link->resource[0]->start, resource_size(link->resource[0])); + + /* Set configuration options for the fdomain driver */ + sprintf(str, "%d,%d", (unsigned int) link->resource[0]->start, link->irq); + fdomain_setup(str); + + host = __fdomain_16x0_detect(&fdomain_driver_template); + if (!host) { + printk(KERN_INFO "fdomain_cs: no SCSI devices found\n"); + goto failed; + } + + if (scsi_add_host(host, NULL)) + goto failed; + scsi_scan_host(host); + + info->host = host; + + return 0; + +failed: + fdomain_release(link); + return -ENODEV; +} /* fdomain_config */ + +/*====================================================================*/ + +static void fdomain_release(struct pcmcia_device *link) +{ + scsi_info_t *info = link->priv; + + dev_dbg(&link->dev, "fdomain_release\n"); + + scsi_remove_host(info->host); + pcmcia_disable_device(link); + scsi_unregister(info->host); +} + +/*====================================================================*/ + +static int fdomain_resume(struct pcmcia_device *link) +{ + fdomain_16x0_host_reset(NULL); + + return 0; +} + +static const struct pcmcia_device_id fdomain_ids[] = { + PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20), + PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e), + PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, fdomain_ids); + +static struct pcmcia_driver fdomain_cs_driver = { + .owner = THIS_MODULE, + .name = "fdomain_cs", + .probe = fdomain_probe, + .remove = fdomain_detach, + .id_table = fdomain_ids, + .resume = fdomain_resume, +}; + +static int __init init_fdomain_cs(void) +{ + return pcmcia_register_driver(&fdomain_cs_driver); +} + +static void __exit exit_fdomain_cs(void) +{ + pcmcia_unregister_driver(&fdomain_cs_driver); +} + +module_init(init_fdomain_cs); +module_exit(exit_fdomain_cs); From patchwork Sun Apr 7 18:56:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ondrej Zary X-Patchwork-Id: 10888599 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6EC8C1800 for ; Sun, 7 Apr 2019 19:02:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 580AB283E7 for ; Sun, 7 Apr 2019 19:02:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3F5A72864F; Sun, 7 Apr 2019 19:02:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BB75228455 for ; Sun, 7 Apr 2019 19:02:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726492AbfDGTBt (ORCPT ); Sun, 7 Apr 2019 15:01:49 -0400 Received: from hosting.gsystem.sk ([212.5.213.30]:55900 "EHLO hosting.gsystem.sk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726397AbfDGTBt (ORCPT ); Sun, 7 Apr 2019 15:01:49 -0400 Received: from gsql.ggedos.sk (off-20.infotel.telecom.sk [212.5.213.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by hosting.gsystem.sk (Postfix) with ESMTPSA id B9A1B7A05F0; Sun, 7 Apr 2019 20:56:24 +0200 (CEST) From: Ondrej Zary To: Rik Faith Cc: linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 2/2] fdomain: Fix compilation Date: Sun, 7 Apr 2019 20:56:17 +0200 Message-Id: <20190407185617.8467-3-linux@zary.sk> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190407185617.8467-1-linux@zary.sk> References: <20190407185617.8467-1-linux@zary.sk> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Remove usage of scsi_module.c, .use_clustering and scsi_(un)register. This allows the driver to compile and also work (tested with PCI card). Signed-off-by: Ondrej Zary --- drivers/scsi/fdomain.c | 48 ++++++++++++++++++++++++++++---------- drivers/scsi/pcmcia/fdomain_stub.c | 2 +- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c index ebbe5a3e665d..fe9e373c27a8 100644 --- a/drivers/scsi/fdomain.c +++ b/drivers/scsi/fdomain.c @@ -415,6 +415,7 @@ static char * fdomain = NULL; module_param(fdomain, charp, 0); #ifndef PCMCIA +static struct Scsi_Host *fdomain_shost; static unsigned long addresses[] = { 0xc8000, @@ -959,7 +960,7 @@ struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) /* Print out a banner here in case we can't get resources. */ - shpnt = scsi_register( tpnt, 0 ); + shpnt = scsi_host_alloc( tpnt, 0 ); if(shpnt == NULL) { release_region(port_base, 0x10); return NULL; @@ -1003,13 +1004,6 @@ struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) return NULL; } -static int fdomain_16x0_detect(struct scsi_host_template *tpnt) -{ - if (fdomain) - fdomain_setup(fdomain); - return (__fdomain_16x0_detect(tpnt) != NULL); -} - static const char *fdomain_16x0_info( struct Scsi_Host *ignore ) { static char buffer[128]; @@ -1739,6 +1733,7 @@ static int fdomain_16x0_biosparam(struct scsi_device *sdev, return 0; } +#ifndef PCMCIA static int fdomain_16x0_release(struct Scsi_Host *shpnt) { if (shpnt->irq) @@ -1749,22 +1744,21 @@ static int fdomain_16x0_release(struct Scsi_Host *shpnt) pci_dev_put(PCI_dev); return 0; } +#endif struct scsi_host_template fdomain_driver_template = { .module = THIS_MODULE, .name = "fdomain", .proc_name = "fdomain", - .detect = fdomain_16x0_detect, .info = fdomain_16x0_info, .queuecommand = fdomain_16x0_queue, .eh_abort_handler = fdomain_16x0_abort, .eh_host_reset_handler = fdomain_16x0_host_reset, .bios_param = fdomain_16x0_biosparam, - .release = fdomain_16x0_release, .can_queue = 1, .this_id = 6, .sg_tablesize = 64, - .use_clustering = DISABLE_CLUSTERING, + .dma_boundary = PAGE_SIZE - 1, }; #ifndef PCMCIA @@ -1778,6 +1772,36 @@ static struct pci_device_id fdomain_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, fdomain_pci_tbl); #endif #define driver_template fdomain_driver_template -#include "scsi_module.c" + +static int __init fdomain_16x0_init(void) +{ + int ret; + + if (fdomain) + fdomain_setup(fdomain); + fdomain_shost = __fdomain_16x0_detect(&driver_template); + if (!fdomain_shost) + return -ENODEV; + + ret = scsi_add_host(fdomain_shost, NULL); + if (ret) + goto fail; + scsi_scan_host(fdomain_shost); + + return 0; +fail: + scsi_remove_host(fdomain_shost); + return ret; +} + +static void __exit fdomain_16x0_exit(void) +{ + scsi_remove_host(fdomain_shost); + fdomain_16x0_release(fdomain_shost); + scsi_host_put(fdomain_shost); +} + +module_init(fdomain_16x0_init); +module_exit(fdomain_16x0_exit); #endif diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index 953a792150ae..5febbb7d611b 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -166,7 +166,7 @@ static void fdomain_release(struct pcmcia_device *link) scsi_remove_host(info->host); pcmcia_disable_device(link); - scsi_unregister(info->host); + scsi_host_put(info->host); } /*====================================================================*/ From patchwork Mon Apr 8 19:35:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ondrej Zary X-Patchwork-Id: 10889993 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 07F8E1669 for ; Mon, 8 Apr 2019 19:36:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DA8A32866D for ; Mon, 8 Apr 2019 19:35:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CD3EB28691; Mon, 8 Apr 2019 19:35:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 320CE2866D for ; Mon, 8 Apr 2019 19:35:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726677AbfDHTf4 (ORCPT ); Mon, 8 Apr 2019 15:35:56 -0400 Received: from hosting.gsystem.sk ([212.5.213.30]:47546 "EHLO hosting.gsystem.sk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726638AbfDHTf4 (ORCPT ); Mon, 8 Apr 2019 15:35:56 -0400 Received: from gsql.ggedos.sk (off-20.infotel.telecom.sk [212.5.213.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by hosting.gsystem.sk (Postfix) with ESMTPSA id 571DC7A00D7; Mon, 8 Apr 2019 21:35:54 +0200 (CEST) From: Ondrej Zary To: Rik Faith Cc: linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 3/2] fdomain: Use SCSI host private data instead of global variables Date: Mon, 8 Apr 2019 21:35:22 +0200 Message-Id: <20190408193522.16621-1-linux@zary.sk> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190407185617.8467-1-linux@zary.sk> References: <20190407185617.8467-1-linux@zary.sk> Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Move global variables into SCSI host private data in order to support multiple cards. Signed-off-by: Ondrej Zary --- drivers/scsi/fdomain.c | 593 +++++++++++++++++++++++++------------------------ 1 file changed, 307 insertions(+), 286 deletions(-) diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c index fe9e373c27a8..83294cf6b668 100644 --- a/drivers/scsi/fdomain.c +++ b/drivers/scsi/fdomain.c @@ -384,29 +384,32 @@ enum out_port_type { /* .bss will zero all the static variables below */ static int port_base; -static unsigned long bios_base; -static void __iomem * bios_mem; -static int bios_major; -static int bios_minor; -static int PCI_bus; -#ifdef CONFIG_PCI -static struct pci_dev *PCI_dev; -#endif -static int Quantum; /* Quantum board variant */ static int interrupt_level; -static volatile int in_command; -static struct scsi_cmnd *current_SC; -static enum chip_type chip = unknown; -static int adapter_mask; static int this_id; static int setup_called; +struct fdomain { + int port_base; + unsigned long bios_base; + void __iomem *bios_mem; + int bios_major; + int bios_minor; + int PCI_bus; +#ifdef CONFIG_PCI + struct pci_dev *PCI_dev; +#endif + int Quantum; /* Quantum board variant */ + int interrupt_level; + volatile int in_command; + struct scsi_cmnd *current_SC; + enum chip_type chip; + int adapter_mask; + int this_id; #if DEBUG_RACE -static volatile int in_interrupt_flag; + volatile int in_interrupt_flag; #endif - -static int FIFO_Size = 0x2000; /* 8k FIFO for - pre-tmc18c30 chips */ + int FIFO_Size; +}; static irqreturn_t do_fdomain_16x0_intr( int irq, void *dev_id ); /* Allow insmod parameters to be like LILO parameters. For example: @@ -518,22 +521,22 @@ static struct signature { static void print_banner( struct Scsi_Host *shpnt ) { - if (!shpnt) return; /* This won't ever happen */ + struct fdomain *fd = shost_priv(shpnt); - if (bios_major < 0 && bios_minor < 0) { + if (fd->bios_major < 0 && fd->bios_minor < 0) { printk(KERN_INFO "scsi%d: No BIOS; using scsi id %d\n", shpnt->host_no, shpnt->this_id); } else { printk(KERN_INFO "scsi%d: BIOS version ", shpnt->host_no); - if (bios_major >= 0) printk("%d.", bios_major); + if (fd->bios_major >= 0) printk("%d.", fd->bios_major); else printk("?."); - if (bios_minor >= 0) printk("%d", bios_minor); + if (fd->bios_minor >= 0) printk("%d", fd->bios_minor); else printk("?."); printk( " at 0x%lx using scsi id %d\n", - bios_base, shpnt->this_id ); + fd->bios_base, shpnt->this_id ); } /* If this driver works for later FD PCI @@ -542,11 +545,11 @@ static void print_banner( struct Scsi_Host *shpnt ) it's PCI it's a TMC-3260 - JTM */ printk(KERN_INFO "scsi%d: %s chip at 0x%x irq ", shpnt->host_no, - chip == tmc1800 ? "TMC-1800" : (chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30") : "Unknown")), - port_base); + fd->chip == tmc1800 ? "TMC-1800" : (fd->chip == tmc18c50 ? "TMC-18C50" : (fd->chip == tmc18c30 ? (fd->PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30") : "Unknown")), + fd->port_base); - if (interrupt_level) - printk("%d", interrupt_level); + if (fd->interrupt_level) + printk("%d", fd->interrupt_level); else printk(""); @@ -568,8 +571,7 @@ int fdomain_setup(char *str) port_base = ints[0] >= 1 ? ints[1] : 0; interrupt_level = ints[0] >= 2 ? ints[2] : 0; this_id = ints[0] >= 3 ? ints[3] : 0; - - bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */ + ++setup_called; return 1; } @@ -582,22 +584,24 @@ static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */ mdelay(10*amount); } -static inline void fdomain_make_bus_idle( void ) +static inline void fdomain_make_bus_idle(struct fdomain *fd) { - outb(0, port_base + SCSI_Cntl); - outb(0, port_base + SCSI_Mode_Cntl); - if (chip == tmc18c50 || chip == tmc18c30) - outb(0x21 | PARITY_MASK, port_base + TMC_Cntl); /* Clear forced intr. */ + outb(0, fd->port_base + SCSI_Cntl); + outb(0, fd->port_base + SCSI_Mode_Cntl); + if (fd->chip == tmc18c50 || fd->chip == tmc18c30) + outb(0x21 | PARITY_MASK, fd->port_base + TMC_Cntl); /* Clear forced intr. */ else - outb(0x01 | PARITY_MASK, port_base + TMC_Cntl); + outb(0x01 | PARITY_MASK, fd->port_base + TMC_Cntl); } -static int fdomain_is_valid_port( int port ) +static int fdomain_is_valid_port(struct fdomain *fd, int port) { #if DEBUG_DETECT printk( " (%x%x),", inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) ); #endif + fd->chip = unknown; + fd->FIFO_Size = 0x2000; /* 8k FIFO for pre-tmc18c30 chips */ /* The MCA ID is a unique id for each MCA compatible board. We are using ISA boards, but Future Domain provides the MCA ID @@ -608,10 +612,10 @@ static int fdomain_is_valid_port( int port ) if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */ if (inb( port + LSB_ID_Code ) != 0x27) return 0; if (inb( port + MSB_ID_Code ) != 0x61) return 0; - chip = tmc1800; + fd->chip = tmc1800; } else { /* test for 0xe960 id */ if (inb( port + MSB_ID_Code ) != 0x60) return 0; - chip = tmc18c50; + fd->chip = tmc18c50; /* Try to toggle 32-bit mode. This only works on an 18c30 chip. (User reports @@ -622,8 +626,8 @@ static int fdomain_is_valid_port( int port ) if ((inb( port + Configuration2 ) & 0x80) == 0x80) { outb( 0x00, port + IO_Control ); if ((inb( port + Configuration2 ) & 0x80) == 0x00) { - chip = tmc18c30; - FIFO_Size = 0x800; /* 2k FIFO */ + fd->chip = tmc18c30; + fd->FIFO_Size = 0x800; /* 2k FIFO */ } } /* If that failed, we are an 18c50. */ @@ -632,14 +636,14 @@ static int fdomain_is_valid_port( int port ) return 1; } -static int fdomain_test_loopback( void ) +static int fdomain_test_loopback(int port_base) { int i; int result; for (i = 0; i < 255; i++) { - outb( i, port_base + Write_Loopback ); - result = inb( port_base + Read_Loopback ); + outb(i, port_base + Write_Loopback); + result = inb(port_base + Read_Loopback); if (i != result) return 1; } @@ -662,7 +666,7 @@ static int fdomain_test_loopback( void ) configure a PCI system so that one of these IRQs will be used by the Future Domain card. */ -static int fdomain_get_irq( int base ) +static int fdomain_get_irq(struct fdomain *fd, int base) { int options = inb(base + Configuration1); @@ -675,12 +679,12 @@ static int fdomain_get_irq( int base ) boards on the PCI bus, so just assume we have the right board. */ - if (chip != tmc18c30 && !PCI_bus && addresses[(options & 0xc0) >> 6 ] != bios_base) + if (fd->chip != tmc18c30 && !fd->PCI_bus && addresses[(options & 0xc0) >> 6 ] != fd->bios_base) return 0; return ints[(options & 0x0e) >> 1]; } -static int fdomain_isa_detect( int *irq, int *iobase ) +static int fdomain_isa_detect(struct fdomain *fd) { int i, j; int base = 0xdeadbeef; @@ -695,18 +699,18 @@ static int fdomain_isa_detect( int *irq, int *iobase ) if (!p) continue; #if DEBUG_DETECT - printk( " %lx(%lx),", addresses[i], bios_base ); + printk( " %lx(%lx),", addresses[i], fd->bios_base ); #endif for (j = 0; j < SIGNATURE_COUNT; j++) { if (check_signature(p + signatures[j].sig_offset, signatures[j].signature, signatures[j].sig_length )) { - bios_major = signatures[j].major_bios_version; - bios_minor = signatures[j].minor_bios_version; - PCI_bus = (signatures[j].flag == 1); - Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0; - bios_base = addresses[i]; - bios_mem = p; + fd->bios_major = signatures[j].major_bios_version; + fd->bios_minor = signatures[j].minor_bios_version; + fd->PCI_bus = (signatures[j].flag == 1); + fd->Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0; + fd->bios_base = addresses[i]; + fd->bios_mem = p; goto found; } } @@ -714,7 +718,7 @@ static int fdomain_isa_detect( int *irq, int *iobase ) } found: - if (bios_major == 2) { + if (fd->bios_major == 2) { /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM. Assuming the ROM is enabled (otherwise we wouldn't have been able to read the ROM signature :-), then the ROM sets up the @@ -723,16 +727,16 @@ static int fdomain_isa_detect( int *irq, int *iobase ) DOS (this geometry has nothing to do with physical geometry). */ - switch (Quantum) { + switch (fd->Quantum) { case 2: /* ISA_200S */ case 3: /* ISA_250MG */ - base = readb(bios_mem + 0x1fa2) + (readb(bios_mem + 0x1fa3) << 8); + base = readb(fd->bios_mem + 0x1fa2) + (readb(fd->bios_mem + 0x1fa3) << 8); break; case 4: /* ISA_200S (another one) */ - base = readb(bios_mem + 0x1fa3) + (readb(bios_mem + 0x1fa4) << 8); + base = readb(fd->bios_mem + 0x1fa3) + (readb(fd->bios_mem + 0x1fa4) << 8); break; default: - base = readb(bios_mem + 0x1fcc) + (readb(bios_mem + 0x1fcd) << 8); + base = readb(fd->bios_mem + 0x1fcc) + (readb(fd->bios_mem + 0x1fcd) << 8); break; } @@ -744,12 +748,12 @@ static int fdomain_isa_detect( int *irq, int *iobase ) if (base == ports[i]) { if (!request_region(base, 0x10, "fdomain")) break; - if (!fdomain_is_valid_port(base)) { + if (!fdomain_is_valid_port(fd, base)) { release_region(base, 0x10); break; } - *irq = fdomain_get_irq( base ); - *iobase = base; + fd->interrupt_level = fdomain_get_irq(fd, base); + fd->port_base = base; return 1; } } @@ -783,7 +787,7 @@ static int fdomain_isa_detect( int *irq, int *iobase ) #if DEBUG_DETECT printk( " %x,", base ); #endif - flag = fdomain_is_valid_port(base); + flag = fdomain_is_valid_port(fd, base); if (flag) break; release_region(base, 0x10); @@ -796,20 +800,18 @@ static int fdomain_isa_detect( int *irq, int *iobase ) if (!flag) return 0; /* iobase not found */ - *irq = fdomain_get_irq( base ); - *iobase = base; + fd->interrupt_level = fdomain_get_irq(fd, base); + fd->port_base = base; return 1; /* success */ } #else /* PCMCIA */ -static int fdomain_isa_detect( int *irq, int *iobase ) +static int fdomain_isa_detect(struct fdomain *fd) { - if (irq) - *irq = 0; - if (iobase) - *iobase = 0; + fd->interrupt_level = 0; + fd->port_base = 0; return 0; } @@ -821,7 +823,7 @@ static int fdomain_isa_detect( int *irq, int *iobase ) the PCI configuration registers. */ #ifdef CONFIG_PCI -static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_pdev ) +static int fdomain_pci_bios_detect(struct fdomain *fd) { unsigned int pci_irq; /* PCI interrupt line */ unsigned long pci_base; /* PCI I/O base address */ @@ -863,27 +865,26 @@ static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_ /* Now we have the I/O base address and interrupt from the PCI configuration registers. */ - *irq = pci_irq; - *iobase = pci_base; - *ret_pdev = pdev; + fd->interrupt_level = pci_irq; + fd->port_base = pci_base; #if DEBUG_DETECT printk( "scsi: TMC-3260 detect:" - " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base ); + " IRQ = %d, I/O base = 0x%x [0x%lx]\n", fd->interrupt_level, fd->port_base, pci_base ); #endif - if (!fdomain_is_valid_port(pci_base)) { + if (!fdomain_is_valid_port(fd, pci_base)) { printk(KERN_ERR "scsi: PCI card detected, but driver not loaded (invalid port)\n" ); release_region(pci_base, 0x10); goto fail; } /* Fill in a few global variables. Ugh. */ - bios_major = bios_minor = -1; - PCI_bus = 1; - PCI_dev = pdev; - Quantum = 0; - bios_base = 0; + fd->bios_major = fd->bios_minor = -1; + fd->PCI_bus = 1; + fd->PCI_dev = pdev; + fd->Quantum = 0; + fd->bios_base = 0; return 1; fail: @@ -893,12 +894,24 @@ static int fdomain_pci_bios_detect( int *irq, int *iobase, struct pci_dev **ret_ #endif +void fdomain_16x0_reset(int port_base) +{ + outb(1, port_base + SCSI_Cntl); + do_pause( 2 ); + outb(0, port_base + SCSI_Cntl); + do_pause( 115 ); + outb(0, port_base + SCSI_Mode_Cntl); + outb(PARITY_MASK, port_base + TMC_Cntl); +} + struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) { int retcode; struct Scsi_Host *shpnt; - struct pci_dev *pdev = NULL; + struct fdomain fd_tmp; + struct fdomain *fd; + memset(&fd_tmp, 0, sizeof(struct fdomain)); if (setup_called) { #if DEBUG_DETECT printk( "scsi: No BIOS, using port_base = 0x%x, irq = %d\n", @@ -909,23 +922,27 @@ struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) printk( "scsi: Bad LILO/INSMOD parameters?\n" ); return NULL; } - if (!fdomain_is_valid_port( port_base )) { + if (!fdomain_is_valid_port(&fd_tmp, port_base)) { printk( "scsi: Cannot locate chip at port base 0x%x\n", port_base ); printk( "scsi: Bad LILO/INSMOD parameters?\n" ); release_region(port_base, 0x10); return NULL; } + fd_tmp.port_base = port_base; + fd_tmp.interrupt_level = interrupt_level; + fd_tmp.this_id = this_id; + fd_tmp.bios_major = fd_tmp.bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */ } else { int flag = 0; #ifdef CONFIG_PCI /* Try PCI detection first */ - flag = fdomain_pci_bios_detect( &interrupt_level, &port_base, &pdev ); + flag = fdomain_pci_bios_detect(&fd_tmp); #endif if (!flag) { /* Then try ISA bus detection */ - flag = fdomain_isa_detect( &interrupt_level, &port_base ); + flag = fdomain_isa_detect(&fd_tmp); if (!flag) { printk( "scsi: Detection failed (no card)\n" ); @@ -934,62 +951,64 @@ struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) } } - fdomain_16x0_host_reset(NULL); + fdomain_16x0_reset(fd_tmp.port_base); - if (fdomain_test_loopback()) { - printk(KERN_ERR "scsi: Detection failed (loopback test failed at port base 0x%x)\n", port_base); + if (fdomain_test_loopback(fd_tmp.port_base)) { + printk(KERN_ERR "scsi: Detection failed (loopback test failed at port base 0x%x)\n", fd_tmp.port_base); if (setup_called) { printk(KERN_ERR "scsi: Bad LILO/INSMOD parameters?\n"); } goto fail; } - if (this_id) { - tpnt->this_id = (this_id & 0x07); - adapter_mask = (1 << tpnt->this_id); + if (fd_tmp.this_id) { + tpnt->this_id = (fd_tmp.this_id & 0x07); + fd_tmp.adapter_mask = (1 << tpnt->this_id); } else { - if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) { + if (fd_tmp.PCI_bus || (fd_tmp.bios_major == 3 && fd_tmp.bios_minor >= 2) || fd_tmp.bios_major < 0) { tpnt->this_id = 7; - adapter_mask = 0x80; + fd_tmp.adapter_mask = 0x80; } else { tpnt->this_id = 6; - adapter_mask = 0x40; + fd_tmp.adapter_mask = 0x40; } } /* Print out a banner here in case we can't get resources. */ - shpnt = scsi_host_alloc( tpnt, 0 ); + shpnt = scsi_host_alloc(tpnt, sizeof(struct fdomain)); if(shpnt == NULL) { - release_region(port_base, 0x10); + release_region(fd_tmp.port_base, 0x10); return NULL; } - shpnt->irq = interrupt_level; - shpnt->io_port = port_base; + fd = shost_priv(shpnt); + memcpy(fd, &fd_tmp, sizeof(struct fdomain)); + shpnt->irq = fd->interrupt_level; + shpnt->io_port = fd->port_base; shpnt->n_io_port = 0x10; print_banner( shpnt ); /* Log IRQ with kernel */ - if (!interrupt_level) { + if (!fd->interrupt_level) { printk(KERN_ERR "scsi: Card Detected, but driver not loaded (no IRQ)\n" ); goto fail; } else { /* Register the IRQ with the kernel */ - retcode = request_irq( interrupt_level, - do_fdomain_16x0_intr, pdev?IRQF_SHARED:0, "fdomain", shpnt); + retcode = request_irq(fd->interrupt_level, + do_fdomain_16x0_intr, fd->PCI_dev?IRQF_SHARED:0, "fdomain", fd); if (retcode < 0) { if (retcode == -EINVAL) { - printk(KERN_ERR "scsi: IRQ %d is bad!\n", interrupt_level ); + printk(KERN_ERR "scsi: IRQ %d is bad!\n", fd->interrupt_level); printk(KERN_ERR " This shouldn't happen!\n" ); printk(KERN_ERR " Send mail to faith@acm.org\n" ); } else if (retcode == -EBUSY) { - printk(KERN_ERR "scsi: IRQ %d is already in use!\n", interrupt_level ); + printk(KERN_ERR "scsi: IRQ %d is already in use!\n", fd->interrupt_level); printk(KERN_ERR " Please use another IRQ!\n" ); } else { - printk(KERN_ERR "scsi: Error getting IRQ %d\n", interrupt_level ); + printk(KERN_ERR "scsi: Error getting IRQ %d\n", fd->interrupt_level); printk(KERN_ERR " This shouldn't happen!\n" ); printk(KERN_ERR " Send mail to faith@acm.org\n" ); } @@ -999,8 +1018,8 @@ struct Scsi_Host *__fdomain_16x0_detect(struct scsi_host_template *tpnt ) } return shpnt; fail: - pci_dev_put(pdev); - release_region(port_base, 0x10); + pci_dev_put(fd->PCI_dev); + release_region(fd->port_base, 0x10); return NULL; } @@ -1036,7 +1055,7 @@ static int fdomain_arbitrate( void ) #endif outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */ - outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */ + outb(fd->adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */ outb(0x04 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */ timeout = 500; @@ -1048,7 +1067,7 @@ static int fdomain_arbitrate( void ) } while (--timeout); /* Make bus idle */ - fdomain_make_bus_idle(); + fdomain_make_bus_idle(fd); #if EVERY_ACCESS printk( "Arbitration failed, status = %x\n", status ); @@ -1060,7 +1079,7 @@ static int fdomain_arbitrate( void ) } #endif -static int fdomain_select( int target ) +static int fdomain_select(struct fdomain *fd, int target) { int status; unsigned long timeout; @@ -1068,25 +1087,25 @@ static int fdomain_select( int target ) static int flag = 0; #endif - outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */ - outb(adapter_mask | (1 << target), port_base + SCSI_Data_NoACK); + outb(0x82, fd->port_base + SCSI_Cntl); /* Bus Enable + Select */ + outb(fd->adapter_mask | (1 << target), fd->port_base + SCSI_Data_NoACK); /* Stop arbitration and enable parity */ - outb(PARITY_MASK, port_base + TMC_Cntl); + outb(PARITY_MASK, fd->port_base + TMC_Cntl); timeout = 350; /* 350 msec */ do { - status = inb(port_base + SCSI_Status); /* Read adapter status */ + status = inb(fd->port_base + SCSI_Status); /* Read adapter status */ if (status & 1) { /* Busy asserted */ /* Enable SCSI Bus (on error, should make bus idle with 0) */ - outb(0x80, port_base + SCSI_Cntl); + outb(0x80,fd-> port_base + SCSI_Cntl); return 0; } mdelay(1); /* wait one msec */ } while (--timeout); /* Make bus idle */ - fdomain_make_bus_idle(); + fdomain_make_bus_idle(fd); #if EVERY_ACCESS if (!target) printk( "Selection failed\n" ); #endif @@ -1101,26 +1120,27 @@ static int fdomain_select( int target ) return 1; } -static void my_done(int error) +static void my_done(struct fdomain *fd, int error) { - if (in_command) { - in_command = 0; - outb(0x00, port_base + Interrupt_Cntl); - fdomain_make_bus_idle(); - current_SC->result = error; - if (current_SC->scsi_done) - current_SC->scsi_done( current_SC ); + if (fd->in_command) { + fd->in_command = 0; + outb(0x00, fd->port_base + Interrupt_Cntl); + fdomain_make_bus_idle(fd); + fd->current_SC->result = error; + if (fd->current_SC->scsi_done) + fd->current_SC->scsi_done(fd->current_SC); else panic( "scsi: current_SC->scsi_done() == NULL" ); } else { panic( "scsi: my_done() called outside of command\n" ); } #if DEBUG_RACE - in_interrupt_flag = 0; + fd->in_interrupt_flag = 0; #endif } static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id) { + struct fdomain *fd = dev_id; unsigned long flags; int status; int done = 0; @@ -1133,23 +1153,23 @@ static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id) running. */ /* Check for other IRQ sources */ - if ((inb(port_base + TMC_Status) & 0x01) == 0) + if ((inb(fd->port_base + TMC_Status) & 0x01) == 0) return IRQ_NONE; /* It is our IRQ */ - outb(0x00, port_base + Interrupt_Cntl); + outb(0x00, fd->port_base + Interrupt_Cntl); /* We usually have one spurious interrupt after each command. Ignore it. */ - if (!in_command || !current_SC) { /* Spurious interrupt */ + if (!fd->in_command || !fd->current_SC) { /* Spurious interrupt */ #if EVERY_ACCESS printk( "Spurious interrupt, in_command = %d, current_SC = %x\n", - in_command, current_SC ); + fd->in_command, fd->current_SC); #endif return IRQ_NONE; } /* Abort calls my_done, so we do nothing here. */ - if (current_SC->SCp.phase & aborted) { + if (fd->current_SC->SCp.phase & aborted) { #if DEBUG_ABORT printk( "scsi: Interrupt after abort, ignoring\n" ); #endif @@ -1158,212 +1178,212 @@ static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id) } #if DEBUG_RACE - ++in_interrupt_flag; + ++fd->in_interrupt_flag; #endif - if (current_SC->SCp.phase & in_arbitration) { - status = inb(port_base + TMC_Status); /* Read adapter status */ + if (fd->current_SC->SCp.phase & in_arbitration) { + status = inb(fd->port_base + TMC_Status); /* Read adapter status */ if (!(status & 0x02)) { #if EVERY_ACCESS printk( " AFAIL " ); #endif - spin_lock_irqsave(current_SC->device->host->host_lock, flags); - my_done( DID_BUS_BUSY << 16 ); - spin_unlock_irqrestore(current_SC->device->host->host_lock, flags); + spin_lock_irqsave(fd->current_SC->device->host->host_lock, flags); + my_done(fd, DID_BUS_BUSY << 16); + spin_unlock_irqrestore(fd->current_SC->device->host->host_lock, flags); return IRQ_HANDLED; } - current_SC->SCp.phase = in_selection; + fd->current_SC->SCp.phase = in_selection; - outb(0x40 | FIFO_COUNT, port_base + Interrupt_Cntl); + outb(0x40 | FIFO_COUNT, fd->port_base + Interrupt_Cntl); - outb(0x82, port_base + SCSI_Cntl); /* Bus Enable + Select */ - outb(adapter_mask | (1 << scmd_id(current_SC)), port_base + SCSI_Data_NoACK); + outb(0x82, fd->port_base + SCSI_Cntl); /* Bus Enable + Select */ + outb(fd->adapter_mask | (1 << scmd_id(fd->current_SC)), fd->port_base + SCSI_Data_NoACK); /* Stop arbitration and enable parity */ - outb(0x10 | PARITY_MASK, port_base + TMC_Cntl); + outb(0x10 | PARITY_MASK, fd->port_base + TMC_Cntl); #if DEBUG_RACE - in_interrupt_flag = 0; + fd->in_interrupt_flag = 0; #endif return IRQ_HANDLED; - } else if (current_SC->SCp.phase & in_selection) { - status = inb(port_base + SCSI_Status); + } else if (fd->current_SC->SCp.phase & in_selection) { + status = inb(fd->port_base + SCSI_Status); if (!(status & 0x01)) { /* Try again, for slow devices */ - if (fdomain_select( scmd_id(current_SC) )) { + if (fdomain_select(fd,scmd_id(fd->current_SC))) { #if EVERY_ACCESS printk( " SFAIL " ); #endif - spin_lock_irqsave(current_SC->device->host->host_lock, flags); - my_done( DID_NO_CONNECT << 16 ); - spin_unlock_irqrestore(current_SC->device->host->host_lock, flags); + spin_lock_irqsave(fd->current_SC->device->host->host_lock, flags); + my_done(fd, DID_NO_CONNECT << 16); + spin_unlock_irqrestore(fd->current_SC->device->host->host_lock, flags); return IRQ_HANDLED; } else { #if EVERY_ACCESS printk( " AltSel " ); #endif /* Stop arbitration and enable parity */ - outb(0x10 | PARITY_MASK, port_base + TMC_Cntl); + outb(0x10 | PARITY_MASK, fd->port_base + TMC_Cntl); } } - current_SC->SCp.phase = in_other; - outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl); - outb(0x80, port_base + SCSI_Cntl); + fd->current_SC->SCp.phase = in_other; + outb(0x90 | FIFO_COUNT, fd->port_base + Interrupt_Cntl); + outb(0x80, fd->port_base + SCSI_Cntl); #if DEBUG_RACE - in_interrupt_flag = 0; + fd->in_interrupt_flag = 0; #endif return IRQ_HANDLED; } /* current_SC->SCp.phase == in_other: this is the body of the routine */ - status = inb(port_base + SCSI_Status); + status = inb(fd->port_base + SCSI_Status); if (status & 0x10) { /* REQ */ switch (status & 0x0e) { case 0x08: /* COMMAND OUT */ - outb(current_SC->cmnd[current_SC->SCp.sent_command++], - port_base + Write_SCSI_Data); + outb(fd->current_SC->cmnd[fd->current_SC->SCp.sent_command++], + fd->port_base + Write_SCSI_Data); #if EVERY_ACCESS printk( "CMD = %x,", - current_SC->cmnd[ current_SC->SCp.sent_command - 1] ); + fd->current_SC->cmnd[fd->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, port_base + TMC_Cntl); + if (fd->chip != tmc1800 && !fd->current_SC->SCp.have_data_in) { + fd->current_SC->SCp.have_data_in = -1; + outb(0xd0 | PARITY_MASK, fd->port_base + TMC_Cntl); } 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, port_base + TMC_Cntl); + if (fd->chip != tmc1800 && !fd->current_SC->SCp.have_data_in) { + fd->current_SC->SCp.have_data_in = 1; + outb(0x90 | PARITY_MASK, fd->port_base + TMC_Cntl); } break; case 0x0c: /* STATUS IN */ - current_SC->SCp.Status = inb(port_base + Read_SCSI_Data); + fd->current_SC->SCp.Status = inb(fd->port_base + Read_SCSI_Data); #if EVERY_ACCESS - printk( "Status = %x, ", current_SC->SCp.Status ); + printk( "Status = %x, ", fd->current_SC->SCp.Status ); #endif #if ERRORS_ONLY - if (current_SC->SCp.Status - && current_SC->SCp.Status != 2 - && current_SC->SCp.Status != 8) { + if (fd->current_SC->SCp.Status + && fd->current_SC->SCp.Status != 2 + && fd->current_SC->SCp.Status != 8) { printk( "scsi: target = %d, command = %x, status = %x\n", - current_SC->device->id, - current_SC->cmnd[0], - current_SC->SCp.Status ); + fd->current_SC->device->id, + fd->current_SC->cmnd[0], + fd->current_SC->SCp.Status ); } #endif break; case 0x0a: /* MESSAGE OUT */ - outb(MESSAGE_REJECT, port_base + Write_SCSI_Data); /* Reject */ + outb(MESSAGE_REJECT, fd->port_base + Write_SCSI_Data); /* Reject */ break; case 0x0e: /* MESSAGE IN */ - current_SC->SCp.Message = inb(port_base + Read_SCSI_Data); + fd->current_SC->SCp.Message = inb(fd->port_base + Read_SCSI_Data); #if EVERY_ACCESS - printk( "Message = %x, ", current_SC->SCp.Message ); + printk( "Message = %x, ", fd->current_SC->SCp.Message ); #endif - if (!current_SC->SCp.Message) ++done; + if (!fd->current_SC->SCp.Message) ++done; #if DEBUG_MESSAGES || EVERY_ACCESS - if (current_SC->SCp.Message) { + if (fd->current_SC->SCp.Message) { printk( "scsi: message = %x\n", - current_SC->SCp.Message ); + fd->current_SC->SCp.Message ); } #endif break; } } - if (chip == tmc1800 && !current_SC->SCp.have_data_in - && (current_SC->SCp.sent_command >= current_SC->cmd_len)) { + if (fd->chip == tmc1800 && !fd->current_SC->SCp.have_data_in + && (fd->current_SC->SCp.sent_command >= fd->current_SC->cmd_len)) { - if(current_SC->sc_data_direction == DMA_TO_DEVICE) + if (fd->current_SC->sc_data_direction == DMA_TO_DEVICE) { - current_SC->SCp.have_data_in = -1; - outb(0xd0 | PARITY_MASK, port_base + TMC_Cntl); + fd->current_SC->SCp.have_data_in = -1; + outb(0xd0 | PARITY_MASK, fd->port_base + TMC_Cntl); } else { - current_SC->SCp.have_data_in = 1; - outb(0x90 | PARITY_MASK, port_base + TMC_Cntl); + fd->current_SC->SCp.have_data_in = 1; + outb(0x90 | PARITY_MASK, fd->port_base + TMC_Cntl); } } - if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */ - while ((data_count = FIFO_Size - inw(port_base + FIFO_Data_Count)) > 512) { + if (fd->current_SC->SCp.have_data_in == -1) { /* DATA OUT */ + while ((data_count = fd->FIFO_Size - inw(fd->port_base + FIFO_Data_Count)) > 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 > fd->current_SC->SCp.this_residual) + data_count = fd->current_SC->SCp.this_residual; if (data_count > 0) { #if EVERY_ACCESS printk( "%d OUT, ", data_count ); #endif if (data_count == 1) { - outb(*current_SC->SCp.ptr++, port_base + Write_FIFO); - --current_SC->SCp.this_residual; + outb(*fd->current_SC->SCp.ptr++, fd->port_base + Write_FIFO); + --fd->current_SC->SCp.this_residual; } else { data_count >>= 1; - outsw(port_base + Write_FIFO, current_SC->SCp.ptr, data_count); - current_SC->SCp.ptr += 2 * data_count; - current_SC->SCp.this_residual -= 2 * data_count; + outsw(fd->port_base + Write_FIFO, fd->current_SC->SCp.ptr, data_count); + fd->current_SC->SCp.ptr += 2 * data_count; + fd->current_SC->SCp.this_residual -= 2 * data_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; + if (!fd->current_SC->SCp.this_residual) { + if (fd->current_SC->SCp.buffers_residual) { + --fd->current_SC->SCp.buffers_residual; + ++fd->current_SC->SCp.buffer; + fd->current_SC->SCp.ptr = sg_virt(fd->current_SC->SCp.buffer); + fd->current_SC->SCp.this_residual = fd->current_SC->SCp.buffer->length; } else break; } } } - if (current_SC->SCp.have_data_in == 1) { /* DATA IN */ - while ((data_count = inw(port_base + FIFO_Data_Count)) > 0) { + if (fd->current_SC->SCp.have_data_in == 1) { /* DATA IN */ + while ((data_count = inw(fd->port_base + FIFO_Data_Count)) > 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 > fd->current_SC->SCp.this_residual) + data_count = fd->current_SC->SCp.this_residual; if (data_count) { #if EVERY_ACCESS printk( "%d IN, ", data_count ); #endif if (data_count == 1) { - *current_SC->SCp.ptr++ = inb(port_base + Read_FIFO); - --current_SC->SCp.this_residual; + *fd->current_SC->SCp.ptr++ = inb(fd->port_base + Read_FIFO); + --fd->current_SC->SCp.this_residual; } else { data_count >>= 1; /* Number of words */ - insw(port_base + Read_FIFO, current_SC->SCp.ptr, data_count); - current_SC->SCp.ptr += 2 * data_count; - current_SC->SCp.this_residual -= 2 * data_count; + insw(fd->port_base + Read_FIFO, fd->current_SC->SCp.ptr, data_count); + fd->current_SC->SCp.ptr += 2 * data_count; + fd->current_SC->SCp.this_residual -= 2 * data_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 (!fd->current_SC->SCp.this_residual + && fd->current_SC->SCp.buffers_residual) { + --fd->current_SC->SCp.buffers_residual; + ++fd->current_SC->SCp.buffer; + fd->current_SC->SCp.ptr = sg_virt(fd->current_SC->SCp.buffer); + fd->current_SC->SCp.this_residual = fd->current_SC->SCp.buffer->length; } } } if (done) { #if EVERY_ACCESS - printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in ); + printk( " ** IN DONE %d ** ", fd->current_SC->SCp.have_data_in ); #endif #if ERRORS_ONLY - if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) { - char *buf = scsi_sglist(current_SC); + if (fd->current_SC->cmnd[0] == REQUEST_SENSE && !fd->current_SC->SCp.Status) { + char *buf = scsi_sglist(fd->current_SC); if ((unsigned char)(*(buf + 2)) & 0x0f) { unsigned char key; unsigned char code; @@ -1390,24 +1410,24 @@ static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id) #if EVERY_ACCESS printk( "BEFORE MY_DONE. . ." ); #endif - spin_lock_irqsave(current_SC->device->host->host_lock, flags); - my_done( (current_SC->SCp.Status & 0xff) - | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) ); - spin_unlock_irqrestore(current_SC->device->host->host_lock, flags); + spin_lock_irqsave(fd->current_SC->device->host->host_lock, flags); + my_done(fd, (fd->current_SC->SCp.Status & 0xff) + | ((fd->current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) ); + spin_unlock_irqrestore(fd->current_SC->device->host->host_lock, flags); #if EVERY_ACCESS printk( "RETURNING.\n" ); #endif } else { - if (current_SC->SCp.phase & disconnect) { - outb(0xd0 | FIFO_COUNT, port_base + Interrupt_Cntl); - outb(0x00, port_base + SCSI_Cntl); + if (fd->current_SC->SCp.phase & disconnect) { + outb(0xd0 | FIFO_COUNT, fd->port_base + Interrupt_Cntl); + outb(0x00, fd->port_base + SCSI_Cntl); } else { - outb(0x90 | FIFO_COUNT, port_base + Interrupt_Cntl); + outb(0x90 | FIFO_COUNT, fd->port_base + Interrupt_Cntl); } } #if DEBUG_RACE - in_interrupt_flag = 0; + fd->in_interrupt_flag = 0; #endif return IRQ_HANDLED; } @@ -1415,7 +1435,9 @@ static irqreturn_t do_fdomain_16x0_intr(int irq, void *dev_id) static int fdomain_16x0_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { - if (in_command) { + struct fdomain *fd = shost_priv(SCpnt->device->host); + + if (fd->in_command) { panic( "scsi: fdomain_16x0_queue() NOT REENTRANT!\n" ); } #if EVERY_ACCESS @@ -1426,38 +1448,38 @@ static int fdomain_16x0_queue_lck(struct scsi_cmnd *SCpnt, scsi_bufflen(SCpnt)); #endif - fdomain_make_bus_idle(); + fdomain_make_bus_idle(fd); - current_SC = SCpnt; /* Save this for the done function */ - current_SC->scsi_done = done; + fd->current_SC = SCpnt; /* Save this for the done function */ + fd->current_SC->scsi_done = done; /* Initialize static data */ - if (scsi_sg_count(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; + if (scsi_sg_count(fd->current_SC)) { + fd->current_SC->SCp.buffer = scsi_sglist(fd->current_SC); + fd->current_SC->SCp.ptr = sg_virt(fd->current_SC->SCp.buffer); + fd->current_SC->SCp.this_residual = fd->current_SC->SCp.buffer->length; + fd->current_SC->SCp.buffers_residual = scsi_sg_count(fd->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; + fd->current_SC->SCp.ptr = NULL; + fd->current_SC->SCp.this_residual = 0; + fd->current_SC->SCp.buffer = NULL; + fd->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; + fd->current_SC->SCp.Status = 0; + fd->current_SC->SCp.Message = 0; + fd->current_SC->SCp.have_data_in = 0; + fd->current_SC->SCp.sent_command = 0; + fd->current_SC->SCp.phase = in_arbitration; /* Start arbitration */ - outb(0x00, port_base + Interrupt_Cntl); - outb(0x00, port_base + SCSI_Cntl); /* Disable data drivers */ - outb(adapter_mask, port_base + SCSI_Data_NoACK); /* Set our id bit */ - ++in_command; - outb(0x20, port_base + Interrupt_Cntl); - outb(0x14 | PARITY_MASK, port_base + TMC_Cntl); /* Start arbitration */ + outb(0x00, fd->port_base + Interrupt_Cntl); + outb(0x00, fd->port_base + SCSI_Cntl); /* Disable data drivers */ + outb(fd->adapter_mask, fd->port_base + SCSI_Data_NoACK); /* Set our id bit */ + ++fd->in_command; + outb(0x20, fd->port_base + Interrupt_Cntl); + outb(0x14 | PARITY_MASK, fd->port_base + TMC_Cntl); /* Start arbitration */ return 0; } @@ -1470,18 +1492,20 @@ static void print_info(struct scsi_cmnd *SCpnt) unsigned int imr; unsigned int irr; unsigned int isr; + struct fdomain *fd; if (!SCpnt || !SCpnt->device || !SCpnt->device->host) { printk(KERN_WARNING "scsi: Cannot provide detailed information\n"); return; } + fd = shost_priv(SCpnt->device->host); printk(KERN_INFO "%s\n", fdomain_16x0_info( SCpnt->device->host ) ); print_banner(SCpnt->device->host); switch (SCpnt->SCp.phase) { - case in_arbitration: printk("arbitration"); break; - case in_selection: printk("selection"); break; - case in_other: printk("other"); break; + case fd->in_arbitration: printk("arbitration"); break; + case fd->in_selection: printk("selection"); break; + case fd->in_other: printk("other"); break; default: printk("unknown"); break; } @@ -1496,7 +1520,7 @@ static void print_info(struct scsi_cmnd *SCpnt) SCpnt->SCp.have_data_in, SCpnt->timeout ); #if DEBUG_RACE - printk( "in_interrupt_flag = %d\n", in_interrupt_flag ); + printk( "in_interrupt_flag = %d\n", fd->in_interrupt_flag ); #endif imr = (inb( 0x0a1 ) << 8) + inb( 0x21 ); @@ -1511,38 +1535,39 @@ static void print_info(struct scsi_cmnd *SCpnt) /* Print out interesting information */ printk( "IMR = 0x%04x", imr ); - if (imr & (1 << interrupt_level)) + if (imr & (1 << fd->interrupt_level)) printk( " (masked)" ); printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr ); - printk( "SCSI Status = 0x%02x\n", inb(port_base + SCSI_Status)); - printk( "TMC Status = 0x%02x", inb(port_base + TMC_Status)); - if (inb((port_base + TMC_Status) & 1)) + printk( "SCSI Status = 0x%02x\n", inb(fd->port_base + SCSI_Status)); + printk( "TMC Status = 0x%02x", inb(fd->port_base + TMC_Status)); + if (inb((fd->port_base + TMC_Status) & 1)) printk( " (interrupt)" ); printk( "\n" ); - printk("Interrupt Status = 0x%02x", inb(port_base + Interrupt_Status)); - if (inb(port_base + Interrupt_Status) & 0x08) + printk("Interrupt Status = 0x%02x", inb(fd->port_base + Interrupt_Status)); + if (inb(fd->port_base + Interrupt_Status) & 0x08) printk( " (enabled)" ); printk( "\n" ); - if (chip == tmc18c50 || chip == tmc18c30) { - printk("FIFO Status = 0x%02x\n", inb(port_base + FIFO_Status)); + if (fd->chip == tmc18c50 || fd->chip == tmc18c30) { + printk("FIFO Status = 0x%02x\n", inb(fd->port_base + FIFO_Status)); printk( "Int. Condition = 0x%02x\n", - inb( port_base + Interrupt_Cond ) ); + inb(fd->port_base + Interrupt_Cond)); } - printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) ); - if (chip == tmc18c50 || chip == tmc18c30) + printk("Configuration 1 = 0x%02x\n", inb(fd->port_base + Configuration1)); + if (fd->chip == tmc18c50 || fd->chip == tmc18c30) printk( "Configuration 2 = 0x%02x\n", - inb( port_base + Configuration2 ) ); + inb(fd->port_base + Configuration2)); } #endif static int fdomain_16x0_abort(struct scsi_cmnd *SCpnt) { + struct fdomain *fd = shost_priv(SCpnt->device->host); #if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT printk( "scsi: abort " ); #endif - if (!in_command) { + if (!fd->in_command) { #if EVERY_ACCESS || ERRORS_ONLY printk( " (not in command)\n" ); #endif @@ -1553,28 +1578,22 @@ static int fdomain_16x0_abort(struct scsi_cmnd *SCpnt) print_info( SCpnt ); #endif - fdomain_make_bus_idle(); - current_SC->SCp.phase |= aborted; - current_SC->result = DID_ABORT << 16; + fdomain_make_bus_idle(fd); + fd->current_SC->SCp.phase |= aborted; + fd->current_SC->result = DID_ABORT << 16; /* Aborts are not done well. . . */ - my_done(DID_ABORT << 16); + my_done(fd, DID_ABORT << 16); return SUCCESS; } int fdomain_16x0_host_reset(struct scsi_cmnd *SCpnt) { + struct fdomain *fd = shost_priv(SCpnt->device->host); unsigned long flags; local_irq_save(flags); - - outb(1, port_base + SCSI_Cntl); - do_pause( 2 ); - outb(0, port_base + SCSI_Cntl); - do_pause( 115 ); - outb(0, port_base + SCSI_Mode_Cntl); - outb(PARITY_MASK, port_base + TMC_Cntl); - + fdomain_16x0_reset(fd->port_base); local_irq_restore(flags); return SUCCESS; } @@ -1591,6 +1610,7 @@ static int fdomain_16x0_biosparam(struct scsi_device *sdev, unsigned char heads; unsigned char sectors; } i; + struct fdomain *fd = shost_priv(sdev->host); /* NOTES: The RAM area starts at 0x1f00 from the bios_base address. @@ -1641,8 +1661,8 @@ static int fdomain_16x0_biosparam(struct scsi_device *sdev, } drive = MINOR(bdev->bd_dev) >> 4; - if (bios_major == 2) { - switch (Quantum) { + if (fd->bios_major == 2) { + switch (fd->Quantum) { case 2: /* ISA_200S */ /* The value of 25 has never been verified. It should probably be 15. */ @@ -1658,14 +1678,14 @@ static int fdomain_16x0_biosparam(struct scsi_device *sdev, offset = 0x1f31 + drive * 25; break; } - memcpy_fromio( &i, bios_mem + offset, sizeof( struct drive_info ) ); + memcpy_fromio( &i, fd->bios_mem + offset, sizeof( struct drive_info ) ); info_array[0] = i.heads; info_array[1] = i.sectors; info_array[2] = i.cylinders; - } else if (bios_major == 3 - && bios_minor >= 0 - && bios_minor < 4) { /* 3.0 and 3.2 BIOS */ - memcpy_fromio( &i, bios_mem + 0x1f71 + drive * 10, + } else if (fd->bios_major == 3 + && fd->bios_minor >= 0 + && fd->bios_minor < 4) { /* 3.0 and 3.2 BIOS */ + memcpy_fromio( &i, fd->bios_mem + 0x1f71 + drive * 10, sizeof( struct drive_info ) ); info_array[0] = i.heads + 1; info_array[1] = i.sectors; @@ -1736,12 +1756,13 @@ static int fdomain_16x0_biosparam(struct scsi_device *sdev, #ifndef PCMCIA static int fdomain_16x0_release(struct Scsi_Host *shpnt) { + struct fdomain *fd = shost_priv(shpnt); if (shpnt->irq) - free_irq(shpnt->irq, shpnt); + free_irq(shpnt->irq, fd); if (shpnt->io_port && shpnt->n_io_port) release_region(shpnt->io_port, shpnt->n_io_port); - if (PCI_bus) - pci_dev_put(PCI_dev); + if (fd->PCI_bus) + pci_dev_put(fd->PCI_dev); return 0; } #endif