diff -Nru linux/Documentation/Configure.help linux-2.4.19-pre5-mjc/Documentation/Configure.help --- linux/Documentation/Configure.help Mon Apr 8 22:26:52 2002 +++ linux-2.4.19-pre5-mjc/Documentation/Configure.help Mon Apr 8 22:36:09 2002 @@ -8103,6 +8103,19 @@ say M here and read . The module will be called sim710.o. +Tekram DC395/U/UW and DC315/U SCSI support +CONFIG_SCSI_DC395x_TRMS1040 + This driver supports the PCI SCSI host adapters baseds on Tekram's + ASIC TRM-S1040 chip, i.e. Tekram DC315 and DC395 variants. + This driver does work, but please note that it is still beta status, + so better have a bootable disk and a backup in case of emergency. + Please read the file . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module will be + called dc395x_trm.o. + Tekram DC390(T) and Am53/79C974 SCSI support CONFIG_SCSI_DC390T This driver supports PCI SCSI host adapters based on the Am53C974A diff -Nru linux/MAINTAINERS linux-2.4.19-pre5-mjc/MAINTAINERS --- linux/MAINTAINERS Mon Apr 8 22:35:07 2002 +++ linux-2.4.19-pre5-mjc/MAINTAINERS Mon Apr 8 22:36:09 2002 @@ -421,10 +421,12 @@ L: linux-hams@vger.kernel.org S: Maintained -DC390/AM53C974 SCSI driver +DC390/AM53C974 and DC395/TRM-S1040 SCSI drivers P: Kurt Garloff M: garloff@suse.de -W: http://www.garloff.de/kurt/linux/dc390/ +M: kurt@garloff.de +W: http://www.garloff.de/kurt/linux/ +L: linux-scsi@vger.kernel.org S: Maintained DECnet NETWORK LAYER diff -Nru linux/drivers/scsi/Config.in linux-2.4.19-pre5-mjc/drivers/scsi/Config.in --- linux/drivers/scsi/Config.in Mon Feb 25 14:38:04 2002 +++ linux-2.4.19-pre5-mjc/drivers/scsi/Config.in Mon Apr 8 22:36:09 2002 @@ -189,6 +189,9 @@ dep_tristate 'Simple 53c710 SCSI support (Compaq, NCR machines)' CONFIG_SCSI_SIM710 $CONFIG_SCSI dep_tristate 'Symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'Tekram DC395/U/UW and DC315/U SCSI support' CONFIG_SCSI_DC395x_TRMS1040 $CONFIG_SCSI + fi dep_tristate 'Tekram DC390(T) and Am53/79C974 SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI if [ "$CONFIG_SCSI_DC390T" != "n" ]; then bool ' _omit_ support for non-DC390 adapters' CONFIG_SCSI_DC390T_NOGENSUPP diff -Nru linux/drivers/scsi/Makefile linux-2.4.19-pre5-mjc/drivers/scsi/Makefile --- linux/drivers/scsi/Makefile Mon Feb 25 14:38:04 2002 +++ linux-2.4.19-pre5-mjc/drivers/scsi/Makefile Mon Apr 8 22:36:09 2002 @@ -104,6 +104,7 @@ obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_DC390T) += tmscsim.o +obj-$(CONFIG_SCSI_DC395x_TRMS1040) += dc395x_trm.o obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o obj-$(CONFIG_SCSI_ACARD) += atp870u.o diff -Nru linux/drivers/scsi/README.dc395x linux-2.4.19-pre5-mjc/drivers/scsi/README.dc395x --- linux/drivers/scsi/README.dc395x Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/drivers/scsi/README.dc395x Mon Apr 8 22:36:09 2002 @@ -0,0 +1,282 @@ +README file for the dc395x_trm SCSI driver +========================================== + +Preliminary. 2000-02-14 Kurt Garloff +$Id: README.dc395x,v 1.16 2002/02/13 10:23:58 garloff Exp $ + +This driver is similar to the DC390/AM53c974 driver (tmscsim), so you might +want to have a look into the README.tmscsim. +The tmscsim has undergone a lot of development since the 1.10 version, most +notably the configuration via parameters and /proc/scsi/tmscsim/?. + +These features are now almost completely implemented for the dc395x_trm +driver, too. + +Status +------ +The dc395x_trm is BETA. This means that it might crash your hard disk or +fail to read your CD-Rom. +It seems to basically work for a lot of cases and success reports have been +sent to me. On the other hand, I have a few failure reports, which I was not +yet able to resolve. So, don't rely on this driver. Not yet. + +It is a good idea, to have a BACKUP, if you have valuable data on your disk. +If you can't do it, print at least your partition table or save it to a safe +place and have a boot floppy ready. + +Status (2) +---------- +The driver works perfectly for my Fireball harddisk but fails sometimes on +my IBM DORS and on my IBM DCAS drive and on my Plextor UltraPlex. +I have no clue at all, why it happens. +I spent a couple of days investigating, creating a driver with extensive +sanity checks and debugging possibilities. To no avail! I will look into +this again, when I get good docu from Tekram. The one I have currently is +lousy. +The driver now at least works partially. +The following things seem to help: +Disallowing disconnection, Sync transfers for the device in question or +lowering the Sync speed. Sync speeds of 10MHz or lower seem to work all the +time for me. + +It seems to be related to Sync transfers done via the S/G DMA. The tests +being done lead to no conclusion, though. If you find some pattern, please +let me know. +More hints: The data integrity checks within the driver all succeed. + +The transfers normally succeed until after some reselections, the device +does not finish the DataIn phase. The SCSI Counter says that a certain +number of bytes have been transfered (and this number seems to be equal to +0x1000 or at least a multiple of 0x200) successfully and the SCSI FIFO is +empty. The DMA engine seems also to approach a block boundary: A Current +TransferCtr of 0x208 with a FIFO counter of 0x08 is a typical value. +(That happens with reading from the DORS.) The bus is locked up (probably +the dev. is waiting for some more ACKs). + +If the S/G support is limited to one segment, preventing the above to +happen, the bus seems to be locked up after a Disconnection, so the next +command being sent (TagQ is enabled) fails. + +Status (3) +---------- +It seems to be a matter of the quality of your SCSI bus and the quality of +the lnie drivers of the DC395. While the NCR53C8xx drives a bus with 2.5m +length and 7 devices without problems at 20MHz, the DC395UW locks up the +bus, if driven with more than 10MHz. Using 10MHz max. Sync Frequency, I +could not produce problems. + +Reducing the bus to 1.2m and 3 devices, the DC395UW happily does 13.3MHz. +The 20MHz are still not perfect, and I could watch a problem once. (Whereas +the bus now should really be perfect for doing high speed transfers.) + +It's also completely unclear to me, why this problem does not occur, if +disconnection is turned off for the fast devices. Then, I can do 20MHz with +the DC395UW on the long bus setup. + +Maybe the chip is extremely sensitive to timing issues, maybe the line +drivers are not very good, maybe its input filters are not very good. + +I did play a little bit with the filter cfg. of the Tekram, but without too +much success. Disabling the Active Negation _Enhance_ feature gave a little +bit better results, so I did disable it for the driver. +You may want to play with the FilterCfg setting yourself, the /proc +interface allows to change it. +(Bits: 0: Always 1, 1: Active Negation, 2: Fast Filter enable, 3: Data + Filter disable, 4: Active Negation enhance) + +Status (4) +---------- +Part of the above said has to be taken back. The driver did still have one +serious bug which -- for timing reasons -- only showed on fast transfers and +a busy SCSI bus. A FIFO was cleared at the wrong place. +Now, the driver seems to work with 20MHz on my system reliably. +The hardware is not quite well supporting more than one initiator controller +on the bus: If the bus is busy and the TRM_S1040 tries to arbitrate and +select a device, it does not always generate an interrupt (which would +indicate Disconnection, Selection Timeout, Reselection or -- in the best +case -- a phase change). The driver takes this into account now and does not +even try. Instead the waiting queue will be woken up by a timer a 50 ms +later to try again. + + +Exception handling and automatic downgrading +-------------------------------------------- +For the positive side: +The exception handling procedure (invoked from mid-level SCSI code) does its +job pretty well in most of the cases, so your transfers proceed after the +SCSI bus has been reset. (No, this won't make you happy, if you are +writing to tapes or CDRs, but otherwise, you may be lucky.) In spite of the +bus lock ups, I did not loose any data on my harddisks. +The driver is clever enough to reduce the settings for the device which +caused the bus lockup, so after a few resets the driver will have found the +maximum possible speed of your drive. So even with a bad bus, you will be +able to use this driver in slow mode. (Use the /proc/scsi interface to try +to change the settings again. You may choose to disable disconnections +instead of lowering the speed of sync transfers.) + + +Usage +----- +I would currently not suggest to use this driver, if you are dealing with +important data, which you don't have a backup of! YOU RISK DATA LOSS! + +OK, you want to use it anyway? +Start with read-only access of your data. catting or dding it to /dev/null +is a good start. Proceed with read-only filesystem checks (e2fsck -nfF). +Produce concurrent load on your SCSI bus. Still no trouble? What does the +badblocks program say? (Take care: With -w it will overwrite your data!) +Go on, mount rw an unused partition and run bonnie a couple of times. +Run it twice concurrently. If you have not seen any problems by now, you +might want to take the risk and use it on your normal filesystems. +If you find any trouble, you can reduce the Sync Speed and try again. That +way, you may find the best safe settings for your bus. + +Please note that the driver spits out some debug messages from time to time. +Those start with "DC395x: Debug:" and are certainly no reason to worry. + +If you find trouble, you can contact me, of course. Please, don't bother +sending useless bug reports saying: It does not work. You should at least be +able to provide some details about your SCSI setup, the messages written to +the syslog and the outputs of /proc/scsi/scsi and /proc/scsi/dc395x_trm/?. + +If your driver crashes badly, log via network or a serial console. (The +latter is what I do during driver development.) + +If you can provide me with exact observations and logs of failures, I would +be very pleased! + + +Parameters +---------- +The driver uses the settings from the EEPROM set in the SCSI BIOS +setup. +If there is no EEPROM, the driver uses default values. +Both can be overriden by command line parameters (module or kernel +parameters). [This is new; formerly the EEprom could not be overriden.] + +The syntax is as follows: + dc395x_trm = AdapterID, SpeedIdx, DevMode, AdaptMode, Tags, DelayReset + +AdapterID : Host Adapter SCSI ID +SpeedIdx : 0,1,...7 = 20,13.3,10,8,6.7,5.8,5,4 MHz [ 7] +DevMode : Bitmap for Dev Cfg [63] +AdaptMode : Bitmap for Adapter Cfg [47] +Tags : The number of tags is 1<= 1. + +If you set AdapterID to -1, the adapter will use conservative ("safe") +default settings instead; more precisely, dc395x_trm=-1 is a shortcut for +dc395x_trm=7,4,9,15,2,10 + +If you specify -2 for a value, it will be ignored. You don't need to specify +all six parameters. + +Note, that you can also influence the behaviour of the host adapter at +runtime by echoing values to /proc/scsi/dc395x_trm/?. Have a look at +README.tmscsim for explanation and examples. The syntax is the same, except +for the added "Wide" setting. + + +Installation +------------ +As long as this driver is not yet integrated into the mainstream kernel, you +have to do some handwork to install the driver. +IF YOU NEVER COMPILED A KERNEL YOURSELF BEFORE, YOU SHOULD NOW BETTER GO AND +READ SOME DOCUMENTATION ABOUT IT. YOU ARE LIKELY TO SCREW YOURSELF, OTHERWISE. +FIRST COMPILE A KERNEL WITHOUT THIS DRIVER AND GET IT TO WORK. +Maybe you Linux distributor will be nice enough to include the driver in its +distribution, so you don't have to do it yourself. + +OK, if you want to do it: Get the driver distribution dc395-1??.tar.gz. +Unpack it: +> tar xvzf dc395-1??.tar.gz +Copy the driver files to the linux source tree: +> cp -p dc395/dc395x_trm.? /usr/src/linux/drivers/scsi/ +> cp -p dc395/README.dc395x /usr/src/linux/drivers/scsi/ +and apply the appropriate patch to your kernel source tree. Depending on +your kernel version, this is dc395-integ20.diff, dc395-integ22.diff or +dc395-integ24.diff. (Replace XX by 20, 22, 23 or 24) +> patch -p1 -d /usr/src/linux cd /usr/src/linux +> make oldconfig (enable CONFIG_SCSI_DC395x_TRMS1040) +> make bzlilo +> make modules +> make modules_install +> depmod -ae + +NEW: You may be able to skip compilation of the kernel by just using the +Makefile. For 2.4 kernels, if you compile outside the kernel source tree, +you don't even need to apply a patch to your kernel any more. + +I will provide a complete patch which makes kernel integration easier and +will ask to apply it to the main tree, as soon as I got enough reports about +the stability of the driver. +Currently, there are still too many problems left :-( + + +Copyright +--------- +The driver is free software. It is protected by the GNU General Public +License (GPL). Please read it, before using this driver. It should be +included in your kernel sources and with your distribution. It carries the +filename COPYING. If you don't have it, please ask me to send you one by +email. +Note: The GNU GPL says also something about warranty and liability. +Please be aware the following: While I do my best to provide a working and +reliable driver, there is a chance, that it will kill your valuable data. +I refuse to take any responsibility for that. The driver is provided as-is +and YOU USE IT AT YOUR OWN RESPONSIBILITY. + + +Updates +------- +Resources for this driver can be found on +http://www.garloff.de/kurt/linux/dc395/ +ftp://ftp.suse.com/pub/people/garloff/linux/dc395/ +(and mirrors, of course) +Announcements will be sent to the linux-scsi@vger.kernel.org list. + + +Acknowledgements +---------------- +I'd like to thank SuSE GmbH, Nuremberg, to allow me working on this driver +during my working hours for them. +Thanks to all the people that took the risk to install the driver in its +alpha stage and sent detailed bug reports to me, sometimes. Without them, +the driver would now not approach stability. They are the reason why free +software rules. diff -Nru linux/drivers/scsi/dc395x_trm.c linux-2.4.19-pre5-mjc/drivers/scsi/dc395x_trm.c --- linux/drivers/scsi/dc395x_trm.c Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/drivers/scsi/dc395x_trm.c Mon Apr 8 22:36:09 2002 @@ -0,0 +1,7674 @@ +//*********************************************************************** +//* O.S : Linux +//* FILE NAME : dc395x_trm.c +//* BY : C.L. Huang +//* Erich Chen +//* Description: Device Driver for Tekram DC395U/UW/F ,DC315/U +//* PCI SCSI Bus Master Host Adapter +//* (SCSI chip set used Tekram ASIC TRM-S1040) +//* (C)Copyright 1995-1999 Tekram Technology Co., Ltd. +//*********************************************************************** +//* Lots of bugfixes and integration into 2.2+ kernels by +//* Kurt Garloff +//* (C) 1999-2000 Kurt Garloff +//* License: GNU GPL +//* $Id: dc395x_trm.c,v 1.79 2002/03/11 02:34:37 garloff Exp $ +//*********************************************************************** +//* Tekram PCI SCSI adapter (DC395/U/UW/F or DC315/U) revision history +//* +//* REV# DATE NAME DESCRIPTION +//* 1.00 99/02/28 Erich Chen First release +//* 1.01 99/06/28 Kurt Garloff SMP fixes for 2.2 (locking), +//* cleanup +//* 1.06 99/06/30 Erich Chen Modify for linux SMP kernel 2.2.5 +//* include spinlock.h +//* 1.07 99/07/12 Kurt Garloff Merge of 1.01 and 1.06 +//* 1.08 99/07/15 Kurt Garloff Fix Oopses, Message Handling, +//* Copy SyncMode to LUNs +//* 1.09 99/07/18 Kurt Garloff Fix Oops. Fix recognition of devs. +//* Fixed TagQ: MaxCommand limit! +//* 1.10 99/07/19 KG Defines for switching off features. +//* 1.10a 99/08/31 KG Fix typo in dc395-integ20.diff +//* 1.11 99/10/14 KG Remove some debug statements. +//* 1.12 00/01/31 KG Tell ML that we only handle 16 tags. +//* 2.3 compat. +//* 1.13 00/02/03 KG Hunt bugs. Use DC395x_READ/WRITE macros +//* 1.14 00/02/06 KG Complete rewrite of message handling +//* (merge from tmscsim) +//* 1.15 00/02/07 KG Rewrite of queueing code +//* (merge from tmscsim) +//* 1.16 00/02/07 KG dynamic DCB handling merge from +//* tmscsim +//* 1.17 00/02/07 KG TRACEPRINTF debugging +//* 1.18 00/02/07 KG Clean DevMode, SyncMode handling +//* Dynamic config via proc if +//* 1.19 00/02/08 KG 2 Timers per ACB: Waiting, DIP1 +//* Clean SDTR/WDTR dynamic handling +//* DC395x_dump (proc if) +//* Partial SG transfer debug +//* .. without success! Sigh! +//* 1.20 00/02/09 KG biosparm: scsicam +//* reading seems semi-stable +//* writing half-way +//* 1.21 00/02/10 KG exception handling does its job +//* and recovers from most trouble +//* 1.22 00/02/10 KG Fixed FIFO settings (clear, latch ..) +//* DC395x_cleanup_after_transfer ! +//* seems to be the long searched for +//* solution +//* 1.23 00/02/11 KG Automatically lower speed etc. in abort +//* Remove CmdBlock copy. +//* 1.24 00/02/14 KG Try to really abort in case of bus lock +//* Fix bug wrt to lowering of sync speed +//* Boot params for BIOS-less adapters +//* Respect reset cfg but not on shutdown +//* 1.25 00/02/16 KG There was one CLRFIFO too much ... +//* The TRM_S1040 does not reliably send an IRQ +//* in caise of a failed selection/arbitration +//* DEBUGTRACE now safe. New DEBUGFIFO option +//* 1.26 00/02/25 KG __init cleanup. IRQ sharing. dev_id for +//* irq_req. 2.0 compatibility (timer) +//* Avoid DMA for up to 4 bytes (=:PIO) +//* DOP1: Don't rely on CTR being zero, as +//* FIFO may be non-empty. +//* 1.27 00/05/23 KG Linux 2.3 and 2.0 compatibility +//* Cleanups +//* Timer for chip flaw (misses SelTO), +//* (but disabled, as it does fail smtms) +//* Connector cfg output changed +//* No clrfifo after MIP1 +//* 1.28 00/09/29 KG Only try sync/wide nego, if device reports +//* to be capable of doing so. 2.4 fixes: +//* New scanning sequence ... Fix typo +//* 1.29 00/10/12 KG Allow in kernel compilation w/ 2.4 +//* 1.30 00/11/22 KG Use DCB field for reported INQ flags +//* 2.4: Use get_options(). Add pci_dev_tbl[]. +//* 1.31 00/11/24 KG Fix another NULL ptr. Allow overriding of +//* BIOS settings by cmd ln params. +//* 1.32 00/12/02 KG Another 2.4 fix: Remove TYPE_NODEV devices +//* 1.33 01/07/09 KG Compile fixes for newer gccs (preproc.) +//* 1.34 01/11/14 KG Fix SG list length != sum length segments (!) +//* case (contributed by Dag Nygren ) +//* Cleanup DOP0/DIP0 handling a bit. +//* Try to work around extraneous 2 DOP bytes. +//* Merge SG segments if possible. +//* 1.35 01/12/06 KG Fix residual calculation. Add MODULE_LICENSE. +//* 1.35a 02/02/08 KG Ignore DMA errors; don't try stupid loop +//* to drain buffers. Set max_cmd_len to 24. +//* 1.36 02/02/27 KG Use pci_map() instead of virt_to_bus(). +//* Linux 2.5 Scsi_Host->host_lock locking +//* Slight variation of chip bug worked around :-( +//* 1.37 02/02/28 KG Free SRB's SG lists on module exit +//* Alloc in many chunks instead of one big +//* and don't waste memory by alignment. +//* 1.38 02/03/11 KG Fix pci_map stuff for 2.4 kernels +//* (sg.address vs. page_address(sg.page) + sg.offset) +//* Correctly clean up SG list after REQUEST_SENSE. +//* kfree() DCBs on module exit. +//* Fix inconsistency SRB_array[] size vs. DC395x_MAX_SRB_CNT +//*********************************************************************** +/* +************************************************************************* +** +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +************************************************************************* +*/ + +/* Debugging */ +//#define DC395x_DEBUG_KG +//#define DC395x_DEBUG0 +//#define DC395x_DEBUG1 +//#define DC395x_DEBUGDCB +#define DC395x_DEBUGTRACE +//#define DC395x_DEBUGTRACEALL +//#define DC395x_DEBUGPARSE +//#define DC395x_SGPARANOIA +//#define DC395x_DEBUGFIFO +//#define DC395x_DEBUGRECURSION +//#define DC395x_DEBUGPIO + +/* DISable features */ +//#define DC395x_NO_DISCONNECT +//#define DC395x_NO_TAGQ +//#define DC395x_NO_SYNC +//#define DC395x_NO_WIDE + +#ifdef DC395x_DEBUG0 +# define DEBUG0(x) x +#else +# define DEBUG0(x) +#endif + +#ifdef DC395x_DEBUG1 +# define DEBUG1(x) x +#else +# define DEBUG1(x) +#endif + +#ifdef DC395x_DEBUGDCB +# define DCBDEBUG(x) x +#else +# define DCBDEBUG(x) +#endif + +#ifdef DC395x_DEBUGPARSE +# define PARSEDEBUG(x) x +#else +# define PARSEDEBUG(x) +#endif + +#ifdef DC395x_DEBUGRECURSION +# define DEBUGRECURSION(x) x +#else +# define DEBUGRECURSION(x) +#endif + +#ifdef DC395x_DEBUGPIO +# define DEBUGPIO(x) x +#else +# define DEBUGPIO(x) +#endif + +/* Here comes the joker of all debugging facilities! */ +#ifdef DC395x_DEBUGTRACEALL +# ifndef DC395x_DEBUGTRACE +# define DC395x_DEBUGTRACE +# endif +# define TRACEOUTALL(x...) printk ( x) +#else +# define TRACEOUTALL(x...) do {} while (0) +#endif +#ifdef DC395x_DEBUGTRACE +# define DEBUGTRACEBUFSZ 512 +char DC395x_tracebuf[64]; +char DC395x_traceoverflow[8] = {0,0,0,0,0,0,0,0}; +# define TRACEPRINTF(x...) \ +do { int ln = sprintf (DC395x_tracebuf, x); \ + if (pSRB->debugpos + ln >= DEBUGTRACEBUFSZ) \ + { pSRB->debugtrace[pSRB->debugpos] = 0; pSRB->debugpos = DEBUGTRACEBUFSZ/5; pSRB->debugtrace[pSRB->debugpos++] = '>'; }; \ + sprintf (pSRB->debugtrace + pSRB->debugpos, "%s", DC395x_tracebuf); \ + pSRB->debugpos += ln - 1; \ + } while (0) +# define TRACEOUT(x...) printk ( x) +#else +# define TRACEPRINTF(x...) do {} while (0) +# define TRACEOUT(x...) do {} while (0) +#endif + +#ifdef MODULE +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,16) +# include +#endif +#include "scsi.h" +#include "hosts.h" +#include "constants.h" +#include "sd.h" +#include "dc395x_trm.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93) +# include +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30) +# include +# else +# include +# endif /* 2,3,30 */ +# define USE_SPINLOCKS 1 +#else +# define __initfunc(A) A +# define __initdata +# define __init +# include +#endif + +#define PCI_VendorID_TEKRAM 0x1DE1 /* Vendor ID */ +#define PCI_DeviceID_TRMS1040 0x0391 /* Device ID */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,99) +static struct pci_device_id dc395x_pci_tbl[] __initdata = { + { + vendor: PCI_VendorID_TEKRAM, + device: PCI_DeviceID_TRMS1040, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, dc395x_pci_tbl); +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70) +# define NEW_PCI 1 +#endif + +#ifdef USE_SPINLOCKS +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +# define DC395x_LOCK_IO(dev) spin_lock_irqsave (((struct Scsi_Host *)dev)->host_lock, flags) +# define DC395x_UNLOCK_IO(dev) spin_unlock_irqrestore (((struct Scsi_Host *)dev)->host_lock, flags) +# else +# define DC395x_LOCK_IO(dev) spin_lock_irqsave (&io_request_lock, flags) +# define DC395x_UNLOCK_IO(dev) spin_unlock_irqrestore (&io_request_lock, flags) +# endif +#else +# define DC395x_LOCK_IO(dev) save_flags (flags); cli () +# define DC395x_UNLOCK_IO(dev) restore_flags (flags) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93) + +# define DC395x_ACB_INITLOCK(pACB) spin_lock_init(&pACB->smp_lock) +# define DC395x_ACB_LOCK(pACB,acb_flags) if(!pACB->lock_level_count[cpuid]) { spin_lock_irqsave(&pACB->smp_lock,acb_flags); pACB->lock_level_count[cpuid]++; } else { pACB->lock_level_count[cpuid]++; } +# define DC395x_ACB_UNLOCK(pACB,acb_flags) if(--pACB->lock_level_count[cpuid] == 0) { spin_unlock_irqrestore(&pACB->smp_lock,acb_flags); } + +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +# define DC395x_SMP_IO_LOCK(dev,irq_flags) spin_lock_irqsave(((struct Scsi_Host *)dev)->host_lock,irq_flags) +# define DC395x_SMP_IO_UNLOCK(dev,irq_flags) spin_unlock_irqrestore(((struct Scsi_Host *)dev)->host_lock,irq_flags) +# else +# define DC395x_SMP_IO_LOCK(dev,irq_flags) spin_lock_irqsave(&io_request_lock,irq_flags) +# define DC395x_SMP_IO_UNLOCK(dev,irq_flags) spin_unlock_irqrestore(&io_request_lock,irq_flags) +# endif +# define DC395x_SCSI_DONE_ACB_LOCK spin_lock(&(pACB->smp_lock)) +# define DC395x_SCSI_DONE_ACB_UNLOCK spin_unlock(&(pACB->smp_lock)) + +#else + +# define DC395x_ACB_INITLOCK(pACB) +# define DC395x_ACB_LOCK(pACB,acb_flags) do { save_flags(acb_flags); cli(); } while (0) +# define DC395x_ACB_UNLOCK(pACB,acb_flags) do { restore_flags(acb_flags); } while (0) + +# define DC395x_SMP_IO_LOCK(dev,irq_flags) do { save_flags(irq_flags); cli(); } while (0) +# define DC395x_SMP_IO_UNLOCK(dev,irq_flags) do { restore_flags(irq_flags); } while (0) + +# define DC395x_SCSI_DONE_ACB_LOCK +# define DC395x_SCSI_DONE_ACB_UNLOCK + +#endif + +# define DC395x_DRV_LOCK(drv_flags) do { save_flags(drv_flags); cli(); } while (0) +# define DC395x_DRV_UNLOCK(drv_flags) do { restore_flags(drv_flags); } while (0) + +#define DC395x_read8(address) \ + (inb (pACB->IOPortBase + (address))) + +#define DC395x_read8_(address, base) \ + (inb ((USHORT)(base) + (address))) + +#define DC395x_read16(address) \ + (inw (pACB->IOPortBase + (address))) + +#define DC395x_read32(address) \ + (inl (pACB->IOPortBase + (address))) + +#define DC395x_write8(address,value) \ + outb ((value), pACB->IOPortBase + (address)) + +#define DC395x_write8_(address,value,base) \ + outb ((value), (USHORT)(base) + (address)) + +#define DC395x_write16(address,value) \ + outw ((value), pACB->IOPortBase + (address)) + +#define DC395x_write32(address,value) \ + outl ((value), pACB->IOPortBase + (address)) + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,16) +# define PCI_MAP_SINGLE(hw,ptr,sz,dir) pci_map_single(hw,ptr,sz,dir) +# define PCI_UNMAP_SINGLE(hw,dma,sz,dir) pci_unmap_single(hw,dma,sz,dir) +# define PCI_MAP_SG(hw,sg,n,dir) pci_map_sg(hw,sg,n,dir) +# define PCI_UNMAP_SG(hw,sg,n,dir) pci_unmap_sg(hw,sg,n,dir) +# define PCI_DMA_SYNC_SINGLE(hw,dma,sz,dir) pci_dma_sync_single(hw,dma,sz,dir) +# define PCI_DMA_SYNC_SG(hw,sg,n,dir) pci_dma_sync_sg(hw,sg,n,dir) +# define BUS_ADDR(sg) ((sg).dma_address) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +# define CPU_ADDR(sg) (page_address((sg).page)+(sg).offset) +# define PAGE_ADDRESS(sg) page_address((sg)->page) +#else +# define CPU_ADDR(sg) ((sg).address? (sg).address: page_address((sg).page)+(sg).offset) +# define PAGE_ADDRESS(sg) ((sg)->address? (sg)->address /*& ~PAGE_MASK*/: page_address((sg)->page)) +#endif +# define SET_DIR(dir,pcmd) dir = scsi_to_pci_dma_dir((pcmd)->sc_data_direction) +#else +# ifndef PCI_DMA_NONE +# define PCI_DMA_BIDIRECTIONAL 0 +# define PCI_DMA_TODEVICE 1 +# define PCI_DMA_FROMDEVICE 2 +# define PCI_DMA_NONE 3 +# endif +# define PCI_MAP_SINGLE(hw,ptr,sz,dir) virt_to_bus(ptr) +# define PCI_UNMAP_SINGLE(hw,dma,sz,dir) +# define PCI_MAP_SG(hw,sg,n,dir) n +# define PCI_UNMAP_SG(hw,sg,n,dir) +# define PCI_DMA_SYNC_SINGLE(hw,dma,sz,dir) +# define PCI_DMA_SYNC_SG(hw,sg,n,dir) +# define BUS_ADDR(sg) (virt_to_bus((sg).address)) +# define CPU_ADDR(sg) ((sg).address) +# define SET_DIR(dir,pcmd) dir = PCI_DMA_BIDIRECTIONAL +#endif + + +/* cmd->result */ +#define RES_TARGET 0x000000FF /* Target State */ +#define RES_TARGET_LNX STATUS_MASK /* Only official ... */ +#define RES_ENDMSG 0x0000FF00 /* End Message */ +#define RES_DID 0x00FF0000 /* DID_ codes */ +#define RES_DRV 0xFF000000 /* DRIVER_ codes */ + +#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)) +#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt)<<1) + +#define SET_RES_TARGET(who,tgt) { who &= ~RES_TARGET; who |= (int)(tgt); } +#define SET_RES_TARGET_LNX(who,tgt) { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; } +#define SET_RES_MSG(who,msg) { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; } +#define SET_RES_DID(who,did) { who &= ~RES_DID; who |= (int)(did) << 16; } +#define SET_RES_DRV(who,drv) { who &= ~RES_DRV; who |= (int)(drv) << 24; } + +/* +************************************************************************** +*/ +#define IRQ_NONE 255 +#define TAG_NONE 255 + +typedef unsigned char BYTE; /* 8 bits */ +typedef unsigned short WORD; /* 16 bits */ +typedef unsigned int DWORD; /* 32 bits */ +typedef unsigned int UINT; /* 32 bits */ +typedef BYTE *PBYTE; +typedef WORD *PWORD; +typedef DWORD *PDWORD; +typedef Scsi_Host_Template *PSHT; +typedef struct Scsi_Host *PSH; +typedef Scsi_Device *PSCSIDEV; +typedef Scsi_Cmnd *PSCSICMD; +typedef void *PVOID; +typedef struct scatterlist *PSGL, SGL; + +/* + **struct scatterlist + **{ + ** char * address; // Location data is to be transferred to + ** char * alt_address; // Location of actual if address is a dma indirect buffer. NULL otherwise + ** unsigned int length; + **}; + */ +/*-----------------------------------------------------------------------*/ +typedef struct _SyncMsg +{ + BYTE ExtendMsg; + BYTE ExtMsgLen; + BYTE SyncXferReq; + BYTE Period; + BYTE ReqOffset; +} SyncMsg; +/*-----------------------------------------------------------------------*/ +typedef struct _SGentry +{ + DWORD address; /* bus! address */ + DWORD length; +} SGentry, *PSGE0; + +/* +;----------------------------------------------------------------------- +; SCSI Request Block +;----------------------------------------------------------------------- +*/ +struct _SRB +{ + struct _SRB *pNextSRB; + struct _DCB *pSRBDCB; + + /* HW scatter list (up to 64 entries) */ + PSGE0 SegmentX; + PSCSICMD pcmd; + + /* Offset 0x20/0x10 */ + unsigned char* virt_addr; /* set by DC395x_update_SGlist */ + + DWORD SRBTotalXferLength; + DWORD Xferred; /* Backup for the already xferred len */ + + DWORD SRBSGBusAddr; /* bus address of DC395x scatterlist */ + + WORD SRBState; + BYTE SRBSGCount; + BYTE SRBSGIndex; + + /* Offset 0x38/0x24 */ + BYTE MsgInBuf[6]; + BYTE MsgOutBuf[6]; + + BYTE AdaptStatus; + BYTE TargetStatus; + BYTE MsgCnt; + BYTE EndMessage; + + /* Offset 0x48/0x34 */ + PBYTE pMsgPtr; + + BYTE TagNumber; + BYTE SRBStatus; + BYTE RetryCnt; + BYTE SRBFlag; + + BYTE ScsiPhase; + //BYTE IORBFlag; /* 81h-Reset, 2-retry */ + BYTE padding; + WORD debugpos; + /* Offset 0x58/0x40 */ +#ifdef DC395x_DEBUGTRACE + char *debugtrace; + /* Offset 0x60/0x44 */ +#endif +}; +typedef struct _SRB DC395X_TRM_SRB, *PSRB; + + +/* +;----------------------------------------------------------------------- +; Device Control Block +;----------------------------------------------------------------------- +*/ +struct _DCB +{ + struct _DCB *pNextDCB; + struct _ACB *pDCBACB; + + PSRB pGoingSRB; + PSRB pGoingLast; + +/* 0x10: */ + PSRB pWaitingSRB; + PSRB pWaitLast; + + PSRB pActiveSRB; + DWORD TagMask; + +/* 0x20: */ + WORD MaxCommand; + BYTE AdaptIndex; /* UnitInfo struc start */ + BYTE UnitIndex; /* nth Unit on this card */ + + WORD GoingSRBCnt; + WORD WaitSRBCnt; + BYTE TargetID; /* SCSI Target ID (SCSI Only) */ + BYTE TargetLUN; /* SCSI Log. Unit (SCSI Only) */ + BYTE IdentifyMsg; + BYTE DevMode; + +/* 0x2c: */ + //BYTE AdpMode; + BYTE Inquiry7; /* To store Inquiry flags */ + BYTE SyncMode; /* 0:async mode */ + BYTE MinNegoPeriod; /* for nego. */ + BYTE SyncPeriod; /* for reg. */ + + BYTE SyncOffset; /* for reg. and nego.(low nibble) */ + BYTE UnitCtrlFlag; + BYTE DCBFlag; + BYTE DevType; + + unsigned long last_derated; /* last time, when features were turned off in abort */ +/* 0x38: */ + /* BYTE Reserved2[3]; for dword alignment */ +}; +typedef struct _DCB DC395X_TRM_DCB, *PDCB; +/* +;----------------------------------------------------------------------- +; Adapter Control Block +;----------------------------------------------------------------------- +*/ +struct _ACB +{ + PSH pScsiHost; + struct _ACB *pNextACB; + + WORD IOPortBase; + WORD Revxx1; + +#if 0 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93) + spinlock_t smp_lock; /* Lock for SMP threading */ + volatile unsigned char lock_level_count[NR_CPUS]; +#endif +#endif + + PDCB pLinkDCB; + PDCB pLastDCB; + PDCB pDCBRunRobin; + + PDCB pActiveDCB; + + PSRB pFreeSRB; + PSRB pTmpSRB; + struct timer_list Waiting_Timer; + struct timer_list SelTO_Timer; + + WORD SRBCount; + WORD AdapterIndex; /* nth Adapter this driver */ + + DWORD QueryCnt; + PSCSICMD pQueryHead; + PSCSICMD pQueryTail; + + BYTE msgin123[4]; + + BYTE status; + BYTE DCBCnt; + BYTE sel_timeout; + BYTE dummy; + + BYTE IRQLevel; + BYTE TagMaxNum; + BYTE ACBFlag; + BYTE Gmode2; + + BYTE Config; + BYTE LUNchk; + BYTE scan_devices; + BYTE HostID_Bit; + + BYTE DCBmap[DC395x_MAX_SCSI_ID]; + + DWORD Cmds; + DWORD SelLost; + DWORD SelConn; + DWORD CmdInQ; + DWORD CmdOutOfSRB; + + //DC395X_TRM_DCB DCB_array[DC395x_MAX_DCB]; /* +74h, Len=3E8 */ +#ifdef NEW_PCI + struct pci_dev *pdev; +#else + BYTE pbus; BYTE pdevfn; +#endif + + BYTE MsgLen; + BYTE DeviceCnt; + + DC395X_TRM_SRB SRB_array[DC395x_MAX_SRB_CNT]; + DC395X_TRM_SRB TmpSRB; +}; +typedef struct _ACB DC395X_TRM_ACB, *PACB; + +/*-----------------------------------------------------------------------*/ + +#define BIT31 0x80000000 +#define BIT30 0x40000000 +#define BIT29 0x20000000 +#define BIT28 0x10000000 +#define BIT27 0x08000000 +#define BIT26 0x04000000 +#define BIT25 0x02000000 +#define BIT24 0x01000000 +#define BIT23 0x00800000 +#define BIT22 0x00400000 +#define BIT21 0x00200000 +#define BIT20 0x00100000 +#define BIT19 0x00080000 +#define BIT18 0x00040000 +#define BIT17 0x00020000 +#define BIT16 0x00010000 +#define BIT15 0x00008000 +#define BIT14 0x00004000 +#define BIT13 0x00002000 +#define BIT12 0x00001000 +#define BIT11 0x00000800 +#define BIT10 0x00000400 +#define BIT9 0x00000200 +#define BIT8 0x00000100 +#define BIT7 0x00000080 +#define BIT6 0x00000040 +#define BIT5 0x00000020 +#define BIT4 0x00000010 +#define BIT3 0x00000008 +#define BIT2 0x00000004 +#define BIT1 0x00000002 +#define BIT0 0x00000001 + +/*---UnitCtrlFlag */ +#define UNIT_ALLOCATED BIT0 +#define UNIT_INFO_CHANGED BIT1 +#define FORMATING_MEDIA BIT2 +#define UNIT_RETRY BIT3 + +/*---UnitFlags */ +#define DASD_SUPPORT BIT0 +#define SCSI_SUPPORT BIT1 +#define ASPI_SUPPORT BIT2 + +/*----SRBState machine definition */ +#define SRB_FREE 0x0000 +#define SRB_WAIT 0x0001 +#define SRB_READY 0x0002 +#define SRB_MSGOUT 0x0004 /*arbitration+msg_out 1st byte*/ +#define SRB_MSGIN 0x0008 +#define SRB_EXTEND_MSGIN 0x0010 +#define SRB_COMMAND 0x0020 +#define SRB_START_ 0x0040 /*arbitration+msg_out+command_out*/ +#define SRB_DISCONNECT 0x0080 +#define SRB_DATA_XFER 0x0100 +#define SRB_XFERPAD 0x0200 +#define SRB_STATUS 0x0400 +#define SRB_COMPLETED 0x0800 +#define SRB_ABORT_SENT 0x1000 +#define SRB_DO_SYNC_NEGO 0x2000 +#define SRB_DO_WIDE_NEGO 0x4000 +#define SRB_UNEXPECT_RESEL 0x8000 +/* +********************************************************************** +** +** ACB Config +** +********************************************************************** +*/ +#define HCC_WIDE_CARD 0x20 +#define HCC_SCSI_RESET 0x10 +#define HCC_PARITY 0x08 +#define HCC_AUTOTERM 0x04 +#define HCC_LOW8TERM 0x02 +#define HCC_UP8TERM 0x01 +/*---ACBFlag */ +#define RESET_DEV BIT0 +#define RESET_DETECT BIT1 +#define RESET_DONE BIT2 + +/*---DCBFlag */ +#define ABORT_DEV_ BIT0 + +/*---SRBstatus */ +#define SRB_OK BIT0 +#define ABORTION BIT1 +#define OVER_RUN BIT2 +#define UNDER_RUN BIT3 +#define PARITY_ERROR BIT4 +#define SRB_ERROR BIT5 + +/*---SRBFlag */ +#define DATAOUT BIT7 +#define DATAIN BIT6 +#define RESIDUAL_VALID BIT5 +#define ENABLE_TIMER BIT4 +#define RESET_DEV0 BIT2 +#define ABORT_DEV BIT1 +#define AUTO_REQSENSE BIT0 + +/*---Adapter status */ +#define H_STATUS_GOOD 0 +#define H_SEL_TIMEOUT 0x11 +#define H_OVER_UNDER_RUN 0x12 +#define H_UNEXP_BUS_FREE 0x13 +#define H_TARGET_PHASE_F 0x14 +#define H_INVALID_CCB_OP 0x16 +#define H_LINK_CCB_BAD 0x17 +#define H_BAD_TARGET_DIR 0x18 +#define H_DUPLICATE_CCB 0x19 +#define H_BAD_CCB_OR_SG 0x1A +#define H_ABORT 0x0FF + +/* SCSI BUS Status byte codes*/ +#define SCSI_STAT_GOOD 0x0 /* Good status */ +#define SCSI_STAT_CHECKCOND 0x02 /* SCSI Check Condition */ +#define SCSI_STAT_CONDMET 0x04 /* Condition Met */ +#define SCSI_STAT_BUSY 0x08 /* Target busy status */ +#define SCSI_STAT_INTER 0x10 /* Intermediate status */ +#define SCSI_STAT_INTERCONDMET 0x14 /* Intermediate condition met */ +#define SCSI_STAT_RESCONFLICT 0x18 /* Reservation conflict */ +#define SCSI_STAT_CMDTERM 0x22 /* Command Terminated */ +#define SCSI_STAT_QUEUEFULL 0x28 /* Queue Full */ +#define SCSI_STAT_UNEXP_BUS_F 0xFD /* Unexpect Bus Free */ +#define SCSI_STAT_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */ +#define SCSI_STAT_SEL_TIMEOUT 0xFF /* Selection Time out */ + +/*---Sync_Mode */ +#define SYNC_WIDE_TAG_ATNT_DISABLE 0 +#define SYNC_NEGO_ENABLE BIT0 +#define SYNC_NEGO_DONE BIT1 +#define WIDE_NEGO_ENABLE BIT2 +#define WIDE_NEGO_DONE BIT3 +#define WIDE_NEGO_STATE BIT4 +#define EN_TAG_QUEUEING BIT5 +#define EN_ATN_STOP BIT6 + +#define SYNC_NEGO_OFFSET 15 + +/*----SCSI MSG BYTE*/ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_PTR 0x02 +#define MSG_RESTORE_PTR 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_INITIATOR_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT_ 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINK_CMD_COMPL 0x0A +#define MSG_LINK_CMD_COMPL_FLG 0x0B +#define MSG_BUS_RESET 0x0C +#define MSG_ABORT_TAG 0x0D +#define MSG_SIMPLE_QTAG 0x20 +#define MSG_HEAD_QTAG 0x21 +#define MSG_ORDER_QTAG 0x22 +#define MSG_IGNOREWIDE 0x23 +#define MSG_IDENTIFY 0x80 +#define MSG_HOST_ID 0xC0 + +/*----SCSI STATUS BYTE*/ +#define STATUS_GOOD 0x00 +#define CHECK_CONDITION_ 0x02 +#define STATUS_BUSY 0x08 +#define STATUS_INTERMEDIATE 0x10 +#define RESERVE_CONFLICT 0x18 + +/* cmd->result */ +#define STATUS_MASK_ 0xFF +#define MSG_MASK 0xFF00 +#define RETURN_MASK 0xFF0000 + +/* +** Inquiry Data format +*/ + +typedef struct _SCSIInqData { /* INQ */ + BYTE DevType; /* Periph Qualifier & Periph Dev Type */ + BYTE RMB_TypeMod; /* rem media bit & Dev Type Modifier */ + BYTE Vers; /* ISO, ECMA, & ANSI versions */ + BYTE RDF; /* AEN, TRMIOP, & response data format */ + BYTE AddLen; /* length of additional data */ + BYTE Res1; /* reserved */ + BYTE Res2; /* reserved */ + BYTE Flags; /* RelADr,Wbus32,Wbus16,Sync,etc. */ + BYTE VendorID[8]; /* Vendor Identification */ + BYTE ProductID[16]; /* Product Identification */ + BYTE ProductRev[4]; /* Product Revision */ +} SCSI_INQDATA, *PSCSI_INQDATA; +/* Inquiry byte 0 masks */ +#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */ +#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */ +/* Inquiry byte 1 mask */ +#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */ +/* Peripheral Device Type definitions */ +/* See include/scsi/scsi.h */ +#define TYPE_NODEV SCSI_DEVTYPE /* Unknown or no device type */ +#ifndef TYPE_PRINTER +# define TYPE_PRINTER 0x02 /* Printer device */ +#endif +#ifndef TYPE_COMM +# define TYPE_COMM 0x09 /* Communications device */ +#endif + +/* +** Inquiry flag definitions (Inq data byte 7) +*/ +#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing */ +#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */ +#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */ +#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */ +#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */ +#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */ +#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */ +/*--------------------------*/ +#define ENABLE_CE 1 +#define DISABLE_CE 0 +#define EEPROM_READ 0x80 +/*------------------------------------------------------------------------------*/ +/* +*********************************************************************** +* The PCI configuration register offset for TRM_S1040 +*********************************************************************** +*/ +#define TRM_S1040_ID 0x00 /* Vendor and Device ID */ +#define TRM_S1040_COMMAND 0x04 /* PCI command register */ +#define TRM_S1040_IOBASE 0x10 /* I/O Space base address */ +#define TRM_S1040_ROMBASE 0x30 /* Expansion ROM Base Address */ +#define TRM_S1040_INTLINE 0x3C /* Interrupt line */ + +/* +*********************************************************************** +** +** The SCSI register offset for TRM_S1040 +** +*********************************************************************** +*/ +#define TRM_S1040_SCSI_STATUS 0x80 /* SCSI Status (R) */ +/* ######### */ +#define COMMANDPHASEDONE 0x2000 /* SCSI command phase done */ +#define SCSIXFERDONE 0x0800 /* SCSI SCSI transfer done */ +#define SCSIXFERCNT_2_ZERO 0x0100 /* SCSI SCSI transfer count to zero */ +#define SCSIINTERRUPT 0x0080 /* SCSI interrupt pending */ +#define COMMANDABORT 0x0040 /* SCSI command abort */ +#define SEQUENCERACTIVE 0x0020 /* SCSI sequencer active */ +#define PHASEMISMATCH 0x0010 /* SCSI phase mismatch */ +#define PARITYERROR 0x0008 /* SCSI parity error */ + +#define PHASEMASK 0x0007 /* Phase MSG/CD/IO */ +#define PH_DATA_OUT 0x00 /* Data out phase */ +#define PH_DATA_IN 0x01 /* Data in phase */ +#define PH_COMMAND 0x02 /* Command phase */ +#define PH_STATUS 0x03 /* Status phase */ +#define PH_BUS_FREE 0x05 /* Invalid phase used as bus free */ +#define PH_MSG_OUT 0x06 /* Message out phase */ +#define PH_MSG_IN 0x07 /* Message in phase */ + +/* +**************************************** +*/ +#define TRM_S1040_SCSI_CONTROL 0x80 /* SCSI Control (W) */ +/* ######### */ +#define DO_CLRATN 0x0400 /* Clear ATN */ +#define DO_SETATN 0x0200 /* Set ATN */ +#define DO_CMDABORT 0x0100 /* Abort SCSI command */ +#define DO_RSTMODULE 0x0010 /* Reset SCSI chip */ +#define DO_RSTSCSI 0x0008 /* Reset SCSI bus */ +#define DO_CLRFIFO 0x0004 /* Clear SCSI transfer FIFO */ +#define DO_DATALATCH 0x0002 /* Enable SCSI bus data input (latched) */ +//#define DO_DATALATCH 0x0000 /* KG: DISable SCSI bus data latch */ +#define DO_HWRESELECT 0x0001 /* Enable hardware reselection */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_FIFOCNT 0x82 /* SCSI FIFO Counter 5bits(R) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_SIGNAL 0x83 /* SCSI low level signal (R/W) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_INTSTATUS 0x84 /* SCSI Interrupt Status (R) */ +/* ######### */ +#define INT_SCAM 0x80 /* SCAM selection interrupt */ +#define INT_SELECT 0x40 /* Selection interrupt */ +#define INT_SELTIMEOUT 0x20 /* Selection timeout interrupt */ +#define INT_DISCONNECT 0x10 /* Bus disconnected interrupt */ +#define INT_RESELECTED 0x08 /* Reselected interrupt */ +#define INT_SCSIRESET 0x04 /* SCSI reset detected interrupt */ +#define INT_BUSSERVICE 0x02 /* Bus service interrupt */ +#define INT_CMDDONE 0x01 /* SCSI command done interrupt */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_OFFSET 0x84 /* SCSI Offset Count (W) */ +/* +** Bit Name Definition +** 07-05 0 RSVD Reversed. Always 0. +** 04 0 OFFSET4 Reversed for LVDS. Always 0. +** 03-00 0 OFFSET[03:00] Offset number from 0 to 15 +*/ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_SYNC 0x85 /* SCSI Synchronous Control (R/W) */ +/* ######### */ +#define LVDS_SYNC 0x20 /* Enable LVDS synchronous */ +#define WIDE_SYNC 0x10 /* Enable WIDE synchronous */ +#define ALT_SYNC 0x08 /* Enable Fast-20 alternate synchronous */ +/* +****************************************************************** +** SYNCM 7 6 5 4 3 2 1 0 +** Name RSVD RSVD LVDS WIDE ALTPERD PERIOD2 PERIOD1 PERIOD0 +** Default 0 0 0 0 0 0 0 0 +** +** +** Bit Name Definition +** 07-06 0 RSVD Reversed. Always read 0 +** 05 0 LVDS Reversed. Always read 0 +** 04 0 WIDE/WSCSI Enable wide (16-bits) SCSI transfer. +** 03 0 ALTPERD/ALTPD Alternate (Sync./Period) mode. +** +** @@ When this bit is set, +** the synchronous period bits 2:0 +** in the Synchronous Mode register +** are used to transfer data +** at the Fast-20 rate. +** @@ When this bit is unset, +** the synchronous period bits 2:0 +** in the Synchronous Mode Register +** are used to transfer data +** at the Fast-10 rate (or Fast-40 w/ LVDS). +** +** 02-00 0 PERIOD[2:0]/SXPD[02:00] Synchronous SCSI Transfer Rate. +** These 3 bits specify +** the Synchronous SCSI Transfer Rate +** for Fast-20 and Fast-10. +** These bits are also reset +** by a SCSI Bus reset. +** +** For Fast-10 bit ALTPD = 0 and LVDS = 0 +** and bit2,bit1,bit0 is defined as follows : +** +** 000 100ns, 10.0 MHz +** 001 150ns, 6.6 MHz +** 010 200ns, 5.0 MHz +** 011 250ns, 4.0 MHz +** 100 300ns, 3.3 MHz +** 101 350ns, 2.8 MHz +** 110 400ns, 2.5 MHz +** 111 450ns, 2.2 MHz +** +** For Fast-20 bit ALTPD = 1 and LVDS = 0 +** and bit2,bit1,bit0 is defined as follows : +** +** 000 50ns, 20.0 MHz +** 001 75ns, 13.3 MHz +** 010 100ns, 10.0 MHz +** 011 125ns, 8.0 MHz +** 100 150ns, 6.6 MHz +** 101 175ns, 5.7 MHz +** 110 200ns, 5.0 MHz + ** 111 250ns, 4.0 MHz KG: Maybe 225ns, 4.4 MHz +** +** For Fast-40 bit ALTPD = 0 and LVDS = 1 +** and bit2,bit1,bit0 is defined as follows : +** +** 000 25ns, 40.0 MHz +** 001 50ns, 20.0 MHz +** 010 75ns, 13.3 MHz +** 011 100ns, 10.0 MHz +** 100 125ns, 8.0 MHz +** 101 150ns, 6.6 MHz +** 110 175ns, 5.7 MHz +** 111 200ns, 5.0 MHz +****************************************************************** +*/ + +/* +**************************************** +*/ +#define TRM_S1040_SCSI_TARGETID 0x86 /* SCSI Target ID (R/W) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_IDMSG 0x87 /* SCSI Identify Message (R) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_HOSTID 0x87 /* SCSI Host ID (W) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_COUNTER 0x88 /* SCSI Transfer Counter 24bits(R/W) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_INTEN 0x8C /* SCSI Interrupt Enable (R/W) */ +/* ######### */ +#define EN_SCAM 0x80 /* Enable SCAM selection interrupt */ +#define EN_SELECT 0x40 /* Enable selection interrupt */ +#define EN_SELTIMEOUT 0x20 /* Enable selection timeout interrupt */ +#define EN_DISCONNECT 0x10 /* Enable bus disconnected interrupt */ +#define EN_RESELECTED 0x08 /* Enable reselected interrupt */ +#define EN_SCSIRESET 0x04 /* Enable SCSI reset detected interrupt */ +#define EN_BUSSERVICE 0x02 /* Enable bus service interrupt */ +#define EN_CMDDONE 0x01 /* Enable SCSI command done interrupt */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_CONFIG0 0x8D /* SCSI Configuration 0 (R/W) */ +/* ######### */ +#define PHASELATCH 0x40 /* Enable phase latch */ +#define INITIATOR 0x20 /* Enable initiator mode */ +#define PARITYCHECK 0x10 /* Enable parity check */ +#define BLOCKRST 0x01 /* Disable SCSI reset1 */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_CONFIG1 0x8E /* SCSI Configuration 1 (R/W) */ +/* ######### */ +#define ACTIVE_NEGPLUS 0x10 /* Enhance active negation */ +#define FILTER_DISABLE 0x08 /* Disable SCSI data filter */ +#define FAST_FILTER 0x04 /* ? */ +#define ACTIVE_NEG 0x02 /* Enable active negation */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_CONFIG2 0x8F /* SCSI Configuration 2 (R/W) */ +#define CFG2_WIDEFIFO 0x02 +/* +**************************************** +*/ +#define TRM_S1040_SCSI_COMMAND 0x90 /* SCSI Command (R/W) */ +/* ######### */ +#define SCMD_COMP 0x12 /* Command complete */ +#define SCMD_SEL_ATN 0x60 /* Selection with ATN */ +#define SCMD_SEL_ATN3 0x64 /* Selection with ATN3 */ +#define SCMD_SEL_ATNSTOP 0xB8 /* Selection with ATN and Stop */ +#define SCMD_FIFO_OUT 0xC0 /* SCSI FIFO transfer out */ +#define SCMD_DMA_OUT 0xC1 /* SCSI DMA transfer out */ +#define SCMD_FIFO_IN 0xC2 /* SCSI FIFO transfer in */ +#define SCMD_DMA_IN 0xC3 /* SCSI DMA transfer in */ +#define SCMD_MSGACCEPT 0xD8 /* Message accept */ +/* +** Code Command Description +** +** 02 Enable reselection with FIFO +** 40 Select without ATN with FIFO +** 60 Select with ATN with FIFO +** 64 Select with ATN3 with FIFO +** A0 Select with ATN and stop with FIFO +** C0 Transfer information out with FIFO +** C1 Transfer information out with DMA +** C2 Transfer information in with FIFO +** C3 Transfer information in with DMA +** 12 Initiator command complete with FIFO +** 50 Initiator transfer information out sequence without ATN with FIFO +** 70 Initiator transfer information out sequence with ATN with FIFO +** 74 Initiator transfer information out sequence with ATN3 with FIFO +** 52 Initiator transfer information in sequence without ATN with FIFO +** 72 Initiator transfer information in sequence with ATN with FIFO +** 76 Initiator transfer information in sequence with ATN3 with FIFO +** 90 Initiator transfer information out command complete with FIFO +** 92 Initiator transfer information in command complete with FIFO +** D2 Enable selection +** 08 Reselection +** 48 Disconnect command with FIFO +** 88 Terminate command with FIFO +** C8 Target command complete with FIFO +** 18 SCAM Arbitration/ Selection +** 5A Enable reselection +** 98 Select without ATN with FIFO +** B8 Select with ATN with FIFO +** D8 Message Accepted +** 58 NOP +*/ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_TIMEOUT 0x91 /* SCSI Time Out Value (R/W) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_FIFO 0x98 /* SCSI FIFO (R/W) */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_TCR0 0x9C /* SCSI Target Control 0 (R/W) */ +/* ######### */ +#define TCR0_WIDE_NEGO_DONE 0x8000 /* Wide nego done */ +#define TCR0_SYNC_NEGO_DONE 0x4000 /* Synchronous nego done */ +#define TCR0_ENABLE_LVDS 0x2000 /* Enable LVDS synchronous */ +#define TCR0_ENABLE_WIDE 0x1000 /* Enable WIDE synchronous */ +#define TCR0_ENABLE_ALT 0x0800 /* Enable alternate synchronous */ +#define TCR0_PERIOD_MASK 0x0700 /* Transfer rate */ + +#define TCR0_DO_WIDE_NEGO 0x0080 /* Do wide NEGO */ +#define TCR0_DO_SYNC_NEGO 0x0040 /* Do sync NEGO */ +#define TCR0_DISCONNECT_EN 0x0020 /* Disconnection enable */ +#define TCR0_OFFSET_MASK 0x001F /* Offset number */ +/* +**************************************** +*/ +#define TRM_S1040_SCSI_TCR1 0x9E /* SCSI Target Control 1 (R/W) */ +/* ######### */ +#define MAXTAG_MASK 0x7F00 /* Maximum tags (127) */ +#define NON_TAG_BUSY 0x0080 /* Non tag command active */ +#define ACTTAG_MASK 0x007F /* Active tags */ +/* +*********************************************************************** +** +** The DMA register offset for TRM_S1040 +** +*********************************************************************** +*/ +#define TRM_S1040_DMA_COMMAND 0xA0 /* DMA Command (R/W) */ +/* ######### */ +#define DMACMD_SG 0x02 /* Enable HW S/G support */ +#define DMACMD_DIR 0x01 /* 1 = read from SCSI write to Host */ +/* ######### */ +#define XFERDATAIN_SG 0x0103 /* Transfer data in w/ SG */ +#define XFERDATAOUT_SG 0x0102 /* Transfer data out w/ SG */ +#define XFERDATAIN 0x0101 /* Transfer data in w/o SG */ +#define XFERDATAOUT 0x0100 /* Transfer data out w/o SG */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_FIFOCNT 0xA1 /* DMA FIFO Counter (R) */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_CONTROL 0xA1 /* DMA Control (W) */ +/* ######### */ +#define DMARESETMODULE 0x10 /* Reset PCI/DMA module */ +#define STOPDMAXFER 0x08 /* Stop DMA transfer */ +#define ABORTXFER 0x04 /* Abort DMA transfer */ +#define CLRXFIFO 0x02 /* Clear DMA transfer FIFO */ +#define STARTDMAXFER 0x01 /* Start DMA transfer */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_STATUS 0xA3 /* DMA Interrupt Status (R/W) */ +/* ######### */ +#define XFERPENDING 0x80 /* Transfer pending */ +#define SCSIBUSY 0x40 /* SCSI busy */ +#define GLOBALINT 0x20 /* DMA_INTEN bit 0-4 set */ +#define FORCEDMACOMP 0x10 /* Force DMA transfer complete */ +#define DMAXFERERROR 0x08 /* DMA transfer error */ +#define DMAXFERABORT 0x04 /* DMA transfer abort */ +#define DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define SCSICOMP 0x01 /* SCSI complete interrupt */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_INTEN 0xA4 /* DMA Interrupt Enable (R/W) */ +/* ######### */ +#define EN_FORCEDMACOMP 0x10 /* Force DMA transfer complete */ +#define EN_DMAXFERERROR 0x08 /* DMA transfer error */ +#define EN_DMAXFERABORT 0x04 /* DMA transfer abort */ +#define EN_DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */ +#define EN_SCSIINTR 0x01 /* Enable SCSI complete interrupt */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_CONFIG 0xA6 /* DMA Configuration (R/W) */ +/* ######### */ +#define DMA_ENHANCE 0x8000 /* Enable DMA enhance feature (SG?) */ +#define DMA_PCI_DUAL_ADDR 0x4000 +#define DMA_CFG_RES 0x2000 /* Always 1 */ +#define DMA_AUTO_CLR_FIFO 0x1000 /* DISable DMA auto clear FIFO */ +#define DMA_MEM_MULTI_READ 0x0800 +#define DMA_MEM_WRITE_INVAL 0x0400 /* Memory write and invalidate */ +#define DMA_FIFO_CTRL 0x0300 /* Control FIFO operation with DMA */ +#define DMA_FIFO_HALF_HALF 0x0200 /* Keep half filled on both read and write */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_XCNT 0xA8 /* DMA Transfer Counter (R/W), 24bits */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_CXCNT 0xAC /* DMA Current Transfer Counter (R) */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_XLOWADDR 0xB0 /* DMA Transfer Physical Low Address */ +/* +**************************************** +*/ +#define TRM_S1040_DMA_XHIGHADDR 0xB4 /* DMA Transfer Physical High Address */ + +/* +*********************************************************************** +** +** The general register offset for TRM_S1040 +** +*********************************************************************** +*/ +#define TRM_S1040_GEN_CONTROL 0xD4 /* Global Control */ +/* ######### */ +#define CTRL_LED 0x80 /* Control onboard LED */ +#define EN_EEPROM 0x10 /* Enable EEPROM programming */ +#define DIS_TERM 0x08 /* Disable onboard termination */ +#define AUTOTERM 0x04 /* Enable Auto SCSI terminator */ +#define LOW8TERM 0x02 /* Enable Lower 8 bit SCSI terminator */ +#define UP8TERM 0x01 /* Enable Upper 8 bit SCSI terminator */ +/* +**************************************** +*/ +#define TRM_S1040_GEN_STATUS 0xD5 /* Global Status */ +/* ######### */ +#define GTIMEOUT 0x80 /* Global timer reach 0 */ +#define EXT68HIGH 0x40 /* Higher 8 bit connected externally */ +#define INT68HIGH 0x20 /* Higher 8 bit connected internally */ +#define CON5068 0x10 /* External 50/68 pin connected (low) */ +#define CON68 0x08 /* Internal 68 pin connected (low) */ +#define CON50 0x04 /* Internal 50 pin connected (low!) */ +#define WIDESCSI 0x02 /* Wide SCSI card */ +#define STATUS_LOAD_DEFAULT 0x01 +/* +**************************************** +*/ +#define TRM_S1040_GEN_NVRAM 0xD6 /* Serial NON-VOLATILE RAM port */ +/* ######### */ +#define NVR_BITOUT 0x08 /* Serial data out */ +#define NVR_BITIN 0x04 /* Serial data in */ +#define NVR_CLOCK 0x02 /* Serial clock */ +#define NVR_SELECT 0x01 /* Serial select */ +/* +**************************************** +*/ +#define TRM_S1040_GEN_EDATA 0xD7 /* Parallel EEPROM data port */ +/* +**************************************** +*/ +#define TRM_S1040_GEN_EADDRESS 0xD8 /* Parallel EEPROM address */ +/* +**************************************** +*/ +#define TRM_S1040_GEN_TIMER 0xDB /* Global timer */ + +/* +*********************************************************************** +** The SEEPROM structure for TRM_S1040 +*********************************************************************** +*/ +typedef struct NVRAM_TARGET_STRUCT +{ + BYTE NvmTarCfg0; /* Target configuration byte 0 */ + BYTE NvmTarPeriod; /* Target period */ + BYTE NvmTarCfg2; /* Target configuration byte 2 */ + BYTE NvmTarCfg3; /* Target configuration byte 3 */ +} NVRAMTARGETTYPE; +/* NvmTarCfg0: Target configuration byte 0 :..pDCB->DevMode */ +#define NTC_DO_WIDE_NEGO 0x20 /* Wide negotiate */ +#define NTC_DO_TAG_QUEUEING 0x10 /* Enable SCSI tag queuing */ +#define NTC_DO_SEND_START 0x08 /* Send start command SPINUP */ +#define NTC_DO_DISCONNECT 0x04 /* Enable SCSI disconnect */ +#define NTC_DO_SYNC_NEGO 0x02 /* Sync negotiation */ +#define NTC_DO_PARITY_CHK 0x01 /* (it sould define at NAC ) + Parity check enable */ + +/* +********************************************************************** +** +** +** +********************************************************************** +*/ +typedef struct NVRAM_STRUC +{ + BYTE NvramSubVendorID[2]; /* 0,1 Sub Vendor ID */ + BYTE NvramSubSysID[2]; /* 2,3 Sub System ID */ + BYTE NvramSubClass; /* 4 Sub Class */ + BYTE NvramVendorID[2]; /* 5,6 Vendor ID */ + BYTE NvramDeviceID[2]; /* 7,8 Device ID */ + BYTE NvramReserved; /* 9 Reserved */ + NVRAMTARGETTYPE NvramTarget[DC395x_MAX_SCSI_ID]; + /** 10,11,12,13 + ** 14,15,16,17 + ** .... + ** .... + ** 70,71,72,73 + */ + BYTE NvramScsiId; /* 74 Host Adapter SCSI ID */ + BYTE NvramChannelCfg; /* 75 Channel configuration */ + BYTE NvramDelayTime; /* 76 Power on delay time */ + BYTE NvramMaxTag; /* 77 Maximum tags */ + BYTE NvramReserved0; /* 78 */ + BYTE NvramBootTarget; /* 79 */ + BYTE NvramBootLun; /* 80 */ + BYTE NvramReserved1; /* 81 */ + WORD Reserved[22]; /* 82,..125 */ + WORD NvramCheckSum; /* 126,127 */ +} NVRAMTYPE,*PNVRAMTYPE; +#if 0 +/* Nvram Initiater bits definition */ +#define MORE2_DRV BIT0 +#define GREATER_1G BIT1 +#define RST_SCSI_BUS BIT2 +#define ACTIVE_NEGATION BIT3 +#define NO_SEEK BIT4 +#define LUN_CHECK BIT5 +#endif +/* Nvram Adapter Cfg bits definition */ +#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */ +#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */ +#define NAC_GREATER_1G 0x02 /* > 1G support enable */ +#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */ +/* +**#define NAC_DO_PARITY_CHK 0x08 // Parity check enable +*/ +/*------------------------------------------------------------------------------*/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,30) +struct proc_dir_entry DC395x_proc_scsi= +{ + PROC_SCSI_DC395X_TRMS1040, + 10, "dc395x_trm", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#endif + +static void DC395x_check_eeprom(PNVRAMTYPE pEEpromBuf,WORD scsiIOPort); +static void TRM_S1040_read_all(PNVRAMTYPE pEEpromBuf,WORD scsiIOPort); +static BYTE TRM_S1040_get_data(WORD scsiIOPort, BYTE bAddr); +static void TRM_S1040_write_all(PNVRAMTYPE pEEpromBuf,WORD scsiIOPort); +static void TRM_S1040_set_data(WORD scsiIOPort, BYTE bAddr, BYTE bData); +static void TRM_S1040_write_cmd(WORD scsiIOPort, BYTE bCmd, BYTE bAddr); +static void TRM_S1040_wait_30us(WORD scsiIOPort); + + void DC395x_DataOutPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status); + void DC395x_DataInPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_CommandPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_StatusPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_MsgOutPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status); + void DC395x_MsgInPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_DataOutPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_DataInPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_CommandPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_StatusPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_MsgOutPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_MsgInPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_Nop0( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_Nop1( PACB pACB, PSRB pSRB, PWORD pscsi_status); +static void DC395x_basic_config (PACB pACB); +static void DC395x_cleanup_after_transfer (PACB pACB, PSRB pSRB); +static void DC395x_ResetSCSIBus( PACB pACB ); + int DC395x_initAdapter( PSH psh, DWORD io_port, BYTE Irq, WORD index ); + void DC395x_DataIO_transfer( PACB pACB, PSRB pSRB, WORD ioDir); + void DC395x_Disconnect( PACB pACB ); + void DC395x_Reselect( PACB pACB ); + BYTE DC395x_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void DC395x_BuildSRB(Scsi_Cmnd* pcmd, PDCB pDCB, PSRB pSRB); + void DC395x_DoingSRB_Done( PACB pACB, BYTE did_code); +static void DC395x_ScsiRstDetect( PACB pACB ); +//static void DC395x_printMsg (BYTE *MsgBuf, DWORD len); +static inline void DC395x_EnableMsgOut_Abort ( PACB pACB, PSRB pSRB ); +//static inline void DC395x_EnableMsgOutAbort1( PACB pACB, PSRB pSRB ); +//static inline void DC395x_EnableMsgOutAbort2( PACB pACB, PSRB pSRB ); + void DC395x_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void DC395x_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ); +static inline void DC395x_SetXferRate( PACB pACB, PDCB pDCB ); + void DC395x_initDCB( PACB pACB, PDCB *ppDCB, BYTE target, BYTE lun ); + +#ifdef MODULE +int DC395x_release(struct Scsi_Host *host); +int DC395x_shutdown (struct Scsi_Host *host); +#endif + +static PACB DC395x_pACB_start = NULL; +static PACB DC395x_pACB_current = NULL; +static WORD DC395x_adapterCnt = 0; +static WORD DC395x_CurrSyncOffset = 0; + +DEBUGRECURSION(static char in_driver = 0;) +static char DC395x_monitor_next_IRQ = 0; + +/* +********************************************************* +** +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** +********************************************************* +*/ +static PVOID DC395x_SCSI_phase0[]={ + DC395x_DataOutPhase0, /* phase:0 */ + DC395x_DataInPhase0, /* phase:1 */ + DC395x_CommandPhase0, /* phase:2 */ + DC395x_StatusPhase0, /* phase:3 */ + DC395x_Nop0, /* phase:4 PH_BUS_FREE .. initial phase */ + DC395x_Nop0, /* phase:5 PH_BUS_FREE .. initial phase */ + DC395x_MsgOutPhase0, /* phase:6 */ + DC395x_MsgInPhase0, /* phase:7 */ + }; +/* +********************************************************************* +** +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** +********************************************************************* +*/ +static PVOID DC395x_SCSI_phase1[]={ + DC395x_DataOutPhase1, /* phase:0 */ + DC395x_DataInPhase1, /* phase:1 */ + DC395x_CommandPhase1, /* phase:2 */ + DC395x_StatusPhase1, /* phase:3 */ + DC395x_Nop1, /* phase:4 PH_BUS_FREE .. initial phase */ + DC395x_Nop1, /* phase:5 PH_BUS_FREE .. initial phase */ + DC395x_MsgOutPhase1, /* phase:6 */ + DC395x_MsgInPhase1, /* phase:7 */ + }; + +NVRAMTYPE dc395x_trm_eepromBuf[DC395x_MAX_ADAPTER_NUM]; +/* +**Fast20: 000 50ns, 20.0 MHz +** 001 75ns, 13.3 MHz +** 010 100ns, 10.0 MHz +** 011 125ns, 8.0 MHz +** 100 150ns, 6.6 MHz +** 101 175ns, 5.7 MHz +** 110 200ns, 5.0 MHz +** 111 250ns, 4.0 MHz +** +**Fast40(LVDS): 000 25ns, 40.0 MHz +** 001 50ns, 20.0 MHz +** 010 75ns, 13.3 MHz +** 011 100ns, 10.0 MHz +** 100 125ns, 8.0 MHz +** 101 150ns, 6.6 MHz +** 110 175ns, 5.7 MHz +** 111 200ns, 5.0 MHz +*/ +//static BYTE dc395x_clock_period[] = {12,19,25,31,37,44,50,62}; +/* real period:48ns,76ns,100ns,124ns,148ns,176ns,200ns,248ns */ +static BYTE dc395x_clock_period[] = { 12, 18, 25, 31, 37, 43, 50, 62}; +static WORD dc395x_clock_speed[] = {200,133,100, 80, 67, 58, 50, 40}; +/* real period:48ns,72ns,100ns,124ns,148ns,172ns,200ns,248ns */ + +/* Override defaults on cmdline: + * dc395x_trm = AdaptID, MaxSpeed (Index), DevMode (Bitmapped), AdaptMode (Bitmapped), Tags (log2-1), DelayReset + */ +int dc395x_trm[] = {-2, -2, -2, -2, -2, -2}; + +# if defined(MODULE) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,30) +MODULE_PARM(dc395x_trm, "1-6i"); +MODULE_PARM_DESC(dc395x_trm, "Host SCSI ID, Speed (0=20MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)"); +# endif + +#if defined(MODULE) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,30) +MODULE_AUTHOR("C.L. Huang / Erich Chen / Kurt Garloff"); +MODULE_DESCRIPTION("SCSI host adapter driver for Tekram TRM-S1040 based adapters: Tekram DC395 and DC315 series"); +MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* Delaying after a reset */ +static char __initdata DC395x_interpd [] = {1,3,5,10,16,30,60,120}; +/* Convert EEprom value to seconds */ +static void __init DC395x_interpret_delay (PNVRAMTYPE pEEpromBuf) +{ + //printk ("DC395x: Debug: Delay: %i\n", pEEpromBuf->NvramDelayTime); + pEEpromBuf->NvramDelayTime = DC395x_interpd[pEEpromBuf->NvramDelayTime]; +}; +/* seconds to EEProm value */ +static int __init DC395x_uninterpret_delay (int delay) +{ + BYTE idx = 0; + while (idx < 7 && DC395x_interpd[idx] < delay) idx++; + return idx; +}; + + +/* Handle "-1" case */ +static void __init DC395x_check_for_safe_settings (void) +{ + if (dc395x_trm[0] == -1 || dc395x_trm[0] > 15) /* modules-2.0.0 passes -1 as string */ + { + dc395x_trm[0] = 7; dc395x_trm[1] = 4; + dc395x_trm[2] = 0x09; dc395x_trm[3] = 0x0f; + dc395x_trm[4] = 2; dc395x_trm[5] = 10; + printk (KERN_INFO "DC395x: Using safe settings.\n"); + } +} + +/* Defaults, to be overriden by (a) BIOS and (b) Cmnd line (kernel/module) args */ +int __initdata dc395x_def[] = {7, 1 /* 13.3MHz */, + NTC_DO_PARITY_CHK | NTC_DO_DISCONNECT | NTC_DO_SYNC_NEGO + | NTC_DO_WIDE_NEGO | NTC_DO_TAG_QUEUEING | NTC_DO_SEND_START, + NAC_GT2DRIVES | NAC_GREATER_1G | NAC_POWERON_SCSI_RESET + /* | NAC_ACTIVE_NEG */ +#ifdef CONFIG_SCSI_MULTI_LUN + | NAC_SCANLUN +#endif + , 3 /* 16 Tags per LUN */, 1 /* s delay after Reset */ +}; + +/* Copy defaults over set values where missing */ +static void __init DC395x_fill_with_defaults (void) +{ + int i; + PARSEDEBUG(printk(KERN_INFO "DC395x: setup %08x %08x %08x %08x %08x %08x\n", dc395x_trm[0],\ + dc395x_trm[1], dc395x_trm[2], dc395x_trm[3], dc395x_trm[4], dc395x_trm[5]);) + for (i = 0; i < 6; i++) + { + if (dc395x_trm[i] < 0 || dc395x_trm[i] > 255) + dc395x_trm[i] = dc395x_def[i]; + } + /* Sanity checks */ + if (dc395x_trm[0] > 15) dc395x_trm[0] = 7; + if (dc395x_trm[1] > 7) dc395x_trm[1] = 4; + if (dc395x_trm[4] > 5) dc395x_trm[4] = 4; + if (dc395x_trm[5] > 180) dc395x_trm[5] = 180; +}; + + +/* Read the parameters from the command line */ +#if !defined(MODULE) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) +void __init DC395x_trm_setup (char *str) +{ + int i, im; + int ints[8]; + (void)get_options (str, ARRAY_SIZE(ints), ints); +# else +void __init DC395x_trm_setup (char *str, int *ints) +{ + int i, im; +# endif + im = ints[0]; + if (im > 6) + { + printk (KERN_NOTICE "DC395x: ignore extra params!\n"); + im = 6; + }; + for (i = 0; i < im; i++) + dc395x_trm[i] = ints[i+1]; + /* DC395x_checkparams (); */ +}; +#if 0 // For the editor's indenting :-) +} +#endif + +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) +__setup("dc395x_trm=", DC395x_trm_setup); +# endif + +#endif /* !MODULE */ + +/* Overrride BIOS values with the set ones */ +static void __init DC395x_EEprom_Override (PNVRAMTYPE pEEpromBuf) +{ + BYTE id; + + /* Adapter Settings */ + if (dc395x_trm[0] != -2) + pEEpromBuf->NvramScsiId = (BYTE)dc395x_trm[0]; /* Adapter ID */ + if (dc395x_trm[3] != -2) + pEEpromBuf->NvramChannelCfg = (BYTE)dc395x_trm[3]; + if (dc395x_trm[5] != -2) + pEEpromBuf->NvramDelayTime = DC395x_uninterpret_delay (dc395x_trm[5]); /* Reset delay */ + if (dc395x_trm[4] != -2) + pEEpromBuf->NvramMaxTag = (BYTE)dc395x_trm[4]; /* Tagged Cmds */ + + /* Device Settings */ + for (id = 0; id < DC395x_MAX_SCSI_ID; id++) + { + if (dc395x_trm[2] != -2) + pEEpromBuf->NvramTarget[id].NvmTarCfg0 = (BYTE)dc395x_trm[2]; /* Cfg0 */ + if (dc395x_trm[1] != -2) + pEEpromBuf->NvramTarget[id].NvmTarPeriod= (BYTE)dc395x_trm[1]; /* Speed */ + }; +} + + +/* Queueing philosphy: + * There are a couple of lists: + * - Query: Contains the Scsi Commands not yet turned into SRBs (per ACB) + * (Note: For new EH, it is unecessary!) + * - Waiting: Contains a list of SRBs not yet sent (per DCB) + * - Free: List of free SRB slots + * + * If there are no waiting commands for the DCB, the new one is sent to the bus + * otherwise the oldest one is taken from the Waiting list and the new one is + * queued to the Waiting List + * + * Lists are managed using two pointers and eventually a counter + */ + +/* Nomen est omen ... */ +static inline void +DC395x_freetag (PDCB pDCB, PSRB pSRB) +{ + if (pSRB->TagNumber < 255) { + pDCB->TagMask &= ~(1 << pSRB->TagNumber); /* free tag mask */ + pSRB->TagNumber = 255; + } +}; + +/* Find cmd in SRB list */ +inline static PSRB DC395x_find_cmd (PSCSICMD pcmd, PSRB start) +{ + PSRB psrb = start; + if (!start) return 0; + do + { + if (psrb->pcmd == pcmd) return psrb; + psrb = psrb->pNextSRB; + } while (psrb && psrb != start); + return 0; +} + +/* Append to Query List */ +static void DC395x_Query_append( PSCSICMD cmd, PACB pACB ) +{ + DEBUG0(printk ("DC395x: Append cmd %li to Query\n", cmd->pid);) + if( !pACB->QueryCnt ) + pACB->pQueryHead = cmd; + else + pACB->pQueryTail->next = cmd; + + pACB->pQueryTail = cmd; + pACB->QueryCnt++; + pACB->CmdOutOfSRB++; + cmd->next = NULL; +} + + +/* Return next cmd from Query list */ +static PSCSICMD DC395x_Query_get ( PACB pACB ) +{ + PSCSICMD pcmd; + + pcmd = pACB->pQueryHead; + if (!pcmd) return pcmd; + DEBUG0(printk ("DC395x: Get cmd %li from Query\n", pcmd->pid);) + pACB->pQueryHead = pcmd->next; + pcmd->next = NULL; + if (!pACB->pQueryHead) pACB->pQueryTail = NULL; + pACB->QueryCnt--; + return( pcmd ); +} + + +/* Return next free SRB */ +static __inline__ PSRB DC395x_Free_get ( PACB pACB ) +{ + PSRB pSRB; + + //DC395x_Free_integrity (pACB); + pSRB = pACB->pFreeSRB; + if (!pSRB) printk ("DC395x: Out of Free SRBs :-(\n"); + if (pSRB) + { + pACB->pFreeSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + } + + return pSRB; +} + +/* Insert SRB oin top of free list */ +static __inline__ void DC395x_Free_insert (PACB pACB, PSRB pSRB) +{ + DEBUG0(printk ("DC395x: Free SRB %p\n", pSRB);) + pSRB->pNextSRB = pACB->pFreeSRB; + pACB->pFreeSRB = pSRB; +} + + +/* Inserts a SRB to the top of the Waiting list */ +static __inline__ void DC395x_Waiting_insert ( PDCB pDCB, PSRB pSRB ) +{ + DEBUG0(printk ("DC395x: Insert pSRB %p cmd %li to Waiting\n", pSRB, pSRB->pcmd->pid);) + pSRB->pNextSRB = pDCB->pWaitingSRB; + if (!pDCB->pWaitingSRB) + pDCB->pWaitLast = pSRB; + pDCB->pWaitingSRB = pSRB; + pDCB->WaitSRBCnt++; +} + + +/* Queue SRB to waiting list */ +static __inline__ void DC395x_Waiting_append ( PDCB pDCB, PSRB pSRB) +{ + DEBUG0(printk ("DC395x: Append pSRB %p cmd %li to Waiting\n", pSRB, pSRB->pcmd->pid);) + if( pDCB->pWaitingSRB ) + pDCB->pWaitLast->pNextSRB = pSRB; + else + pDCB->pWaitingSRB = pSRB; + + pDCB->pWaitLast = pSRB; + /* No next one in waiting list */ + pSRB->pNextSRB = NULL; + pDCB->WaitSRBCnt++; + //pDCB->pDCBACB->CmdInQ++; +} + +static __inline__ void DC395x_Going_append (PDCB pDCB, PSRB pSRB) +{ + DEBUG0(printk("DC395x: Append SRB %p to Going\n", pSRB);) + /* Append to the list of Going commands */ + if( pDCB->pGoingSRB ) + pDCB->pGoingLast->pNextSRB = pSRB; + else + pDCB->pGoingSRB = pSRB; + + pDCB->pGoingLast = pSRB; + /* No next one in sent list */ + pSRB->pNextSRB = NULL; + pDCB->GoingSRBCnt++; +}; + +/* Find predecessor SRB */ +inline static PSRB DC395x_find_SRBpre (PSRB pSRB, PSRB start) +{ + PSRB srb = start; + if (!start) return 0; + do + { + if (srb->pNextSRB == pSRB) return srb; + srb = srb->pNextSRB; + } while (srb && srb != start); + return 0; +} + +/* Remove SRB from SRB queue */ +inline static PSRB DC395x_rmv_SRB (PSRB pSRB, PSRB pre) +{ + if (pre->pNextSRB != pSRB) + pre = DC395x_find_SRBpre (pSRB, pre); + if (!pre) + { + printk ("DC395x: Internal ERROR: SRB to rmv not found in Q!\n"); + return 0; + } + pre->pNextSRB = pSRB->pNextSRB; + //pSRB->pNextSRB = 0; + return pre; +} + +/* Remove SRB from Going queue */ +static void DC395x_Going_remove (PDCB pDCB, PSRB pSRB, PSRB hint) +{ + PSRB pre = 0; + DEBUG0(printk("DC395x: Remove SRB %p from Going\n", pSRB);) + if (!pSRB) printk ("DC395x: Going_remove %p!\n", pSRB); + if (pSRB == pDCB->pGoingSRB) + pDCB->pGoingSRB = pSRB->pNextSRB; + else if (hint && hint->pNextSRB == pSRB) + pre = DC395x_rmv_SRB (pSRB, hint); + else + pre = DC395x_rmv_SRB (pSRB, pDCB->pGoingSRB); + if (pSRB == pDCB->pGoingLast) + pDCB->pGoingLast = pre; + pDCB->GoingSRBCnt--; +} + +/* Remove SRB from Waiting queue */ +static void DC395x_Waiting_remove (PDCB pDCB, PSRB pSRB, PSRB hint) +{ + PSRB pre = 0; + DEBUG0(printk("DC395x: Remove SRB %p from Waiting\n", pSRB);) + if (!pSRB) printk ("DC395x: Waiting_remove %p!\n", pSRB); + if (pSRB == pDCB->pWaitingSRB) + pDCB->pWaitingSRB = pSRB->pNextSRB; + else if (hint && hint->pNextSRB == pSRB) + pre = DC395x_rmv_SRB (pSRB, hint); + else + pre = DC395x_rmv_SRB (pSRB, pDCB->pWaitingSRB); + if (pSRB == pDCB->pWaitLast) + pDCB->pWaitLast = pre; + pDCB->WaitSRBCnt--; +} + +/* Moves SRB from Going list to the top of Waiting list */ +static void DC395x_Going_to_Waiting ( PDCB pDCB, PSRB pSRB ) +{ + DEBUG0(printk(KERN_INFO "DC395x: Going_to_Waiting (SRB %p) pid = %li\n", pSRB, pSRB->pcmd->pid);) + /* Remove SRB from Going */ + DC395x_Going_remove (pDCB, pSRB, 0); + TRACEPRINTF("GtW *"); + /* Insert on top of Waiting */ + DC395x_Waiting_insert (pDCB, pSRB); + /* Tag Mask must be freed elsewhere ! (KG, 99/06/18) */ +} + +/* Moves first SRB from Waiting list to Going list */ +static __inline__ void DC395x_Waiting_to_Going ( PDCB pDCB, PSRB pSRB ) +{ + /* Remove from waiting list */ + DEBUG0(printk("DC395x: Remove SRB %p from head of Waiting\n", pSRB);) + DC395x_Waiting_remove (pDCB, pSRB, 0); + TRACEPRINTF("WtG *"); + DC395x_Going_append (pDCB, pSRB); +} + +/* 2.0 timer compatibility */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,30) + static inline int timer_pending(struct timer_list * timer) + { + return timer->prev != NULL; + } + #define time_after(a,b) ((long)(b) - (long)(a) < 0) + #define time_before(a,b) time_after(b,a) +#endif + +void DC395x_waiting_timed_out (unsigned long ptr); +/* Sets the timer to wake us up */ +static void DC395x_waiting_timer (PACB pACB, unsigned long to) +{ + if (timer_pending (&pACB->Waiting_Timer)) return; + init_timer (&pACB->Waiting_Timer); + pACB->Waiting_Timer.function = DC395x_waiting_timed_out; + pACB->Waiting_Timer.data = (unsigned long)pACB; + if (time_before (jiffies + to, pACB->pScsiHost->last_reset - HZ/2)) + pACB->Waiting_Timer.expires = pACB->pScsiHost->last_reset - HZ/2 + 1; + else + pACB->Waiting_Timer.expires = jiffies + to + 1; + add_timer (&pACB->Waiting_Timer); +} + +/* Send the next command from the waiting list to the bus */ +void DC395x_Waiting_process ( PACB pACB ) +{ + PDCB ptr, ptr1; + PSRB pSRB; + + if( (pACB->pActiveDCB) || (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) ) + return; + if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer); + ptr = pACB->pDCBRunRobin; + if( !ptr ) + { /* This can happen! */ + ptr = pACB->pLinkDCB; + pACB->pDCBRunRobin = ptr; + } + ptr1 = ptr; + if (!ptr1) return; + do + { + /* Make sure, the next another device gets scheduled ... */ + pACB->pDCBRunRobin = ptr1->pNextDCB; + if( !( pSRB = ptr1->pWaitingSRB ) || + ( ptr1->MaxCommand <= ptr1->GoingSRBCnt )) + ptr1 = ptr1->pNextDCB; + else + { + /* Try to send to the bus */ + if( !DC395x_StartSCSI(pACB, ptr1, pSRB) ) + DC395x_Waiting_to_Going (ptr1, pSRB); + else + DC395x_waiting_timer (pACB, HZ/50); + break; + } + } while (ptr1 != ptr); + return; +} + +/* Wake up waiting queue */ +void DC395x_waiting_timed_out (unsigned long ptr) +{ + unsigned int flags; + const PACB pACB = (PACB)ptr; +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Debug: Waiting queue woken up by timer.\n"); +#endif + DC395x_LOCK_IO(pACB->pScsiHost); + DC395x_Waiting_process (pACB); + DC395x_UNLOCK_IO(pACB->pScsiHost); +} + +/* Get the DCB for a given ID/LUN combination */ +static inline PDCB DC395x_findDCB ( PACB pACB, BYTE id, BYTE lun) +{ + PDCB pDCB = pACB->pLinkDCB; + if (!pDCB) return 0; + do + { + if (pDCB->TargetID == id && pDCB->TargetLUN == lun) + return pDCB; + pDCB = pDCB->pNextDCB; + } while (pDCB != pACB->pLinkDCB); + return 0; +}; + + +/*********************************************************************** + * Function: static void DC395x_SendSRB (PACB pACB, PSRB pSRB) + * + * Purpose: Send SCSI Request Block (pSRB) to adapter (pACB) + * + ** DC395x_queue_command + ** DC395x_Waiting_process + * + ***********************************************************************/ + +static void DC395x_SendSRB( PACB pACB, PSRB pSRB ) +{ + PDCB pDCB; + + pDCB = pSRB->pSRBDCB; + if( (pDCB->MaxCommand <= pDCB->GoingSRBCnt) || (pACB->pActiveDCB) || + (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) ) + { + DC395x_Waiting_append (pDCB, pSRB); + DC395x_Waiting_process (pACB); + return; + } + +#if 0 + if( pDCB->pWaitingSRB ) + { + DC395x_Waiting_append (pDCB, pSRB); +/* pSRB = GetWaitingSRB(pDCB); */ /* non-existent */ + pSRB = pDCB->pWaitingSRB; + /* Remove from waiting list */ + pDCB->pWaitingSRB = pSRB->pNextSRB; + pSRB->pNextSRB = NULL; + if (!pDCB->pWaitingSRB) pDCB->pWaitLast = NULL; + } +#endif + + if (!DC395x_StartSCSI(pACB, pDCB, pSRB)) + DC395x_Going_append (pDCB, pSRB); + else + { + DC395x_Waiting_insert (pDCB, pSRB); + DC395x_waiting_timer (pACB, HZ/50); + } +} + +/* +********************************************************************** +** +** Function: static void DC395x_BuildSRB (Scsi_Cmd *pcmd, PDCB pDCB, PSRB pSRB) +** +** Purpose: Prepare SRB for being sent to Device DCB w/ command *pcmd +** +********************************************************************** +*/ +static void DC395x_BuildSRB (Scsi_Cmnd* pcmd, PDCB pDCB, PSRB pSRB) +{ + int i,max; + PSGE0 sgp; + struct scatterlist *sl; + DWORD request_size; + int dir; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_BuildSRB..............\n "); +#endif + //memset (pSRB, 0, sizeof (DC395X_TRM_SRB)); + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + // Find out about direction + SET_DIR(dir,pcmd); + + if (pcmd->use_sg && dir != PCI_DMA_NONE) { + unsigned int len = 0; + /* TODO: In case usg_sg and the no of segments differ, things + * will probably go wrong. */ + pSRB->SRBSGCount = PCI_MAP_SG(pDCB->pDCBACB->pdev, (struct scatterlist*)pcmd->request_buffer, + pcmd->use_sg, dir); + sgp = pSRB->SegmentX; + request_size = pcmd->request_bufflen; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO "DC395x: BuildSRB: Bufflen = %d, buffer = %p, use_sg = %d\n", + pcmd->request_bufflen, pcmd->request_buffer, pcmd->use_sg); + printk(KERN_INFO "DC395x: Mapped %i Segments to %i\n", + pcmd->use_sg, pSRB->SRBSGCount); +#endif + sl = (struct scatterlist *) pcmd->request_buffer; + max = pcmd->use_sg; + if (max > DC395x_MAX_SG_LISTENTRY) { + printk(KERN_INFO "DC395x: cmd->use_sg=%4d bigger then DC395x_MAX_SG_LISTENTRY=%4d for pid %li\n", + pcmd->use_sg,DC395x_MAX_SG_LISTENTRY,pcmd->pid); + } + pSRB->virt_addr = PAGE_ADDRESS((struct scatterlist*)sl); + for (i = 0; i < max; i++, sgp++) { + DWORD busaddr = (DWORD) BUS_ADDR(sl[i]); + DWORD seglen = (DWORD) sl[i].length; + if (i > 0 && ((sgp-1)->address + (sgp-1)->length) == busaddr && + (sgp-1)->length + seglen <= 131072) { + sgp--; +#if defined(DC395x_DEBUG_KG) || defined (DC395x_SGPARANOIA) + printk (KERN_INFO "DC395x: Merge SG segment %i(%08x) to %i(%08x) (%d->%d)\n", + i, busaddr, i-1, sgp->address, + sgp->length, sgp->length+seglen); +#endif + sgp->length += seglen; + len += seglen; + pSRB->SRBSGCount--; + } else { + sgp->address = busaddr; + sgp->length = seglen; + len += seglen; + } +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO "DC395x: Setting up sgp %d, address = 0x%08x, length = %d, tot len = %d\n", + i, busaddr, seglen, len); +#endif + } + sgp--; + /* Fixup for last buffer too big as it is allocated on even page boundaries */ + if (len > request_size) { +#if defined(DC395x_DEBUG_KG) || defined (DC395x_SGPARANOIA) + printk(KERN_INFO "DC395x: Fixup SG total length: %d->%d, last seg %d->%d\n", + len, request_size, sgp->length, sgp->length - (len - request_size)); +#endif + sgp->length -= (len - request_size); + len = request_size; + } + /* WIDE padding */ + if (pDCB->SyncPeriod & WIDE_SYNC && len%2) { + len++; + sgp->length++; + } + pSRB->SRBTotalXferLength = len; //? + /* Hopefully this does not cross a page boundary ... */ + pSRB->SRBSGBusAddr = PCI_MAP_SINGLE(pDCB->pDCBACB->pdev, pSRB->SegmentX, + sizeof(SGentry)*DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); +#ifdef DC395x_SGPARANOIA + printk("DC395x: Map SG descriptor list %p (%05x) to %08x\n", + pSRB->SegmentX, sizeof(SGentry)*DC395x_MAX_SG_LISTENTRY, + pSRB->SRBSGBusAddr); +#endif + } else if (pcmd->request_buffer && dir != PCI_DMA_NONE) { + DWORD len = pcmd->request_bufflen; /* Actual request size */ + pSRB->SRBSGCount = 1; + pSRB->SegmentX[0].address = PCI_MAP_SINGLE(pDCB->pDCBACB->pdev, + pcmd->request_buffer, len, dir); + /* WIDE padding */ + if (pDCB->SyncPeriod & WIDE_SYNC && len%2) + len++; + pSRB->SegmentX[0].length = len; + pSRB->SRBTotalXferLength = len; + pSRB->virt_addr = pcmd->request_buffer; + pSRB->SRBSGBusAddr = 0; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO "DC395x: BuildSRB: len = %d, buffer = %p, use_sg = %d, map %08x\n", + len, pcmd->request_buffer, pcmd->use_sg, pSRB->SegmentX[0].address); +#endif + } else { + pSRB->SRBSGCount = 0; + pSRB->SRBTotalXferLength = 0; + pSRB->SRBSGBusAddr = 0; + pSRB->virt_addr = 0; +#ifdef DC395x_SGPARANOIA + printk(KERN_INFO "DC395x: BuildSRB: buflen = %d, buffer = %p, use_sg = %d, NOMAP %08x\n", + pcmd->bufflen, pcmd->request_buffer, pcmd->use_sg, pSRB->SegmentX[0].address); +#endif + } + + pSRB->SRBSGIndex = 0; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->SRBState = 0; + pSRB->RetryCnt = 0; + +#if DC395x_SGPARANOIA + if ((unsigned long)pSRB->debugtrace & (DEBUGTRACEBUFSZ-1)) + printk ("DC395x: SRB %i (%p): debugtrace %p corrupt!\n", + (pSRB - pDCB->pDCBACB->SRB_array) / sizeof(DC395X_TRM_SRB), + pSRB, pSRB->debugtrace); +#endif + pSRB->debugpos = 0; + TRACEPRINTF ("pid %li(%li):%02x %02x..(%i-%i) *", pcmd->pid, jiffies, + pcmd->cmnd[0], pcmd->cmnd[1], pcmd->target, pcmd->lun); + pSRB->TagNumber = TAG_NONE; + + pSRB->ScsiPhase = PH_BUS_FREE; /* initial phase */ + pSRB->EndMessage = 0; + return; +}; + +/* Put cmnd from Query to Waiting list and send next Waiting cmnd */ +static void DC395x_Query_to_Waiting (PACB pACB) +{ + Scsi_Cmnd *pcmd; + PSRB pSRB; + PDCB pDCB; + + if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) + return; + + while (pACB->QueryCnt) + { + pSRB = DC395x_Free_get ( pACB ); + if (!pSRB) return; + pcmd = DC395x_Query_get ( pACB ); + if (!pcmd) { DC395x_Free_insert (pACB, pSRB); return; }; /* should not happen */ + pDCB = DC395x_findDCB (pACB, pcmd->target, pcmd->lun); + if (!pDCB) + { + DC395x_Free_insert (pACB, pSRB); + printk (KERN_ERR "DC395x: Command in queue to non-existing device!\n"); + pcmd->result = MK_RES(DRIVER_ERROR,DID_ERROR,0,0); + //DC395x_UNLOCK_ACB_NI; + pcmd->done (pcmd); + //DC395x_LOCK_ACB_NI; + }; + DC395x_BuildSRB (pcmd, pDCB, pSRB); + DC395x_Waiting_append ( pDCB, pSRB ); + } +} + +/*********************************************************************** + * Function : static int DC395x_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - callback function called on + * completion, with a pointer to the command descriptor. + * + * Returns : (depending on kernel version) + * 2.0.x: always return 0 + * 2.1.x: old model: (use_new_eh_code == 0): like 2.0.x + * new model: return 0 if successful + * return 1 if command cannot be queued (queue full) + * command will be inserted in midlevel queue then ... + * + ***********************************************************************/ + +int DC395x_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) +{ + PDCB pDCB; + PSRB pSRB; + PACB pACB = (PACB) cmd->host->hostdata; + + + DEBUG0(/* if(pACB->scan_devices) */ \ + printk(KERN_INFO "DC395x: Queue Cmd=%02x,Tgt=%d,LUN=%d (pid=%li)\n",\ + cmd->cmnd[0],cmd->target,cmd->lun,cmd->pid);) + + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) printk ("DC395x: %i queue_command () recursion? (pid=%li)\n", + in_driver, cmd->pid);) + + /* Assume BAD_TARGET; will be cleared later */ + cmd->result = DID_BAD_TARGET << 16; + + /* TODO: Change the policy: Alway accept TEST_UNIT_READY or INQUIRY + * commands and alloc a DCB for the device if not yet there. DCB will + * be removed in DC39x_SRBdone if SEL_TIMEOUT */ + + if( (pACB->scan_devices == DC395x_END_SCAN) && (cmd->cmnd[0] != INQUIRY) ) + pACB->scan_devices = 0; + + else if( (pACB->scan_devices) && (cmd->cmnd[0] == READ_6) ) + pACB->scan_devices = 0; + + if ( ( cmd->target >= pACB->pScsiHost->max_id ) || + (cmd->lun >= pACB->pScsiHost->max_lun) ) + { +/* printk ("DC390: Ignore target %d lun %d\n", + cmd->target, cmd->lun); */ + DEBUGRECURSION(in_driver--;) + //return (1); + done (cmd); + return (0); + } + + if( (pACB->scan_devices || cmd->cmnd[0] == TEST_UNIT_READY || cmd->cmnd[0] == INQUIRY) + && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + { + pACB->scan_devices = 1; + + DC395x_initDCB( pACB, &pDCB, cmd->target, cmd->lun ); + if (!pDCB) + { + printk (KERN_ERR "DC395x: kmalloc for DCB failed, target %02x lun %02x\n", + cmd->target, cmd->lun); + printk ("DC395x: No DCB in queuecommand!\n"); + DEBUGRECURSION(in_driver--;) +#ifdef USE_NEW_EH + return (1); +#else + done (cmd); + return (0); +#endif + }; + + } + else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + { + printk(KERN_INFO "DC395x: Ignore target %02x lun %02x\n", + cmd->target, cmd->lun); + //return (1); + DEBUGRECURSION(in_driver--;) + done (cmd); + return (0); + } + else + { + pDCB = DC395x_findDCB (pACB, cmd->target, cmd->lun); + if (!pDCB) + { /* should never happen */ + printk (KERN_ERR "DC395x: no DCB failed, target %02x lun %02x\n", + cmd->target, cmd->lun); + printk ("DC395x: No DCB in queuecommand (2)!\n"); + DEBUGRECURSION(in_driver--;) +#ifdef USE_NEW_EH + return (1); +#else + done (cmd); + return (0); +#endif + }; + } + + pACB->Cmds++; + cmd->scsi_done = done; + cmd->result = 0; + + DC395x_Query_to_Waiting (pACB); + + if( pACB->QueryCnt ) /* Unsent commands ? */ + { + DEBUG0(printk ("DC395x: QueryCnt != 0\n");) + DC395x_Query_append ( cmd, pACB ); + DC395x_Waiting_process (pACB); + } + else if (pDCB->pWaitingSRB) + { + pSRB = DC395x_Free_get ( pACB ); + DEBUG0(if (!pSRB) printk ("DC395x: No free SRB but Waiting\n"); else printk ("DC395x: Free SRB w/ Waiting\n");) + if (!pSRB) DC395x_Query_append (cmd, pACB); + else + { + DC395x_BuildSRB (cmd, pDCB, pSRB); + DC395x_Waiting_append (pDCB, pSRB); + } + DC395x_Waiting_process (pACB); + } + else + { + pSRB = DC395x_Free_get ( pACB ); + DEBUG0(if (!pSRB) printk ("DC395x: No free SRB w/o Waiting\n"); else printk ("DC395x: Free SRB w/o Waiting\n");) + if (!pSRB) + { + DC395x_Query_append (cmd, pACB); + DC395x_Waiting_process (pACB); + } + else + { + DC395x_BuildSRB (cmd, pDCB, pSRB); + DC395x_SendSRB (pACB, pSRB); + }; + }; + + //DC395x_ACB_LOCK(pACB,acb_flags); + DEBUG1(printk (KERN_DEBUG " ... command (pid %li) queued successfully.\n", cmd->pid);) + DEBUGRECURSION(in_driver--;) + return(0); +} + +/*********************************************************************** + * Function : static void DC395_updateDCB() + * + * Purpose : Set the configuration dependent DCB parameters + ***********************************************************************/ + +void DC395x_updateDCB (PACB pACB, PDCB pDCB) +{ + /* Prevent disconnection of narrow devices if this_id > 7 */ + if (!(pDCB->DevMode & NTC_DO_WIDE_NEGO) && pACB->pScsiHost->this_id > 7) + pDCB->DevMode &= ~NTC_DO_DISCONNECT; + + /* TagQ w/o DisCn is impossible */ + if (!(pDCB->DevMode & NTC_DO_DISCONNECT)) pDCB->DevMode &= ~NTC_DO_TAG_QUEUEING; + pDCB->IdentifyMsg = IDENTIFY ((pDCB->DevMode & NTC_DO_DISCONNECT), pDCB->TargetLUN); + + pDCB->SyncMode &= EN_TAG_QUEUEING | SYNC_NEGO_DONE | WIDE_NEGO_DONE/*| EN_ATN_STOP*/; + if (pDCB->DevMode & NTC_DO_TAG_QUEUEING) { + if (pDCB->SyncMode & EN_TAG_QUEUEING) + pDCB->MaxCommand = pACB->TagMaxNum; + } else { + pDCB->SyncMode &= ~EN_TAG_QUEUEING; + pDCB->MaxCommand = 1; + }; + + if( pDCB->DevMode & NTC_DO_SYNC_NEGO ) + pDCB->SyncMode |= SYNC_NEGO_ENABLE; + else { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE | SYNC_NEGO_ENABLE); + pDCB->SyncOffset &= ~0x0f; + }; + + if( pDCB->DevMode & NTC_DO_WIDE_NEGO && pACB->Config & HCC_WIDE_CARD) + pDCB->SyncMode |= WIDE_NEGO_ENABLE; + else { + pDCB->SyncMode &= ~(WIDE_NEGO_DONE | WIDE_NEGO_ENABLE); + pDCB->SyncPeriod &= ~WIDE_SYNC; + }; + //if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; +}; + + +/*********************************************************************** + * Function : static void DC395x_updateDCBs () + * + * Purpose : Set the configuration dependent DCB params for all DCBs + ***********************************************************************/ + +static void DC395x_updateDCBs (PACB pACB) +{ + int i; + PDCB pDCB = pACB->pLinkDCB; + for (i = 0; i < pACB->DCBCnt; i++) + { + DC395x_updateDCB (pACB, pDCB); + pDCB = pDCB->pNextDCB; + }; +}; + + +/* +********************************************************************** +** +** Function : DC395x_bios_param +** Description: Return the disk geometry for the given SCSI device. +********************************************************************** +*/ +int DC395x_bios_param(Disk *disk, kdev_t devno, int geom[]) +{ +#ifdef CONFIG_SCSI_DC395x_TRMS1040_TRADMAP + int heads, sectors, cylinders; + PACB pACB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_bios_param..............\n "); +#endif + pACB = (PACB) disk->device->host->hostdata; + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + if ( (pACB->Gmode2 & NAC_GREATER_1G) && (cylinders > 1024) ) + { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + return (0); +#else + return scsicam_bios_param (disk, devno, geom); +#endif +}; + + +/* DC395x register dump */ +void DC395x_dumpinfo (PACB pACB, PDCB pDCB, PSRB pSRB) +{ + WORD pstat; +#ifdef NEW_PCI + struct pci_dev *pdev = pACB->pdev; + pci_read_config_word (pdev, PCI_STATUS, &pstat); +#else + BYTE pbus = pACB->pbus; + BYTE pdevfn = pACB->pdevfn; + pcibios_read_config_word (pbus, pdevfn, PCI_STATUS, &pstat); +#endif + if (!pDCB) pDCB = pACB->pActiveDCB; + if (!pSRB && pDCB) pSRB = pDCB->pActiveSRB; + if (pSRB) + { + if (!(pSRB->pcmd)) + printk ("DC395x: dump: SRB %p: cmd %p OOOPS!\n", + pSRB, pSRB->pcmd); + else + printk ("DC395x: dump: SRB %p: cmd %p pid %li: %02x (%02i-%i)\n", + pSRB, pSRB->pcmd, pSRB->pcmd->pid, pSRB->pcmd->cmnd[0], + pSRB->pcmd->target, pSRB->pcmd->lun); + printk (" SGList %p Cnt %i Idx %i Len %i\n", + pSRB->SegmentX, pSRB->SRBSGCount, pSRB->SRBSGIndex, + pSRB->SRBTotalXferLength); + printk (" State %04x Status %02x Phase %02x (%sconn.)\n", + pSRB->SRBState, pSRB->SRBStatus, pSRB->ScsiPhase, + (pACB->pActiveDCB)? "": "not"); + TRACEOUT (" %s\n", pSRB->debugtrace); + } + printk ("DC395x: dump: SCSI block\n"); + printk (" Status %04x FIFOCnt %02x Signals %02x IRQStat %02x\n", + DC395x_read16 (TRM_S1040_SCSI_STATUS), DC395x_read8 (TRM_S1040_SCSI_FIFOCNT), + DC395x_read8 (TRM_S1040_SCSI_SIGNAL), DC395x_read8 (TRM_S1040_SCSI_INTSTATUS)); + printk (" Sync %02x Target %02x RSelID %02x SCSICtr %08x\n", + DC395x_read8 (TRM_S1040_SCSI_SYNC), DC395x_read8 (TRM_S1040_SCSI_TARGETID), + DC395x_read8 (TRM_S1040_SCSI_IDMSG), DC395x_read32 (TRM_S1040_SCSI_COUNTER)); + printk (" IRQEn %02x Config %04x Cfg2 %02x Cmd %02x SelTO %02x\n", + DC395x_read8 (TRM_S1040_SCSI_INTEN), DC395x_read16 (TRM_S1040_SCSI_CONFIG0), + DC395x_read8 (TRM_S1040_SCSI_CONFIG2), + DC395x_read8 (TRM_S1040_SCSI_COMMAND), DC395x_read8 (TRM_S1040_SCSI_TIMEOUT)); + printk ("DC395x: dump: DMA block\n"); + printk (" Cmd %04x FIFOCnt %04x IRQStat %02x IRQEn %02x Cfg %04x\n", + DC395x_read16 (TRM_S1040_DMA_COMMAND), DC395x_read16 (TRM_S1040_DMA_FIFOCNT), + DC395x_read8 (TRM_S1040_DMA_STATUS), DC395x_read8 (TRM_S1040_DMA_INTEN), + DC395x_read16 (TRM_S1040_DMA_CONFIG)); + printk (" TCtr %08x CTCtr %08x Addr %08x%08x\n", + DC395x_read32 (TRM_S1040_DMA_XCNT), DC395x_read32 (TRM_S1040_DMA_CXCNT), + DC395x_read32 (TRM_S1040_DMA_XHIGHADDR), DC395x_read32 (TRM_S1040_DMA_XLOWADDR)); + printk ("DC395x: dump: Misc: GCtrl %02x GStat %02x GTmr %02x\n", + DC395x_read8 (TRM_S1040_GEN_CONTROL), DC395x_read8 (TRM_S1040_GEN_STATUS), + DC395x_read8 (TRM_S1040_GEN_TIMER)); + printk ("DC395x: dump: PCI Status %04x\n", pstat); + + +} + + +static int DC395x_recover (PACB pACB, PDCB pDCB, PSRB pSRB) +{ + int ctr = 4097; + //unsigned char lines = DC395x_read8 (TRM_S1040_SCSI_SIGNAL); + DC395x_EnableMsgOut_Abort (pACB, pSRB); + DC395x_cleanup_after_transfer (pACB, pSRB); + if (/*lines & */1) + { + /* read */ + //DC395x_write8 (TRM_S1040_SCSI_CONTNTROL, DO_HWRESELECT | DO_DATALATCH); + while (--ctr && !(DC395x_read16 (TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT)) + { + DC395x_write8(TRM_S1040_SCSI_SIGNAL, 0x48); + udelay (1); + DC395x_write8(TRM_S1040_SCSI_SIGNAL, 0x08); + udelay (1); + } + return ctr; + DC395x_write8 (TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); + } + else + { + /* write */ + while (--ctr && !(DC395x_read16 (TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT)) + { + int ctr2 = 16; + DC395x_write32 (TRM_S1040_SCSI_COUNTER, ctr2); + while (ctr2--) DC395x_write8 (TRM_S1040_SCSI_FIFO, 0); + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); + } + return ctr; + } +} + +static inline void DC395x_clrfifo (PACB pACB, char *txt) +{ +#ifdef DC395x_DEBUGFIFO + BYTE lines = DC395x_read8(TRM_S1040_SCSI_SIGNAL); + BYTE fifocnt = DC395x_read8 (TRM_S1040_SCSI_FIFOCNT); + if (!(fifocnt & 0x40)) + printk ("DC395x: Clr FIFO (%i bytes) on phase %02x in %s\n", + fifocnt & 0x3f, lines, txt); +#endif + if (pACB->pActiveDCB && pACB->pActiveDCB->pActiveSRB) { + PSRB pSRB = pACB->pActiveDCB->pActiveSRB; + TRACEPRINTF ("#*"); + } + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_CLRFIFO); +} + +/* +********************************************************************** +** +** Function : int DC395x_abort (Scsi_Cmnd *cmd) +** Purpose : Abort an errant SCSI command +** Inputs : cmd - command to abort +** Returns : 0 on success, -1 on failure. +********************************************************************** +*/ +int DC395x_abort (Scsi_Cmnd *cmd) +{ + PACB pACB; + PDCB pDCB; + PSRB pSRB = 0; + //PSRB psrb; + WORD count, i; + PSCSICMD pcmd; + int status; + //DWORD acb_flags=0; + //BYTE lines; + + pACB = (PACB) cmd->host->hostdata; + printk(KERN_INFO "DC395x_abort: pid=%li, target=%02i-%i\n", + cmd->pid, cmd->target, cmd->lun); + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) printk ("DC395x: %i abort() recursion? (pid=%li)\n", + in_driver, cmd->pid);) + + //DC395x_ACB_LOCK(pACB,acb_flags); + + /* First scan Query list */ + if( pACB->QueryCnt ) + { + pcmd = pACB->pQueryHead; + if( pcmd == cmd ) + { + /* Found: Dequeue */ + pACB->pQueryHead = pcmd->next; + pcmd->next = NULL; + if (cmd == pACB->pQueryTail) pACB->pQueryTail = NULL; + pACB->QueryCnt--; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + for( count = pACB->QueryCnt, i=0; inext == cmd ) + { + pcmd->next = cmd->next; + cmd->next = NULL; + if (cmd == pACB->pQueryTail) pACB->pQueryTail = NULL; + pACB->QueryCnt--; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + else + { + pcmd = pcmd->next; + } + } + } + + pDCB = DC395x_findDCB (pACB, cmd->target, cmd->lun); + if( !pDCB ) goto NOT_RUN; + +#if 0 + lines = DC395x_read8 (TRM_S1040_SCSI_SIGNAL); + if (lines & 0x20 && !pACB->pActiveDCB) + { + /* We are not connected, but the device is ? */ + pSRB = pACB->pTmpSRB; + printk ("DC395x: Device is busy, but we are not connected!\n"); + DC395x_clrfifo (pACB, "abort"); + //DC395x_EnableMsgOut_Abort (pACB, pSRB); + pDCB->DCBFlag |= ABORT_DEV_; + pDCB->pActiveSRB = pSRB; + TRACEPRINTF("abort tmp (SN)!*"); + status = SCSI_ABORT_SNOOZE; + goto ABO_X; + } +#endif + /* Handle waiting queue */ + pSRB = DC395x_find_cmd (cmd, pDCB->pWaitingSRB); + if (pSRB) + { + DC395x_Waiting_remove (pDCB, pSRB, 0); + TRACEPRINTF("abort (SU)!*"); + DC395x_freetag (pDCB, pSRB); + DC395x_Free_insert (pACB, pSRB); + //cmd->next = NULL; + status = SCSI_ABORT_SUCCESS; + goto ABO_X; + } + + /* Handle Going queue */ + pSRB = DC395x_find_cmd (cmd, pDCB->pGoingSRB); + if (pSRB) + { + PNVRAMTYPE pEEpromBuf = &dc395x_trm_eepromBuf[pACB->AdapterIndex]; + /* The command has already been sent: Send MSG_ABORT on the next occasion ! + * This will abort all cmnds for this initiator on the device + */ + pDCB->DCBFlag |= ABORT_DEV_; + //This would disallow Sync transfers ... + //pDCB->DevMode &= ~(NTC_DO_SYNC_NEGO); + if (time_before (pDCB->last_derated, pACB->pScsiHost->last_reset)) + { + if ((pDCB->SyncPeriod & 7) < 7) + { + printk ("DC395x: abort: Lower SyncFreq to for dev %02i-%i!\n", + pDCB->TargetID, pDCB->TargetLUN); + pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarPeriod + = pDCB->SyncPeriod + 1; + } + else if (pDCB->DevMode & NTC_DO_SYNC_NEGO) + { + printk ("DC395x: abort: Disable sync transfers for dev %02i-%i!\n", + pDCB->TargetID, pDCB->TargetLUN); + pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarCfg0 &= ~NTC_DO_SYNC_NEGO; + } + else if (pDCB->DevMode & NTC_DO_WIDE_NEGO) + { + printk ("DC395x: abort: Disable wide transfers for dev %02i-%i!\n", + pDCB->TargetID, pDCB->TargetLUN); + pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarCfg0 &= ~NTC_DO_WIDE_NEGO; + } + else if (pDCB->DevMode & NTC_DO_DISCONNECT) + { + printk ("DC395x: abort: Disable disconnection for dev %02i-%i!\n", + pDCB->TargetID, pDCB->TargetLUN); + pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarCfg0 &= ~NTC_DO_DISCONNECT; + } + else printk ("DC395x: abort: All features already disabled for dev %02i-%i!\n", + pDCB->TargetID, pDCB->TargetLUN); + pDCB->last_derated = jiffies; + //DC395x_updateDCB (pACB, pDCB); + } + + //DC395x_write8 (TRM_S1040_DMA_STATUS, FORCEDMACOMP); + //DC395x_write16 (TRM_S1040_DMA_CONTROL, DMARESETMODULE); + //DC395x_basic_config (pACB); + if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) ) + { + status = SCSI_ABORT_BUSY; + TRACEPRINTF("abort (BU)!*"); + printk ("DC395x: Abort current command (pid %li, SRB %p)\n", + cmd->pid, pSRB); + DC395x_dumpinfo (pACB, pDCB, pSRB); + //DC395x_EnableMsgOut_Abort (pACB, pSRB); + if (DC395x_recover (pACB, pDCB, pSRB)) { + printk ("DC395x: abort() seemed to be successful!\n"); + status = SCSI_ABORT_SNOOZE; + } + goto ABO_X; + } + else + { + status = SCSI_ABORT_SNOOZE; + TRACEPRINTF("abort (SN)!*"); + goto ABO_X; + } + } + +NOT_RUN: + /* If we get here, the cmnd is not in our queues! */ + printk ("DC395x: Abort non-ex. command pid %li (%02i-%i)?", + cmd->pid, cmd->target, cmd->lun); + status = SCSI_ABORT_NOT_RUNNING; + +ABO_X: + if (pSRB) TRACEOUT (KERN_WARNING " %s\n", pSRB->debugtrace); + TRACEOUT (" TmpSRB: %s\n", pACB->pTmpSRB->debugtrace); + cmd->result = DID_ABORT << 16; + printk(KERN_INFO "DC395x: Aborted pid %li with status %i\n", cmd->pid, status); +#ifndef USE_NEW_EH + if (status == SCSI_ABORT_SUCCESS) { + // TODO: PCI_UNMAP + cmd->scsi_done(cmd); + } +#endif + DEBUGRECURSION(in_driver--;) + return( status ); +} + +/* +********************************************************************* +** +** DC395x_reset DC395x_ScsiRstDetect +** +********************************************************************* +*/ +static void DC395x_ResetDevParam( PACB pACB ) +{ + PDCB pDCB, pDCBTemp; + PNVRAMTYPE pEEpromBuf; + BYTE PeriodIndex; + WORD index; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_ResetDevParam..............\n "); +#endif + pDCB = pACB->pLinkDCB; + if( pDCB == NULL ) + return; + + pDCBTemp = pDCB; + do + { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE + WIDE_NEGO_DONE); + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + index = pACB->AdapterIndex; + pEEpromBuf = &dc395x_trm_eepromBuf[index]; + + pDCB->DevMode = pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarCfg0; + //pDCB->AdpMode = pEEpromBuf->NvramChannelCfg; + PeriodIndex = pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarPeriod & 0x07; + pDCB->MinNegoPeriod = dc395x_clock_period[ PeriodIndex ]; + if (!(pDCB->DevMode & NTC_DO_WIDE_NEGO) || !(pACB->Config & HCC_WIDE_CARD)) + pDCB->SyncMode &= ~WIDE_NEGO_ENABLE; + + pDCB = pDCB->pNextDCB; + } + while( pDCBTemp != pDCB && pDCB != NULL ); +} + +#if 0 +/* +********************************************************************* +** +** DC395x_ScsiRstDetect +** +********************************************************************* +*/ +/* Moves all SRBs from Going to Waiting for all DCBs */ +static void DC395x_RecoverSRB( PACB pACB ) +{ + PDCB pDCB, pDCBTemp; + PSRB pSRBTemp, pSRBTemp2; + WORD cnt, i; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_RecoverSRB.............\n "); +#endif + pDCB = pACB->pLinkDCB; + if( pDCB == NULL ) + return; + pDCBTemp = pDCB; + do + { + cnt = pDCBTemp->GoingSRBCnt; + pSRBTemp = pDCBTemp->pGoingSRB; + for (i=0; ipNextSRB; + /* DC395x_RewaitSRB( pDCB, pSRBTemp ); */ + if( pDCBTemp->pWaitingSRB ) + { + pSRBTemp2->pNextSRB = pDCBTemp->pWaitingSRB; + pDCBTemp->pWaitingSRB = pSRBTemp2; + } + else + { + pDCBTemp->pWaitingSRB = pSRBTemp2; + pDCBTemp->pWaitLast = pSRBTemp2; + pSRBTemp2->pNextSRB = NULL; + } + } + pDCBTemp->GoingSRBCnt = 0; + pDCBTemp->pGoingSRB = NULL; + pDCBTemp->TagMask = 0; + pDCBTemp = pDCBTemp->pNextDCB; + } + while( pDCBTemp != pDCB ); +} +#endif + +/* +********************************************************************** +** +** Function : int DC395x_reset (Scsi_Cmnd *cmd, ...) +** Purpose : perform a hard reset on the SCSI bus +** Inputs : cmd - command which caused the SCSI RESET +** Returns : 0 on success. +********************************************************************** +*/ +int DC395x_reset(Scsi_Cmnd *cmd, unsigned int resetFlags) +{ + PACB pACB; + //DWORD acb_flags=0; + + pACB = (PACB ) cmd->host->hostdata; + printk(KERN_INFO "DC395x: reset requested!\n"); + pACB = (PACB) cmd->host->hostdata; + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) printk ("DC395x: %i reset recursion? (pid=%li)\n", + in_driver, cmd->pid);) + //DC395x_ACB_LOCK(pACB,acb_flags); + + if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer); + /* + ** disable interrupt + */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0x00); + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + + DC395x_ResetSCSIBus( pACB ); + udelay(500); + /* We may be in serious trouble. Wait some seconds */ + pACB->pScsiHost->last_reset = jiffies + 3*HZ/2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + /* + ** re-enable interrupt + */ + /* Clear SCSI FIFO */ + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + DC395x_clrfifo (pACB, "reset"); + /* Delete pending IRQ */ + DC395x_read8 (TRM_S1040_SCSI_INTSTATUS); + DC395x_basic_config (pACB); + + DC395x_ResetDevParam( pACB ); + DC395x_DoingSRB_Done( pACB, DID_RESET ); + pACB->pActiveDCB = NULL; + + pACB->ACBFlag = 0; /* RESET_DETECT, RESET_DONE ,RESET_DEV */ + DC395x_Waiting_process( pACB ); + + //DC395x_ACB_LOCK(pACB,acb_flags); + DEBUGRECURSION(in_driver--;) + return( SCSI_RESET_SUCCESS ); +} + +/* SDTR */ +static void DC395x_Build_SDTR (PACB pACB, PDCB pDCB, PSRB pSRB) +{ + PBYTE ptr = pSRB->MsgOutBuf + pSRB->MsgCnt; + if (pSRB->MsgCnt > 1) + { + printk ("DC395x: Build_SDTR: MsgOutBuf BUSY (%i: %02x %02x)\n", + pSRB->MsgCnt, pSRB->MsgOutBuf[0], pSRB->MsgOutBuf[1]); + return; + } + if (!(pDCB->DevMode & NTC_DO_SYNC_NEGO)) { + pDCB->SyncOffset = 0; + pDCB->MinNegoPeriod = 200 >> 2; + } + else if (pDCB->SyncOffset == 0) + pDCB->SyncOffset = SYNC_NEGO_OFFSET; + + *ptr++ = MSG_EXTENDED; /* (01h) */ + *ptr++ = 3; /* length */ + *ptr++ = EXTENDED_SDTR; /* (01h) */ + *ptr++ = pDCB->MinNegoPeriod; /* Transfer period (in 4ns) */ + *ptr++ = pDCB->SyncOffset; /* Transfer period (max. REQ/ACK dist) */ + pSRB->MsgCnt += 5; + pSRB->SRBState |= SRB_DO_SYNC_NEGO; + TRACEPRINTF("S *"); +} + +/* SDTR */ +static void DC395x_Build_WDTR (PACB pACB, PDCB pDCB, PSRB pSRB) +{ + BYTE wide = ((pDCB->DevMode & NTC_DO_WIDE_NEGO) & (pACB->Config & HCC_WIDE_CARD))? 1: 0; + PBYTE ptr = pSRB->MsgOutBuf + pSRB->MsgCnt; + if (pSRB->MsgCnt > 1) + { + printk ("DC395x: Build_WDTR: MsgOutBuf BUSY (%i: %02x %02x)\n", + pSRB->MsgCnt, pSRB->MsgOutBuf[0], pSRB->MsgOutBuf[1]); + return; + } + *ptr++ = MSG_EXTENDED; /* (01h) */ + *ptr++ = 2; /* length */ + *ptr++ = EXTENDED_WDTR; /* (03h) */ + *ptr++ = wide; + pSRB->MsgCnt += 4; + pSRB->SRBState |= SRB_DO_WIDE_NEGO; + TRACEPRINTF("W *"); +} + + +/* Timer to work around chip flaw: When selecting and the bus is + * busy, we sometimes miss a Selection timeout IRQ */ +void DC395x_selection_timeout_missed (unsigned long ptr); +/* Sets the timer to wake us up */ +static void DC395x_selto_timer (PACB pACB) +{ + if (timer_pending (&pACB->SelTO_Timer)) return; + init_timer (&pACB->SelTO_Timer); + pACB->SelTO_Timer.function = DC395x_selection_timeout_missed; + pACB->SelTO_Timer.data = (unsigned long)pACB; + if (time_before (jiffies + HZ, pACB->pScsiHost->last_reset + HZ/2)) + pACB->SelTO_Timer.expires = pACB->pScsiHost->last_reset + HZ/2 + 1; + else + pACB->SelTO_Timer.expires = jiffies + HZ + 1; + add_timer (&pACB->SelTO_Timer); +} + +void DC395x_selection_timeout_missed (unsigned long ptr) +{ + unsigned int flags; + PACB pACB = (PACB)ptr; PSRB pSRB; + printk ("DC395x: Debug: Chip forgot to produce SelTO IRQ!\n"); + if (!pACB->pActiveDCB || !pACB->pActiveDCB->pActiveSRB) { + printk ("DC395x: ... but no cmd pending? Oops!\n"); + return; + } + DC395x_LOCK_IO(pACB->pScsiHost); + pSRB = pACB->pActiveDCB->pActiveSRB; + TRACEPRINTF("N/TO *"); + DC395x_Disconnect (pACB); + DC395x_UNLOCK_IO(pACB->pScsiHost); +} + + +/* +********************************************************************** +** scsiio +** DC395x_DoWaitingSRB DC395x_SRBdone +** DC395x_SendSRB DC395x_RequestSense +** +** +** +********************************************************************* +*/ +BYTE DC395x_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ) +{ + WORD s_stat2, return_code; + BYTE s_stat, scsicommand, i, identify_message; + PBYTE ptr; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_StartSCSI..............\n "); +#endif + pSRB->TagNumber = TAG_NONE; /* pACB->TagMaxNum: had error read in eeprom */ + + s_stat = DC395x_read8 (TRM_S1040_SCSI_SIGNAL); s_stat2 = 0; + s_stat2 = DC395x_read16 (TRM_S1040_SCSI_STATUS); + TRACEPRINTF("Start %02x *", s_stat); +#if 1 + if (s_stat & 0x20/* s_stat2 & 0x02000 */) + { +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Debug: StartSCSI: pid %li(%02i-%i): BUSY %02x %04x\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN, s_stat, s_stat2); +#endif + /* Try anyway? */ + /* We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection + * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed! + * (This is likely to be a bug in the hardware. Obviously, most people + * only have one initiator per SCSI bus.) + * Instead let this fail and have the timer make sure the command is + * tried again after a short time */ + TRACEPRINTF ("^*"); + //DC395x_selto_timer (pACB); + //DC395x_monitor_next_IRQ = 1; + return 1; + }; +#endif + if (pACB->pActiveDCB) + { + printk ("DC395x: We try to start a SCSI command (%li)!\n", pSRB->pcmd->pid); + printk ("DC395x: While another one (%li) is active!!\n", + (pACB->pActiveDCB->pActiveSRB? pACB->pActiveDCB->pActiveSRB->pcmd->pid: 0)); + TRACEOUT (" %s\n", pSRB->debugtrace); + if (pACB->pActiveDCB->pActiveSRB) + TRACEOUT (" %s\n", pACB->pActiveDCB->pActiveSRB->debugtrace); + return 1; + } + if( DC395x_read16(TRM_S1040_SCSI_STATUS ) & SCSIINTERRUPT ) + { +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Debug: StartSCSI failed (busy) for pid %li(%02i-%i)\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); +#endif + TRACEPRINTF ("°*"); + return 1; + } + /* Allow starting of SCSI commands half a second before we allow the mid-level + * to queue them again after a reset */ + if (time_before (jiffies, pACB->pScsiHost->last_reset - HZ/2)) + { +#ifdef DC395x_DEBUG_KG + printk ("DC395x: We were just reset and don't accept commands yet!\n"); +#endif + return 1; + } + + /* Flush FIFO */ + DC395x_clrfifo (pACB, "Start"); + DC395x_write8(TRM_S1040_SCSI_HOSTID,pACB->pScsiHost->this_id); + DC395x_write8(TRM_S1040_SCSI_TARGETID,pDCB->TargetID); + DC395x_write8(TRM_S1040_SCSI_SYNC,pDCB->SyncPeriod); + DC395x_write8(TRM_S1040_SCSI_OFFSET,pDCB->SyncOffset); + pSRB->ScsiPhase = PH_BUS_FREE;/* initial phase */ + + identify_message = pDCB->IdentifyMsg; + //DC395x_TRM_write8(TRM_S1040_SCSI_IDMSG, identify_message); + /* Don't allow disconnection for AUTO_REQSENSE: Cont.All.Cond.! */ + if (pSRB->SRBFlag & AUTO_REQSENSE) + identify_message &= 0xBF; + + if( ( (pSRB->pcmd->cmnd[0] == INQUIRY) || (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE) ) + && ( ((pDCB->SyncMode & WIDE_NEGO_ENABLE) && !(pDCB->SyncMode & WIDE_NEGO_DONE)) + || ((pDCB->SyncMode & SYNC_NEGO_ENABLE) && !(pDCB->SyncMode & SYNC_NEGO_DONE)) ) + && (pDCB->TargetLUN == 0) ) + { + pSRB->MsgOutBuf[0] = identify_message; pSRB->MsgCnt = 1; + scsicommand = SCMD_SEL_ATNSTOP; + pSRB->SRBState = SRB_MSGOUT; +#ifndef SYNC_FIRST + if (pDCB->SyncMode & WIDE_NEGO_ENABLE && + pDCB->Inquiry7 & SCSI_INQ_WBUS16) + { + DC395x_Build_WDTR (pACB, pDCB, pSRB); + goto no_cmd; + } +#endif + if (pDCB->SyncMode & SYNC_NEGO_ENABLE && + pDCB->Inquiry7 & SCSI_INQ_SYNC) + { + DC395x_Build_SDTR (pACB, pDCB, pSRB); + goto no_cmd; + } + if (pDCB->SyncMode & WIDE_NEGO_ENABLE && + pDCB->Inquiry7 & SCSI_INQ_WBUS16) + { + DC395x_Build_WDTR (pACB, pDCB, pSRB); + goto no_cmd; + } + pSRB->MsgCnt = 0; + } + /* + ** Send identify message + */ + DC395x_write8(TRM_S1040_SCSI_FIFO,identify_message); + + scsicommand = SCMD_SEL_ATN; + pSRB->SRBState = SRB_START_; +#ifndef DC395x_NO_TAGQ + if ((pDCB->SyncMode & EN_TAG_QUEUEING) && (identify_message & 0xC0)) + { + /* Send Tag message */ + DWORD tag_mask = 1; + BYTE tag_number = 0; + while( tag_mask & pDCB->TagMask && tag_number <= pDCB->MaxCommand) + { + tag_mask = tag_mask << 1; + tag_number++; + } + if (tag_number >= pDCB->MaxCommand) + { + printk (KERN_WARNING "DC395x: Start_SCSI: Out of tags for pid %li (%i-%i)\n", + pSRB->pcmd->pid, pSRB->pcmd->target, pSRB->pcmd->lun); + pSRB->SRBState = SRB_READY; + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + return 1; + }; + /* + ** Send Tag id + */ + DC395x_write8(TRM_S1040_SCSI_FIFO,MSG_SIMPLE_QTAG); + DC395x_write8(TRM_S1040_SCSI_FIFO,tag_number); + pDCB->TagMask |= tag_mask; + pSRB->TagNumber = tag_number; + TRACEPRINTF("Tag %i *", tag_number); + + scsicommand = SCMD_SEL_ATN3; + pSRB->SRBState = SRB_START_; + } +#endif +//polling: + /* + ** Send CDB ..command block ......... + */ +#ifdef DC395x_DEBUG_KG + printk (KERN_INFO "DC395x: StartSCSI (pid %li) %02x (%i-%i): Tag %i\n", + pSRB->pcmd->pid, pSRB->pcmd->cmnd[0], pSRB->pcmd->target, + pSRB->pcmd->lun, pSRB->TagNumber); +#endif + if( pSRB->SRBFlag & AUTO_REQSENSE ) + { + DC395x_write8(TRM_S1040_SCSI_FIFO,REQUEST_SENSE); + DC395x_write8(TRM_S1040_SCSI_FIFO,(pDCB->TargetLUN << 5)); + DC395x_write8(TRM_S1040_SCSI_FIFO,0); + DC395x_write8(TRM_S1040_SCSI_FIFO,0); + DC395x_write8(TRM_S1040_SCSI_FIFO,sizeof(pSRB->pcmd->sense_buffer)); + DC395x_write8(TRM_S1040_SCSI_FIFO,0); + } + else + { + ptr = (PBYTE) pSRB->pcmd->cmnd; + for(i=0; i < pSRB->pcmd->cmd_len; i++) + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr++); + } +no_cmd: + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); + if( DC395x_read16(TRM_S1040_SCSI_STATUS ) & SCSIINTERRUPT ) + { + /* + ** If DC395x_StartSCSI return 1: + ** we caught an interrupt (must be reset or reselection ... ) + ** : Let's process it first! + */ + DEBUG0(printk ("DC395x: Debug: StartSCSI failed (busy) for pid %li(%02i-%i)!\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN);) + //DC395x_clrfifo (pACB, "Start2"); + //DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); + pSRB->SRBState = SRB_READY; + DC395x_freetag (pDCB, pSRB); + pSRB->MsgCnt = 0; + return_code = 1; + /* This IRQ should NOT get lost, as we did not acknowledge it */ + } + else + { + /* + ** If DC395x_StartSCSI returns 0: + ** we know that the SCSI processor is free + */ + + pSRB->ScsiPhase = PH_BUS_FREE;/* initial phase */ + pDCB->pActiveSRB = pSRB; + pACB->pActiveDCB = pDCB; + return_code = 0; + /* it's important for atn stop */ + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH | DO_HWRESELECT); + /* + ** SCSI command + */ + TRACEPRINTF ("%02x *", scsicommand); + DC395x_write8 (TRM_S1040_SCSI_COMMAND, scsicommand); + } + return( return_code ); +} + +/* +********************************************************************* +** scsiio +** DC395x_initAdapter +** +********************************************************************* +*/ +//inline +void DC395x_Interrupt( int irq, void *dev_id, struct pt_regs *regs) +{ + PACB pACB; + PDCB pDCB; + PSRB pSRB; + WORD phase,i,scsi_status=0; + void (*DC395x_stateV)( PACB, PSRB, PWORD ); + BYTE scsi_intstatus, dma_status; + DWORD flags; + //DWORD acb_flags=0,drv_flags=0; + pACB = DC395x_pACB_start; + if( pACB == NULL ) + return; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_Interrupt..............\n "); +#endif + + pACB = DC395x_pACB_start; + if( pACB == NULL ) + { + printk(KERN_WARNING "DC395x: Interrupt on uninitialized pACB!\n"); + return; + } + + DC395x_LOCK_IO(pACB->pScsiHost); + + DEBUGRECURSION(if (in_driver++ > NORM_REC_LVL) printk ("DC395x: %i interrupt recursion?\n", in_driver);) + + //DC395x_DRV_LOCK(drv_flags); + + for( i=0; i < DC395x_adapterCnt; i++ ) + { + /* + ** find mach correct pACB with same IRQLevel + ** and request SCSI interrupt service + ** :...search which pACB->IRQLevel is matching with irq + */ + if( pACB->IRQLevel == (BYTE) irq ) + { + scsi_status = DC395x_read16(TRM_S1040_SCSI_STATUS ); + dma_status = DC395x_read8 (TRM_S1040_DMA_STATUS); + if( scsi_status & SCSIINTERRUPT ) + break; + if (dma_status & 0x20) + { + printk ("DC395x: Interrupt from DMA engine: %02x!\n", dma_status); +#if 0 + printk ("DC395x: This means DMA error! Try to handle ...\n"); + if (pACB->pActiveDCB) + { + pACB->pActiveDCB->DCBFlag |= ABORT_DEV_; + if (pACB->pActiveDCB->pActiveSRB) + DC395x_EnableMsgOut_Abort (pACB, pACB->pActiveDCB->pActiveSRB); + } + DC395x_write16 (TRM_S1040_DMA_CONTROL, ABORTXFER | CLRXFIFO); + break; +#else + printk ("DC395x: Ignore and hope for the best ...\n"); + pACB = (PACB)-1; + break; +#endif + } + else + pACB = pACB->pNextACB; + } + else + pACB = pACB->pNextACB; + } + //DC395x_DRV_UNLOCK(drv_flags); + + if( pACB == (PACB)-1 ) + { + //printk("DC395x_Interrupt: Spurious interrupt detected!\n"); + goto out_unlock; + } + /* This acknowledges the IRQ */ + scsi_intstatus = DC395x_read8(TRM_S1040_SCSI_INTSTATUS ); + if ((scsi_status & 0x2007) == 0x2002) + printk ("DC395x: COP after COP completed? %04x\n", scsi_status); +#if 1//def DC395x_DEBUG0 + if (DC395x_monitor_next_IRQ) { + printk(KERN_INFO "DC395x: status=%04x intstatus=%02x\n", scsi_status, scsi_intstatus); + DC395x_monitor_next_IRQ--; + } +#endif + //DC395x_ACB_LOCK(pACB,acb_flags); +#ifdef DC395x_DEBUG_KG + if (scsi_intstatus & INT_SELTIMEOUT) + printk (KERN_INFO "DC395x: Sel Timeout IRQ\n"); +#endif + //printk ("DC395x_IRQ: intstatus = %02x ", scsi_intstatus); + + if (timer_pending (&pACB->SelTO_Timer)) + del_timer (&pACB->SelTO_Timer); + + if(scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) + { + DC395x_Disconnect( pACB );/* bus free interrupt */ + goto out_unlock; + } + if(scsi_intstatus & INT_RESELECTED) + { + DC395x_Reselect( pACB ); + goto out_unlock; + } + if(scsi_intstatus & INT_SELECT) + { + printk (KERN_INFO "DC395x: Host does not support target mode!\n"); + goto out_unlock; + } + if(scsi_intstatus & INT_SCSIRESET) + { + DC395x_ScsiRstDetect( pACB ); + goto out_unlock; + } + if( scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE) ) + { + pDCB = pACB->pActiveDCB; + if (!pDCB) { + printk ("DC395x: Oops: BusService (%04x %02x) w/o ActiveDCB!\n", + scsi_status, scsi_intstatus); + goto out_unlock; + } + pSRB = pDCB->pActiveSRB; + if( pDCB->DCBFlag & ABORT_DEV_ ) + { +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "MsgOut Abort Device..... "); +#endif + DC395x_EnableMsgOut_Abort( pACB, pSRB ); + } + /* + ************************************************************ + ** software sequential machine + ************************************************************ + */ + phase = (WORD) pSRB->ScsiPhase; + /* + ** 62037 or 62137 + ** call DC395x_SCSI_phase0[]... "phase entry" + ** handle every phase before start transfer + */ + /* DC395x_DataOutPhase0, phase:0 */ + /* DC395x_DataInPhase0, phase:1 */ + /* DC395x_CommandPhase0, phase:2 */ + /* DC395x_StatusPhase0, phase:3 */ + /* DC395x_Nop0, phase:4 PH_BUS_FREE .. initial phase */ + /* DC395x_Nop0, phase:5 PH_BUS_FREE .. initial phase */ + /* DC395x_MsgOutPhase0, phase:6 */ + /* DC395x_MsgInPhase0, phase:7 */ + DC395x_stateV = (void *) DC395x_SCSI_phase0[phase]; + DC395x_stateV( pACB, pSRB, &scsi_status ); + /* + **$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + ** + ** if there were any exception occured + ** scsi_status will be modify to bus free phase + ** new scsi_status transfer out from ... previous DC395x_stateV + ** + **$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + */ + pSRB->ScsiPhase = scsi_status & PHASEMASK; + phase = (WORD) scsi_status & PHASEMASK; + /* + ** call DC395x_SCSI_phase1[]... "phase entry" + ** handle every phase do transfer + */ + /* DC395x_DataOutPhase1, phase:0 */ + /* DC395x_DataInPhase1, phase:1 */ + /* DC395x_CommandPhase1, phase:2 */ + /* DC395x_StatusPhase1, phase:3 */ + /* DC395x_Nop1, phase:4 PH_BUS_FREE .. initial phase */ + /* DC395x_Nop1, phase:5 PH_BUS_FREE .. initial phase */ + /* DC395x_MsgOutPhase1, phase:6 */ + /* DC395x_MsgInPhase1, phase:7 */ + DC395x_stateV = (void *) DC395x_SCSI_phase1[phase]; + DC395x_stateV( pACB, pSRB, &scsi_status ); + } + out_unlock: + DEBUGRECURSION(in_driver--;) + DC395x_UNLOCK_IO(pACB->pScsiHost); + //printk ("... done\n"); + //DC395x_ACB_UNLOCK(pACB,acb_flags); + return; +} +#if 0 +/* +********************************************************************* +** scsiio +** DC395x_initAdapter +** do_DC395x_Interrupt +** +********************************************************************* +*/ +void do_DC395x_Interrupt( int irq, void *dev_id, struct pt_regs *regs) +{ + //if( cpuid != 0 ) + // return; + //DC395x_LOCK_IO(pACB->pScsiHost); + DC395x_Interrupt(irq, dev_id, regs); + //DC395x_UNLOCK_IO(pACB->pScsiHost); +} +#endif +/* +********************************************************************* +** scsiio +** DC395x_MsgOutPhase0: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =6 +** +** +********************************************************************* +*/ +static void DC395x_MsgOutPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_MsgOutPhase0..... "); +#endif + if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) ) + { + *pscsi_status = PH_BUS_FREE;/*.. initial phase*/ + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + pSRB->SRBState &= ~SRB_MSGOUT; + TRACEPRINTF ("MOP0 *"); +} + +/* +********************************************************************* +** scsiio +** DC395x_MsgOutPhase1: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =6 +** +** +********************************************************************* +*/ +static void DC395x_MsgOutPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + WORD i; + PBYTE ptr; + PDCB pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_MsgOutPhase1..............\n "); +#endif + TRACEPRINTF ("MOP1*"); + pDCB = pACB->pActiveDCB; + DC395x_clrfifo (pACB, "MOP1"); + if( !(pSRB->SRBState & SRB_MSGOUT) ) + { + pSRB->SRBState |= SRB_MSGOUT; + printk ("DC395x: Debug: pid %li: MsgOut Phase unexpected.\n", + pSRB->pcmd->pid); /* So what ? */ + } + if (!pSRB->MsgCnt) + { + DEBUG0(printk ("DC395x: Debug: pid %li: NOP Msg (no output message there).\n", + pSRB->pcmd->pid);) + DC395x_write8( TRM_S1040_SCSI_FIFO, MSG_NOP); + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); + TRACEPRINTF ("\\*"); + TRACEOUT (" %s\n", pSRB->debugtrace); + return; + } + ptr = (PBYTE) pSRB->MsgOutBuf; + TRACEPRINTF("(*"); + //printk ("DC395x: Send msg: "); DC395x_printMsg (ptr, pSRB->MsgCnt); + //printk ("DC395x: MsgOut: "); + for(i=0; i < pSRB->MsgCnt ; i++) { + TRACEPRINTF ("%02x *", *ptr); + DC395x_write8 (TRM_S1040_SCSI_FIFO, *ptr++); + } + TRACEPRINTF(")*"); + pSRB->MsgCnt = 0; + //printk ("\n"); + if( /*(pDCB->DCBFlag & ABORT_DEV_) && */ (pSRB->MsgOutBuf[0] == MSG_ABORT) ) + pSRB->SRBState = SRB_ABORT_SENT; + + //1.25 + //DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + /* + ** SCSI command + */ + //TRACEPRINTF (".*"); + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} +/* +********************************************************************* +** scsiio +** DC395x_CommandPhase0: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =2 +** +** +********************************************************************* +*/ +static void DC395x_CommandPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + TRACEPRINTF("COP0 *"); + //1.25 + //DC395x_clrfifo (pACB, COP0); + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); +} +/* +********************************************************************* +** scsiio +** DC395x_CommandPhase1: one of DC395x_SCSI_phase1[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** if phase =2 +** +** +********************************************************************* +*/ +static void DC395x_CommandPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + PDCB pDCB; + PBYTE ptr; + WORD i; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_CommandPhase1..............\n "); +#endif + TRACEPRINTF("COP1*"); + DC395x_clrfifo (pACB, "COP1"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_CLRATN); + if( !(pSRB->SRBFlag & AUTO_REQSENSE) ) + { + ptr = (PBYTE) pSRB->pcmd->cmnd; + for(i=0; i < pSRB->pcmd->cmd_len; i++) + { + DC395x_write8(TRM_S1040_SCSI_FIFO, *ptr); + ptr++; + } + } + else + { + DC395x_write8(TRM_S1040_SCSI_FIFO, REQUEST_SENSE); + pDCB = pACB->pActiveDCB; + /* target id */ + DC395x_write8(TRM_S1040_SCSI_FIFO, (pDCB->TargetLUN << 5)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + DC395x_write8(TRM_S1040_SCSI_FIFO, sizeof(pSRB->pcmd->sense_buffer)); + DC395x_write8(TRM_S1040_SCSI_FIFO, 0); + } + pSRB->SRBState |= SRB_COMMAND; + /* it's important for atn stop */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + /* SCSI command */ + TRACEPRINTF(".*"); + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); +} + +/* Do sanity checks for S/G list */ +#ifdef DC395x_SGPARANOIA +static inline void DC395x_check_SG (PSRB pSRB) +{ + unsigned Length = 0; + unsigned Idx = pSRB->SRBSGIndex; + PSGE0 psge = pSRB->SegmentX + Idx; + for ( ; Idx < pSRB->SRBSGCount; psge++, Idx++) + Length += psge->length; + if (Length != pSRB->SRBTotalXferLength) + printk ("DC395x: Inconsistent SRB S/G lengths (Tot=%i, Count=%i) !!\n", + pSRB->SRBTotalXferLength, Length); +}; +#else +static inline void DC395x_check_SG (PSRB pSRB) {}; +#endif + +#if 0 +/* Check DMA to SCSI block consistency */ +static void DC395x_check_SG_TX (PSRB pSRB, DWORD tx) +{ + PACB pACB = pSRB->pSRBDCB->pDCBACB; + DWORD len = 0; + PSGE0 psge = pSRB->SegmentX + pSRB->SRBSGIndex; + unsigned dma_idx = DC395x_read32 (TRM_S1040_DMA_CXCNT) >> 3; + while (dma_idx-- > 0) { len += psge->length; psge++; } + if (tx < len || tx > (len + psge->length)) + printk (KERN_DEBUG "DC395x: DMA read between %i and %i bytes, SCSI read %i!!\n", + len, len + psge->length, tx); + else + printk (KERN_DEBUG "DC395x: Congrats: DMA (%i--%i) and SCSI (%i) agree on transferred data!\n", + len, len + psge->length, tx); +} +#endif + +/* Compute the next Scatter Gather list index and adjust its length + * and address if necessary; also compute virt_addr */ +void DC395x_update_SGlist (PSRB pSRB, DWORD Left) +{ + PSGE0 psge; + DWORD Xferred = 0; + BYTE Idx; + PSCSICMD pcmd = pSRB->pcmd; + struct scatterlist *sg; + int segment = pcmd->use_sg; + +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Update SG: Total %i, Left %i\n", + pSRB->SRBTotalXferLength, Left); +#endif + DC395x_check_SG (pSRB); + psge = pSRB->SegmentX + pSRB->SRBSGIndex; + /* data that has already been transferred */ + Xferred = pSRB->SRBTotalXferLength - Left; + if (pSRB->SRBTotalXferLength != Left) + { + //DC395x_check_SG_TX (pSRB, Xferred); + /* Remaining */ + pSRB->SRBTotalXferLength = Left; + /* parsing from last time disconnect SGIndex */ + for ( Idx = pSRB->SRBSGIndex; Idx < pSRB->SRBSGCount ; Idx++) + { + /* Complete SG entries done */ + if (Xferred >= psge->length) + Xferred -= psge->length; + /* Partial SG entries done */ + else + { + psge->length -= Xferred; /* residue data length */ + psge->address += Xferred; /* residue data pointer */ + pSRB->SRBSGIndex = Idx; + PCI_DMA_SYNC_SINGLE(pSRB->pSRBDCB->pDCBACB->pdev, + pSRB->SRBSGBusAddr, + sizeof(SGentry)*DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); + break; + } + psge++; + } + DC395x_check_SG (pSRB); + } + /* We need the corresponding virtual address sg_to_virt */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,16) + pSRB->virt_addr = bus_to_virt (psge->address); + return; +#else + //printk ("DC395x: sg_to_virt: bus %08x -> virt ", psge->address); + if (!segment) { + pSRB->virt_addr += Xferred; + //printk ("%p\n", pSRB->virt_addr); + return; + } + /* We have to walk the scatterlist to find it */ + sg = (struct scatterlist*) pcmd->request_buffer; + while (segment--) { + //printk ("(%08x)%p ", BUS_ADDR(*sg), PAGE_ADDRESS(sg)); + unsigned long mask = ~((unsigned long)sg->length-1) & PAGE_MASK; + if ((BUS_ADDR(*sg) & mask) == (psge->address & mask)) { + pSRB->virt_addr = (PAGE_ADDRESS(sg) + + psge->address - (psge->address & PAGE_MASK)); + //printk ("%p\n", pSRB->virt_addr); + return; + } + ++sg; + } + printk ("DC395x: sg_to_virt failed!\n"); + pSRB->virt_addr = 0; +#endif +} + + /* + * DC395x_cleanup_after_transfer + * + * Makes sure, DMA and SCSI engine are empty, after the transfer has finished + * KG: Currently called from StatusPhase1 () + * Should probably also be called from other places + * Best might be to call it in DataXXPhase0, if new phase will differ + */ +static void DC395x_cleanup_after_transfer (PACB pACB, PSRB pSRB) +{ + TRACEPRINTF (" Cln*"); + //DC395x_write8 (TRM_S1040_DMA_STATUS, FORCEDMACOMP); + if (DC395x_read16(TRM_S1040_DMA_COMMAND) & 0x0001) + { /* read */ + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + DC395x_clrfifo (pACB, "ClnIn"); + + if (!(DC395x_read16(TRM_S1040_DMA_FIFOCNT) & 0x8000)) + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + } + else + { /* write */ + if (!(DC395x_read16(TRM_S1040_DMA_FIFOCNT) & 0x8000)) + DC395x_write8(TRM_S1040_DMA_CONTROL, CLRXFIFO); + + if (!(DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x40)) + DC395x_clrfifo (pACB, "ClnOut"); + + } + //1.25 + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH); +} + + +/* Those no of bytes will be transfered w/ PIO through the SCSI FIFO * + * Seems to be needed for unknown reasons; could be a hardware bug :-( */ +#define DC395x_LASTPIO 4 +/* + ********************************************************************* + ** scsiio + ** DC395x_DataOutPhase0: one of DC395x_SCSI_phase0[] vectors + ** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] + ** if phase =0 + ** + ** + ********************************************************************* + */ +void DC395x_DataOutPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + WORD scsi_status; + DWORD dLeftCounter = 0; + PDCB pDCB = pSRB->pSRBDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_DataOutPhase0.....\n "); +#endif + TRACEPRINTF("DOP0*"); + pDCB = pSRB->pSRBDCB; + scsi_status = *pscsi_status; + + /* + * KG: We need to drain the buffers before we draw any conclusions! + * This means telling the DMA to push the rest into SCSI, telling + * SCSI to push the rest to the bus. + * However, the device might have been the one to stop us (phase + * change), and the data in transit just needs to be accounted so + * it can be retransmitted.) + */ + /* + * KG: Stop DMA engine pushing more data into the SCSI FIFO + * If we need more data, the DMA SG list will be freshly set up, anyway + */ +#ifdef DC395x_DEBUGPIO + printk ("DC395x: DOP0: DMA_FCNT: %04x, SCSI_FCNT: %02x, CTR %06x, stat %04x, Tot: %06x\n", + DC395x_read16 (TRM_S1040_DMA_FIFOCNT), + DC395x_read8 (TRM_S1040_SCSI_FIFOCNT), + DC395x_read32 (TRM_S1040_SCSI_COUNTER), + scsi_status, pSRB->SRBTotalXferLength); + //DC395x_dumpinfo(pACB, pDCB, pSRB); +#endif + DC395x_write16 (TRM_S1040_DMA_CONTROL, STOPDMAXFER | CLRXFIFO); + + if( !(pSRB->SRBState & SRB_XFERPAD) ) + { + if( scsi_status & PARITYERROR ) + pSRB->SRBStatus |= PARITY_ERROR; + + /* KG: Right, we can't just rely on the SCSI_COUNTER, because this + * is the no of bytes it got from the DMA engine not the no it + * transferred successfully to the device. (And the difference could + * be as much as the FIFO size, I guess ...) */ + if (!(scsi_status & SCSIXFERDONE)) + { + /* + ** when data transfer from DMA FIFO to SCSI FIFO + ** if there was some data left in SCSI FIFO + */ + dLeftCounter = (DWORD) (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1F); + if (pDCB->SyncPeriod & WIDE_SYNC) + dLeftCounter <<= 1; + +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Debug: SCSI FIFO contains %i %s in DOP0\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT), + (pDCB->SyncPeriod & WIDE_SYNC)? "words": "bytes"); + printk ("DC395x: SCSI FIFOCNT %02x, SCSI CTR %08x\n", + DC395x_read8 (TRM_S1040_SCSI_FIFOCNT), + DC395x_read32 (TRM_S1040_SCSI_COUNTER)); + printk ("DC395x: DMA FIFOCNT %04x, DMA CTR %08x\n", + DC395x_read16 (TRM_S1040_DMA_FIFOCNT), + DC395x_read32 (TRM_S1040_DMA_CXCNT)); +#endif + /* + ** if WIDE scsi SCSI FIFOCNT unit is word !!! + ** so need to *= 2 + */ + } + /* + ** calculate all the residue data that not yet tranfered + ** SCSI transfer counter + left in SCSI FIFO data + ** + ** .....TRM_S1040_SCSI_COUNTER (24bits) + ** The counter always decrement by one for every SCSI byte transfer. + ** .....TRM_S1040_SCSI_FIFOCNT ( 5bits) + ** The counter is SCSI FIFO offset counter (in units of bytes or! words) + */ + if (pSRB->SRBTotalXferLength > DC395x_LASTPIO) + dLeftCounter += DC395x_read32(TRM_S1040_SCSI_COUNTER); + TRACEPRINTF ("%06x *", dLeftCounter); + + /* Is this a good idea? */ + //DC395x_clrfifo (pACB, "DOP1"); + /* KG: What is this supposed to be useful for? WIDE padding stuff? */ + if ( dLeftCounter == 1 && + pDCB->SyncPeriod & WIDE_SYNC && + pSRB->pcmd->request_bufflen % 2) + { + dLeftCounter = 0; + printk ("DC395x: DOP0: Discard 1 byte. (%02x)\n", scsi_status); + } + /* KG: Oops again. Same thinko as above: The SCSI might have been + * faster than the DMA engine, so that it ran out of data. + * In that case, we have to do just nothing! + * But: Why the interrupt: No phase change. No XFERCNT_2_ZERO. Or? */ + + /* KG: This is nonsense: We have been WRITING data to the bus + * If the SCSI engine has no bytes left, how should the DMA engine? */ + if ((dLeftCounter == 0) /*|| (scsi_status & SCSIXFERCNT_2_ZERO) )*/) + { /* + int ctr = 6000000; BYTE TempDMAstatus; + do + { + TempDMAstatus = DC395x_read8(TRM_S1040_DMA_STATUS); + } while( !(TempDMAstatus & DMAXFERCOMP) && --ctr); + if (ctr < 6000000-1) printk ("DC395x: DMA should be complete ... in DOP1\n"); + if (!ctr) printk (KERN_ERR "DC395x: Deadlock in DataOutPhase0 !!\n"); + */ + pSRB->SRBTotalXferLength = 0; + } + else /* Update SG list */ + { + /* + ** if transfer not yet complete + ** there were some data residue in SCSI FIFO or + ** SCSI transfer counter not empty + */ + long oldXferred = pSRB->SRBTotalXferLength - dLeftCounter; + const int diff = (pDCB->SyncPeriod & WIDE_SYNC)? 2: 1; + DC395x_update_SGlist (pSRB, dLeftCounter); + /* KG: Most ugly hack! Apparently, this works around a chip bug */ + if ( (pSRB->SegmentX[pSRB->SRBSGIndex].length == diff && pSRB->pcmd->use_sg) + || ((oldXferred & ~PAGE_MASK) == (PAGE_SIZE-diff)) + ) { + printk ("DC395x: Work around chip bug (%i)?\n", diff); + dLeftCounter = pSRB->SRBTotalXferLength - diff; + DC395x_update_SGlist (pSRB, dLeftCounter); + //pSRB->SRBTotalXferLength -= diff; + //pSRB->virt_addr += diff; + //if (pSRB->pcmd->use_sg) + // pSRB->SRBSGIndex++; + } + } + } +#if 0 + if (!(DC395x_read8 (TRM_S1040_SCSI_FIFOCNT) & 0x40)) + printk ("DC395x: DOP0(%li): %i bytes in SCSI FIFO! (Clear!)\n", + pSRB->pcmd->pid, DC395x_read8 (TRM_S1040_SCSI_FIFOCNT) & 0x1f); +#endif + //DC395x_clrfifo (pACB, "DOP0"); + //DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO | ABORTXFER); +#if 1 + if ((*pscsi_status & PHASEMASK) != PH_DATA_OUT) { + //printk ("DC395x: Debug: Clean up after Data Out ...\n"); + DC395x_cleanup_after_transfer (pACB, pSRB); + } +#endif + TRACEPRINTF(".*"); +} +/* +********************************************************************* +** scsiio +** DC395x_DataOutPhase1: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =0 +** +** 62037 +********************************************************************* +*/ +static void DC395x_DataOutPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_DataOutPhase1.....\n"); +#endif + //1.25 + TRACEPRINTF("DOP1*"); + DC395x_clrfifo (pACB, "DOP1"); + /* + ** do prepare befor transfer when data out phase + */ + DC395x_DataIO_transfer (pACB, pSRB, XFERDATAOUT); + TRACEPRINTF(".*"); +} + + +/* +********************************************************************* +** scsiio +** DC395x_DataInPhase0: one of DC395x_SCSI_phase1[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** if phase =1 +** +** +********************************************************************* +*/ +void DC395x_DataInPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + WORD scsi_status; + DWORD dLeftCounter = 0; + //PDCB pDCB = pSRB->pSRBDCB; + //BYTE bval; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_DataInPhase0..............\n "); +#endif + TRACEPRINTF("DIP0*"); + scsi_status = *pscsi_status; + + /* KG: DataIn is much more tricky than DataOut. When the device is finished + * and switches to another phase, the SCSI engine should be finished too. + * But: There might still be bytes left in its FIFO to be fetched by the DMA + * engine and transferred to memory. + * We should wait for the FIFOs to be emptied by that (is there any way to + * enforce this?) and then stop the DMA engine, because it might think, that + * there are more bytes to follow. Yes, the device might disconnect prior to + * having all bytes transferred! + * Also we should make sure that all data from the DMA engine buffer's really + * made its way to the system memory! Some documentation on this would not + * seem to be a bad idea, actually. + */ + + if( !(pSRB->SRBState & SRB_XFERPAD) ) { + if( scsi_status & PARITYERROR ) { + printk("DC395x: Parity Error (pid %li, target %02i-%i)\n", + pSRB->pcmd->pid, pSRB->pcmd->target, pSRB->pcmd->lun); + pSRB->SRBStatus |= PARITY_ERROR; + } + // KG: We should wait for the DMA FIFO to be empty ... + // but: it would be better to wait first for the SCSI FIFO and then the + // the DMA FIFO to become empty? How do we know, that the device not already + // sent data to the FIFO in a MsgIn phase, eg.? + if (!(DC395x_read16 (TRM_S1040_DMA_FIFOCNT) & 0x8000)) { +#if 0 + int ctr = 6000000; + printk ("DC395x: DIP0: Wait for DMA FIFO to flush ...\n"); + //DC395x_write16 (TRM_S1040_DMA_CONTROL, STOPDMAXFER); + //DC395x_write32 (TRM_S1040_SCSI_COUNTER, 7); + //DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_IN); + while (!(DC395x_read16 (TRM_S1040_DMA_FIFOCNT) & 0x8000) && --ctr); + if (ctr < 6000000-1) printk ("DC395x: Debug: DIP0: Had to wait for DMA ...\n"); + if (!ctr) printk (KERN_ERR "DC395x: Deadlock in DIP0 waiting for DMA FIFO empty!!\n"); + //DC395x_write32 (TRM_S1040_SCSI_COUNTER, 0); +#endif +#ifdef DC395x_DEBUG_KG + printk ("DC395x: DIP0: DMA_FIFO: %04x\n", + DC395x_read16 (TRM_S1040_DMA_FIFOCNT)); +#endif + } + /* Now: Check remainig data: The SCSI counters should tell us ... */ + dLeftCounter = DC395x_read32 (TRM_S1040_SCSI_COUNTER) + + ((DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f) + << ((pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC)? 1: 0)); + +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Debug: SCSI FIFO contains %i %s in DIP0\n", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f, + (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC)? "words": "bytes"); + printk ("DC395x: SCSI FIFOCNT %02x, SCSI CTR %08x\n", + DC395x_read8 (TRM_S1040_SCSI_FIFOCNT), + DC395x_read32 (TRM_S1040_SCSI_COUNTER)); + printk ("DC395x: DMA FIFOCNT %04x, DMA CTR %08x\n", + DC395x_read16 (TRM_S1040_DMA_FIFOCNT), + DC395x_read32 (TRM_S1040_DMA_CXCNT)); + printk ("DC395x: Remaining: TotXfer: %i, SCSI FIFO+Ctr: %i\n", + pSRB->SRBTotalXferLength, dLeftCounter); +#endif +#if DC395x_LASTPIO + /* KG: Less than or equal to 4 bytes can not be transfered via DMA, it seems. */ + if (dLeftCounter && pSRB->SRBTotalXferLength <= DC395x_LASTPIO) + { + //DWORD addr = (pSRB->SegmentX[pSRB->SRBSGIndex].address); + //DC395x_update_SGlist (pSRB, dLeftCounter); + DEBUGPIO( + printk ("DC395x: DIP0: PIO (%i %s) to %p for remaining %i bytes:", + DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1f, + (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC)? "words": "bytes", + pSRB->virt_addr, pSRB->SRBTotalXferLength); + ) + + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + DC395x_write8 (TRM_S1040_SCSI_CONFIG2, CFG2_WIDEFIFO); + + while (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) != 0x40) + { + BYTE byte = DC395x_read8 (TRM_S1040_SCSI_FIFO); + *(pSRB->virt_addr)++ = byte; DEBUGPIO(printk (" %02x", byte);) + pSRB->SRBTotalXferLength--; dLeftCounter--; + pSRB->SegmentX[pSRB->SRBSGIndex].length--; + if (pSRB->SRBTotalXferLength && !pSRB->SegmentX[pSRB->SRBSGIndex].length) { + DEBUGPIO(printk (" (next segment)");) + pSRB->SRBSGIndex++; + DC395x_update_SGlist (pSRB, dLeftCounter); + } + } + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + { +#if 1 /* Read the last byte ... */ + if (pSRB->SRBTotalXferLength > 0) + { + BYTE byte = DC395x_read8 (TRM_S1040_SCSI_FIFO); + *(pSRB->virt_addr)++ = byte; pSRB->SRBTotalXferLength--; + DEBUGPIO(printk (" %02x", byte);) + } +#endif + DC395x_write8 (TRM_S1040_SCSI_CONFIG2, 0); + } + //printk (" %08x", *(DWORD*)(bus_to_virt (addr))); + //pSRB->SRBTotalXferLength = 0; + DEBUGPIO(printk ("\n");) + } +#endif /* DC395x_LASTPIO */ + +#if 0 + // KG: This was in DATAOUT. Does it also belong here? + // Nobody seems to know what counter and fifo_cnt count exactly ... + if (!(scsi_status & SCSIXFERDONE)) + { + /* + ** when data transfer from DMA FIFO to SCSI FIFO + ** if there was some data left in SCSI FIFO + */ + dLeftCounter = (DWORD) (DC395x_read8(TRM_S1040_SCSI_FIFOCNT) & 0x1F); + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + dLeftCounter <<= 1; + /* + ** if WIDE scsi SCSI FIFOCNT unit is word !!! + ** so need to *= 2 + ** KG: Seems to be correct ... + */ + } +#endif + + //dLeftCounter += DC395x_read32(TRM_S1040_SCSI_COUNTER); +#if 0 + printk ("DC395x: DIP0: ctr=%08x, DMA_FIFO=%04x, SCSI_FIFO=%02x\n", + dLeftCounter, DC395x_read16 (TRM_S1040_DMA_FIFOCNT), + DC395x_read8 (TRM_S1040_SCSI_FIFOCNT)); + printk ("DC395x: DIP0: DMAStat %02x\n", + DC395x_read8 (TRM_S1040_DMA_STATUS)); +#endif + + // KG: This should not be needed any more! + if((dLeftCounter == 0) || (scsi_status & SCSIXFERCNT_2_ZERO) ) + { +#if 0 + int ctr = 6000000; + BYTE TempDMAstatus; + do { + TempDMAstatus = DC395x_read8(TRM_S1040_DMA_STATUS); + } while( !(TempDMAstatus & DMAXFERCOMP) && --ctr); + if (!ctr) printk (KERN_ERR "DC395x: Deadlock in DataInPhase0 waiting for DMA!!\n"); + pSRB->SRBTotalXferLength = 0; +#endif +#if 0 //def DC395x_DEBUG_KG + printk ("DC395x: DIP0: DMA not yet ready: %02x: %i -> %i bytes\n", + DC395x_read8(TRM_S1040_DMA_STATUS), pSRB->SRBTotalXferLength, dLeftCounter); +#endif + pSRB->SRBTotalXferLength = dLeftCounter; + } + else /* phase changed */ + { + /* + ** parsing the case: + ** when a transfer not yet complete + ** but be disconnected by target + ** if transfer not yet complete + ** there were some data residue in SCSI FIFO or + ** SCSI transfer counter not empty + */ + DC395x_update_SGlist (pSRB, dLeftCounter); + } + } + /* KG: The target may decide to disconnect: Empty FIFO before! */ + if ((*pscsi_status & PHASEMASK) != PH_DATA_IN) { + //printk ("DC395x: Debug: Clean up after Data In ...\n"); + DC395x_cleanup_after_transfer (pACB, pSRB); + } + +#if 0 + /* KG: Make sure, no previous transfers are pending! */ + bval = DC395x_read8 (TRM_S1040_SCSI_FIFOCNT); + if (!(bval & 0x40)) + { + bval &= 0x1f; + printk ("DC395x: DIP0(%li): %i bytes in SCSI FIFO (stat %04x) (left %08x)!!\n", + pSRB->pcmd->pid, bval & 0x1f, scsi_status, dLeftCounter); + if((dLeftCounter == 0) || (scsi_status & SCSIXFERCNT_2_ZERO) ) { + printk ("DC395x: Clear FIFO!\n"); + DC395x_clrfifo (pACB, "DIP0"); + } + } +#endif + //DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO | ABORTXFER); + + //DC395x_clrfifo (pACB, "DIP0"); + //DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + TRACEPRINTF(".*"); +} +/* +********************************************************************* +** scsiio +** DC395x_DataInPhase1: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =1 +** +** +********************************************************************* +*/ +static void DC395x_DataInPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_DataInPhase1..... "); +#endif + /* FIFO should be cleared, if previous phase was not DataPhase */ + //DC395x_clrfifo (pACB, "DIP1"); + /* Allow data in! */ + //DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); + TRACEPRINTF("DIP1:*"); + /* + ** do prepare before transfer when data in phase + */ + DC395x_DataIO_transfer (pACB, pSRB, XFERDATAIN); + TRACEPRINTF(".*"); +} + +/* +********************************************************************* +** scsiio +** DC395x_DataOutPhase1 +** DC395x_DataInPhase1 +** +********************************************************************* +*/ +void DC395x_DataIO_transfer( PACB pACB, PSRB pSRB, WORD ioDir) +{ + BYTE bval; + PDCB pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x: DataIO_transfer %c (pid %li): len = %i, SG: %i/%i\n", + ((ioDir & DMACMD_DIR)? 'r': 'w'), pSRB->pcmd->pid, + pSRB->SRBTotalXferLength, pSRB->SRBSGIndex, pSRB->SRBSGCount); +#endif + TRACEPRINTF("%05x(%i/%i)*", pSRB->SRBTotalXferLength, + pSRB->SRBSGIndex, pSRB->SRBSGCount); + pDCB = pSRB->pSRBDCB; + if (pSRB == pACB->pTmpSRB) + { + printk ("DC395x: ERROR! Using TmpSRB in DataPhase!\n"); + } + if( pSRB->SRBSGIndex < pSRB->SRBSGCount ) + { + if( pSRB->SRBTotalXferLength > DC395x_LASTPIO ) + { + BYTE dma_status = DC395x_read8 (TRM_S1040_DMA_STATUS); + /* KG: What should we do: Use SCSI Cmd 0x90/0x92? */ + /* Maybe, even ABORTXFER would be appropriate */ + if (dma_status & XFERPENDING) { + printk ("DC395x: Xfer pending! Expect trouble!!\n"); + DC395x_dumpinfo (pACB, pDCB, pSRB); + DC395x_write8 (TRM_S1040_DMA_CONTROL, CLRXFIFO); + } + //DC395x_clrfifo (pACB, "IO"); + /* + ** load what physical address of Scatter/Gather list table want to be + ** transfer + */ + pSRB->SRBState |= SRB_DATA_XFER; + DC395x_write32(TRM_S1040_DMA_XHIGHADDR, 0); + if (pSRB->pcmd->use_sg) { /* with S/G */ + ioDir |= DMACMD_SG; + DC395x_write32(TRM_S1040_DMA_XLOWADDR, pSRB->SRBSGBusAddr + sizeof(SGentry)*pSRB->SRBSGIndex); + /* load how many bytes in the Scatter/Gather list table */ + DC395x_write32(TRM_S1040_DMA_XCNT, ((DWORD) (pSRB->SRBSGCount - pSRB->SRBSGIndex) << 3)); + } else { /* without S/G */ + ioDir &= ~DMACMD_SG; + DC395x_write32(TRM_S1040_DMA_XLOWADDR, pSRB->SegmentX[0].address); + DC395x_write32(TRM_S1040_DMA_XCNT, pSRB->SegmentX[0].length); + } + /* load total transfer length (24bits) max value 16Mbyte */ + DC395x_write32(TRM_S1040_SCSI_COUNTER, pSRB->SRBTotalXferLength); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + if (ioDir & DMACMD_DIR) /* read */ { + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_IN); + DC395x_write16(TRM_S1040_DMA_COMMAND, ioDir); + } else { + DC395x_write16(TRM_S1040_DMA_COMMAND, ioDir); + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_DMA_OUT); + } + + } +#if DC395x_LASTPIO + else if (pSRB->SRBTotalXferLength > 0) + { /* The last four bytes: Do PIO */ + //DC395x_clrfifo (pACB, "IO"); + /* + ** load what physical address of Scatter/Gather list table want to be + ** transfer + */ + pSRB->SRBState |= SRB_DATA_XFER; + /* load total transfer length (24bits) max value 16Mbyte */ + DC395x_write32(TRM_S1040_SCSI_COUNTER, pSRB->SRBTotalXferLength); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + if (ioDir & DMACMD_DIR) { /* read */ + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN); + } else { /* write */ + int ln = pSRB->SRBTotalXferLength; + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) + DC395x_write8 (TRM_S1040_SCSI_CONFIG2, CFG2_WIDEFIFO); + DEBUGPIO(printk ("DC395x: DOP1: PIO %i bytes from %p:", + pSRB->SRBTotalXferLength, pSRB->virt_addr);) + while (pSRB->SRBTotalXferLength) + { + DEBUGPIO(printk (" %02x", (unsigned char)*(pSRB->virt_addr));) + DC395x_write8 (TRM_S1040_SCSI_FIFO, *(pSRB->virt_addr)++); + pSRB->SRBTotalXferLength--; + pSRB->SegmentX[pSRB->SRBSGIndex].length--; + if (pSRB->SRBTotalXferLength && !pSRB->SegmentX[pSRB->SRBSGIndex].length) { + DEBUGPIO(printk (" (next segment)");) + pSRB->SRBSGIndex++; + DC395x_update_SGlist (pSRB, pSRB->SRBTotalXferLength); + } + } + if (pSRB->pSRBDCB->SyncPeriod & WIDE_SYNC) { + if (ln % 2) { + DC395x_write8 (TRM_S1040_SCSI_FIFO, 0); + DEBUGPIO(printk (" |00");) + } + DC395x_write8 (TRM_S1040_SCSI_CONFIG2, 0); + } + //DC395x_write32(TRM_S1040_SCSI_COUNTER, ln); + DEBUGPIO(printk ("\n");) + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT); + } + } +#endif /* DC395x_LASTPIO */ + else /* xfer pad */ + { + BYTE data = 0, data2 = 0; + if( pSRB->SRBSGCount ) + { + pSRB->AdaptStatus = H_OVER_UNDER_RUN; + pSRB->SRBStatus |= OVER_RUN; + } + // KG: despite the fact that we are using 16 bits I/O ops + // the SCSI FIFO is only 8 bits according to the docs + // (we can set bit 1 in 0x8f to serialize FIFO access ...) + if (pDCB->SyncPeriod & WIDE_SYNC) + { + DC395x_write32(TRM_S1040_SCSI_COUNTER, 2); + DC395x_write8 (TRM_S1040_SCSI_CONFIG2, CFG2_WIDEFIFO); + if (ioDir & DMACMD_DIR) /* read */ { + data = DC395x_read8(TRM_S1040_SCSI_FIFO); + data2 = DC395x_read8(TRM_S1040_SCSI_FIFO); + //printk ("DC395x: DataIO: Xfer pad: %02x %02x\n", data, data2); + } else { + /* Danger, Robinson: If you find KGs scattered over the wide + * disk, the driver or chip is to blame :-( */ + DC395x_write8 (TRM_S1040_SCSI_FIFO, 'K'); + DC395x_write8 (TRM_S1040_SCSI_FIFO, 'G'); + } + DC395x_write8 (TRM_S1040_SCSI_CONFIG2, 0); + } + else + { + DC395x_write32(TRM_S1040_SCSI_COUNTER, 1); + /* Danger, Robinson: If you find a collection of Ks on your disk + * something broke :-( */ + if (ioDir & DMACMD_DIR) { /* read */ + data = DC395x_read8(TRM_S1040_SCSI_FIFO); + //printk ("DC395x: DataIO: Xfer pad: %02x\n", data); + } else { + DC395x_write8(TRM_S1040_SCSI_FIFO, 'K'); + } + } + pSRB->SRBState |= SRB_XFERPAD; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + /* + ** SCSI command + */ + bval = (ioDir & DMACMD_DIR) ? SCMD_FIFO_IN : SCMD_FIFO_OUT; + DC395x_write8(TRM_S1040_SCSI_COMMAND, bval); + } + } + //DC395x_monitor_next_IRQ = 2; + //printk (" done\n"); +} +/* +********************************************************************* +** scsiio +** DC395x_StatusPhase0: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =3 +** +** +********************************************************************* +*/ +static void DC395x_StatusPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x: StatusPhase0 (pid %li)\n", pSRB->pcmd->pid); +#endif + TRACEPRINTF("STP0 *"); + pSRB->TargetStatus = DC395x_read8(TRM_S1040_SCSI_FIFO); + pSRB->EndMessage = DC395x_read8(TRM_S1040_SCSI_FIFO); /* get message */ + pSRB->SRBState = SRB_COMPLETED; + *pscsi_status = PH_BUS_FREE; /*.. initial phase*/ + //1.25 + //DC395x_clrfifo (pACB, "STP0"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + /* + ** SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} +/* +********************************************************************* +** scsiio +** DC395x_StatusPhase1: one of DC395x_SCSI_phase1[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** if phase =3 +** +** +********************************************************************* +*/ +static void DC395x_StatusPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x: StatusPhase1 (pid=%li)\n", pSRB->pcmd->pid); +#endif + TRACEPRINTF("STP1 *"); + /* Cleanup is now done at the end of DataXXPhase0 */ + //DC395x_cleanup_after_transfer (pACB, pSRB); + + pSRB->SRBState = SRB_STATUS; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + /* + ** SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_COMP); +} + +/* Message handling */ + +#if 0 +/* Print received message */ +static void +DC395x_printMsg (BYTE *MsgBuf, DWORD len) +{ + int i; + printk (" %02x", MsgBuf[0]); + for (i = 1; i < len; i++) + printk (" %02x", MsgBuf[i]); + printk ("\n"); +}; +#endif + +/* Check if the message is complete */ +static inline BYTE +DC395x_MsgIn_complete (BYTE *msgbuf, DWORD len) +{ + if (*msgbuf == EXTENDED_MESSAGE) + { + if (len < 2) return 0; + if (len < msgbuf[1] + 2) return 0; + } + else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages + if (len < 2) return 0; + return 1; +} + +#define DC395x_ENABLE_MSGOUT \ + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_SETATN); \ + pSRB->SRBState |= SRB_MSGOUT + +/* reject_msg */ +static inline void +DC395x_MsgIn_reject (PACB pACB, PSRB pSRB) +{ + pSRB->MsgOutBuf[0] = MESSAGE_REJECT; + pSRB->MsgCnt = 1; DC395x_ENABLE_MSGOUT; + pSRB->SRBState &= ~SRB_MSGIN; pSRB->SRBState |= SRB_MSGOUT; + printk (KERN_INFO "DC395x: Reject message %02x from %02i-%i\n", + pSRB->MsgInBuf[0], pSRB->pSRBDCB->TargetID, pSRB->pSRBDCB->TargetLUN); + TRACEPRINTF("\\*"); +} + +/* abort command */ +static inline void +DC395x_EnableMsgOut_Abort ( PACB pACB, PSRB pSRB ) +{ + pSRB->MsgOutBuf[0] = ABORT; + pSRB->MsgCnt = 1; DC395x_ENABLE_MSGOUT; + pSRB->SRBState &= ~SRB_MSGIN; pSRB->SRBState |= SRB_MSGOUT; + /* + if (pSRB->pSRBDCB) + pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_; + */ + TRACEPRINTF("#*"); +} + +static PSRB +DC395x_MsgIn_QTag (PACB pACB, PDCB pDCB, BYTE tag) +{ + PSRB lastSRB = pDCB->pGoingLast; + PSRB pSRB = pDCB->pGoingSRB; +#ifdef DC395x_DEBUG0 + printk ("DC395x: QTag Msg (SRB %p): %i ", pSRB, tag); +#endif + if (!(pDCB->TagMask & (1 << tag))) + printk ("DC395x: MsgIn_QTag: TagMask (%08x) does not reserve tag %i!\n", + pDCB->TagMask, tag); + + if (!pSRB) goto mingx0; + while (pSRB) + { + if (pSRB->TagNumber == tag) break; + if (pSRB == lastSRB) goto mingx0; + pSRB = pSRB->pNextSRB; + } +#ifdef DC395x_DEBUG0 + printk ("pid %li (%i-%i)\n", pSRB->pcmd->pid, + pSRB->pSRBDCB->TargetID, pSRB->pSRBDCB->TargetLUN); +#endif + if( pDCB->DCBFlag & ABORT_DEV_ ) + { + //pSRB->SRBState = SRB_ABORT_SENT; + DC395x_EnableMsgOut_Abort( pACB, pSRB ); + } + + if( !(pSRB->SRBState & SRB_DISCONNECT) ) + goto mingx0; + + /* Tag found */ + TRACEPRINTF("[%s]*", pDCB->pActiveSRB->debugtrace); + TRACEPRINTF("RTag*"); + /* Just for debugging ... */ + lastSRB = pSRB; pSRB = pDCB->pActiveSRB; + TRACEPRINTF("Found.*"); + pSRB = lastSRB; + + memcpy (pSRB->MsgInBuf, pDCB->pActiveSRB->MsgInBuf, pACB->MsgLen); + pSRB->SRBState |= pDCB->pActiveSRB->SRBState; + pSRB->SRBState |= SRB_DATA_XFER; + pDCB->pActiveSRB = pSRB; + /* How can we make the DORS happy? */ + return pSRB; + + mingx0: + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; + pSRB->MsgCnt = 1; DC395x_ENABLE_MSGOUT; + TRACEPRINTF ("?*"); + printk ("DC395x: Unknown tag received: %i: abort !!\n", tag); + return pSRB; +} + +/* Reprogram registers */ +static inline void +DC395x_reprog (PACB pACB, PDCB pDCB) +{ + DC395x_write8 (TRM_S1040_SCSI_TARGETID, pDCB->TargetID); + DC395x_write8 (TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); + DC395x_write8 (TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); + DC395x_SetXferRate (pACB, pDCB); +}; + + +/* set async transfer mode */ +static void +DC395x_MsgIn_set_async (PACB pACB, PSRB pSRB) +{ + PDCB pDCB = pSRB->pSRBDCB; + printk ("DC395x: Target %02i: No sync transfers\n", + pDCB->TargetID); + TRACEPRINTF("!S *"); + pDCB->SyncMode &= ~(SYNC_NEGO_ENABLE); pDCB->SyncMode |= SYNC_NEGO_DONE; + //pDCB->SyncPeriod &= 0; + pDCB->SyncOffset = 0; + pDCB->MinNegoPeriod = 200 >> 2; /* 200ns <=> 5 MHz */ + pSRB->SRBState &= ~SRB_DO_SYNC_NEGO; + DC395x_reprog (pACB, pDCB); + if ((pDCB->SyncMode & WIDE_NEGO_ENABLE) && !(pDCB->SyncMode & WIDE_NEGO_DONE)) + { + DC395x_Build_WDTR (pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk ("DC395x: SDTR(rej): Try WDTR anyway ...\n");) + } +} + +/* set sync transfer mode */ +static void +DC395x_MsgIn_set_sync (PACB pACB, PSRB pSRB) +{ + BYTE bval; int fact; + PDCB pDCB = pSRB->pSRBDCB; + //BYTE oldsyncperiod = pDCB->SyncPeriod; + //BYTE oldsyncoffset = pDCB->SyncOffset; + +#ifdef DC395x_DEBUG1 + printk (KERN_INFO "DC395x: Target %02i: Sync: %ins (%02i.%01i MHz) Offset %i\n", + pDCB->TargetID, pSRB->MsgInBuf[3]<<2, + (250/pSRB->MsgInBuf[3]), ((250%pSRB->MsgInBuf[3])*10)/pSRB->MsgInBuf[3], + pSRB->MsgInBuf[4]); +#endif + + if (pSRB->MsgInBuf[4] > 15) + pSRB->MsgInBuf[4] = 15; + if (!(pDCB->DevMode & NTC_DO_SYNC_NEGO)) + pDCB->SyncOffset = 0; + else if (pDCB->SyncOffset == 0) + pDCB->SyncOffset = pSRB->MsgInBuf[4]; + if (pSRB->MsgInBuf[4] > pDCB->SyncOffset) + pSRB->MsgInBuf[4] = pDCB->SyncOffset; + else + pDCB->SyncOffset = pSRB->MsgInBuf[4]; + bval = 0; + while ( bval < 7 && + (pSRB->MsgInBuf[3] > dc395x_clock_period[bval] || + pDCB->MinNegoPeriod > dc395x_clock_period[bval]) ) + bval++; + if (pSRB->MsgInBuf[3] < dc395x_clock_period[bval]) + printk (KERN_INFO "DC395x: Increase sync nego period to %ins\n", + dc395x_clock_period[bval] << 2); + pSRB->MsgInBuf[3] = dc395x_clock_period[bval]; + pDCB->SyncPeriod &= 0xf0; + pDCB->SyncPeriod |= ALT_SYNC | bval; + pDCB->MinNegoPeriod = pSRB->MsgInBuf[3]; + + if (pDCB->SyncPeriod & WIDE_SYNC) fact = 500; + else fact = 250; + + printk (KERN_INFO "DC395x: Target %02i: %s Sync: %ins Offset %i (%02i.%01i MB/s)\n", + pDCB->TargetID, (fact == 500)? "Wide16" :"", pDCB->MinNegoPeriod<<2, + pDCB->SyncOffset, (fact/pDCB->MinNegoPeriod), + ((fact%pDCB->MinNegoPeriod)*10+pDCB->MinNegoPeriod/2)/pDCB->MinNegoPeriod); + + TRACEPRINTF("S%i *", pDCB->MinNegoPeriod << 2); + if (!(pSRB->SRBState & SRB_DO_SYNC_NEGO)) + { + /* Reply with corrected SDTR Message */ + printk ("DC395x: .. answer w/ %ins %i\n", + pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]); + + memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5); + pSRB->MsgCnt = 5; + DC395x_ENABLE_MSGOUT; + pDCB->SyncMode |= SYNC_NEGO_DONE; + } + else + { + if ((pDCB->SyncMode & WIDE_NEGO_ENABLE) && !(pDCB->SyncMode & WIDE_NEGO_DONE)) + { + DC395x_Build_WDTR (pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0 (printk ("DC395x: SDTR: Also try WDTR ...\n");) + } + } + pSRB->SRBState &= ~SRB_DO_SYNC_NEGO; + pDCB->SyncMode |= SYNC_NEGO_DONE | SYNC_NEGO_ENABLE; + + DC395x_reprog (pACB, pDCB); +}; + + +static inline void +DC395x_MsgIn_set_nowide (PACB pACB, PSRB pSRB) +{ + PDCB pDCB = pSRB->pSRBDCB; +#ifdef DC395x_DEBUG_KG + printk ("DC395x: WDTR got rejected from target %02i\n", + pDCB->TargetID); +#endif + TRACEPRINTF("!W *"); + pDCB->SyncPeriod &= ~WIDE_SYNC; + pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE); pDCB->SyncMode |= WIDE_NEGO_DONE; + pSRB->SRBState &= ~SRB_DO_WIDE_NEGO; + DC395x_reprog (pACB, pDCB); + if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) && !(pDCB->SyncMode & SYNC_NEGO_DONE)) + { + DC395x_Build_SDTR (pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk ("DC395x: WDTR(rej): Try SDTR anyway ...\n");) + } +} + +static void +DC395x_MsgIn_set_wide (PACB pACB, PSRB pSRB) +{ + PDCB pDCB = pSRB->pSRBDCB; + BYTE wide = (pDCB->DevMode & NTC_DO_WIDE_NEGO && pACB->Config & HCC_WIDE_CARD)? 1 : 0; + if (pSRB->MsgInBuf[3] > wide) + pSRB->MsgInBuf[3] = wide; + /* Completed */ + if (!(pSRB->SRBState & SRB_DO_WIDE_NEGO)) + { + printk ("DC395x: Target %02i initiates Wide Nego ...\n", pDCB->TargetID); + memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 4); + pSRB->MsgCnt = 4; pSRB->SRBState |= SRB_DO_WIDE_NEGO; + DC395x_ENABLE_MSGOUT; + } + + pDCB->SyncMode |= (WIDE_NEGO_ENABLE | WIDE_NEGO_DONE); + if (pSRB->MsgInBuf[3] > 0) pDCB->SyncPeriod |= WIDE_SYNC; + else pDCB->SyncPeriod &= ~WIDE_SYNC; + pSRB->SRBState &= ~SRB_DO_WIDE_NEGO; + TRACEPRINTF("W%i *", (pDCB->SyncPeriod & WIDE_SYNC? 1: 0)); + //pDCB->SyncMode &= ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Wide transfers (%i bit) negotiated with target %02i\n", + (8 << pSRB->MsgInBuf[3]), pDCB->TargetID); +#endif + DC395x_reprog (pACB, pDCB); + if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) && !(pDCB->SyncMode & SYNC_NEGO_DONE)) + { + DC395x_Build_SDTR (pACB, pDCB, pSRB); + DC395x_ENABLE_MSGOUT; + DEBUG0(printk ("DC395x: WDTR: Also try SDTR ...\n");) + } +} + + +#if 0 +/* handle RESTORE_PTR */ +static void +DC395x_restore_ptr (PACB pACB, PSRB pSRB) +{ + PSGL psgl; + pSRB->TotalXferredLen = 0; + pSRB->SGIndex = 0; + if( pSRB->pcmd->use_sg ) + { + /* NOTE: This is wrong, since we have segment merging nowadays ... */ + pSRB->SGcount = (BYTE) pSRB->pcmd->use_sg; + pSRB->pSegmentList = (PSGL) pSRB->pcmd->request_buffer; + psgl = pSRB->pSegmentList; + while (pSRB->TotalXferredLen + (ULONG) psgl->length < pSRB->Saved_Ptr) + { + pSRB->TotalXferredLen += (ULONG) psgl->length; + pSRB->SGIndex++; + if( pSRB->SGIndex < pSRB->SGcount ) + { + pSRB->pSegmentList++; + psgl = pSRB->pSegmentList; + + /* And here we have to use the pci map ! */ + pSRB->SGBusAddr = virt_to_bus( psgl->address ); + pSRB->SGToBeXferLen = (ULONG) psgl->length; + } + else + pSRB->SGToBeXferLen = 0; + } + pSRB->SGToBeXferLen -= (pSRB->Saved_Ptr - pSRB->TotalXferredLen); + pSRB->SGBusAddr += (pSRB->Saved_Ptr - pSRB->TotalXferredLen); + printk (KERN_INFO "DC395x: Pointer restored. Segment %i, Total %li, Bus %08lx\n", pSRB->SGIndex, pSRB->Saved_Ptr, pSRB->SGBusAddr); + } + else if( pSRB->pcmd->request_buffer ) + { + pSRB->SGcount = 1; + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PBYTE) pSRB->pcmd->request_buffer + pSRB->Saved_Ptr; + pSRB->Segmentx.length = pSRB->pcmd->request_bufflen - pSRB->Saved_Ptr; + printk (KERN_INFO "DC395x: Pointer restored. Total %li, Bus %p\n", + pSRB->Saved_Ptr, pSRB->Segmentx.address); + } + else + { + pSRB->SGcount = 0; + printk (KERN_INFO "DC395x: RESTORE_PTR message for Transfer without Scatter-Gather ??\n"); + }; + + pSRB->TotalXferredLen = pSRB->Saved_Ptr; +} +#endif + +/* +********************************************************************* +** scsiio +** DC395x_MsgInPhase0: one of DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** if phase =7 +** +** extended message codes: +** +** code description +** +** 02h Reserved +** 00h MODIFY DATA POINTER +** 01h SYNCHRONOUS DATA TRANSFER REQUEST +** 03h WIDE DATA TRANSFER REQUEST +** 04h - 7Fh Reserved +** 80h - FFh Vendor specific +** +********************************************************************* +*/ +void DC395x_MsgInPhase0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + PDCB pDCB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_MsgInPhase0..............\n "); +#endif + TRACEPRINTF("MIP0*"); + pDCB = pACB->pActiveDCB; + + pSRB->MsgInBuf[pACB->MsgLen++] = DC395x_read8 (TRM_S1040_SCSI_FIFO); + if (DC395x_MsgIn_complete (pSRB->MsgInBuf, pACB->MsgLen)) + { + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[0]); + //printk (KERN_INFO "DC395x: MsgIn:"); + //DC395x_printMsg (pSRB->MsgInBuf, pACB->MsgLen); + + /* Now eval the msg */ + switch (pSRB->MsgInBuf[0]) + { + case DISCONNECT: + pSRB->SRBState = SRB_DISCONNECT; break; + + case SIMPLE_QUEUE_TAG: + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[1]); + pSRB = DC395x_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]); + break; + + case MESSAGE_REJECT: + DC395x_write16(TRM_S1040_SCSI_CONTROL,DO_CLRATN | DO_DATALATCH); + /* A sync nego message was rejected ! */ + if (pSRB->SRBState & SRB_DO_SYNC_NEGO) + { + DC395x_MsgIn_set_async (pACB, pSRB); + break; + } + /* A wide nego message was rejected ! */ + if (pSRB->SRBState & SRB_DO_WIDE_NEGO) + { + DC395x_MsgIn_set_nowide (pACB, pSRB); + break; + } + DC395x_EnableMsgOut_Abort (pACB, pSRB); + //pSRB->SRBState |= SRB_ABORT_SENT; + break; + + case EXTENDED_MESSAGE: + TRACEPRINTF("(%02x)*", pSRB->MsgInBuf[2]); + /* SDTR */ + if (pSRB->MsgInBuf[1] == 3 && pSRB->MsgInBuf[2] == EXTENDED_SDTR) + { + DC395x_MsgIn_set_sync (pACB, pSRB); + break; + }; + /* WDTR */ + if (pSRB->MsgInBuf[1] == 2 && pSRB->MsgInBuf[2] == EXTENDED_WDTR + && pSRB->MsgInBuf[3] <= 2) // sanity check ... + { + DC395x_MsgIn_set_wide (pACB, pSRB); + break; + }; + DC395x_MsgIn_reject (pACB, pSRB); + break; + + // Discard wide residual + case MSG_IGNOREWIDE: + DEBUG0(printk ("DC395x: Ignore Wide Residual!\n");) + //DC395x_write32 (TRM_S1040_SCSI_COUNTER, 1); + //DC395x_read8 (TRM_S1040_SCSI_FIFO); + break; + + // nothing has to be done + case COMMAND_COMPLETE: break; + + // SAVE POINTER may be ignored as we have the PSRB associated with the + // scsi command. Thanks, Gérard, for pointing it out. + case SAVE_POINTERS: +#ifdef DC395x_DEBUG0 + printk ("DC395x: SAVE POINTER message received (pid %li: rem.%i) ... ignore :-(\n", + pSRB->pcmd->pid, pSRB->SRBTotalXferLength); +#endif + //pSRB->Saved_Ptr = pSRB->TotalXferredLen; + break; + // The device might want to restart transfer with a RESTORE + case RESTORE_POINTERS: + printk ("DC395x: RESTORE POINTER message received ... ignore :-(\n"); + //dc395x_restore_ptr (pACB, pSRB); + break; + case ABORT: + printk ("DC395x: ABORT msg received (pid %li %02i-%i)\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); + pDCB->DCBFlag |= ABORT_DEV_; + DC395x_EnableMsgOut_Abort (pACB, pSRB); + break; + // reject unknown messages + default: + if (pSRB->MsgInBuf[0] & IDENTIFY_BASE) + { + printk ("DC395x: Identify Message received?\n"); + //TRACEOUT (" %s\n", pSRB->debugtrace); + pSRB->MsgCnt = 1; pSRB->MsgOutBuf[0] = pDCB->IdentifyMsg; + DC395x_ENABLE_MSGOUT; pSRB->SRBState |= SRB_MSGOUT; + //break; + } + DC395x_MsgIn_reject (pACB, pSRB); + TRACEOUT (" %s\n", pSRB->debugtrace); + } + TRACEPRINTF (".*"); + + /* Clear counter and MsgIn state */ + pSRB->SRBState &= ~SRB_MSGIN; + pACB->MsgLen = 0; + }; + + //1.25 + if ((*pscsi_status & PHASEMASK) != PH_MSG_IN) +#if 0 + DC395x_clrfifo (pACB, "MIP0_"); +#else + TRACEPRINTF("N/Cln *"); +#endif + *pscsi_status = PH_BUS_FREE; + DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important ... you know! */ + DC395x_write8 (TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + +/* +********************************************************************* +** scsiio +** DC395x_MsgInPhase1: one of DC395x_SCSI_phase1[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** if phase =7 +** +** +********************************************************************* +*/ +static void DC395x_MsgInPhase1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_MsgInPhase1..............\n "); +#endif + TRACEPRINTF("MIP1 *"); + DC395x_clrfifo (pACB, "MIP1"); + DC395x_write32(TRM_S1040_SCSI_COUNTER, 1); + if( !(pSRB->SRBState & SRB_MSGIN) ) + { + pSRB->SRBState &= ~SRB_DISCONNECT; + pSRB->SRBState |= SRB_MSGIN; + } + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop */ + /* + ** SCSI command + */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_FIFO_IN); +} + +/* +********************************************************************* +** scsiio +** DC395x_Nop0: one of DC395x_SCSI_phase1[] ,DC395x_SCSI_phase0[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** if phase =4 ..PH_BUS_FREE +** +** +********************************************************************* +*/ +static void +DC395x_Nop0( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + //TRACEPRINTF("NOP0 *"); +} +/* +********************************************************************* +** scsiio +** DC395x_Nop1: one of DC395x_SCSI_phase0[] ,DC395x_SCSI_phase1[] vectors +** DC395x_stateV = (void *) DC395x_SCSI_phase0[phase] +** DC395x_stateV = (void *) DC395x_SCSI_phase1[phase] +** if phase =5 +** +** +********************************************************************* +*/ +static void DC395x_Nop1( PACB pACB, PSRB pSRB, PWORD pscsi_status) +{ + //TRACEPRINTF("NOP1 *"); +} +/* +********************************************************************* +** scsiio +** DC395x_MsgInPhase0 +** +********************************************************************* +*/ +static void DC395x_SetXferRate( PACB pACB, PDCB pDCB ) +{ + BYTE bval; + WORD cnt, i; + PDCB pDCBTemp; + + /* + ** set all lun device's period , offset + */ + if( !(pDCB->IdentifyMsg & 0x07) ) + { + if( pACB->scan_devices ) + DC395x_CurrSyncOffset = pDCB->SyncOffset; + else + { + pDCBTemp = pACB->pLinkDCB; + cnt = pACB->DCBCnt; + bval = pDCB->TargetID; + for(i=0; iTargetID == bval ) + { + pDCBTemp->SyncPeriod = pDCB->SyncPeriod; + pDCBTemp->SyncOffset = pDCB->SyncOffset; + pDCBTemp->SyncMode = pDCB->SyncMode; + pDCBTemp->MinNegoPeriod = pDCB->MinNegoPeriod; + } + pDCBTemp = pDCBTemp->pNextDCB; + } + } + } + return; +} + +/* +********************************************************************* +** scsiio +** DC395x_Interrupt +** +********************************************************************* +*/ +void DC395x_Disconnect ( PACB pACB ) +{ + PDCB pDCB; + PSRB pSRB; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x: Disconnect (pid=%li)\n", + pACB->pActiveDCB->pActiveSRB->pcmd->pid); +#endif + pDCB = pACB->pActiveDCB; + if (!pDCB) + { + printk(KERN_ERR "DC395x: Disc: Exception Disconnect pDCB=NULL !!\n "); + udelay (500); + // Suspend queue for a while + pACB->pScsiHost->last_reset = jiffies + HZ/2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + DC395x_clrfifo (pACB, "DiscEx"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + return; + } + pSRB = pDCB->pActiveSRB; + pACB->pActiveDCB = 0; + TRACEPRINTF("DISC *"); + + pSRB->ScsiPhase = PH_BUS_FREE;/* initial phase */ + DC395x_clrfifo (pACB, "Disc"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) + { + printk(KERN_ERR "DC395x: Disc: Unexpected Reselection (%i-%i)\n", pDCB->TargetID, pDCB->TargetLUN); + pSRB->SRBState = 0; + DC395x_Waiting_process ( pACB ); + } + else if( pSRB->SRBState & SRB_ABORT_SENT ) + { + //PSCSICMD pcmd = pSRB->pcmd; + pDCB->DCBFlag &= ~ABORT_DEV_; + pACB->pScsiHost->last_reset = jiffies + HZ/2 + 1; + printk(KERN_ERR "DC395x: Disc: SRB_ABORT_SENT!\n"); + DC395x_DoingSRB_Done (pACB, DID_ABORT); + DC395x_Query_to_Waiting (pACB); + DC395x_Waiting_process (pACB); + } + else + { + if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) || !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) ) + { + /* + ** Selection time out + ** SRB_START_ || SRB_MSGOUT || (!SRB_DISCONNECT && !SRB_COMPLETED) + */ + /* Unexp. Disc / Sel Timeout */ + if (pSRB->SRBState != SRB_START_ && pSRB->SRBState != SRB_MSGOUT) + { + pSRB->SRBState = SRB_READY; + printk ("DC395x: Unexpected Disconnection (pid %li)!\n", pSRB->pcmd->pid); + pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; + TRACEPRINTF("UnExpD *"); + TRACEOUT ("%s\n", pSRB->debugtrace); + goto disc1; + } + else + { + /* Normal selection timeout */ + TRACEPRINTF("SlTO *"); +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Disc: SelTO (pid=%li) for dev %02i-%i\n", pSRB->pcmd->pid, + pDCB->TargetID, pDCB->TargetLUN); +#endif + if (pSRB->RetryCnt++ > DC395x_MAX_RETRIES || pACB->scan_devices) + { + pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT; + goto disc1; + } + DC395x_freetag (pDCB, pSRB); + DC395x_Going_to_Waiting (pDCB, pSRB); +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Retry pid %li ...\n", pSRB->pcmd->pid); +#endif + DC395x_waiting_timer (pACB, HZ/20); + } + } + else if( pSRB->SRBState & SRB_DISCONNECT ) + { + BYTE bval = DC395x_read8 (TRM_S1040_SCSI_SIGNAL); + /* + ** SRB_DISCONNECT (This is what we expect!) + */ + // printk ("DC395x: DoWaitingSRB (pid=%li)\n", pSRB->pcmd->pid); + TRACEPRINTF("+*"); + if (bval & 0x40) + { + DEBUG0(printk ("DC395x: Debug: DISC: SCSI bus stat %02x: ACK set! Other controllers?\n", + bval);) + // It could come from another initiator, therefore don't do much ! + TRACEPRINTF("ACK(%02x) *", bval); + //DC395x_dumpinfo (pACB, pDCB, pSRB); + //TRACEOUT (" %s\n", pSRB->debugtrace); + //pDCB->DCBFlag |= ABORT_DEV_; + //DC395x_EnableMsgOut_Abort (pACB, pSRB); + //DC395x_write16 (TRM_S1040_SCSI_CONTROL, DO_CLRFIFO | DO_CLRATN | DO_HWRESELECT); + } + else + DC395x_Waiting_process ( pACB ); + } + else if( pSRB->SRBState & SRB_COMPLETED ) + { +disc1: + /* + ** SRB_COMPLETED + */ + DC395x_freetag (pDCB, pSRB); + pDCB->pActiveSRB = 0; + pSRB->SRBState = SRB_FREE; + //printk ("DC395x: done (pid=%li)\n", pSRB->pcmd->pid); + DC395x_SRBdone( pACB, pDCB, pSRB); + } + } + return; +} + +/* +********************************************************************* +** scsiio +** DC395x_Reselect +** +********************************************************************* +*/ +void DC395x_Reselect( PACB pACB ) +{ + PDCB pDCB; + PSRB pSRB = 0; + WORD RselTarLunId; + BYTE id, lun; + BYTE arblostflag = 0; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_Reselect..............\n "); +#endif + + DC395x_clrfifo (pACB, "Resel"); + //DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT | DO_DATALATCH); + /* Read Reselected Target ID and LUN */ + RselTarLunId = DC395x_read16(TRM_S1040_SCSI_TARGETID); + pDCB = pACB->pActiveDCB; + if( pDCB ) + { /* Arbitration lost but Reselection win */ + pSRB = pDCB->pActiveSRB; + if (!pSRB) { + printk ("DC395x: Arb lost Resel won, but pActiveSRB == 0!\n"); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + return; + } + /* Why the if ? */ + if( !( pACB->scan_devices ) ) + { +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Arb lost but Resel win pid %li (%02i-%i) Rsel %04x Stat %04x\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN, + RselTarLunId, DC395x_read16 (TRM_S1040_SCSI_STATUS)); +#endif + TRACEPRINTF ("ArbLResel!*"); + //TRACEOUT (" %s\n", pSRB->debugtrace); + arblostflag = 1; + //pSRB->SRBState |= SRB_DISCONNECT; + + pSRB->SRBState = SRB_READY; + DC395x_freetag (pDCB, pSRB); + DC395x_Going_to_Waiting (pDCB, pSRB); + DC395x_waiting_timer (pACB, HZ/20); + + // return; + } + } + /* Read Reselected Target Id and LUN */ + if (!(RselTarLunId & (IDENTIFY_BASE << 8))) + printk ("DC395x: Resel expects identify msg! Got %04x!\n", RselTarLunId); + id = RselTarLunId & 0xff; + lun = (RselTarLunId >> 8) & 7; + pDCB = DC395x_findDCB (pACB, id, lun); + if( !pDCB ) { + printk (KERN_ERR "DC395x: Reselect from non existing device (%02i-%i)\n", + id, lun); + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + return; + } + + pACB->pActiveDCB = pDCB; + + if (!(pDCB->DevMode & NTC_DO_DISCONNECT)) + printk ("DC395x: Reselection in spite of forbidden disconnection? (%02i-%i)\n", + pDCB->TargetID, pDCB->TargetLUN); + + if( (pDCB->SyncMode & EN_TAG_QUEUEING) /*&& !arblostflag*/) + { + PSRB oldSRB = pSRB; + pSRB = pACB->pTmpSRB; +#ifdef DC395x_DEBUGTRACE + pSRB->debugpos = 0; pSRB->debugtrace[0] = 0; +#endif + pDCB->pActiveSRB = pSRB; + if (oldSRB) TRACEPRINTF ("ArbLResel(%li):*", oldSRB->pcmd->pid); + //if (arblostflag) printk ("DC395x: Reselect: Wait for Tag ... \n"); + } + else + { + /* There can be only one! */ + pSRB = pDCB->pActiveSRB; + if (pSRB) + TRACEPRINTF("RSel *"); + if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) ) + { + /* + ** abort command + */ + printk ("DC395x: Reselected w/o disconnected cmds from %02i-%i?\n", + pDCB->TargetID, pDCB->TargetLUN); + pSRB = pACB->pTmpSRB; + pSRB->SRBState = SRB_UNEXPECT_RESEL; + pDCB->pActiveSRB = pSRB; + DC395x_EnableMsgOut_Abort( pACB, pSRB ); + } + else + { + if( pDCB->DCBFlag & ABORT_DEV_ ) + { + //pSRB->SRBState = SRB_ABORT_SENT; + DC395x_EnableMsgOut_Abort( pACB, pSRB ); + } + else + pSRB->SRBState = SRB_DATA_XFER; + + } + //if (arblostflag) TRACEOUT (" %s\n", pSRB->debugtrace); + } + pSRB->ScsiPhase = PH_BUS_FREE;/* initial phase */ + /* + *********************************************** + ** Program HA ID, target ID, period and offset + *********************************************** + */ + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); /* host ID */ + DC395x_write8(TRM_S1040_SCSI_TARGETID, pDCB->TargetID); /* target ID */ + DC395x_write8(TRM_S1040_SCSI_OFFSET, pDCB->SyncOffset); /* offset */ + DC395x_write8(TRM_S1040_SCSI_SYNC, pDCB->SyncPeriod); /* sync period, wide */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_DATALATCH);/* it's important for atn stop*/ + /* SCSI command */ + DC395x_write8(TRM_S1040_SCSI_COMMAND, SCMD_MSGACCEPT); +} + + +/* Dynamic device handling */ + +/* Remove dev (and DCB) */ +static void +DC395x_remove_dev (PACB pACB, PDCB pDCB) +{ + PDCB pPrevDCB = pACB->pLinkDCB; + + if (pDCB->GoingSRBCnt > 1) + { + DCBDEBUG(printk (KERN_INFO "DC395x: Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n",\ + pDCB->TargetID, pDCB->TargetLUN, (int)pDCB, pDCB->GoingSRBCnt);) + return; + }; + pACB->DCBmap[pDCB->TargetID] &= ~(1 << pDCB->TargetLUN); + + // The first one + if (pDCB == pACB->pLinkDCB) + { + // The last one + if (pACB->pLastDCB == pDCB) { + pDCB->pNextDCB = 0; pACB->pLastDCB = 0; + } + pACB->pLinkDCB = pDCB->pNextDCB; + } + else + { + while (pPrevDCB->pNextDCB != pDCB) pPrevDCB = pPrevDCB->pNextDCB; + pPrevDCB->pNextDCB = pDCB->pNextDCB; + if (pDCB == pACB->pLastDCB) pACB->pLastDCB = pPrevDCB; + } + + DCBDEBUG(\ + printk (KERN_INFO "DC395x: Driver about to free DCB (ID %i, LUN %i): %p\n",\ + pDCB->TargetID, pDCB->TargetLUN, pDCB);\ + ) + if (pDCB == pACB->pActiveDCB) pACB->pActiveDCB = 0; + if (pDCB == pACB->pLinkDCB) pACB->pLinkDCB = pDCB->pNextDCB; + if (pDCB == pACB->pDCBRunRobin) pACB->pDCBRunRobin = pDCB->pNextDCB; + pACB->DCBCnt--; + kfree (pDCB); + /* pACB->DeviceCnt--; */ +}; + + +static inline BYTE +DC395x_tagq_blacklist (char* name) +{ +#ifndef DC395x_NO_TAGQ +#if 0 + BYTE i; + for(i=0; iVers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2 ) + { + if ( (ptr->Flags & SCSI_INQ_CMDQUEUE) && + (pDCB->DevMode & NTC_DO_TAG_QUEUEING) && + //(pDCB->DevMode & NTC_DO_DISCONNECT) + /* ((pDCB->DevType == TYPE_DISK) + || (pDCB->DevType == TYPE_MOD)) &&*/ + !DC395x_tagq_blacklist (((char*)ptr)+8) ) + { + if (pDCB->MaxCommand ==1) + pDCB->MaxCommand = pDCB->pDCBACB->TagMaxNum; + pDCB->SyncMode |= EN_TAG_QUEUEING; + //pDCB->TagMask = 0; + } + else + pDCB->MaxCommand = 1; + } +}; + +static void +DC395x_add_dev (PACB pACB, PDCB pDCB, PSCSI_INQDATA ptr) +{ + BYTE bval1 = ptr->DevType & SCSI_DEVTYPE; + pDCB->DevType = bval1; + /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */ + DC395x_disc_tagq_set (pDCB, ptr); +}; + + +/* +********************************************************************* +** scsiio +** DC395x_Disconnected +** Complete execution of a SCSI command +** Signal completion to the generic SCSI driver +** +********************************************************************* +*/ +void DC395x_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ) +{ + BYTE tempcnt,status,DCB_removed=0; + PSCSICMD pcmd; + PSCSI_INQDATA ptr; + //DWORD drv_flags=0; + int dir; + + pcmd = pSRB->pcmd; + TRACEPRINTF("DONE *"); + + SET_DIR(dir,pcmd); + ptr = (PSCSI_INQDATA) (pcmd->request_buffer); + if( pcmd->use_sg ) + ptr = (PSCSI_INQDATA) CPU_ADDR(*(PSGL)ptr); +#ifdef DC395x_SGPARANOIA + printk (KERN_INFO "DC395x: SRBdone SG=%i (%i/%i), req_buf = %p, adr = %p\n", + pcmd->use_sg, pSRB->SRBSGIndex, pSRB->SRBSGCount, pcmd->request_buffer, ptr); +#endif +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO "DC395x: SRBdone (pid %li, target %02i-%i): ", + pSRB->pcmd->pid, pSRB->pcmd->target, pSRB->pcmd->lun); +#endif + status = pSRB->TargetStatus; + if(pSRB->SRBFlag & AUTO_REQSENSE) + { +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "AUTO_REQSENSE1..............\n "); +#endif + /* Unmap sense buffer */ +#ifdef DC395x_SGPARANOIA + printk ("DC395x: Unmap sense buffer from %08x (%05x)\n", + pSRB->SegmentX[0].address, sizeof(pcmd->sense_buffer)); +#endif + PCI_UNMAP_SINGLE(pACB->pdev, pSRB->SegmentX[0].address, + pSRB->SegmentX[0].length, PCI_DMA_FROMDEVICE); + // Restore SG stuff + //printk ("Auto_ReqSense finished: Restore Counters ...\n"); + pSRB->SRBTotalXferLength = pSRB->Xferred; + pSRB->SegmentX[0].address = pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY-1].address; + pSRB->SegmentX[0].length = pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY-1].length; + /* + ** target status.......................... + */ + pSRB->SRBFlag &= ~AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = CHECK_CONDITION << 1; +#ifdef DC395x_DEBUG_KG + switch (pcmd->sense_buffer[2] & 0x0f) + { + case NOT_READY: printk ("\nDC395x: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, + status, pACB->scan_devices); break; + case UNIT_ATTENTION: printk ("\nDC395x: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, + status, pACB->scan_devices); break; + case ILLEGAL_REQUEST: printk ("\nDC395x: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, + status, pACB->scan_devices); break; + case MEDIUM_ERROR: printk ("\nDC395x: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, + status, pACB->scan_devices); break; + case HARDWARE_ERROR: printk ("\nDC395x: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i) ", + pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, + status, pACB->scan_devices); break; + } + if (pcmd->sense_buffer[7] >= 6) + printk ("\nDC395x: Sense=%02x, ASC=%02x, ASCQ=%02x (%08x %08x) ", + pcmd->sense_buffer[2], pcmd->sense_buffer[12], + pcmd->sense_buffer[13], + *((unsigned int*)(pcmd->sense_buffer+3)), + *((unsigned int*)(pcmd->sense_buffer+8))); + else + printk ("\nDC395x: Sense=%02x, No ASC/ASCQ (%08x) ", + pcmd->sense_buffer[2], + *((unsigned int*)(pcmd->sense_buffer+3))); +#endif + + if (status == (CHECK_CONDITION << 1)) + { + pcmd->result = DID_BAD_TARGET << 16; + goto ckc_e; + } +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "AUTO_REQSENSE2..............\n "); +#endif + + if( (pSRB->SRBTotalXferLength) && (pSRB->SRBTotalXferLength >= pcmd->underflow) ) + pcmd->result = MK_RES_LNX(DRIVER_SENSE,DID_OK,pSRB->EndMessage,CHECK_CONDITION); + //SET_RES_DID(pcmd->result,DID_OK) + else + pcmd->result = MK_RES_LNX(DRIVER_SENSE,DID_OK,pSRB->EndMessage,CHECK_CONDITION); + + goto ckc_e; + } + +//*********************************************************** + if( status ) + { + /* + ** target status.......................... + */ + if( status_byte(status) == CHECK_CONDITION ) + { + DC395x_RequestSense( pACB, pDCB, pSRB ); + return; + } + else if( status_byte(status) == QUEUE_FULL ) + { + tempcnt = (BYTE) pDCB->GoingSRBCnt; + printk ("\nDC395x: QUEUE_FULL for dev %02i-%i with %i cmnds\n", + pDCB->TargetID, pDCB->TargetLUN, tempcnt); + if (tempcnt > 1) tempcnt--; + pDCB->MaxCommand = tempcnt; + DC395x_freetag (pDCB, pSRB); + DC395x_Going_to_Waiting ( pDCB, pSRB ); + DC395x_waiting_timer (pACB, HZ/20); + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + return; + } + else if(status == SCSI_STAT_SEL_TIMEOUT) + { + pSRB->AdaptStatus = H_SEL_TIMEOUT; + pSRB->TargetStatus = 0; + pcmd->result = DID_NO_CONNECT << 16; + } + else if (status_byte(status) == BUSY && + (pcmd->cmnd[0] == TEST_UNIT_READY || pcmd->cmnd[0] == INQUIRY) && + pACB->scan_devices) + { + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = status; + pcmd->result = MK_RES(0,0,pSRB->EndMessage,status); + } + else + { + pSRB->AdaptStatus = 0; + SET_RES_DID(pcmd->result,DID_ERROR); + SET_RES_MSG(pcmd->result,pSRB->EndMessage); + SET_RES_TARGET(pcmd->result,status); + } + } + else + { + /* + ** process initiator status.......................... + */ + status = pSRB->AdaptStatus; + if(status & H_OVER_UNDER_RUN) + { + pSRB->TargetStatus = 0; + SET_RES_DID(pcmd->result,DID_OK); + SET_RES_MSG(pcmd->result,pSRB->EndMessage); + } + else if( pSRB->SRBStatus & PARITY_ERROR) + { + SET_RES_DID(pcmd->result,DID_PARITY); + SET_RES_MSG(pcmd->result,pSRB->EndMessage); + } + else /* No error */ + { + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + SET_RES_DID(pcmd->result,DID_OK); + } + } + + if (pcmd->use_sg && dir != PCI_DMA_NONE) + PCI_DMA_SYNC_SG(pACB->pdev, (struct scatterlist*)pcmd->request_buffer, + pcmd->use_sg, dir); + else if (pcmd->request_buffer && dir != PCI_DMA_NONE) + PCI_DMA_SYNC_SINGLE(pACB->pdev, pSRB->SegmentX[0].address, + pcmd->request_bufflen, dir); + if ((pcmd->result & RES_DID) == 0 && + pcmd->cmnd[0] == INQUIRY && + pcmd->cmnd[2] == 0 && + pcmd->request_bufflen >= 8 && + dir != PCI_DMA_NONE && + ptr && + (ptr->Vers & 0x07) >= 2) + pDCB->Inquiry7 = ptr->Flags; +/* Check Error Conditions */ +ckc_e: + if( pACB->scan_devices ) + { + if( pcmd->cmnd[0] == TEST_UNIT_READY || + pcmd->cmnd[0] == INQUIRY) + { +#ifdef DC395x_DEBUG_KG + printk ("\nDC395x: %s: result: %08x", + (pcmd->cmnd[0] == INQUIRY? "INQUIRY": "TEST_UNIT_READY"), + pcmd->result); + if (pcmd->result & (DRIVER_SENSE << 24)) printk (" (sense: %02x %02x %02x %02x)\n", + pcmd->sense_buffer[0], pcmd->sense_buffer[1], + pcmd->sense_buffer[2], pcmd->sense_buffer[3]); + else printk ("\n"); +#endif + if( (!(host_byte(pcmd->result) == DID_OK) && + !(status_byte(pcmd->result) & CHECK_CONDITION) && + !(status_byte(pcmd->result) & BUSY)) + || + ((driver_byte(pcmd->result) & DRIVER_SENSE) && + (pcmd->sense_buffer[0] & 0x70) == 0x70 && + (pcmd->sense_buffer[2] & 0xf) == ILLEGAL_REQUEST) + || host_byte(pcmd->result) & DID_ERROR ) + { + /* device not present: remove */ + DC395x_remove_dev (pACB, pDCB); DCB_removed = 1; + + if( (pcmd->target == pACB->pScsiHost->max_id - 1) && + ((pcmd->lun == 0) || (pcmd->lun == pACB->pScsiHost->max_lun - 1)) ) + pACB->scan_devices = 0; + } + else + { + /* device present: add */ + if( (pcmd->target == pACB->pScsiHost->max_id - 1) && + (pcmd->lun == pACB->pScsiHost->max_lun - 1) ) + pACB->scan_devices = DC395x_END_SCAN ; + /* pACB->DeviceCnt++; */ /* Dev is added on INQUIRY */ + } + } + } + + //if( pSRB->pcmd->cmnd[0] == INQUIRY && + // (host_byte(pcmd->result) == DID_OK || status_byte(pcmd->result) & CHECK_CONDITION) ) + if( pcmd->cmnd[0] == INQUIRY && + (pcmd->result == (DID_OK << 16) || status_byte(pcmd->result) & CHECK_CONDITION) ) + { + DEBUG0(int i;) +#ifdef DC395x_DEBUG0 + printk ("DC395x: Inquiry result:"); + for (i = 0; i < 8; i++) + printk (" %02x", ((PBYTE)ptr)[i]); + ((PBYTE)ptr)[96] = 0; + printk ("DC395x: %s\n", (char*)ptr + 8); +#endif + if ( (ptr->Vers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2 ) + pDCB->Inquiry7 = ptr->Flags; + if ((ptr->DevType & SCSI_DEVTYPE) == TYPE_NODEV && !DCB_removed) + { +#ifdef DC395x_DEBUG_KG + printk ("\nDC395x: Device %02i-%i: TYPE_NODEV?\n", + pDCB->TargetID, pDCB->TargetLUN); +#endif + /* device not present: remove */ + DC395x_remove_dev (pACB, pDCB); + DCB_removed = 1; + } + else + { + /* device found: add */ + DC395x_add_dev (pACB, pDCB, ptr); + if (pACB->scan_devices) pACB->DeviceCnt++; + } + if( (pcmd->target == pACB->pScsiHost->max_id - 1) && + (pcmd->lun == pACB->pScsiHost->max_lun - 1) ) + pACB->scan_devices = 0; + }; + + /* Here is the info for Doug Gilbert's sg3 ... */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,30) + pcmd->resid = pSRB->SRBTotalXferLength; +#endif + /* This may be interpreted by sb. or not ... */ + pcmd->SCp.this_residual = pSRB->SRBTotalXferLength; + pcmd->SCp.buffers_residual = 0; +#ifdef DC395x_DEBUG_KG + if (pSRB->SRBTotalXferLength && !DCB_removed) + printk ("\nDC395x: pid %li: %02x (%02i-%i): Missed %i bytes\n", + pcmd->pid, pcmd->cmnd[0], pcmd->target, pcmd->lun, pSRB->SRBTotalXferLength); +#endif + if (!DCB_removed) DC395x_Going_remove (pDCB, pSRB, 0); + /* Add to free list */ + if (pSRB == pACB->pTmpSRB) + printk ("\nDC395x: ERROR! Completed Cmnd with TmpSRB!\n"); + else + DC395x_Free_insert (pACB, pSRB); + + DEBUG0(printk (KERN_DEBUG "DC395x: SRBdone: done pid %li\n", pcmd->pid);) +#ifdef DC395x_DEBUG_KG + printk (" 0x%08x\n", pcmd->result); +#endif + TRACEPRINTF("%08x(%li)*", pcmd->result, jiffies); + if (pcmd->use_sg && dir != PCI_DMA_NONE) { + /* unmap DC395x SG list */ +#ifdef DC395x_SGPARANOIA + printk ("DC395x: Unmap SG descriptor list %08x (%05x)\n", + pSRB->SRBSGBusAddr, sizeof(SGentry)*DC395x_MAX_SG_LISTENTRY); +#endif + PCI_UNMAP_SINGLE(pACB->pdev, pSRB->SRBSGBusAddr, + sizeof(SGentry)*DC395x_MAX_SG_LISTENTRY, PCI_DMA_TODEVICE); +#ifdef DC395x_SGPARANOIA + printk ("DC395x: Unmap %i SG segments from %p\n", + pcmd->use_sg, pcmd->request_buffer); +#endif + /* unmap the sg segments */ + PCI_UNMAP_SG(pACB->pdev, (struct scatterlist*)pcmd->request_buffer, + pcmd->use_sg, dir); + } + else if (pcmd->request_buffer && dir != PCI_DMA_NONE) { +#ifdef DC395x_SGPARANOIA + printk ("DC395x: Unmap buffer at %08x (%05x)\n", + pSRB->SegmentX[0].address, pcmd->request_bufflen); +#endif + PCI_UNMAP_SINGLE(pACB->pdev, pSRB->SegmentX[0].address, + pcmd->request_bufflen, dir); + } + //DC395x_UNLOCK_ACB_NI; + pcmd->scsi_done (pcmd); + //DC395x_LOCK_ACB_NI; + TRACEOUTALL (KERN_INFO " %s\n", pSRB->debugtrace); + + DC395x_Query_to_Waiting (pACB); + DC395x_Waiting_process (pACB); + return; +} + +/* +********************************************************************* +** scsiio +** DC395x_reset +** abort all cmds in our queues +********************************************************************* +*/ +void DC395x_DoingSRB_Done( PACB pACB, BYTE did_flag ) +{ + PDCB pDCB; + PSRB pSRB, pSRBTemp; + WORD cnt; + PSCSICMD pcmd; + + pDCB = pACB->pLinkDCB; + if (!pDCB) return; + printk(KERN_INFO "DC395x_DoingSRB_Done: pids "); + do + { + /* As the ML may queue cmnds again, cache old values */ + PSRB pWaitingSRB = pDCB->pWaitingSRB; + //PSRB pWaitLast = pDCB->pWaitLast; + WORD WaitSRBCnt = pDCB->WaitSRBCnt; + /* Going queue */ + cnt = pDCB->GoingSRBCnt; + pSRB = pDCB->pGoingSRB; + while (cnt--) + { + int result; + pSRBTemp = pSRB->pNextSRB; + pcmd = pSRB->pcmd; + result = MK_RES(0,did_flag,0,0); + //result = MK_RES(0,DID_RESET,0,0); + TRACEPRINTF ("Reset(%li):%08x*", jiffies, result); + printk (" (G)"); +#ifndef DC395x_DEBUGTRACE + printk ("%li(%02i-%i) ", pcmd->pid, pcmd->target, pcmd->lun); +#endif + TRACEOUT ("%s\n", pSRB->debugtrace); + pDCB->pGoingSRB = pSRBTemp; pDCB->GoingSRBCnt--; + if (!pSRBTemp) pDCB->pGoingLast = NULL; + DC395x_freetag (pDCB, pSRB); + DC395x_Free_insert (pACB, pSRB); +#ifndef USE_NEW_EH + /* For new EH, don't give commands back */ + pcmd->result = result; + //DC395x_SCSI_DONE_ACB_UNLOCK; + // TODO: PCI_UNMAP + pcmd->scsi_done( pcmd ); + //DC395x_SCSI_DONE_ACB_LOCK; +#endif + pSRB = pSRBTemp; + } + if (pDCB->pGoingSRB) + printk ("DC395x: How could the ML send cmnds to the Going queue? (%02i-%i)!!\n", + pDCB->TargetID, pDCB->TargetLUN); + if (pDCB->TagMask) + printk ("DC395x: TagMask for %02i-%i should be empty, is %08x!\n", + pDCB->TargetID, pDCB->TargetLUN, pDCB->TagMask); + //pDCB->GoingSRBCnt = 0;; + //pDCB->pGoingSRB = NULL; pDCB->pGoingLast = NULL; + + /* Waiting queue */ + cnt = WaitSRBCnt; + pSRB = pWaitingSRB; + while (cnt--) + { + int result; + pSRBTemp = pSRB->pNextSRB; + pcmd = pSRB->pcmd; + result = MK_RES(0,did_flag,0,0); + TRACEPRINTF ("Reset(%li):%08x*", jiffies, result); + printk (" (W)"); +#ifndef DC395x_DEBUGTRACE + printk ("%li(%i-%i)", pcmd->pid, pcmd->target, pcmd->lun); +#endif + TRACEOUT ("%s\n", pSRB->debugtrace); + pDCB->pWaitingSRB = pSRBTemp; pDCB->WaitSRBCnt--; + if (!pSRBTemp) pDCB->pWaitLast = NULL; + DC395x_Free_insert (pACB, pSRB); + +#ifndef USE_NEW_EH + /* For new EH, don't give commands back */ + pcmd->result = result; + //DC395x_SCSI_DONE_ACB_UNLOCK; + // TODO: PCI_UNMAP + pcmd->scsi_done( pcmd ); + //DC395x_SCSI_DONE_ACB_LOCK; +#endif + pSRB = pSRBTemp; + } + if (pDCB->WaitSRBCnt) + printk ("\nDC395x: Debug: ML queued %i cmnds again to %02i-%i\n", + pDCB->WaitSRBCnt, pDCB->TargetID, pDCB->TargetLUN); + /* The ML could have queued the cmnds again! */ + //pDCB->WaitSRBCnt = 0;; + //pDCB->pWaitingSRB = NULL; pDCB->pWaitLast = NULL; + pDCB->DCBFlag &= ~ABORT_DEV_; + pDCB = pDCB->pNextDCB; + } + while( pDCB != pACB->pLinkDCB && pDCB ); + printk ("\n"); +} + +/* +********************************************************************* +** scsiio +** DC395x_shutdown DC395x_reset +** +********************************************************************* +*/ +static void DC395x_ResetSCSIBus( PACB pACB ) +{ + //DWORD drv_flags=0; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_ResetSCSIBus..............\n "); +#endif + + //DC395x_DRV_LOCK(drv_flags); + pACB->ACBFlag |= RESET_DEV; /* RESET_DETECT, RESET_DONE, RESET_DEV */ + + DC395x_write16(TRM_S1040_SCSI_CONTROL,DO_RSTSCSI); + while (!( DC395x_read8(TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET )); + + //DC395x_DRV_UNLOCK(drv_flags); + return; +} + +/* Set basic config */ +static void DC395x_basic_config (PACB pACB) +{ + BYTE bval; WORD wval; + DC395x_write8 (TRM_S1040_SCSI_TIMEOUT, pACB->sel_timeout); + if (pACB->Config & HCC_PARITY) + bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK; + else + bval = PHASELATCH | INITIATOR | BLOCKRST; + + DC395x_write8 (TRM_S1040_SCSI_CONFIG0, bval); + + /* program configuration 1: Act_Neg (+ Act_Neg_Enh? + Fast_Filter? + DataDis?) */ + DC395x_write8(TRM_S1040_SCSI_CONFIG1, 0x03); /* was 0x13: default */ + /* program Host ID */ + DC395x_write8(TRM_S1040_SCSI_HOSTID, pACB->pScsiHost->this_id); + /* set ansynchronous transfer */ + DC395x_write8(TRM_S1040_SCSI_OFFSET, 0x00); + /* Turn LED control off*/ + wval = DC395x_read16 (TRM_S1040_GEN_CONTROL) & 0x7F; + DC395x_write16(TRM_S1040_GEN_CONTROL, wval); + /* DMA config */ + wval = DC395x_read16(TRM_S1040_DMA_CONFIG) & ~DMA_FIFO_CTRL; + wval |= DMA_FIFO_HALF_HALF | DMA_ENHANCE /*| DMA_MEM_MULTI_READ*/; + //printk ("DC395: DMA_Config: %04x\n", wval); + DC395x_write16(TRM_S1040_DMA_CONFIG,wval); + /* Clear pending interrupt status*/ + DC395x_read8(TRM_S1040_SCSI_INTSTATUS); + /* Enable SCSI interrupt */ + DC395x_write8(TRM_S1040_SCSI_INTEN,0x7F); + DC395x_write8(TRM_S1040_DMA_INTEN, EN_SCSIINTR | EN_DMAXFERERROR /*| EN_DMAXFERABORT | EN_DMAXFERCOMP | EN_FORCEDMACOMP */); +} + +/* +********************************************************************* +** scsiio +** DC395x_Interrupt +** +********************************************************************* +*/ +static void DC395x_ScsiRstDetect( PACB pACB ) +{ + printk (KERN_INFO "DC395x_ScsiRstDetect\n"); + /* delay half a second */ + if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer); + + DC395x_write8(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + DC395x_write8(TRM_S1040_DMA_CONTROL, DMARESETMODULE); + //DC395x_write8(TRM_S1040_DMA_CONTROL,STOPDMAXFER); + udelay (500); + // Maybe we locked up the bus? Then lets wait even longer ... + pACB->pScsiHost->last_reset = jiffies + 5*HZ/2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + + DC395x_clrfifo (pACB, "RstDet"); + DC395x_basic_config (pACB); + //1.25 + //DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); + + if( pACB->ACBFlag & RESET_DEV ) /* RESET_DETECT, RESET_DONE, RESET_DEV */ + { + pACB->ACBFlag |= RESET_DONE; + } + else + { + pACB->ACBFlag |= RESET_DETECT; + DC395x_ResetDevParam( pACB ); + DC395x_DoingSRB_Done( pACB, DID_RESET ); + //DC395x_RecoverSRB( pACB ); + pACB->pActiveDCB = NULL; + pACB->ACBFlag = 0; + DC395x_Waiting_process( pACB ); + } + + return; +} + +/* +********************************************************************* +** scsiio +** DC395x_SRBdone +** +********************************************************************* +*/ +static void DC395x_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ) +{ + PSCSICMD pcmd; + + pcmd = pSRB->pcmd; +#ifdef DC395x_DEBUG_KG + printk(KERN_INFO "DC395x_RequestSense for pid %li, target %02i-%i\n", + pcmd->pid, pcmd->target, pcmd->lun); +#endif + TRACEPRINTF ("RqSn*"); + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + + /* KG: Can this prevent crap sense data ? */ + memset (pcmd->sense_buffer, 0, sizeof(pcmd->sense_buffer)); + + /* Save some data */ + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY-1].address = pSRB->SegmentX[0].address; + pSRB->SegmentX[DC395x_MAX_SG_LISTENTRY-1].length = pSRB->SegmentX[0].length; + pSRB->Xferred = pSRB->SRBTotalXferLength; + /* pSRB->SegmentX : a one entry of S/G list table */ + pSRB->SRBTotalXferLength = sizeof(pcmd->sense_buffer); + pSRB->SegmentX[0].length = sizeof(pcmd->sense_buffer); + /* Map sense buffer */ + pSRB->SegmentX[0].address = PCI_MAP_SINGLE(pACB->pdev, pcmd->sense_buffer, + sizeof(pcmd->sense_buffer), PCI_DMA_FROMDEVICE); +#ifdef DC395x_SGPARANOIA + printk ("DC395x: Map sense buffer at %p (%05x) to %08x\n", + pcmd->sense_buffer, sizeof(pcmd->sense_buffer), + pSRB->SegmentX[0].address); +#endif + pSRB->SRBSGCount = 1; + pSRB->SRBSGIndex = 0; + + if( DC395x_StartSCSI( pACB, pDCB, pSRB ) ) + { /* Should only happen, if sb. else grabs the bus */ + printk ("DC395x: Request Sense failed for pid %li (%02i-%i)!\n", + pSRB->pcmd->pid, pDCB->TargetID, pDCB->TargetLUN); + TRACEPRINTF ("?*"); + DC395x_Going_to_Waiting (pDCB, pSRB); + DC395x_waiting_timer (pACB, HZ/100); + } + TRACEPRINTF (".*"); +} + +#if 0 +/* +********************************************************************* +** scsiio +** DC395x_MsgInPhase0 DC395x_EnableMsgOutAbort1 +** +********************************************************************* +*/ +static void DC395x_EnableMsgOutAbort2( PACB pACB, PSRB pSRB ) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_EnableMsgOutAbort2..............\n "); +#endif + pSRB->MsgCnt = 1; + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_SETATN); +} + +/* +********************************************************************* +** scsiio +** DC395x_Reselect DC395x_Interrupt DC395x_MsgInPhase0 +** +********************************************************************* +*/ +static inline void DC395x_EnableMsgOutAbort1( PACB pACB, PSRB pSRB ) +{ +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_EnableMsgOutAbort1..............\n "); +#endif + pSRB->MsgOutBuf[0] = MSG_ABORT; + DC395x_EnableMsgOutAbort2( pACB, pSRB ); +} +#endif + +/* +********************************************************************** +** DC395x_queue_command +** +** Function : void DC395x_initDCB +** Purpose : initialize the internal structures for a given DCB +** Inputs : cmd - pointer to this scsi cmd request block structure +** +********************************************************************** +*/ +void DC395x_initDCB( PACB pACB, PDCB *ppDCB, BYTE target, BYTE lun) +{ + PNVRAMTYPE pEEpromBuf; + BYTE PeriodIndex; + WORD index; + PDCB pDCB, pDCB2; + +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x_initDCB..............\n "); +#endif + pDCB = kmalloc (sizeof(DC395X_TRM_DCB), GFP_ATOMIC); + //pDCB = DC395x_findDCB (pACB, target, lun); + *ppDCB = pDCB; pDCB2 = 0; + if (!pDCB) return; + + if( pACB->DCBCnt == 0 ) + { + pACB->pLinkDCB = pDCB; + pACB->pDCBRunRobin = pDCB; + } + else + { + pACB->pLastDCB->pNextDCB = pDCB; + }; + + pACB->DCBCnt++; + pDCB->pNextDCB = pACB->pLinkDCB; + pACB->pLastDCB = pDCB; + + /* $$$$$$$ */ + pDCB->pDCBACB = pACB; + pDCB->TargetID = target; + pDCB->TargetLUN = lun; + /* $$$$$$$ */ + pDCB->pWaitingSRB = NULL; + pDCB->pGoingSRB = NULL; + pDCB->GoingSRBCnt = 0; + pDCB->WaitSRBCnt = 0; + pDCB->pActiveSRB = NULL; + /* $$$$$$$ */ + pDCB->TagMask = 0; + pDCB->DCBFlag = 0; + pDCB->MaxCommand = 1; + pDCB->AdaptIndex = pACB->AdapterIndex; + /* $$$$$$$ */ + index = pACB->AdapterIndex; + pEEpromBuf = &dc395x_trm_eepromBuf[index]; + pDCB->DevMode = pEEpromBuf->NvramTarget[target].NvmTarCfg0; + //pDCB->AdpMode = pEEpromBuf->NvramChannelCfg; + pDCB->Inquiry7 = 0; + pDCB->SyncMode = 0; + pDCB->last_derated = pACB->pScsiHost->last_reset - 2; + /* $$$$$$$ */ + pDCB->SyncPeriod = 0; + pDCB->SyncOffset = 0; + PeriodIndex = pEEpromBuf->NvramTarget[target].NvmTarPeriod & 0x07; + pDCB->MinNegoPeriod = dc395x_clock_period[ PeriodIndex ] ; + +#ifndef DC395x_NO_WIDE + if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) && (pACB->Config & HCC_WIDE_CARD)) + pDCB->SyncMode |= WIDE_NEGO_ENABLE; +#endif +#ifndef DC395x_NO_SYNC + if (pDCB->DevMode & NTC_DO_SYNC_NEGO) + if( !(lun) || DC395x_CurrSyncOffset ) + pDCB->SyncMode |= SYNC_NEGO_ENABLE; +#endif + /* $$$$$$$ */ +#ifndef DC395x_NO_DISCONNECT + pDCB->IdentifyMsg = IDENTIFY(pDCB->DevMode & NTC_DO_DISCONNECT, lun); +#else + pDCB->IdentifyMsg = IDENTIFY(0, lun); +#endif + /* $$$$$$$ */ + if (pDCB->TargetLUN != 0) + { + /* Copy settings */ + PDCB prevDCB = pACB->pLinkDCB; + while (prevDCB->TargetID != pDCB->TargetID) + prevDCB = prevDCB->pNextDCB; +#ifdef DC395x_DEBUG_KG + printk ("DC395x: Copy settings from %02i-%02i to %02i-%02i\n", + prevDCB->TargetID, prevDCB->TargetLUN, + pDCB->TargetID, pDCB->TargetLUN); +#endif + pDCB->SyncMode = prevDCB->SyncMode; + pDCB->SyncPeriod = prevDCB->SyncPeriod; + pDCB->MinNegoPeriod = prevDCB->MinNegoPeriod; + pDCB->SyncOffset = prevDCB->SyncOffset; + pDCB->Inquiry7 = prevDCB->Inquiry7; + }; + + pACB->DCBmap[target] |= (1 << lun); +} + + +/* Dynamically allocated memory handling */ + +#ifdef DC395x_DEBUGTRACE +/* Memory for trace buffers */ +void DC395x_free_tracebufs (PACB pACB, int SRBIdx) +{ + int srbidx; + const unsigned bufs_per_page = PAGE_SIZE/DEBUGTRACEBUFSZ; + for (srbidx = 0; srbidx < SRBIdx; srbidx += bufs_per_page) { + //printk ("DC395x: Free tracebuf %p (for %i)\n", + // pACB->SRB_array[srbidx].debugtrace, srbidx); + kfree (pACB->SRB_array[srbidx].debugtrace); + } +} + +int DC395x_alloc_tracebufs (PACB pACB) +{ + const unsigned mem_needed = (DC395x_MAX_SRB_CNT + 1) * DEBUGTRACEBUFSZ; + int pages = (mem_needed + (PAGE_SIZE-1)) / PAGE_SIZE; + const unsigned bufs_per_page = PAGE_SIZE/DEBUGTRACEBUFSZ; + int SRBIdx = 0; unsigned i = 0; + unsigned char *ptr; + //printk ("DC395x: Alloc %i pages for tracebufs\n", pages); + while (pages--) { + ptr = kmalloc (PAGE_SIZE, GFP_KERNEL); + if (!ptr) { + DC395x_free_tracebufs (pACB, SRBIdx); + return 1; + } + //printk ("DC395x: Alloc %li bytes at %p for tracebuf %i\n", + // PAGE_SIZE, ptr, SRBIdx); + i = 0; + while (i < bufs_per_page && SRBIdx < DC395x_MAX_SRB_CNT) + pACB->SRB_array[SRBIdx++].debugtrace + = ptr + (i++*DEBUGTRACEBUFSZ); + } + if (i < bufs_per_page) + pACB->TmpSRB.debugtrace = ptr + (i*DEBUGTRACEBUFSZ); + else + printk ("DC395x: No space for tmpSRB tracebuf reserved?!\n"); + return 0; +} +#endif + +/* Free SG tables */ +void DC395x_free_SG_tables (PACB pACB, int SRBIdx) +{ + int srbidx; + const unsigned SRBs_per_page = PAGE_SIZE/(DC395x_MAX_SG_LISTENTRY * sizeof(SGentry)); + for (srbidx = 0; srbidx < SRBIdx; srbidx += SRBs_per_page) { + //printk ("DC395x: Free SG segs %p (for %i)\n", + // pACB->SRB_array[srbidx].SegmentX, srbidx); + kfree (pACB->SRB_array[srbidx].SegmentX); + } +} + +/* Allocate SG tables; as we have to pci_map them, an SG list (PSGE0) + * should never cross a page boundary */ +int DC395x_alloc_SG_tables (PACB pACB) +{ + const unsigned mem_needed = (DC395x_MAX_SRB_CNT + 1) + * DC395x_MAX_SG_LISTENTRY * sizeof(SGentry); + int pages = (mem_needed + (PAGE_SIZE-1)) / PAGE_SIZE; + const unsigned SRBs_per_page = PAGE_SIZE + / (DC395x_MAX_SG_LISTENTRY * sizeof(SGentry)); + int SRBIdx = 0; unsigned i = 0; + PSGE0 ptr; + //printk ("DC395x: Alloc %i pages for SG tables\n", pages); + while (pages--) { + ptr = (PSGE0)kmalloc (PAGE_SIZE, GFP_KERNEL); + if (!ptr) { + DC395x_free_SG_tables (pACB, SRBIdx); + return 1; + } + //printk ("DC395x: Alloc %li bytes at %p for SG segments %i\n", + // PAGE_SIZE, ptr, SRBIdx); + i = 0; + while (i < SRBs_per_page && SRBIdx < DC395x_MAX_SRB_CNT) + pACB->SRB_array[SRBIdx++].SegmentX + = ptr + (i++*DC395x_MAX_SG_LISTENTRY); + } + if (i < SRBs_per_page) + pACB->TmpSRB.SegmentX = ptr + (i*DC395x_MAX_SG_LISTENTRY); + else + printk ("DC395x: No space for tmpSRB SG table reserved?!\n"); + return 0; +} + + +/* +********************************************************************* +** scsiio +** DC395x_initACB +** +********************************************************************* +*/ +void __init DC395x_linkSRB( PACB pACB ) +{ + int i; + + for( i=0; i < pACB->SRBCount - 1; i++) + pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1]; + pACB->SRB_array[i].pNextSRB = NULL; + //DC395x_Free_integrity (pACB); +} + + +/* +************************************************************************ +** DC395x_init +** +** Function : static void DC395x_initACB +** Purpose : initialize the internal structures for a given SCSI host +** Inputs : psh - pointer to this host adapter's structure +** +************************************************************************ +*/ +int __init DC395x_initACB( PSH psh, DWORD io_port, BYTE Irq, WORD index ) +{ + PNVRAMTYPE pEEpromBuf; + PACB pACB; + WORD i; + + pEEpromBuf = &dc395x_trm_eepromBuf[index]; +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30) + psh->max_cmd_len = 24; +#endif + psh->can_queue = DC395x_MAX_CMD_QUEUE; + psh->cmd_per_lun = DC395x_MAX_CMD_PER_LUN; + psh->this_id = (int) pEEpromBuf->NvramScsiId; + psh->io_port = io_port; + psh->n_io_port = 0x80; + psh->dma_channel = -1; + psh->unique_id = io_port; + psh->irq = Irq; + psh->last_reset = jiffies; + + pACB = (PACB) psh->hostdata; + + psh->max_id = 16; + if( psh->max_id - 1 == pEEpromBuf->NvramScsiId ) + psh->max_id--; +#ifdef CONFIG_SCSI_MULTI_LUN + if( pEEpromBuf->NvramChannelCfg & NAC_SCANLUN ) + psh->max_lun =8; + else + psh->max_lun =1; +#else + psh->max_lun =1; +#endif + /* + ******************************** + */ + pACB->pScsiHost = psh; + pACB->IOPortBase = (WORD) io_port; + pACB->pLinkDCB = NULL; + pACB->pDCBRunRobin = NULL; + pACB->pActiveDCB = NULL; + pACB->SRBCount = DC395x_MAX_SRB_CNT; + pACB->AdapterIndex = index; + pACB->status = 0; + pACB->pScsiHost->this_id = pEEpromBuf->NvramScsiId; + pACB->HostID_Bit = (1 << pACB->pScsiHost->this_id); + //pACB->pScsiHost->this_lun = 0; + pACB->DCBCnt = 0; pACB->DeviceCnt = 0; + pACB->IRQLevel = Irq; + pACB->TagMaxNum = 1 << pEEpromBuf->NvramMaxTag; + if (pACB->TagMaxNum > 30) pACB->TagMaxNum = 30; + pACB->ACBFlag = 0; /* RESET_DETECT, RESET_DONE, RESET_DEV */ + pACB->scan_devices = 1; + pACB->MsgLen = 0; + pACB->Gmode2 = pEEpromBuf->NvramChannelCfg; + if( pEEpromBuf->NvramChannelCfg & NAC_SCANLUN ) + pACB->LUNchk = 1; + /* + ** link all device's SRB Q of this adapter + */ + + if (DC395x_alloc_SG_tables (pACB)) { + printk ("DC395x: SG table allocation failed!\n"); + return 1; + } +#ifdef DC395x_DEBUGTRACE + if (DC395x_alloc_tracebufs (pACB)) { + printk ("DC395x: SG trace buffer allocation failed!\n"); + DC395x_free_SG_tables (pACB, DC395x_MAX_SRB_CNT); + return 1; + } +#endif + DC395x_linkSRB( pACB ); + pACB->pFreeSRB = pACB->SRB_array; + /* + ** temp SRB for Q tag used or abort command used + */ + pACB->pTmpSRB = &pACB->TmpSRB; + pACB->TmpSRB.pSRBDCB = 0; + pACB->TmpSRB.pNextSRB = 0; + init_timer (&pACB->Waiting_Timer); + + for (i = 0; i < DC395x_MAX_SCSI_ID; i++) + pACB->DCBmap[i] = 0; +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x: pACB = %p, pDCBmap = %p, pSRB_array = %p\n", + pACB, pACB->DCBmap, pACB->SRB_array); + printk(KERN_INFO "DC395x: ACB size= %04lx, DCB size= %04lx, SRB size= %04lx\n", + sizeof(DC395X_TRM_ACB), sizeof(DC395X_TRM_DCB), sizeof(DC395X_TRM_SRB) ); +#endif + return 0; +} + + +/* +********************************************************************** +** +** DC395x_init +** +** Function : static int DC395x_initAdapter +** Purpose : initialize the SCSI chip ctrl registers +** Inputs : psh - pointer to this host adapter's structure +** +********************************************************************** +*/ +int __init DC395x_initAdapter( PSH psh, DWORD io_port, BYTE Irq, WORD index ) +{ + PNVRAMTYPE pEEpromBuf; + PACB pACB, pTempACB; + WORD used_irq = 0; + + pEEpromBuf = &dc395x_trm_eepromBuf[index]; + pTempACB = DC395x_pACB_start; + if( pTempACB != NULL ) + { + for ( ; (pTempACB != (PACB) -1) ; ) + { + if( pTempACB->IRQLevel == Irq ) + { + used_irq = 1; + break; + } + else + pTempACB = pTempACB->pNextACB; + } + } + if (check_region (io_port, psh->n_io_port)) + { + printk(KERN_ERR "DC395x: register IO ports error!\n"); + return( -1 ); + } + else + { + /* %%%%%%%%%%%%% */ + request_region(io_port,psh->n_io_port,"DC395x_TRM"); + /* %%%%%%%%%%%%% */ + } + + if( !used_irq ) + { + /* + ** If request_irq() fails with the SA_INTERRUPT flag set, + ** then try again without the SA_INTERRUPT flag set. This + ** allows IRQ sharing to work even with other drivers that + ** do not set the SA_INTERRUPT flag. + ** + ** If SA_INTERRUPT is not set, then interrupts are enabled + ** before the driver interrupt function is called. + */ + + if( request_irq(Irq, DC395x_Interrupt, SA_SHIRQ, "DC395x_TRM", (void*)psh->hostdata) ) + { + printk(KERN_INFO "DC395x: register IRQ error!\n"); + return( -1 ); + } + } + pACB = (PACB) psh->hostdata; + pACB->IOPortBase = io_port; + /* selection timeout = 250 ms */ + pACB->sel_timeout = DC395x_SEL_TIMEOUT; + /* Mask all the interrupt */ + DC395x_write8(TRM_S1040_DMA_INTEN,0x00); + DC395x_write8(TRM_S1040_SCSI_INTEN,0x00); + /* Reset SCSI module */ + DC395x_write16(TRM_S1040_SCSI_CONTROL, DO_RSTMODULE); + /* Reset PCI/DMA module */ + DC395x_write8 (TRM_S1040_DMA_CONTROL, DMARESETMODULE); + udelay (20); + /* program configuration 0 */ + pACB->Config = HCC_AUTOTERM | HCC_PARITY; + if ( DC395x_read8(TRM_S1040_GEN_STATUS) & WIDESCSI ) + pACB->Config |= HCC_WIDE_CARD; + if (pEEpromBuf->NvramChannelCfg & NAC_POWERON_SCSI_RESET) + pACB->Config |= HCC_SCSI_RESET; + if (pACB->Config & HCC_SCSI_RESET) + { + printk (KERN_INFO "DC395: Performing initial SCSI bus reset\n"); + DC395x_write8 (TRM_S1040_SCSI_CONTROL, DO_RSTSCSI); + //while (!( DC395x_read8(TRM_S1040_SCSI_INTSTATUS) & INT_SCSIRESET )); + //spin_unlock_irq (&io_request_lock); + udelay (500); + pACB->pScsiHost->last_reset = jiffies + HZ/2 + + HZ * dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime; + //spin_lock_irq (&io_request_lock); + } + DC395x_basic_config (pACB); + return(0); +} +/* +************************************************************************ +** DC395x_check_eeprom +** +**---------------------------------------------------------------------- +** Function : TRM_S1040_write_all +** Description : write pEEpromBuf 128 bytes to seeprom +** Input : scsiIOPort - chip's base address +** Output : none +************************************************************************ +*/ +static void __init TRM_S1040_write_all(PNVRAMTYPE pEEpromBuf,WORD scsiIOPort) +{ + BYTE *bpEeprom = (BYTE *) pEEpromBuf; + BYTE bAddr; + + /* Enable SEEPROM */ + outb( (inb(scsiIOPort+TRM_S1040_GEN_CONTROL) | EN_EEPROM) , scsiIOPort+TRM_S1040_GEN_CONTROL); + /* + ** Write enable + */ + TRM_S1040_write_cmd(scsiIOPort, 0x04, 0xFF); + outb( 0 , scsiIOPort+TRM_S1040_GEN_NVRAM ); + TRM_S1040_wait_30us(scsiIOPort); + for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) + { + TRM_S1040_set_data(scsiIOPort, bAddr, *bpEeprom); + } + /* + ** Write disable + */ + TRM_S1040_write_cmd(scsiIOPort, 0x04, 0x00); + outb( 0 , scsiIOPort+TRM_S1040_GEN_NVRAM ); + TRM_S1040_wait_30us(scsiIOPort); + /* Disable SEEPROM */ + outb( (inb(scsiIOPort+TRM_S1040_GEN_CONTROL) & ~EN_EEPROM) , scsiIOPort+TRM_S1040_GEN_CONTROL); + return; +} +/* +*********************************************************************** +** TRM_S1040_write_all +** +**--------------------------------------------------------------------- +** Function : TRM_S1040_set_data +** Description : write one byte to seeprom +** Input : scsiIOPort - chip's base address +** bAddr - address of SEEPROM +** bData - data of SEEPROM +** Output : none +*********************************************************************** +*/ +static void __init TRM_S1040_set_data(WORD scsiIOPort, BYTE bAddr, BYTE bData) +{ + int i; + BYTE bSendData; + /* + ** Send write command & address + */ + TRM_S1040_write_cmd(scsiIOPort, 0x05, bAddr); + /* + ** Write data + */ + for (i = 0; i < 8; i++, bData <<= 1) + { + bSendData = NVR_SELECT; + if (bData & 0x80) /* Start from bit 7 */ + bSendData |= NVR_BITOUT; + + outb( bSendData , scsiIOPort+TRM_S1040_GEN_NVRAM ); + TRM_S1040_wait_30us(scsiIOPort); + outb( (bSendData | NVR_CLOCK) , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + } + outb( NVR_SELECT , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + /* + ** Disable chip select + */ + outb( 0 , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + outb( NVR_SELECT ,scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + /* + ** Wait for write ready + */ + while (1) + { + outb( (NVR_SELECT | NVR_CLOCK) , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + outb( NVR_SELECT , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + if (inb(scsiIOPort+TRM_S1040_GEN_NVRAM) & NVR_BITIN) + break; + } + /* + ** Disable chip select + */ + outb( 0 , scsiIOPort+TRM_S1040_GEN_NVRAM); + return; +} +/* +************************************************************************ +** DC395x_check_eeprom +** +**---------------------------------------------------------------------- +** Function : TRM_S1040_read_all +** Description : read seeprom 128 bytes to pEEpromBuf +** Input : pEEpromBuf,scsiIOPort - chip's base address +** Output : none +************************************************************************ +*/ +static void __init TRM_S1040_read_all(PNVRAMTYPE pEEpromBuf, WORD scsiIOPort) +{ + BYTE *bpEeprom = (BYTE *) pEEpromBuf; + BYTE bAddr; + + /* + ** Enable SEEPROM + */ + outb( (inb(scsiIOPort+TRM_S1040_GEN_CONTROL) | EN_EEPROM) , scsiIOPort+TRM_S1040_GEN_CONTROL); + for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) + { + *bpEeprom = TRM_S1040_get_data(scsiIOPort, bAddr); + } + /* + ** Disable SEEPROM + */ + outb( (inb(scsiIOPort+TRM_S1040_GEN_CONTROL) & ~EN_EEPROM) , scsiIOPort+TRM_S1040_GEN_CONTROL ); + return; +} +/* +************************************************************************ +** TRM_S1040_read_all +** +**---------------------------------------------------------------------- +** Function : TRM_S1040_get_data +** Description : read one byte from seeprom +** Input : scsiIOPort - chip's base address +** bAddr - address of SEEPROM +** Output : bData - data of SEEPROM +************************************************************************ +*/ +static BYTE __init TRM_S1040_get_data(WORD scsiIOPort, BYTE bAddr) +{ + int i; + BYTE bReadData, bData = 0; + /* + ** Send read command & address + */ + TRM_S1040_write_cmd(scsiIOPort, 0x06, bAddr); + + for (i = 0; i < 8; i++) + { /* + ** Read data + */ + outb( (NVR_SELECT | NVR_CLOCK) , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + outb( NVR_SELECT , scsiIOPort+TRM_S1040_GEN_NVRAM); + /* + ** Get data bit while falling edge + */ + bReadData = inb(scsiIOPort+TRM_S1040_GEN_NVRAM); + bData <<= 1; + if (bReadData & NVR_BITIN) + bData |= 1; + + TRM_S1040_wait_30us(scsiIOPort); + } + /* + ** Disable chip select + */ + outb( 0 , scsiIOPort+TRM_S1040_GEN_NVRAM); + return (bData); +} +/* +************************************************************************ +** +** Function : TRM_S1040_wait_30us +** Description : wait 30 us +** Input : scsiIOPort - chip's base address +** Output : none +************************************************************************ +*/ +static void __init TRM_S1040_wait_30us(WORD scsiIOPort) +{ + /* ScsiPortStallExecution(30); wait 30 us */ + outb( 5 , scsiIOPort+TRM_S1040_GEN_TIMER); + while (!(inb(scsiIOPort+TRM_S1040_GEN_STATUS) & GTIMEOUT)); + return; +} +/* +************************************************************************ +** TRM_S1040_get_data TRM_S1040_write_all +** TRM_S1040_set_data +**---------------------------------------------------------------------- +** Function : TRM_S1040_write_cmd +** Description : write SB and Op Code into seeprom +** Input : scsiIOPort ... chip's base address +** bCmd ... SB + Op Code +** bAddr ... address of SEEPROM +** Output : none +************************************************************************ +*/ +static void __init TRM_S1040_write_cmd(WORD scsiIOPort, BYTE bCmd, BYTE bAddr) +{ + int i; + BYTE bSendData; + + + for (i = 0; i < 3; i++, bCmd <<= 1) + { /* + ** Program SB+OP code + */ + bSendData = NVR_SELECT; + if (bCmd & 0x04) /* Start from bit 2 */ + bSendData |= NVR_BITOUT; + + outb( bSendData , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + outb( (bSendData | NVR_CLOCK) , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + } + + for (i = 0; i < 7; i++, bAddr <<= 1) + { /* + ** Program address + */ + bSendData = NVR_SELECT; + if (bAddr & 0x40) /* Start from bit 6 */ + bSendData |= NVR_BITOUT; + + outb( bSendData , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + outb( (bSendData | NVR_CLOCK) , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); + } + outb( NVR_SELECT , scsiIOPort+TRM_S1040_GEN_NVRAM); + TRM_S1040_wait_30us(scsiIOPort); +} + +/* +************************************************************************ +** DC395x_init +** +**---------------------------------------------------------------------- +** Function : DC395x_check_eeprom +** Description : read seeprom 128 bytes to pEEpromBuf and check +** checksum. If it is worong, updated with default value +** Input : eeprom,scsiIOPort - chip's base address +** Output : none +************************************************************************ +*/ +static void __init DC395x_check_eeprom(PNVRAMTYPE pEEpromBuf, WORD scsiIOPort) +{ + WORD *wpEeprom = (WORD *) pEEpromBuf; + WORD wAddr, wCheckSum; + DWORD dAddr, *dpEeprom; + + TRM_S1040_read_all(pEEpromBuf,scsiIOPort); + wCheckSum = 0; + for (wAddr = 0, wpEeprom = (WORD *) pEEpromBuf; wAddr < 64; wAddr++, wpEeprom++) + wCheckSum += *wpEeprom; + + if (wCheckSum != 0x1234) + { /* + ** Checksum error, load default + */ + printk ("DC395x: EEProm ChkSum error: Use defaults/options!\n"); + pEEpromBuf->NvramSubVendorID[0] = (BYTE) PCI_VendorID_TEKRAM; + pEEpromBuf->NvramSubVendorID[1] = (BYTE) (PCI_VendorID_TEKRAM >> 8); + pEEpromBuf->NvramSubSysID[0] = (BYTE) PCI_DeviceID_TRMS1040; + pEEpromBuf->NvramSubSysID[1] = (BYTE) (PCI_DeviceID_TRMS1040 >> 8); + pEEpromBuf->NvramSubClass = 0x00; + pEEpromBuf->NvramVendorID[0] = (BYTE) PCI_VendorID_TEKRAM; + pEEpromBuf->NvramVendorID[1] = (BYTE) (PCI_VendorID_TEKRAM >> 8); + pEEpromBuf->NvramDeviceID[0] = (BYTE) PCI_DeviceID_TRMS1040; + pEEpromBuf->NvramDeviceID[1] = (BYTE) (PCI_DeviceID_TRMS1040 >> 8); + pEEpromBuf->NvramReserved = 0x00; + + for (dAddr = 0, dpEeprom = (DWORD *) pEEpromBuf->NvramTarget; dAddr < 16; dAddr++, dpEeprom++) + *dpEeprom = 0x00000077;/* NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 */ + + *dpEeprom++ = 0x04000F07;/* NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId */ + *dpEeprom++ = 0x00000015;/* NvramReserved1,NvramBootLun ,NvramBootTarget,NvramReserved0 */ + for (dAddr = 0; dAddr < 12; dAddr++, dpEeprom++) + *dpEeprom = 0x00; + + /* Now load defaults (maybe set by boot/module params) */ + DC395x_check_for_safe_settings (); + DC395x_fill_with_defaults (); + DC395x_EEprom_Override (pEEpromBuf); + + pEEpromBuf->NvramCheckSum = 0x00; + for (wAddr = 0, wCheckSum = 0, wpEeprom = (WORD *) pEEpromBuf; wAddr < 63; wAddr++, wpEeprom++) + wCheckSum += *wpEeprom; + + *wpEeprom = 0x1234 - wCheckSum; + TRM_S1040_write_all(pEEpromBuf,scsiIOPort); + pEEpromBuf->NvramDelayTime = dc395x_trm[5]; + } + else + { + DC395x_check_for_safe_settings (); + DC395x_interpret_delay (pEEpromBuf); + DC395x_EEprom_Override (pEEpromBuf); + } + return; +} + +/* Print connector and termination config */ +static void __init DC395x_print_config (PACB pACB) +{ + BYTE bval; + + bval = DC395x_read8(TRM_S1040_GEN_STATUS); + printk ("DC395%c: Connectors: ", + ((bval & WIDESCSI)? 'W': ' ')); + if (!(bval & CON5068)) { + printk ("ext"); + if (!(bval & EXT68HIGH)) printk ("68 "); + else printk ("50 "); + } + if (!(bval & CON68)) { + printk ("int68"); + if (!(bval & INT68HIGH)) printk (" "); + else printk ("(50) "); + } + if (!(bval & CON50)) + printk ("int50 "); + if ((bval & (CON5068 | CON50 | CON68)) == 0 /*(CON5068 | CON50 | CON68)*/) + printk (" Oops! (All 3?) "); + bval = DC395x_read8(TRM_S1040_GEN_CONTROL); + printk (" Termination: "); + if (bval & DIS_TERM) + printk ("Disabled\n"); + else { + if (bval & AUTOTERM) + printk ("Auto "); + if (bval & LOW8TERM) + printk ("Low "); + if (bval & UP8TERM) + printk ("High "); + printk ("\n"); + } +} + +/* +********************************************************************** +** +** DC395x_detect +** +** Function : static int DC395x_init (struct Scsi_Host *host) +** Purpose : initialize the internal structures for a given SCSI host +** Inputs : host - pointer to this host adapter's structure/ +** Preconditions : when this function is called, the chip_type +** field of the pACB structure MUST have been set. +********************************************************************** +*/ +static PSH __init DC395x_init(PSHT psht, DWORD io_port, BYTE Irq, WORD index) +{ + PSH psh; + PACB pACB; + //DWORD acb_flags=0; + + //$$$$$$$$$$$$$$$$$$$$$$$ EEPROM CHECKSUM $$$$$$$$$$$$$$$$$$$$$$$$$ + DC395x_check_eeprom( &dc395x_trm_eepromBuf[index], (WORD) io_port); + //$$$$$$$$$$$ MEMORY ALLOCATE FOR ADAPTER CONTROL BLOCK $$$$$$$$$$$$ + psh = scsi_register( psht, sizeof(DC395X_TRM_ACB) ); + if( !psh ) + { + printk(KERN_INFO "DC395x_init : pSH scsi_register ERROR\n"); + return( 0 ); + } + printk (KERN_INFO "DC395x: Used settings: AdapterID=%02i, Speed=%i(%02i.%01iMHz), DevMode=0x%02x\n", + dc395x_trm_eepromBuf[index].NvramScsiId, + dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarPeriod, + dc395x_clock_speed[dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarPeriod]/10, + dc395x_clock_speed[dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarPeriod]%10, + dc395x_trm_eepromBuf[index].NvramTarget[0].NvmTarCfg0); + printk (KERN_INFO "DC395x: AdaptMode=0x%02x, Tags=%i(%02i), DelayReset=%is\n", + dc395x_trm_eepromBuf[index].NvramChannelCfg, + dc395x_trm_eepromBuf[index].NvramMaxTag, + 1 << dc395x_trm_eepromBuf[index].NvramMaxTag, + dc395x_trm_eepromBuf[index].NvramDelayTime); + + pACB = (PACB) psh->hostdata; + //DC395x_ACB_INITLOCK(pACB); + //DC395x_ACB_LOCK(pACB,acb_flags); + //$$$$$$$$ INITIAL ADAPTER CONTROL BLOCK $$$$$$$$$$$$ + if (DC395x_initACB( psh, io_port, Irq, index )) { + scsi_unregister (psh); + //DC395x_ACB_UNLOCK(pACB,acb_flags); + return 0; + } + DC395x_print_config (pACB); + //$$$$$$$$$$$$$$$$$ INITIAL ADAPTER $$$$$$$$$$$$$$$$$ + if( !DC395x_initAdapter( psh, io_port, Irq, index ) ) + { + if( !DC395x_pACB_start ) + { + DC395x_pACB_start = pACB; + DC395x_pACB_current = pACB; + pACB->pNextACB = (PACB) -1; + } + else + { + DC395x_pACB_current->pNextACB = pACB; + DC395x_pACB_current = pACB; + pACB->pNextACB = (PACB) -1; + } + //DC395x_ACB_UNLOCK(pACB,acb_flags); + return( psh ); + } + else + { + printk(KERN_INFO "DC395x_initAdapter initial ERROR\n"); + scsi_unregister( psh ); + //DC395x_ACB_UNLOCK(pACB,acb_flags); + return( 0 ); + } +} + +/* +********************************************************************** +** +** DC395x_set_master +** Function : +** Purpose : +** Inputs : +** Preconditions : +** +********************************************************************** +*/ +#ifndef NEW_PCI +static void __init DC395x_set_master(BYTE pci_bus,BYTE pci_device_fn) +{ + WORD cmd; + BYTE latency_timer; + + pcibios_read_config_word(pci_bus,pci_device_fn,PCI_COMMAND, &cmd); + if (! (cmd & PCI_COMMAND_MASTER)) + { + printk(KERN_INFO "PCI: Enabling bus mastering for device %02x:%02x\n",pci_bus,pci_device_fn); + cmd |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus,pci_device_fn, PCI_COMMAND, cmd); + } + pcibios_read_config_byte(pci_bus,pci_device_fn,PCI_LATENCY_TIMER, &latency_timer); + if (latency_timer < 16 /* || latency_timer == 255 */) + { + printk(KERN_INFO "PCI: Setting latency timer of device %02x:%02x from %i to 64\n", pci_bus,pci_device_fn, latency_timer); + pcibios_write_config_byte(pci_bus,pci_device_fn, PCI_LATENCY_TIMER, 64); + } +} +#endif + +#if 0 +/* +********************************************************************** +** +** DC395x_set_pci_cfg +** Function : +** Purpose : +** Inputs : +** Preconditions : +** +********************************************************************** +*/ +#ifdef NEW_PCI +static void __init DC395x_set_pci_cfg( struct pci_dev *pPCI_DEVICE ) +{ + WORD cmd, stat; + + pci_read_config_word(pPCI_DEVICE, PCI_COMMAND, &cmd); + printk ("DC395x: PCI_COMMAND: %04x", cmd); + cmd |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO | PCI_COMMAND_INVALIDATE; + pci_write_config_word(pPCI_DEVICE, PCI_COMMAND, cmd); + pci_read_config_word(pPCI_DEVICE, PCI_COMMAND, &cmd); + printk ("->%04x", cmd); + pci_read_config_word(pPCI_DEVICE, PCI_STATUS, &stat); + printk (", PCI_STATUS: %04x", stat); + stat |= (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); + pci_write_config_word(pPCI_DEVICE, PCI_STATUS, stat); + pci_read_config_word(pPCI_DEVICE, PCI_STATUS, &stat); + printk ("->%04x\n", stat); +} +#else +static void __init DC395x_set_pci_cfg(BYTE pci_bus,BYTE pci_device_fn) +{ + WORD cmd; + + pcibios_read_config_word(pci_bus, pci_device_fn,PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO; + pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, cmd); + pcibios_write_config_word(pci_bus, pci_device_fn, PCI_STATUS,(PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY) ); +} +#endif +#endif + +/* +********************************************************************** +** +** SCSI driver entry of Linux +** +** Function : int DC395x_detect(Scsi_Host_Template *psht) +** Purpose : detects and initializes TRM S1040 SCSI chips +** that were autoprobed, overridden on the LILO command line, +** or specified at compile time. +** Inputs : psht - template for this SCSI adapter +** Returns : number of host adapters detected +** +** There is one pci_dev structure for each slot-number/function-number +** combination: +** +** struct pci_dev { +** struct pci_bus *bus; // bus this device is on +** struct pci_dev *sibling; // next device on this bus +** struct pci_dev *next; // chain of all devices +** +** void *sysdata; // hook for sys-specific extension +** struct proc_dir_entry *procent; // device entry in /proc/bus/pci +** +** unsigned int devfn; // encoded device & function index +** unsigned short vendor; +** unsigned short device; +** unsigned int class; // 3 bytes: (base,sub,prog-if) +** unsigned int hdr_type; // PCI header type +** unsigned int master : 1; // set if device is master capable +** +** // In theory, the irq level can be read from configuration +** // space and all would be fine. However, old PCI chips don't +** // support these registers and return 0 instead. For example, +** // the Vision864-P rev 0 chip can uses INTA, but returns 0 in +** // the interrupt line and pin registers. pci_init() +** // initializes this field with the value at PCI_INTERRUPT_LINE +** // and it is the job of pcibios_fixup() to change it if +** // necessary. The field must not be 0 unless the device +** // cannot generate interrupts at all. +** +** unsigned int irq; // irq generated by this device +** +** // Base registers for this device, can be adjusted by +** // pcibios_fixup() as necessary. +** +** unsigned long base_address[6]; +** unsigned long rom_address; +**}; +** +**struct pci_bus { +** struct pci_bus *parent; // parent bus this bridge is on +** struct pci_bus *children; // chain of P2P bridges on this bus +** struct pci_bus *next; // chain of all PCI buses +** +** struct pci_dev *self; // bridge device as seen by parent +** struct pci_dev *devices; // devices behind this bridge +** +** void *sysdata; // hook for sys-specific extension +** struct proc_dir_entry *procdir; // directory entry in /proc/bus/pci +** +** unsigned char number; // bus number +** unsigned char primary; // number of primary bridge +** unsigned char secondary; // number of secondary bridge +** unsigned char subordinate; // max number of subordinate buses +**}; +********************************************************************** +*/ +int __init DC395x_detect (Scsi_Host_Template *psht) +{ +#ifdef NEW_PCI + struct pci_dev *pPCI_DEVICE = NULL; +#else + BYTE pci_bus, pci_device_fn; + WORD pci_index = 0; /* Device index to PCI BIOS calls */ + int error = 0; +#endif + + BYTE irq; + UINT io_port; + +#ifndef USE_NEW_EH + DWORD flags; + //DC395x_LOCK_IO; // Don't lock for new EH +#endif + + DC395x_pACB_start = NULL; + /* search DC395x SCSI adapter in each slot */ +#ifdef NEW_PCI + if(pci_present()) + { + printk (KERN_INFO "DC395x (TRM-S1040) SCSI driver %s\n", DC395x_VERSION); + while( (pPCI_DEVICE = pci_find_device( PCI_VendorID_TEKRAM,PCI_DeviceID_TRMS1040, pPCI_DEVICE) ) ) +#else + if(pcibios_present()) + { + printk (KERN_INFO "DC395x_TRM (TRM-S1040) driver %s\n", DC395x_VERSION); + while( !pcibios_find_device( PCI_VendorID_TEKRAM, PCI_DeviceID_TRMS1040, pci_index++, &pci_bus, &pci_device_fn) ) +#endif + { + PSH psh; + /* get adapter io_port ,irq */ + //DC395x_DRV_LOCK(drv_flags); +#ifdef NEW_PCI +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,30) + if (pci_enable_device (pPCI_DEVICE)) + continue; + io_port = pci_resource_start (pPCI_DEVICE, 0) & PCI_BASE_ADDRESS_IO_MASK; +# else + io_port = pPCI_DEVICE->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; +# endif + irq = pPCI_DEVICE->irq; +#else + error = pcibios_read_config_dword(pci_bus, pci_device_fn,PCI_BASE_ADDRESS_0, &io_port); + error |= pcibios_read_config_byte(pci_bus, pci_device_fn,PCI_INTERRUPT_LINE, &irq); + if( error ) + { + printk(KERN_INFO "DC395x/DC315x: ERROR reading PCI config registers !\n"); + continue; + } + (WORD) io_port = (WORD) io_port & PCI_BASE_ADDRESS_IO_MASK; +#endif +#ifdef DC395x_DEBUG0 + printk(KERN_INFO "DC395x: IO_PORT=%04x,IRQ=%x\n",(UINT) io_port, irq); +#endif + if( (psh = DC395x_init(psht, io_port, irq, DC395x_adapterCnt)) ) + { +#ifdef NEW_PCI + pci_set_master(pPCI_DEVICE); + ((PACB)(psh->hostdata))->pdev = pPCI_DEVICE; + //DC395x_set_pci_cfg(pPCI_DEVICE); +#else + DC395x_set_master(pci_bus,pci_device_fn); + ((PACB)(psh->hostdata))->pbus = pci_bus; + ((PACB)(psh->hostdata))->pdevfn = pci_device_fn; + //DC395x_set_pci_cfg(pci_bus,pci_device_fn); +#endif + DC395x_adapterCnt++; + } + //DC395x_DRV_UNLOCK(drv_flags); + } + } + else + { + printk (KERN_ERR "No PCI BIOS found!\n"); + } + + if(DC395x_adapterCnt) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30) + psht->proc_name = "dc395x_trm"; +#else + psht->proc_dir = &DC395x_proc_scsi; +#endif + } + printk (KERN_INFO "DC395x (TRM-S1040): %i adapters found\n", DC395x_adapterCnt); + //printk (KERN_WARNING "DC395x: *** WARNING: This driver is not completely stable! ***\n"); + +#ifndef USE_NEW_EH + //DC395x_UNLOCK_IO; +#endif + return( DC395x_adapterCnt ); +} + + +/*********************************************************************** + * Functions: DC395x_inquiry(), DC395x_inquiry_done() + * + * Purpose: When changing speed etc., we have to issue an INQUIRY + * command to make sure, we agree upon the nego parameters + * with the device + ***********************************************************************/ + +static void DC395x_inquiry_done (Scsi_Cmnd* cmd) +{ + PACB pACB = (PACB)cmd->host->hostdata; + PDCB pDCB = DC395x_findDCB (pACB, cmd->target, cmd->lun); +#ifdef DC395x_DEBUGTRACE + PSRB pSRB = pACB->pFreeSRB; +#endif + printk (KERN_INFO "DC395x: INQUIRY (%02i-%i) returned %08x: %02x %02x %02x %02x ...\n", + cmd->target, cmd->lun, cmd->result, + ((PBYTE)cmd->request_buffer)[0], ((PBYTE)cmd->request_buffer)[1], + ((PBYTE)cmd->request_buffer)[2], ((PBYTE)cmd->request_buffer)[3]); + //TRACEOUT ("%s\n", pSRB->debugtrace); + if (cmd->result) + { + printk ("DC395x: Unsetting Wide, Sync and TagQ!\n"); + if (pDCB) + { + TRACEOUT ("%s\n", pSRB->debugtrace); + pDCB->DevMode &= ~(NTC_DO_SYNC_NEGO | NTC_DO_WIDE_NEGO | NTC_DO_TAG_QUEUEING); + DC395x_updateDCB (pACB, pDCB); + }; + }; + if (pDCB) + { + if (!(pDCB->SyncMode & SYNC_NEGO_DONE)) + { pDCB->SyncOffset = 0; /*pDCB->SyncMode &= ~SYNC_NEGO_ENABLE;*/ }; + if (!(pDCB->SyncMode & WIDE_NEGO_DONE)) + { pDCB->SyncPeriod &= ~WIDE_SYNC; pDCB->SyncMode &= ~WIDE_NEGO_ENABLE; }; + } + else + { + printk ("DC395x: ERROR! No DCB existent for %02i-%i ?\n", + cmd->target, cmd->lun); + } + kfree (cmd); +}; + +/* Perform INQUIRY */ +void DC395x_inquiry (PACB pACB, PDCB pDCB) +{ + char* buffer; + Scsi_Cmnd* cmd; + cmd = kmalloc (sizeof(Scsi_Cmnd) + 256, GFP_ATOMIC); + if (!cmd) { printk ("DC395x: kmalloc failed in inquiry!\n"); return; }; + buffer = (char*)cmd + sizeof(Scsi_Cmnd); + + memset (cmd, 0, sizeof(Scsi_Cmnd) + 256); + cmd->cmnd[0] = INQUIRY; + cmd->cmnd[1] = (pDCB->TargetLUN << 5) & 0xe0; + cmd->cmnd[4] = 0xff; + + cmd->cmd_len = 6; cmd->old_cmd_len = 6; + cmd->host = pACB->pScsiHost; + cmd->target = pDCB->TargetID; + cmd->lun = pDCB->TargetLUN; + cmd->serial_number = 1; + cmd->pid = 395; + cmd->bufflen = 128; + cmd->buffer = buffer; + cmd->request_bufflen = 128; + cmd->request_buffer = &buffer[128]; + cmd->done = DC395x_inquiry_done; + cmd->scsi_done = DC395x_inquiry_done; + cmd->timeout_per_command = HZ; + + cmd->request.rq_status = RQ_SCSI_BUSY; + + pDCB->SyncMode &= ~SYNC_NEGO_DONE; pDCB->SyncMode |= SYNC_NEGO_ENABLE; + pDCB->SyncMode &= ~WIDE_NEGO_DONE; pDCB->SyncMode |= WIDE_NEGO_ENABLE; + printk (KERN_INFO "DC395x: Queue INQUIRY command to dev %02i-%i\n", + pDCB->TargetID, pDCB->TargetLUN); + DC395x_queue_command (cmd, DC395x_inquiry_done); +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +static char *DC395x_strtok_ptr; +static char* DC395x_strtok (char* init, char* sep) +{ + char* ptr; + if (init) + DC395x_strtok_ptr = init; + do { + ptr = strsep (&DC395x_strtok_ptr, sep); + } while (ptr && *ptr == 0); + return ptr; +} +# define strtok DC395x_strtok +#endif + +/* +******************************************************************* +** +** Function: DC395x_set_info() +** Purpose: Set adapter info (!) +** Strings are parsed similar to the output of tmscsim_proc_info () +** '-' means no change +********************************************************************/ + +static int DC395x_scanf (char** p1, char** p2, int* var) +{ + *p2 = *p1; + *var = simple_strtoul (*p2, p1, 10); + if (*p2 == *p1) return -1; + *p1 = strtok (0, " \t\n:=,;."); + return 0; +}; + +#define SCANF(p1, p2, var, min, max) \ +if (DC395x_scanf (&p1, &p2, &var)) goto einv; \ +else if (varmax) goto einv2 + +static int DC395x_yesno (char** p, char* var, char bmask) +{ + switch (**p) + { + case 'Y': *var |= bmask; break; + case 'N': *var &= ~bmask; break; + case '-': break; + default: return -1; + } + *p = strtok (0, " \t\n:=,;"); + return 0; +}; + +#define YESNO(p, var, bmask) \ +if (DC395x_yesno (&p, &var, bmask)) goto einv; \ +else DC395x_updateDCB (pACB, pDCB); \ +if (!p) goto ok + +static int DC395x_search (char **p1, char **p2, char *var, char* txt, int max, int scale, char* ign) +{ + int dum; + if (! memcmp (*p1, txt, strlen(txt))) + { + *p2 = strtok (0, " \t\n:=,;"); + if (!*p2) return -1; + dum = simple_strtoul (*p2, p1, 10); + if (*p2 == *p1) return -1; + if (dum >= 0 && dum <= max) + { *var = (dum * 100) / scale; } + else return -2; + *p1 = strtok (0, " \t\n:=,;"); + if (*ign && *p1 && strlen(*p1) >= strlen(ign) && + !(memcmp (*p1, ign, strlen(ign)))) + *p1 = strtok (0, " \t\n:=,;"); + + } + return 0; +}; + +#define SEARCH(p1, p2, var, txt, max) \ +if (DC395x_search (&p1, &p2, (PBYTE)(&var), txt, max, 100, "")) goto einv2; \ +else if (!p1) goto ok2 + +#define SEARCH2(p1, p2, var, txt, max, scale) \ +if (DC395x_search (&p1, &p2, &var, txt, max, scale, "")) goto einv2; \ +else if (!p1) goto ok2 + +#define SEARCH3(p1, p2, var, txt, max, scale, ign) \ +if (DC395x_search (&p1, &p2, &var, txt, max, scale, ign)) goto einv2; \ +else if (!p1) goto ok2 + + +#ifdef DC395x_PARSEDEBUG +static char _prstr[256]; +char* prstr (char* p, char* e) +{ + char* c = _prstr; + while (p < e) + if (*p == 0) { *c++ = ':'; p++; } + else if (*p == 10) { *c++ = '\\'; *c++ = 'n'; p++; } + else *c++ = *p++; + *c = 0; + return _prstr; +}; +#endif + +int DC395x_set_info(char *buffer, int length, PACB pACB) +{ + char *pos = buffer, *p0 = buffer; + char needs_inquiry = 0; + char dev; + int dum = 0; + PDCB pDCB = pACB->pLinkDCB; + unsigned long flags; + pos[length] = 0; + + DC395x_LOCK_IO(pACB->pScsiHost); + /* UPPERCASE */ + /* Don't use kernel toupper, because of 2.0.x bug: ctmp unexported */ + while (*pos) + { if (*pos >='a' && *pos <= 'z') *pos = *pos + 'A' - 'a'; pos++; }; + + /* We should protect __strtok ! */ + /* spin_lock (strtok_lock); */ + + /* Remove WS */ + pos = strtok (buffer, " \t:\n=,;"); + if (!pos) goto ok; + + next: + if (!memcmp (pos, "RESET", 5)) goto reset; + else if (!memcmp (pos, "INQUIRY", 7)) goto inquiry; + else if (!memcmp (pos, "REMOVE", 6)) goto remove; + else if (!memcmp (pos, "ADD", 3)) goto add; + else if (!memcmp (pos, "DUMP", 4)) goto dump; + + if (isdigit (*pos)) + { + /* Device config line */ + int dev, id, lun; char* pdec; + char olddevmode; + + SCANF (pos, p0, dev, 0, pACB->DCBCnt-1); + if (pos) { SCANF (pos, p0, id, 0, 15); } else goto einv; + if (pos) { SCANF (pos, p0, lun, 0, 7); } else goto einv; + if (!pos) goto einv; + + PARSEDEBUG(printk (KERN_INFO "DC395x: config line %i %i %i:\"%s\"\n", dev, id, lun, prstr (pos, &buffer[length]));) + pDCB = pACB->pLinkDCB; + for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB; + /* Sanity Check */ + if (pDCB->TargetID != id || pDCB->TargetLUN != lun) + { + printk (KERN_ERR "DC395x: no such device: Idx=%02i ID=%02i LUN=%02i\n", + dev, id, lun); + goto einv2; + }; + + /* + if (pDCB->pWaitingSRB || pDCB->pGoingSRB) + { + printk ("DC395x: Cannot change dev (%i-%i) cfg: Pending requests\n", + pDCB->TargetID, pDCB->TargetLUN); + goto einv; + }; + */ + + olddevmode = pDCB->DevMode; + YESNO (pos, pDCB->DevMode, NTC_DO_PARITY_CHK); + needs_inquiry++; + YESNO (pos, pDCB->DevMode, NTC_DO_SYNC_NEGO); + if ((olddevmode & NTC_DO_SYNC_NEGO) == (pDCB->DevMode & NTC_DO_SYNC_NEGO)) needs_inquiry--; + needs_inquiry++; + YESNO (pos, pDCB->DevMode, NTC_DO_WIDE_NEGO); + if ((olddevmode & NTC_DO_WIDE_NEGO) == (pDCB->DevMode & NTC_DO_WIDE_NEGO)) needs_inquiry--; + needs_inquiry++; + YESNO (pos, pDCB->DevMode, NTC_DO_DISCONNECT); + if ((olddevmode & NTC_DO_DISCONNECT) == (pDCB->DevMode & NTC_DO_DISCONNECT)) needs_inquiry--; + YESNO (pos, pDCB->DevMode, NTC_DO_SEND_START); + needs_inquiry++; + YESNO (pos, pDCB->DevMode, NTC_DO_TAG_QUEUEING); + if ((olddevmode & NTC_DO_TAG_QUEUEING) == (pDCB->DevMode & NTC_DO_TAG_QUEUEING)) needs_inquiry--; + + DC395x_updateDCB (pACB, pDCB); + if (!pos) goto ok; + + olddevmode = pDCB->MinNegoPeriod; + /* Look for decimal point (Speed) */ + pdec = pos; + while (pdec++ < &buffer[length]) if (*pdec == '.') break; + /* NegoPeriod */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 48, 800); + pDCB->MinNegoPeriod = dum >> 2; + if (pDCB->MinNegoPeriod != olddevmode) needs_inquiry++; + if (!pos) goto ok; + if (memcmp (pos, "NS", 2) == 0) pos = strtok (0, " \t\n:=,;."); + } + else pos = strtok (0, " \t\n:=,;."); + if (!pos) goto ok; + + /* Sync Speed in MHz */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 1, 21); + pDCB->MinNegoPeriod = (1000/dum) >> 2; + if (pDCB->MinNegoPeriod != olddevmode && !pos) needs_inquiry++; + if (!pos) goto ok; + /* decimal */ + if (pos-1 == pdec) + { + int dumold = dum; + dum = simple_strtoul (pos, &p0, 10) * 10; + for (; p0-pos > 1; p0--) dum /= 10; + pDCB->MinNegoPeriod = (100000/(100*dumold + dum)) >> 2; + if (pDCB->MinNegoPeriod < 12) pDCB->MinNegoPeriod = 12; + pos = strtok (0, " \t\n:=,;"); + if (!pos) goto ok; + }; + if (*pos == 'M') pos = strtok (0, " \t\n:=,;"); + if (pDCB->MinNegoPeriod != olddevmode) needs_inquiry++; + } + else pos = strtok (0, " \t\n:=,;"); + /* dc395x_updateDCB (pACB, pDCB); */ + if (!pos) goto ok; + + olddevmode = pDCB->SyncOffset; + /* SyncOffs */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 0, 0x0f); + pDCB->SyncOffset = dum; + if (pDCB->SyncOffset > olddevmode) needs_inquiry++; + } + else pos = strtok (0, " \t\n:=,;"); + if (!pos) goto ok; + DC395x_updateDCB (pACB, pDCB); + + //olddevmode = pDCB->MaxCommand; + /* MaxCommand (Tags) */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 1, 30 /*pACB->TagMaxNum*/); + if (pDCB->SyncMode & EN_TAG_QUEUEING) + pDCB->MaxCommand = dum; + else printk (KERN_INFO "DC395x: Can't set MaxCmd larger than one without Tag Queueing!\n"); + } + else pos = strtok (0, " \t\n:=,;"); + + } + else + { + char* p1 = pos; char newadaptid = pACB->pScsiHost->this_id; + BYTE filtercfg = DC395x_read8(TRM_S1040_SCSI_CONFIG1); + PARSEDEBUG(printk (KERN_INFO "DC395x: chg adapt cfg \"%s\"\n", prstr (pos, &buffer[length]));) + /* Adapter setting */ + SEARCH (pos, p0, pACB->pScsiHost->max_id, "MAXID", 16); + SEARCH (pos, p0, pACB->pScsiHost->max_lun, "MAXLUN", 8); + SEARCH (pos, p0, newadaptid, "ADAPTERID", 15); + SEARCH (pos, p0, pACB->TagMaxNum, "TAGMAXNUM", 30); + SEARCH (pos, p0, pACB->ACBFlag, "ACBFLAG", 255); + SEARCH (pos, p0, filtercfg, "FILTERCFG", 255); + SEARCH3 (pos, p0, pACB->sel_timeout, "SELTIMEOUT", 400, 163, "MS"); + SEARCH3 (pos, p0, dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime, "DELAYRESET", 180, 100, "S"); + ok2: + if (pACB->sel_timeout < 60) pACB->sel_timeout = 60; + DC395x_write8 (TRM_S1040_SCSI_TIMEOUT, pACB->sel_timeout); + if (newadaptid != pACB->pScsiHost->this_id) + { + pACB->pScsiHost->this_id = newadaptid; + DC395x_write8 (TRM_S1040_SCSI_HOSTID, newadaptid); + DC395x_ResetDevParam (pACB); + } + DC395x_write8 (TRM_S1040_SCSI_CONFIG1, filtercfg); + //dum = 0; while (1 << dum <= pACB->TagMaxNum) dum ++; + //pACB->TagMaxNum &= (1 << --dum); + DC395x_updateDCBs (pACB); + //We should INQUIRY all now! + //OTOH: If we changed AdaptID to a new one, we will get UNIT_ATTNETION and + // will renegotiate on AUTO_REQSENSE + if (pos == p1) goto einv; + } + if (pos) goto next; + + ok: + /* spin_unlock (strtok_lock); */ + //DC395x_ACB_UNLOCK(acb_flags); + if (needs_inquiry) + { DC395x_updateDCB (pACB, pDCB); DC395x_inquiry (pACB, pDCB); }; + DC395x_UNLOCK_IO(pACB->pScsiHost); + return (length); + + einv2: + pos = p0; + einv: + /* spin_unlock (strtok_lock); */ + //DC395x_ACB_UNLOCK(acb_flags); + DC395x_UNLOCK_IO(pACB->pScsiHost); + printk (KERN_WARNING "DC395x: parse error near \"%s\"\n", (pos? pos: "NULL")); + return (-EINVAL); + + reset: + { + Scsi_Cmnd cmd; cmd.host = pACB->pScsiHost; + printk (KERN_WARNING "DC395x: Driver reset requested!\n"); + //DC395x_ACB_UNLOCK(acb_flags); + DC395x_reset (&cmd, 0); + DC395x_UNLOCK_IO(pACB->pScsiHost); + }; + return (length); + + dump: + { + DC395x_dumpinfo (pACB, 0, 0); + DC395x_UNLOCK_IO(pACB->pScsiHost); + }; + return length; + + inquiry: + { + pos = strtok (0, " \t\n.:;="); if (!pos) goto einv; + dev = simple_strtoul (pos, &p0, 10); + if (dev >= pACB->DCBCnt) goto einv_dev; + for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB; + printk (KERN_NOTICE " DC395x: Issue INQUIRY command to Dev(Idx) %i SCSI ID %i LUN %i\n", + dev, pDCB->TargetID, pDCB->TargetLUN); + //DC395x_ACB_UNLOCK(acb_flags); + DC395x_inquiry (pACB, pDCB); + DC395x_UNLOCK_IO(pACB->pScsiHost); + }; + return (length); + + remove: + { + pos = strtok (0, " \t\n.:;="); if (!pos) goto einv; + dev = simple_strtoul (pos, &p0, 10); + if (dev >= pACB->DCBCnt) goto einv_dev; + for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB; + printk (KERN_NOTICE " DC395x: Remove DCB for Dev(Idx) %i SCSI ID %i LUN %i\n", + dev, pDCB->TargetID, pDCB->TargetLUN); + /* TO DO: We should make sure no pending commands are left */ + DC395x_remove_dev (pACB, pDCB); + //DC395x_ACB_UNLOCK(acb_flags); + DC395x_UNLOCK_IO(pACB->pScsiHost); + }; + return (length); + + add: + { + int id, lun; + pos = strtok (0, " \t\n.:;="); + if (pos) { SCANF (pos, p0, id, 0, 15); } else goto einv; + if (pos) { SCANF (pos, p0, lun, 0, 7); } else goto einv; + pDCB = DC395x_findDCB (pACB, id, lun); + if (pDCB) { printk ("DC395x: ADD: Device already existing\n"); goto einv; }; + DC395x_initDCB (pACB, &pDCB, id, lun); + //DC395x_ACB_UNLOCK(acb_flags); + DC395x_inquiry (pACB, pDCB); + DC395x_UNLOCK_IO(pACB->pScsiHost); + }; + return (length); + + einv_dev: + printk (KERN_WARNING "DC395x: Ignore cmnd to illegal Dev(Idx) %i. Valid range: 0 - %i.\n", + dev, pACB->DCBCnt - 1); + //DC395x_ACB_UNLOCK(acb_flags); + DC395x_UNLOCK_IO(pACB->pScsiHost); + return (-EINVAL); +} + +#if 0 +int DC395x_set_info(char *buffer, int length, PACB pACB) +{ + return -ENOSYS; +} +#endif + +#undef SEARCH +#undef YESNO +#undef SCANF + + +/* +******************************************************************* +** Function: DC395x_proc_info(char* buffer, char **start, +** off_t offset, int length, int hostno, int inout) +** Purpose: return SCSI Adapter/Device Info +** Input: +** buffer: Pointer to a buffer where to write info +** start : +** offset: +** hostno: Host adapter index +** inout : Read (=0) or set(!=0) info +** Output: +** buffer: contains info length +** +** return value: length of info in buffer +** +******************************************************************* +*/ + +/* KG: proc_info taken from driver aha152x.c */ + +#undef SPRINTF +#define SPRINTF(args...) pos += sprintf(pos, ## args) + +#define YESNO(YN) \ + if (YN) SPRINTF(" Yes ");\ + else SPRINTF(" No ") + +int DC395x_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout) +{ + int dev, spd, spd1; + char *pos = buffer; + PSH shpnt; + PACB pACB; + PDCB pDCB; + DWORD flags; + PSCSICMD pcmd; + + /* Scsi_Cmnd *ptr; */ + + pACB = DC395x_pACB_start; + + while(pACB != (PACB)-1) + { + shpnt = pACB->pScsiHost; + if (shpnt->host_no == hostno) + break; + pACB = pACB->pNextACB; + } + if (pACB == (PACB)-1) + return(-ESRCH); + + if(!shpnt) + return(-ESRCH); + + if(inout) /* Has data been written to the file ? */ + return(DC395x_set_info(buffer, length, pACB)); + + SPRINTF(DC395x_BANNER " PCI SCSI Host Adapter\n"); + SPRINTF(" Driver Version " DC395x_VERSION "\n"); + + DC395x_LOCK_IO(pACB->pScsiHost); + + SPRINTF("SCSI Host Nr %i, ", shpnt->host_no); + SPRINTF("DC395U/UW/F DC315/U %s Adapter Nr %i\n", + (pACB->Config & HCC_WIDE_CARD)? "Wide": "", pACB->AdapterIndex); + SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase); + SPRINTF("IRQLevel 0x%02x, ", pACB->IRQLevel); + SPRINTF(" SelTimeout %ims\n", (1638*pACB->sel_timeout)/1000); + + SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun); + SPRINTF("AdapterID %i\n", shpnt->this_id); + + SPRINTF("TagMaxNum %i, Status %i", pACB->TagMaxNum, pACB->status); + //SPRINTF(", DMA_Status %i\n", DC395x_read8(TRM_S1040_DMA_STATUS)); + SPRINTF (", FilterCfg 0x%02x", DC395x_read8(TRM_S1040_SCSI_CONFIG1)); + SPRINTF (", DelayReset %is\n", dc395x_trm_eepromBuf[pACB->AdapterIndex].NvramDelayTime); + //SPRINTF("\n"); + + SPRINTF("Nr of attached devices: %i, Nr of DCBs: %i\n", pACB->DeviceCnt, pACB->DCBCnt); + SPRINTF("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n", + pACB->DCBmap[0], pACB->DCBmap[1], pACB->DCBmap[2], pACB->DCBmap[3], + pACB->DCBmap[4], pACB->DCBmap[5], pACB->DCBmap[6], pACB->DCBmap[7]); + SPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n", + pACB->DCBmap[8], pACB->DCBmap[9], pACB->DCBmap[10], pACB->DCBmap[11], + pACB->DCBmap[12], pACB->DCBmap[13], pACB->DCBmap[14], pACB->DCBmap[15]); + + SPRINTF("Un ID LUN Prty Sync Wide DsCn SndS TagQ NegoPeriod SyncFreq SyncOffs MaxCmd\n"); + + pDCB = pACB->pLinkDCB; + for (dev = 0; dev < pACB->DCBCnt; dev++) + { + int NegoPeriod; + SPRINTF("%02i %02i %02i ", dev, pDCB->TargetID, pDCB->TargetLUN); + YESNO(pDCB->DevMode & NTC_DO_PARITY_CHK); + YESNO(pDCB->SyncOffset); + YESNO(pDCB->SyncPeriod & WIDE_SYNC ); + YESNO(pDCB->DevMode & NTC_DO_DISCONNECT); + YESNO(pDCB->DevMode & NTC_DO_SEND_START); + YESNO(pDCB->SyncMode & EN_TAG_QUEUEING); + NegoPeriod = dc395x_clock_period[pDCB->SyncPeriod & 0x07] << 2; + if (pDCB->SyncOffset) + SPRINTF(" %03i ns ", NegoPeriod); + else + SPRINTF(" (%03i ns)", (pDCB->MinNegoPeriod << 2)); + + if (pDCB->SyncOffset & 0x0f) + { + spd = 1000/(NegoPeriod); + spd1 = 1000%(NegoPeriod); + spd1 = (spd1 * 10 + NegoPeriod/2)/(NegoPeriod); + SPRINTF(" %2i.%1i M %02i ", spd, spd1, (pDCB->SyncOffset & 0x0f)); + } + else + SPRINTF(" "); + + /* Add more info ...*/ + SPRINTF (" %02i\n", pDCB->MaxCommand); + pDCB = pDCB->pNextDCB; + } + + SPRINTF ("Commands in Queues: Query: %i:", pACB->QueryCnt); + for (pcmd = pACB->pQueryHead; pcmd; pcmd = pcmd->next) + SPRINTF (" %li", pcmd->pid); + if (timer_pending(&pACB->Waiting_Timer)) SPRINTF ("Waiting queue timer running\n"); + else SPRINTF ("\n"); + pDCB = pACB->pLinkDCB; + + for (dev = 0; dev < pACB->DCBCnt; dev++) + { + PSRB pSRB; + if (pDCB->WaitSRBCnt) + SPRINTF ("DCB (%02i-%i): Waiting: %i:", pDCB->TargetID, pDCB->TargetLUN, + pDCB->WaitSRBCnt); + for (pSRB = pDCB->pWaitingSRB; pSRB; pSRB = pSRB->pNextSRB) + SPRINTF(" %li", pSRB->pcmd->pid); + if (pDCB->GoingSRBCnt) + SPRINTF ("\nDCB (%02i-%i): Going : %i:", pDCB->TargetID, pDCB->TargetLUN, + pDCB->GoingSRBCnt); + for (pSRB = pDCB->pGoingSRB; pSRB; pSRB = pSRB->pNextSRB) +#ifdef DC395x_DEBUGTRACE + SPRINTF("\n %s", pSRB->debugtrace); +#else + SPRINTF(" %li", pSRB->pcmd->pid); +#endif + if (pDCB->WaitSRBCnt || pDCB->GoingSRBCnt + ) SPRINTF ("\n"); + pDCB = pDCB->pNextDCB; + } + +#ifdef DC395x_DEBUGDCB + SPRINTF ("DCB list for ACB %p:\n", pACB); + pDCB = pACB->pLinkDCB; + SPRINTF ("%p", pDCB); + for (dev = 0; dev < pACB->DCBCnt; dev++, pDCB=pDCB->pNextDCB) + SPRINTF ("->%p", pDCB->pNextDCB); + SPRINTF("\n"); +#endif + + *start = buffer + offset; + DC395x_UNLOCK_IO(pACB->pScsiHost); + + if (pos - buffer < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + else + return length; +} + +#ifdef MODULE + +/* +********************************************************************** +** Function : int DC395x_shutdown (struct Scsi_Host *host) +** Purpose : does a clean (we hope) shutdown of the SCSI chip. +** Use prior to dumping core, unloading the driver, etc. +** Returns : 0 on success +********************************************************************** +*/ +int DC395x_shutdown (struct Scsi_Host *host) +{ + PACB pACB; + //DWORD acb_flags=0; + + pACB = (PACB)(host->hostdata); + + /* pACB->soft_reset(host); */ + /* + ** disable interrupt + */ + DC395x_write8(TRM_S1040_DMA_INTEN, 0); + DC395x_write8(TRM_S1040_SCSI_INTEN, 0); + if (timer_pending (&pACB->Waiting_Timer)) + del_timer (&pACB->Waiting_Timer); + if (timer_pending (&pACB->SelTO_Timer)) + del_timer (&pACB->SelTO_Timer); + + if (1 || pACB->Config & HCC_SCSI_RESET) + DC395x_ResetSCSIBus( pACB ); + + DC395x_read8 (TRM_S1040_SCSI_INTSTATUS); +#ifdef DC395x_DEBUGTRACE + DC395x_free_tracebufs (pACB, DC395x_MAX_SRB_CNT); +#endif + DC395x_free_SG_tables (pACB, DC395x_MAX_SRB_CNT); + return( 0 ); +} + + +/* Free all DCBs */ +void DC395x_freeDCBs (struct Scsi_Host *host) +{ + PDCB pDCB, nDCB; + PACB pACB = (PACB)(host->hostdata); + + DCBDEBUG (printk (KERN_INFO "DC395x: Free %i DCBs\n", pACB->DCBCnt);) + pDCB = pACB->pLinkDCB; + if (!pDCB) return; + do + { + nDCB = pDCB->pNextDCB; + DCBDEBUG(printk (KERN_INFO "DC395x: Free DCB (ID %i, LUN %i): %p\n",\ + pDCB->TargetID, pDCB->TargetLUN, pDCB);) + DC395x_remove_dev (pACB, pDCB); + kfree (pDCB); + printk ("."); + pDCB = nDCB; + } while (pDCB && pACB->pLinkDCB); +}; + +/* +********************************************************************* +** +** +** +********************************************************************* +*/ +int DC395x_release(struct Scsi_Host *host) +{ + int irq_count; + DWORD flags; + PACB pACB = (PACB)(host->hostdata); + //DWORD acb_flags=0; + DC395x_LOCK_IO(pACB->pScsiHost); + //DC395x_ACB_LOCK(pACB,acb_flags); + printk ("DC395x: Shutdown ."); + DC395x_shutdown (host); + DC395x_freeDCBs (host); + + if (host->irq != IRQ_NONE) + { + for (irq_count = 0, pACB = DC395x_pACB_start; + pACB != (PACB)-1; pACB = pACB->pNextACB) + { + if ( pACB->IRQLevel == host->irq ) + ++irq_count; + } + if (irq_count == 1) + free_irq(host->irq, DC395x_pACB_start); + } + release_region(host->io_port,host->n_io_port); + + DC395x_UNLOCK_IO(pACB->pScsiHost); + //DC395x_DRV_UNLOCK(drv_flags); + return( 1 ); +} +#endif /* def MODULE */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99) +static Scsi_Host_Template driver_template = DC395x_TRMS1040; +# include "scsi_module.c" +#elif defined (MODULE) +Scsi_Host_Template driver_template = DC395x_TRMS1040; +# include "scsi_module.c" +#endif diff -Nru linux/drivers/scsi/dc395x_trm.h linux-2.4.19-pre5-mjc/drivers/scsi/dc395x_trm.h --- linux/drivers/scsi/dc395x_trm.h Wed Dec 31 19:00:00 1969 +++ linux-2.4.19-pre5-mjc/drivers/scsi/dc395x_trm.h Mon Apr 8 22:36:09 2002 @@ -0,0 +1,135 @@ +/* +********************************************************************** +** File Name : dc395x_trm.h +** +** TEKRAM DC395U/UW/F ,DC315/U +** PCI SCSI Bus Master Host AdapterDevice Driver +** (SCSI chip set used Tekram ASIC TRM-S1040) +** +********************************************************************** +*/ +/* $Id: dc395x_trm.h,v 1.39 2002/03/11 02:34:37 garloff Exp $ */ +/* +***************************************************** +** Tekram TRM_S1040 for DC395x driver, header file +***************************************************** +*/ +#ifndef DC395x_trm_H +#define DC395x_trm_H + +#include + +#define DC395x_BANNER "Tekram DC395U/UW/F DC315/U" +#define DC395x_VERSION "1.38, 2002-03-11" + +/* Kernel version autodetection */ +#include +#ifndef KERNEL_VERSION +# define KERNEL_VERSION(V, P, S) (((V) << 16) + ((P) << 8) + (S)) +#endif + +//----------------------------------------------------- +#if LINUX_VERSION_CODE < KERNEL_VERSION(1,3,50) + #define VERSION_ELF_1_2_13 +#elseif LINUX_VERSION_CODE < KERNEL_VERSION(1,3,95) + #define VERSION_1_3_85 +#else + #define VERSION_2_0_0 +#endif + +#if defined(__SMP__) && !defined(CONFIG_SMP) +# define CONFIG_SMP +#endif + +#define DC395x_MAX_CMD_QUEUE 32 +//#define DC395x_MAX_QTAGS 32 +#define DC395x_MAX_QTAGS 16 +#define DC395x_MAX_ADAPTER_NUM 4 +#define DC395x_MAX_SCSI_ID 16 +#define DC395x_MAX_CMD_PER_LUN DC395x_MAX_QTAGS +#define DC395x_MAX_SG_TABLESIZE 64 /* HW limitation */ +#define DC395x_MAX_SG_LISTENTRY 64 /* Must be equal or lower to previous item */ +#define DC395x_MAX_SRB_CNT 63 +//#define DC395x_MAX_CAN_QUEUE 7 * DC395x_MAX_QTAGS +#define DC395x_MAX_CAN_QUEUE DC395x_MAX_SRB_CNT +#define DC395x_END_SCAN 2 +#define DC395x_SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ +#define DC395x_MAX_RETRIES 3 + +//#define SYNC_FIRST + +/* We don't have eh_abort_handler, eh_device_reset_handler, + * eh_bus_reset_handler, eh_host_reset_handler yet! + * So long: Use old exception handling :-( */ +#define OLD_EH + +#if (LINUX_VERSION_CODE < KERNEL_VERSION (2,1,70)) || defined(OLD_EH) +# define NEW_EH +# define NORM_REC_LVL 1 +#else +# define USE_NEW_EH +# define NORM_REC_LVL 0 +# define NEW_EH use_new_eh_code: 1, +#endif + +#if defined(HOSTS_C) || defined(MODULE) || LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99) + +# include + +extern int DC395x_detect(Scsi_Host_Template *psht); +extern int DC395x_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); +extern int DC395x_abort(Scsi_Cmnd *cmd); +extern int DC395x_reset(Scsi_Cmnd *cmd ,unsigned int resetFlags); +extern int DC395x_bios_param(Disk *disk, kdev_t devno, int geom[]); +//-------------- +# ifdef MODULE +static int DC395x_release(struct Scsi_Host *); +# else +# define DC395x_release NULL +# endif +//-------------- +extern int DC395x_proc_info(char*, char**, off_t, int, int, int); +//-------------- + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30) +# define DC395x_TRMS1040 { \ + proc_name: "dc395x_trm", \ + proc_info: DC395x_proc_info, \ + name: DC395x_BANNER " V" DC395x_VERSION,\ + detect: DC395x_detect, \ + release: DC395x_release, \ + queuecommand: DC395x_queue_command, \ + abort: DC395x_abort, \ + reset: DC395x_reset, \ + bios_param: DC395x_bios_param, \ + can_queue: DC395x_MAX_CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: DC395x_MAX_SG_TABLESIZE, \ + cmd_per_lun: DC395x_MAX_CMD_PER_LUN, \ + NEW_EH \ + unchecked_isa_dma: 0, \ + use_clustering: DISABLE_CLUSTERING \ + } +#else +extern struct proc_dir_entry DC395x_proc_scsi; +# define DC395x_TRMS1040 { \ + proc_dir: &DC395x_proc_scsi, \ + proc_info: DC395x_proc_info, \ + name: DC395x_BANNER " V" DC395x_VERSION,\ + detect: DC395x_detect, \ + release: DC395x_release, \ + queuecommand: DC395x_queue_command, \ + abort: DC395x_abort, \ + reset: DC395x_reset, \ + bios_param: DC395x_bios_param, \ + can_queue: DC395x_MAX_CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: DC395x_MAX_SG_TABLESIZE, \ + cmd_per_lun: DC395x_MAX_CMD_PER_LUN, \ + NEW_EH \ + unchecked_isa_dma: 0, \ + use_clustering: DISABLE_CLUSTERING \ + } +#endif /* 2,3,30 */ +#endif /* defined(HOSTS_C) || defined(MODULE) || LINUX_VERSION_CODE > 2,3,99 */ +#endif /* DC395x_H */