diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /drivers/scsi/ibmmca.c | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'drivers/scsi/ibmmca.c')
-rw-r--r-- | drivers/scsi/ibmmca.c | 3541 |
1 files changed, 1923 insertions, 1618 deletions
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index bd65b5e84..af2aa49ea 100644 --- a/drivers/scsi/ibmmca.c +++ b/drivers/scsi/ibmmca.c @@ -3,291 +3,30 @@ * * Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU * General Public License. Written by Martin Kolinek, December 1995. + * Further development by: Chris Beauregard, Klaus Kudielka, Michael Lang + * See the file README.ibmmca for a detailed description of this driver, + * the commandline arguments and the history of its development. + * See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest + * updates and info. */ -/* Update history: - Jan 15 1996: First public release. - - Martin Kolinek - - Jan 23 1996: Scrapped code which reassigned scsi devices to logical - device numbers. Instead, the existing assignment (created - when the machine is powered-up or rebooted) is used. - A side effect is that the upper layer of Linux SCSI - device driver gets bogus scsi ids (this is benign), - and also the hard disks are ordered under Linux the - same way as they are under dos (i.e., C: disk is sda, - D: disk is sdb, etc.). - - Martin Kolinek - - I think that the CD-ROM is now detected only if a CD is - inside CD_ROM while Linux boots. This can be fixed later, - once the driver works on all types of PS/2's. - - Martin Kolinek - - Feb 7 1996: Modified biosparam function. Fixed the CD-ROM detection. - For now, devices other than harddisk and CD_ROM are - ignored. Temporarily modified abort() function - to behave like reset(). - - Martin Kolinek - - Mar 31 1996: The integrated scsi subsystem is correctly found - in PS/2 models 56,57, but not in model 76. Therefore - the ibmmca_scsi_setup() function has been added today. - This function allows the user to force detection of - scsi subsystem. The kernel option has format - ibmmcascsi=n - where n is the scsi_id (pun) of the subsystem. Most likely, n is 7. - - Martin Kolinek - - Aug 21 1996: Modified the code which maps ldns to (pun,0). It was - insufficient for those of us with CD-ROM changers. - - Chris Beauregard - - Dec 14 1996: More improvements to the ldn mapping. See check_devices - for details. Did more fiddling with the integrated SCSI detection, - but I think it's ultimately hopeless without actually testing the - model of the machine. The 56, 57, 76 and 95 (ultimedia) all have - different integrated SCSI register configurations. However, the 56 - and 57 are the only ones that have problems with forced detection. - - Chris Beauregard - - Mar 8-16 1997: Modified driver to run as a module and to support - multiple adapters. A structure, called ibmmca_hostdata, is now - present, containing all the variables, that were once only - available for one single adapter. The find_subsystem-routine has vanished. - The hardware recognition is now done in ibmmca_detect directly. - This routine checks for presence of MCA-bus, checks the interrupt - level and continues with checking the installed hardware. - Certain PS/2-models do not recognize a SCSI-subsystem automatically. - Hence, the setup defined by command-line-parameters is checked first. - Thereafter, the routine probes for an integrated SCSI-subsystem. - Finally, adapters are checked. This method has the advantage to cover all - possible combinations of multiple SCSI-subsystems on one MCA-board. Up to - eight SCSI-subsystems can be recognized and announced to the upper-level - drivers with this improvement. A set of defines made changes to other - routines as small as possible. - - Klaus Kudielka - - May 30 1997: (v1.5b) - 1) SCSI-command capability enlarged by the recognition of MODE_SELECT. - This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which - allows data to be written from the system to the device. It is a - necessary step to be allowed to set blocksize of SCSI-tape-drives and - the tape-speed, whithout confusing the SCSI-Subsystem. - 2) The recognition of a tape is included in the check_devices routine. - This is done by checking for TYPE_TAPE, that is already defined in - the kernel-scsi-environment. The markup of a tape is done in the - global ldn_is_tape[] array. If the entry on index ldn - is 1, there is a tapedrive connected. - 3) The ldn_is_tape[] array is necessary to distinguish between tape- and - other devices. Fixed blocklength devices should not cause a problem - with the SCB-command for read and write in the ibmmca_queuecommand - subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for - the tape-devices, as recommended by IBM in this Technical Reference, - mentioned below. (IBM recommends to avoid using the read/write of the - subsystem, but the fact was, that read/write causes a command error from - the subsystem and this causes kernel-panic.) - 4) In addition, I propose to use the ldn instead of a fix char for the - display of PS2_DISK_LED_ON(). On 95, one can distinguish between the - devices that are accessed. It shows activity and easyfies debugging. - The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2 - (I do not know yet the type). Optimization and CD-ROM audio-support, - I am working on ... - - Michael Lang - - June 19 1997: (v1.6b) - 1) Submitting the extra-array ldn_is_tape[] -> to the local ld[] - device-array. - 2) CD-ROM Audio-Play seems to work now. - 3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code - 0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears - also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that - the problem is independent of the low-level-driver/bus-architecture. - 4) Hexadecimal ldn on PS/2-95 LED-display. - 5) Fixing of the PS/2-LED on/off that it works right with tapedrives and - does not confuse the disk_rw_in_progress counter. - - Michael Lang - - June 21 1997: (v1.7b) - 1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/<host> the - outer-world about operational load statistics on the different ldns, - seen by the driver. Everybody that has more than one IBM-SCSI should - test this, because I only have one and cannot see what happens with more - than one IBM-SCSI hosts. - 2) Definition of a driver version-number to have a better recognition of - the source when there are existing too much releases that may confuse - the user, when reading about release-specific problems. Up to know, - I calculated the version-number to be 1.7. Because we are in BETA-test - yet, it is today 1.7b. - 3) Sorry for the heavy bug I programmed on June 19 1997! After that, the - CD-ROM did not work any more! The C7-command was a fake impression - I got while programming. Now, the READ and WRITE commands for CD-ROM are - no longer running over the subsystem, but just over - IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts - much faster(!) and hopefully all fancy multimedia-functions, like direct - digital recording from audio-CDs also work. (I tried it with cdda2wav - from the cdwtools-package and it filled up the harddisk immediately :-).) - To easify boolean logics, a further local device-type in ld[], called - is_cdrom has been included. - 4) If one uses a SCSI-device of unsupported type/commands, one - immediately runs into a kernel-panic caused by Command Error. To better - understand which SCSI-command caused the problem, I extended this - specific panic-message slightly. - - Michael Lang - - June 25 1997: (v1.8b) - 1) Some cosmetical changes for the handling of SCSI-device-types. - Now, also CD-Burners / WORMs and SCSI-scanners should work. For - MO-drives I have no experience, therefore not yet supported. - In logical_devices I changed from different type-variables to one - called 'device_type' where the values, corresponding to scsi.h, - of a SCSI-device are stored. - 2) There existed a small bug, that maps a device, coming after a SCSI-tape - wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong - -> problem removed. - 3) Extension of the logical_device structure. Now it contains also device, - vendor and revision-level of a SCSI-device for internal usage. - - Michael Lang - - June 26-29 1997: (v2.0b) - 1) The release number 2.0b is necessary because of the completely new done - recognition and handling of SCSI-devices with the adapter. As I got - from Chris the hint, that the subsystem can reassign ldns dynamically, - I remembered this immediate_assign-command, I found once in the handbook. - Now, the driver first kills all ldn assignments that are set by default - on the SCSI-subsystem. After that, it probes on all puns and luns for - devices by going through all combinations with immediate_assign and - probing for devices, using device_inquiry. The found physical(!) pun,lun - structure is stored in get_scsi[][] as device types. This is followed - by the assignment of all ldns to existing SCSI-devices. If more ldns - than devices are available, they are assigned to non existing pun,lun - combinations to satisfy the adapter. With this, the dynamical mapping - was possible to implement. (For further info see the text in the - source-code and in the description below. Read the description - below BEFORE installing this driver on your system!) - 2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION. - 3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID - (pun) of the accessed SCSI-device. This is now senseful, because the - pun known within the driver is exactly the pun of the physical device - and no longer a fake one. - 4) The /proc/scsi/ibmmca/<host_no> consists now of the first part, where - hit-statistics of ldns is shown and a second part, where the maps of - physical and logical SCSI-devices are displayed. This could be very - interesting, when one is using more than 15 SCSI-devices in order to - follow the dynamical remapping of ldns. - - Michael Lang - - June 26-29 1997: (v2.0b-1) - 1) I forgot to switch the local_checking_phase_flag to 1 and back to 0 - in the dynamical remapping part in ibmmca_queuecommand for the - device_exist routine. Sorry. - - Michael Lang - - July 1-13 1997: (v3.0b,c) - 1) Merging of the driver-developments of Klaus Kudielka and Michael Lang - in order to get a optimum and unified driver-release for the - IBM-SCSI-Subsystem-Adapter(s). - For people, using the Kernel-release >=2.1.0, module-support should - be no problem. For users, running under <2.1.0, module-support may not - work, because the methods have changed between 2.0.x and 2.1.x. - 2) Added some more effective statistics for /proc-output. - 3) Change typecasting at necessary points from (unsigned long) to - virt_to_bus(). - 4) Included #if... at special points to have specific adaption of the - driver to kernel 2.0.x and 2.1.x. It should therefore also run with - later releases. - 5) Magneto-Optical drives and medium-changers are also recognized, now. - Therefore, we have a completely gapfree recognition of all SCSI- - device-types, that are known by Linux up to kernel 2.1.31. - 6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within - the configuration, each connected SCSI-device will get a reset command - during boottime. This can be necessary for some special SCSI-devices. - This flag should be included in Config.in. - (See also the new Config.in file.) - Probable next improvement: bad disk handler. - - Michael Lang - - Sept 14 1997: (v3.0c) - 1) Some debugging and speed optimization applied. - - Michael Lang - - Dec 15, 1997 - - chrisb@truespectra.com - - made the front panel display thingy optional, specified from the - command-line via ibmmcascsi=display. Along the lines of the /LED - option for the OS/2 driver. - - fixed small bug in the LED display that would hang some machines. - - reversed ordering of the drives (using the - IBMMCA_SCSI_ORDER_STANDARD define). This is necessary for two main - reasons: - - users who've already installed Linux won't be screwed. Keep - in mind that not everyone is a kernel hacker. - - be consistent with the BIOS ordering of the drives. In the - BIOS, id 6 is C:, id 0 might be D:. With this scheme, they'd be - backwards. This confuses the crap out of those heathens who've - got a impure Linux installation (which, <wince>, I'm one of). - This whole problem arises because IBM is actually non-standard with - the id to BIOS mappings. You'll find, in fdomain.c, a similar - comment about a few FD BIOS revisions. The Linux (and apparently - industry) standard is that C: maps to scsi id (0,0). Let's stick - with that standard. - - Since this is technically a branch of my own, I changed the - version number to 3.0e-cpb. - - Jan 17, 1998: (v3.0f) - 1) Addition of some statistical info for /proc in proc_info. - 2) Taking care of the SCSI-assignment problem, dealed by Chris at Dec 15 - 1997. In fact, IBM is right, concerning the assignment of SCSI-devices - to driveletters. It is conform to the ANSI-definition of the SCSI- - standard to assign drive C: to SCSI-id 6, because it is the highest - hardware priority after the hostadapter (that has still today by - default everywhere id 7). Also realtime-operating systems that I use, - like LynxOS and OS9, which are quite industrial systems use top-down - numbering of the harddisks, that is also starting at id 6. Now, one - sits a bit between two chairs. On one hand side, using the define - IBMMCA_SCSI_ORDER_STANDARD makes Linux assigning disks conform to - the IBM- and ANSI-SCSI-standard and keeps this driver downward - compatible to older releases, on the other hand side, people is quite - habituated in believing that C: is assigned to (0,0) and much other - SCSI-BIOS do so. Therefore, I moved the IBMMCA_SCSI_ORDER_STANDARD - define out of the driver and put it into Config.in as subitem of - 'IBM SCSI support'. A help, added to Documentation/Configure.help - explains the differences between saying 'y' or 'n' to the user, when - IBMMCA_SCSI_ORDER_STANDARD prompts, so the ordinary user is enabled to - choose the way of assignment, depending on his own situation and gusto. - 3) Adapted SCSI_IBMMCA_DEV_RESET to the local naming convention, so it is - now called IBMMCA_SCSI_DEV_RESET. - 4) Optimization of proc_info and its subroutines. - 5) Added more in-source-comments and extended the driver description by - some explanation about the SCSI-device-assignment problem. - - Michael Lang - - Jan 18, 1998: (v3.0g) - 1) Correcting names to be absolutely conform to the later 2.1.x releases. - This is necessary for - IBMMCA_SCSI_DEV_RESET -> CONFIG_IBMMCA_SCSI_DEV_RESET - IBMMCA_SCSI_ORDER_STANDARD -> CONFIG_IBMMCA_SCSI_ORDER_STANDARD - - Michael Lang - - TODO: - - - It seems that the handling of bad disks is really bad - - non-existent, in fact. - - More testing of the full driver-controlled dynamical ldn - (re)mapping for up to 56 SCSI-devices. - - Support more SCSI-device-types, if Linux defines more. - - Support more of the SCSI-command set. - - Support some of the caching abilities, particularly Read Prefetch. - This fetches data into the cache, which later gets hit by the - regular Read Data. - - Abort and Reset functions still slightly buggy. Especially when - floppydisk(!) operations report errors. - -******************************************************************************/ +/******************* HEADER FILE INCLUDES ************************************/ +#ifndef LINUX_VERSION_CODE +#include <linux/version.h> +#endif + +/* choose adaption for Kernellevel */ +#define local_LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#if LINUX_VERSION_CODE < local_LinuxVersionCode(2,1,0) +#define OLDKERN +#else +#undef OLDKERN +#endif #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> +#include <linux/ctype.h> #include <linux/string.h> #include <linux/ioport.h> #include <linux/delay.h> @@ -297,7 +36,9 @@ #include <linux/stat.h> #include <linux/mca.h> #include <asm/system.h> +#ifndef OLDKERN #include <asm/spinlock.h> +#endif #include <asm/io.h> #include "sd.h" #include "scsi.h" @@ -306,199 +47,29 @@ #include <linux/config.h> /* for CONFIG_SCSI_IBMMCA etc. */ -/*--------------------------------------------------------------------*/ +/******************* LOCAL DEFINES *******************************************/ -/* current version of this driver-source: */ -#define IBMMCA_SCSI_DRIVER_VERSION "3.0f" - -/* use standard Linux ordering, where C: maps to (0,0), unlike the IBM -standard which seems to like C: => (6,0) */ -/* #define IBMMCA_SCSI_ORDER_STANDARD is defined/undefined in Config.in - * now, while configuring the kernel. */ - -/* - Driver Description - - (A) Subsystem Detection - This is done in the ibmmca_detect() function and is easy, since - the information about MCA integrated subsystems and plug-in - adapters is readily available in structure *mca_info. - - (B) Physical Units, Logical Units, and Logical Devices - There can be up to 56 devices on SCSI bus (besides the adapter): - there are up to 7 "physical units" (each identified by physical unit - number or pun, also called the scsi id, this is the number you select - with hardware jumpers), and each physical unit can have up to 8 - "logical units" (each identified by logical unit number, or lun, - between 0 and 7). - - Typically the adapter has pun=7, so puns of other physical units - are between 0 and 6. Almost all physical units have only one - logical unit, with lun=0. A CD-ROM jukebox would be an example of - a physical unit with more than one logical unit. - - The embedded microprocessor of IBM SCSI subsystem hides the complex - two-dimensional (pun,lun) organization from the operating system. - When the machine is powered-up (or rebooted, I am not sure), the - embedded microprocessor checks, on it own, all 56 possible (pun,lun) - combinations, and first 15 devices found are assigned into a - one-dimensional array of so-called "logical devices", identified by - "logical device numbers" or ldn. The last ldn=15 is reserved for - the subsystem itself. - - One consequence of information hiding is that the real (pun,lun) - numbers are also hidden. Therefore this driver takes the following - approach: It checks the ldn's (0 to 6) to find out which ldn's - have devices assigned. This is done by function check_devices() and - device_exists(). The interrupt handler has a special paragraph of code - (see local_checking_phase_flag) to assist in the checking. Assume, for - example, that three logical devices were found assigned at ldn 0, 1, 2. - These are presented to the upper layer of Linux SCSI driver - as devices with bogus (pun, lun) equal to (0,0), (1,0), (2,0). - On the other hand, if the upper layer issues a command to device - say (4,0), this driver returns DID_NO_CONNECT error. - - That last paragraph is no longer correct, but is left for - historical purposes. It limited the number of devices to 7, far - fewer than the 15 that it could use. Now it just maps - ldn -> (ldn/8,ldn%8). We end up with a real mishmash of puns - and luns, but it all seems to work. - Chris Beaurgard - - And that last paragraph is also no longer correct. It uses a - slightly more complex mapping that will always map hard disks to - (x,0), for some x, and consecutive none disk devices will usually - share puns. - - Again, the last paragraphs are no longer correct. Now, the physical - SCSI-devices on the SCSI-bus are probed via immediate_assign- and - device_inquiry-commands. This delivers a exact map of the physical - SCSI-world that is now stored in the get_scsi[][]-array. This means, - that the once hidden pun,lun assignment is now known to this driver. - It no longer believes in default-settings of the subsystem and maps all - ldns to existing pun,lun by foot. This assures full control of the ldn - mapping and allows dynamical remapping of ldns to different pun,lun, if - there are more SCSI-devices installed than ldns available (n>15). The - ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0, - excluding the pun of the subsystem. This assures, that at least simple - SCSI-installations have optimum access-speed and are not touched by - dynamical remapping. The ldns 7 to 14 are put to existing devices with - lun>0 or to non-existing devices, in order to satisfy the subsystem, if - there are less than 15 SCSI-devices connected. In the case of more than 15 - devices, the dynamical mapping goes active. If the get_scsi[][] reports a - device to be existant, but it has no ldn assigned, it gets a ldn out of 7 - to 14. The numbers are assigned in cyclic order. Therefore it takes 8 - dynamical assignments on SCSI-devices, until a certain device - looses its ldn again. This assures, that dynamical remapping is avoided - during intense I/O between up to eight SCSI-devices (means pun,lun - combinations). A further advantage of this method is, that people who - build their kernel without probing on all luns will get what they expect. - - IMPORTANT: Because of the now correct recognition of physical pun,lun, and - their report to mid-level- and higher-level-drivers, the new reported puns - can be different from the old, faked puns. Therefore, Linux will eventually - change /dev/sdXXX assignments and prompt you for corrupted superblock - repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!! - You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file - entries right. After that, the system should come up as errorfree as before. - If your boot-partition is not coming up, also edit the /etc/lilo.conf-file - in a Linux session booted on old kernel and run lilo before reboot. Check - lilo.conf anyway to get boot on other partitions with foreign OSes right - again. - - The problem is, that Linux does not assign the SCSI-devices in the - way as described in the ANSI-SCSI-standard. Linux assigns /dev/sda to - the device with at minimum id 0. But the first drive should be at id 6, - because for historical reasons, drive at id 6 has, by hardware, the highest - priority and a drive at id 0 the lowest. IBM was one of the rare producers, - where the BIOS assigns drives belonging to the ANSI-SCSI-standard. Most - other producers' BIOS does not (I think even Adaptec-BIOS). The - IBMMCA_SCSI_ORDER_STANDARD flag helps to be able to choose the preferred - way of SCSI-device-assignment. Defining this flag would result in Linux - determining the devices in the same order as DOS and OS/2 does on your - MCA-machine. This is also standard on most industrial computers. Leaving - this flag undefined will get your devices ordered in the default way of - Linux. See also the remarks of Chris Beauregard from Dec 15, 1997 and - the followups. - - (C) Regular Processing - Only three functions get involved: ibmmca_queuecommand(), issue_cmd(), - and interrupt_handler(). - - The upper layer issues a scsi command by calling function - ibmmca_queuecommand(). This function fills a "subsystem control block" - (scb) and calls a local function issue_cmd(), which writes a scb - command into subsystem I/O ports. Once the scb command is carried out, - interrupt_handler() is invoked. If a device is determined to be existant - and it has not assigned any ldn, it gets one dynamically. - - (D) Abort, Reset. - These are implemented with busy waiting for interrupt to arrive. - The abort does not worked well for me, so I instead call the - ibmmca_reset() from the ibmmca_abort() function. - - (E) Disk Geometry - The ibmmca_biosparams() function should return same disk geometry - as bios. This is needed for fdisk, etc. The returned geometry is - certainly correct for disk smaller than 1 gigabyte, but I am not - 100% sure that it is correct for larger disks. - - (F) Kernel Boot Option - The function ibmmca_scsi_setup() is called if option ibmmcascsi=n - is passed to the kernel. See file linux/init/main.c for details. - - (G) Driver Module Support - Is implemented and tested by K. Kudielka. This could probably not work - on kernels <2.1.0. - - (H) Multiple Hostadapter Support - This driver supports up to eight interfaces of type IBM-SCSI-Subsystem. - Integrated-, and MCA-adapters are automatically recognized. Unrecognizable - IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters. - - (I) /proc-Filesystem Information - Information about the driver condition is given in - /proc/scsi/ibmmca/<host_no>. ibmmca_proc_info provides this information. - */ +#ifndef mdelay +#define mdelay(a) udelay((a) * 1000) +#endif /*--------------------------------------------------------------------*/ -/* Here are the values and structures specific for the subsystem. - * The source of information is "Update for the PS/2 Hardware - * Interface Technical Reference, Common Interfaces", September 1991, - * part number 04G3281, available in the U.S. for $21.75 at - * 1-800-IBM-PCTB, elsewhere call your local friendly IBM - * representative. - * In addition to SCSI subsystem, this update contains fairly detailed - * (at hardware register level) sections on diskette controller, - * keyboard controller, serial port controller, VGA, and XGA. - * - * Additional information from "Personal System/2 Micro Channel SCSI - * Adapter with Cache Technical Reference", March 1990, PN 68X2365, - * probably available from the same source (or possibly found buried - * in officemates desk). - * - * Further literature/program-sources referred for this driver: - * - * Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie- - * Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl. - * Addison Wesley, 1996. - * - * Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel - * Hill - North Carolina, 1995 - * - * Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart - * 1993 - */ + +/* current version of this driver-source: */ +#define IBMMCA_SCSI_DRIVER_VERSION "3.1e" /*--------------------------------------------------------------------*/ /* driver configuration */ #define IM_MAX_HOSTS 8 /* maximum number of host adapters */ -#define IM_RESET_DELAY 10 /* seconds allowed for a reset */ +#define IM_RESET_DELAY 60 /* seconds allowed for a reset */ /* driver debugging - #undef all for normal operation */ /* if defined: count interrupts and ignore this special one: */ #undef IM_DEBUG_TIMEOUT 50 +#define TIMEOUT_PUN 0 +#define TIMEOUT_LUN 0 /* verbose interrupt: */ #undef IM_DEBUG_INT /* verbose queuecommand: */ @@ -512,11 +83,11 @@ standard which seems to like C: => (6,0) */ #define IM_DEBUG_CMD_DEVICE TYPE_TAPE /* relative addresses of hardware registers on a subsystem */ -#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */ -#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */ -#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */ -#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */ -#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */ +#define IM_CMD_REG(hi) (hosts[(hi)]->io_port) /*Command Interface, (4 bytes long) */ +#define IM_ATTN_REG(hi) (hosts[(hi)]->io_port+4) /*Attention (1 byte) */ +#define IM_CTR_REG(hi) (hosts[(hi)]->io_port+5) /*Basic Control (1 byte) */ +#define IM_INTR_REG(hi) (hosts[(hi)]->io_port+6) /*Interrupt Status (1 byte, r/o) */ +#define IM_STAT_REG(hi) (hosts[(hi)]->io_port+7) /*Basic Status (1 byte, read only) */ /* basic I/O-port of first adapter */ #define IM_IO_PORT 0x3540 @@ -668,9 +239,15 @@ struct im_tsb /* use_display is set by the ibmmcascsi=display command line arg */ static int use_display = 0; +/* use_adisplay is set by ibmmcascsi=adisplay, which offers a higher + * level of displayed luxus on PS/2 95 (really fancy! :-))) */ +static int use_adisplay = 0; + #define PS2_DISK_LED_ON(ad,id) {\ if( use_display ) { outb((char)(id+48), MOD95_LED_PORT ); \ outb((char)(ad+48), MOD95_LED_PORT+1); } \ + else if( use_adisplay ) { if (id<7) outb((char)(id+48), \ + MOD95_LED_PORT+1+id); outb((char)(ad+48), MOD95_LED_PORT); } \ else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \ } @@ -678,6 +255,11 @@ static int use_display = 0; #define PS2_DISK_LED_OFF() {\ if( use_display ) { outb( ' ', MOD95_LED_PORT ); \ outb(' ', MOD95_LED_PORT+1); } \ + if ( use_adisplay ) { outb(' ',MOD95_LED_PORT ); \ + outb(' ',MOD95_LED_PORT+1); outb(' ',MOD95_LED_PORT+2); \ + outb(' ',MOD95_LED_PORT+3); outb(' ',MOD95_LED_PORT+4); \ + outb(' ',MOD95_LED_PORT+5); outb(' ',MOD95_LED_PORT+6); \ + outb(' ',MOD95_LED_PORT+7); } \ else outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); \ } @@ -693,18 +275,20 @@ struct subsys_list_struct /* List of possible IBM-SCSI-adapters */ struct subsys_list_struct subsys_list[] = { - {0x8efc, "IBM Fast SCSI-2 Adapter"}, - {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/cache"}, - {0x8ef8, "IBM Expansion Unit SCSI Controller"}, - {0x8eff, "IBM SCSI Adapter w/Cache"}, - {0x8efe, "IBM SCSI Adapter"}, -}; + {0x8efc, "IBM Fast SCSI-2 Adapter"}, /* special = 0 */ + {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/cache"}, /* special = 1 */ + {0x8ef8, "IBM Expansion Unit SCSI Controller"},/* special = 2 */ + {0x8eff, "IBM SCSI Adapter w/Cache"}, /* special = 3 */ + {0x8efe, "IBM SCSI Adapter"}, /* special = 4 */ +}; /*for /proc filesystem */ struct proc_dir_entry proc_scsi_ibmmca = { PROC_SCSI_IBMMCA, 6, "ibmmca", - S_IFDIR | S_IRUGO | S_IXUGO, 2 + S_IFDIR | S_IRUGO | S_IXUGO, 2, + 0, 0, 0, NULL, NULL, NULL, NULL, + NULL, NULL, NULL }; /* Max number of logical devices (can be up from 0 to 14). 15 is the address @@ -715,8 +299,9 @@ of the adapter itself. */ struct logical_device { struct im_scb scb; /* SCSI-subsystem-control-block structure */ - struct im_tsb tsb; - struct im_sge sge[16]; + struct im_tsb tsb; /* SCSI command complete status block structure */ + struct im_sge sge[16]; /* scatter gather list structure */ + unsigned char buf[256]; /* SCSI command return data buffer */ Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */ int device_type; /* type of the SCSI-device. See include/scsi/scsi.h @@ -736,6 +321,7 @@ struct Driver_Statistics int total_accesses; /* total accesses on all ldns */ int total_interrupts; /* total interrupts (should be same as total_accesses) */ + int total_errors; /* command completed with error */ /* dynamical assignment statistics */ int total_scsi_devices; /* number of physical pun,lun */ int dyn_flag; /* flag showing dynamical mode */ @@ -747,44 +333,54 @@ struct Driver_Statistics /* data structure for each host adapter */ struct ibmmca_hostdata { - /* array of logical devices: */ - struct logical_device _ld[MAX_LOG_DEV]; - /* array to convert (pun, lun) into logical device number: */ - unsigned char _get_ldn[8][8]; - /*array that contains the information about the physical SCSI-devices - attached to this host adapter: */ - unsigned char _get_scsi[8][8]; - /* used only when checking logical devices: */ - int _local_checking_phase_flag; - /* report received interrupt: */ - int _got_interrupt; - /* report termination-status of SCSI-command: */ - int _stat_result; - /* reset status (used only when doing reset): */ - int _reset_status; - /* code of the last SCSI command (needed for panic info): */ - int _last_scsi_command; - /* Counter that points on the next reassignable ldn for dynamical - remapping. The default value is 7, that is the first reassignable - number in the list at boottime: */ - int _next_ldn; - /* Statistics-structure for this IBM-SCSI-host: */ - struct Driver_Statistics _IBM_DS; + /* array of logical devices: */ + struct logical_device _ld[MAX_LOG_DEV+1]; + /* array to convert (pun, lun) into logical device number: */ + unsigned char _get_ldn[8][8]; + /*array that contains the information about the physical SCSI-devices + attached to this host adapter: */ + unsigned char _get_scsi[8][8]; + /* used only when checking logical devices: */ + int _local_checking_phase_flag; + /* report received interrupt: */ + int _got_interrupt; + /* report termination-status of SCSI-command: */ + int _stat_result; + /* reset status (used only when doing reset): */ + int _reset_status; + /* code of the last SCSI command (needed for panic info): */ + int _last_scsi_command[MAX_LOG_DEV+1]; + /* identifier of the last SCSI-command type */ + int _last_scsi_type[MAX_LOG_DEV+1]; + /* Counter that points on the next reassignable ldn for dynamical + remapping. The default value is 7, that is the first reassignable + number in the list at boottime: */ + int _next_ldn; + /* Statistics-structure for this IBM-SCSI-host: */ + struct Driver_Statistics _IBM_DS; + /* This hostadapters pos-registers pos2 and pos3 */ + unsigned _pos2, _pos3; + /* assign a special variable, that contains dedicated info about the + adaptertype */ + int _special; }; /* macros to access host data structure */ -#define HOSTDATA(shpnt) ((struct ibmmca_hostdata *) shpnt->hostdata) -#define subsystem_pun (shpnt->this_id) -#define ld (HOSTDATA(shpnt)->_ld) -#define get_ldn (HOSTDATA(shpnt)->_get_ldn) -#define get_scsi (HOSTDATA(shpnt)->_get_scsi) -#define local_checking_phase_flag (HOSTDATA(shpnt)->_local_checking_phase_flag) -#define got_interrupt (HOSTDATA(shpnt)->_got_interrupt) -#define stat_result (HOSTDATA(shpnt)->_stat_result) -#define reset_status (HOSTDATA(shpnt)->_reset_status) -#define last_scsi_command (HOSTDATA(shpnt)->_last_scsi_command) -#define next_ldn (HOSTDATA(shpnt)->_next_ldn) -#define IBM_DS (HOSTDATA(shpnt)->_IBM_DS) +#define subsystem_pun(hi) (hosts[(hi)]->this_id) +#define ld(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_ld) +#define get_ldn(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_get_ldn) +#define get_scsi(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_get_scsi) +#define local_checking_phase_flag(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_local_checking_phase_flag) +#define got_interrupt(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_got_interrupt) +#define stat_result(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_stat_result) +#define reset_status(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_reset_status) +#define last_scsi_command(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_command) +#define last_scsi_type(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_type) +#define next_ldn(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_next_ldn) +#define IBM_DS(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_IBM_DS) +#define special(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_special) +#define pos2(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos2) +#define pos3(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos3) /* Define a arbitrary number as subsystem-marker-type. This number is, as described in the ANSI-SCSI-standard, not occupied by other device-types. */ @@ -805,11 +401,23 @@ struct ibmmca_hostdata #define SET_LDN 0 #define REMOVE_LDN 1 +/* ldn which is used to probe the SCSI devices */ +#define PROBE_LDN 0 + /* reset status flag contents */ -#define IM_RESET_NOT_IN_PROGRESS 0 -#define IM_RESET_IN_PROGRESS 1 -#define IM_RESET_FINISHED_OK 2 -#define IM_RESET_FINISHED_FAIL 3 +#define IM_RESET_NOT_IN_PROGRESS 0 +#define IM_RESET_IN_PROGRESS 1 +#define IM_RESET_FINISHED_OK 2 +#define IM_RESET_FINISHED_FAIL 3 +#define IM_RESET_NOT_IN_PROGRESS_NO_INT 4 +#define IM_RESET_FINISHED_OK_NO_INT 5 + +/* special flags for hostdata structure */ +#define FORCED_DETECTION 100 +#define INTEGRATED_SCSI 101 + +/* define undefined SCSI-command */ +#define NO_SCSI 0xffff /*-----------------------------------------------------------------------*/ @@ -823,277 +431,517 @@ static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 }; MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); MODULE_PARM(display, "1i"); +MODULE_PARM(adisplay, "1i"); +MODULE_PARM(bypass, "1i"); +MODULE_PARM(normal, "1i"); +MODULE_PARM(ansi, "1i"); #endif /*counter of concurrent disk read/writes, to turn on/off disk led */ static int disk_rw_in_progress = 0; +/* spinlock handling to avoid command clash while in operation */ +#ifndef OLDKERN +spinlock_t info_lock = SPIN_LOCK_UNLOCKED; +spinlock_t proc_lock = SPIN_LOCK_UNLOCKED; +spinlock_t abort_lock = SPIN_LOCK_UNLOCKED; +spinlock_t reset_lock = SPIN_LOCK_UNLOCKED; +spinlock_t issue_lock = SPIN_LOCK_UNLOCKED; +spinlock_t intr_lock = SPIN_LOCK_UNLOCKED; +#endif + /* host information */ static int found = 0; static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL }; +static unsigned int pos[8]; /* whole pos register-line */ +/* Taking into account the additions, made by ZP Gu. + * This selects now the preset value from the configfile and + * offers the 'normal' commandline option to be accepted */ +#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD +static char ibm_ansi_order = 1; +#else +static char ibm_ansi_order = 0; +#endif + /*-----------------------------------------------------------------------*/ -/*local functions in forward declaration */ -static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs); -static void do_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs); -static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, - unsigned char attn_reg); +/******************* FUNCTIONS IN FORWARD DECLARATION ************************/ + +static void interrupt_handler (int, void *, struct pt_regs *); +#ifndef OLDKERN +static void do_interrupt_handler (int, void *, struct pt_regs *); +#endif +static void issue_cmd (int, unsigned long, unsigned char); static void internal_done (Scsi_Cmnd * cmd); -static void check_devices (struct Scsi_Host *shpnt); -static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, - unsigned int lun, unsigned int ldn, - unsigned int operation); -static int device_inquiry(struct Scsi_Host *shpnt, int ldn, - unsigned char *buf); -static char *ti_p(int value); -static char *ti_l(int value); -static int device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, - int *device_type); -static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template, - int port, int id); +static void check_devices (int); +static int immediate_assign(int, unsigned int, unsigned int, unsigned int, + unsigned int); +#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET +static int immediate_reset(int, unsigned int); +#endif +static int device_inquiry(int, int); +static int read_capacity(int, int); +static char *ti_p(int); +static char *ti_l(int); +static int device_exists (int, int, int *, int *); +static struct Scsi_Host *ibmmca_register(Scsi_Host_Template *, + int, int, char *); /* local functions needed for proc_info */ -static int ldn_access_load(struct Scsi_Host *shpnt, int ldn); -static int ldn_access_total_read_write(struct Scsi_Host *shpnt); +static int ldn_access_load(int, int); +static int ldn_access_total_read_write(int); +static int bypass_controller = 0; /* bypass integrated SCSI-cmd set flag */ /*--------------------------------------------------------------------*/ -static void -do_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) +/******************* LOCAL FUNCTIONS IMPLEMENTATION *************************/ + +#ifndef OLDKERN +/* newer Kernels need the spinlock interrupt handler */ +static void do_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) { unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); interrupt_handler(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags); + return; } +#endif -static void -interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) +static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) { - int i = 0; - struct Scsi_Host *shpnt; - unsigned int intr_reg; - unsigned int cmd_result; - unsigned int ldn; - unsigned long flags; - - /* search for one adapter-response on shared interrupt */ - do - shpnt = hosts[i++]; - while (shpnt && !(inb(IM_STAT_REG) & IM_INTR_REQUEST)); - - /* return if some other device on this IRQ caused the interrupt */ - if (!shpnt) return; - - /*get command result and logical device */ - intr_reg = inb (IM_INTR_REG); - cmd_result = intr_reg & 0xf0; - ldn = intr_reg & 0x0f; - - /*must wait for attention reg not busy, then send EOI to subsystem */ - save_flags(flags); - while (1) { - cli (); - if (!(inb (IM_STAT_REG) & IM_BUSY)) - break; - restore_flags(flags); - } - outb (IM_EOI | ldn, IM_ATTN_REG); - restore_flags (flags); - - /*these should never happen (hw fails, or a local programming bug) */ - if (cmd_result == IM_ADAPTER_HW_FAILURE) - panic ("IBM MCA SCSI: subsystem hardware failure. Last SCSI_CMD=0x%X. \n", - last_scsi_command); - if (cmd_result == IM_CMD_ERROR) - panic ("IBM MCA SCSI: command error. Last SCSI_CMD=0x%X. \n", - last_scsi_command); - if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) - panic ("IBM MCA SCSI: software sequencing error. Last SCSI_CMD=0x%X. \n", - last_scsi_command); - - /* if no panic appeared, increase the interrupt-counter */ - IBM_DS.total_interrupts++; - - /*only for local checking phase */ - if (local_checking_phase_flag) - { - stat_result = cmd_result; - got_interrupt = 1; - reset_status = IM_RESET_FINISHED_OK; - return; - } - - /*handling of commands coming from upper level of scsi driver */ - else - { - Scsi_Cmnd *cmd; - - /*verify ldn, and may handle rare reset immediate command */ - if (ldn >= MAX_LOG_DEV) - { - if (ldn == 0xf && reset_status == IM_RESET_IN_PROGRESS) - { - if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) - { - reset_status = IM_RESET_FINISHED_FAIL; - } - else - { - /*reset disk led counter, turn off disk led */ - disk_rw_in_progress = 0; - PS2_DISK_LED_OFF (); - reset_status = IM_RESET_FINISHED_OK; - } - return; - } - else - panic ("IBM MCA SCSI: invalid logical device number.\n"); - } + int host_index; + unsigned int intr_reg; + unsigned int cmd_result; + unsigned int ldn; + static unsigned long flags; + Scsi_Cmnd *cmd; + int errorflag; + int interror; + + host_index=0; /* make sure, host_index is 0, else this won't work and + never dare to ask, what happens, if an interrupt-handler + does not work :-((( .... */ + + /* search for one adapter-response on shared interrupt */ + while (hosts[host_index] + && !(inb(IM_STAT_REG(host_index)) & IM_INTR_REQUEST)) + host_index++; + + /* return if some other device on this IRQ caused the interrupt */ + if (!hosts[host_index]) return; -#ifdef IM_DEBUG_TIMEOUT - { - static int count = 0; + /* the reset-function already did all the job, even ints got + renabled on the subsystem, so just return */ + if ((reset_status(host_index) == IM_RESET_NOT_IN_PROGRESS_NO_INT)|| + (reset_status(host_index) == IM_RESET_FINISHED_OK_NO_INT)) + { + reset_status(host_index) = IM_RESET_NOT_IN_PROGRESS; + return; + } + + /*get command result and logical device */ + intr_reg = inb (IM_INTR_REG(host_index)); + cmd_result = intr_reg & 0xf0; + ldn = intr_reg & 0x0f; - if (++count == IM_DEBUG_TIMEOUT) { - printk("IBM MCA SCSI: Ignoring interrupt.\n"); - return; - } - } + /*must wait for attention reg not busy, then send EOI to subsystem */ + while (1) + { +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&intr_lock, flags); +#endif + if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) + break; +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); #endif + } + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); + /* get the last_scsi_command here */ + interror = last_scsi_command(host_index)[ldn]; +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif + errorflag = 0; /* no errors by default */ + /*these should never happen (hw fails, or a local programming bug) */ + if (cmd_result == IM_ADAPTER_HW_FAILURE) + { + printk("\n"); + printk("IBM MCA SCSI: ERROR - subsystem hardware failure!\n"); + printk(" Last SCSI-command=0x%X, ldn=%d, host=%d.\n", + last_scsi_command(host_index)[ldn],ldn,host_index); + errorflag = 1; + } + if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) + { + printk("\n"); + printk("IBM MCA SCSI: ERROR - software sequencing error!\n"); + printk(" Last SCSI-command=0x%X, ldn=%d, host=%d.\n", + last_scsi_command(host_index)[ldn],ldn,host_index); + errorflag = 1; + } + if (cmd_result == IM_CMD_ERROR) + { + printk("\n"); + printk("IBM MCA SCSI: ERROR - command error!\n"); + printk(" Last SCSI-command=0x%X, ldn=%d, host=%d.\n", + last_scsi_command(host_index)[ldn],ldn,host_index); + errorflag = 1; + } + if (errorflag) + { /* if errors appear, enter this section to give detailed info */ + printk("IBM MCA SCSI: Subsystem Error-Status follows:\n"); + printk(" Command Type................: %x\n", + last_scsi_type(host_index)[ldn]); + printk(" Attention Register..........: %x\n", + inb (IM_ATTN_REG(host_index))); + printk(" Basic Control Register......: %x\n", + inb (IM_CTR_REG(host_index))); + printk(" Interrupt Status Register...: %x\n", + intr_reg); + printk(" Basic Status Register.......: %x\n", + inb (IM_STAT_REG(host_index))); + if ((last_scsi_type(host_index)[ldn]==IM_SCB)|| + (last_scsi_type(host_index)[ldn]==IM_LONG_SCB)) + { + printk(" SCB End Status Word.........: %x\n", + ld(host_index)[ldn].tsb.end_status); + printk(" Command Status..............: %x\n", + ld(host_index)[ldn].tsb.cmd_status); + printk(" Device Status...............: %x\n", + ld(host_index)[ldn].tsb.dev_status); + printk(" Command Error...............: %x\n", + ld(host_index)[ldn].tsb.cmd_error); + printk(" Device Error................: %x\n", + ld(host_index)[ldn].tsb.dev_error); + printk(" Last SCB Address (LSW)......: %x\n", + ld(host_index)[ldn].tsb.low_of_last_scb_adr); + printk(" Last SCB Address (MSW)......: %x\n", + ld(host_index)[ldn].tsb.high_of_last_scb_adr); + } + printk(" Send report to the maintainer.\n"); + panic("IBM MCA SCSI: Fatal errormessage from the subsystem!\n"); + } + + /* if no panic appeared, increase the interrupt-counter */ + IBM_DS(host_index).total_interrupts++; - /*if no command structure, just return, else clear cmd */ - cmd = ld[ldn].cmd; - if (!cmd) + /*only for local checking phase */ + if (local_checking_phase_flag(host_index)) + { + stat_result(host_index) = cmd_result; + got_interrupt(host_index) = 1; + reset_status(host_index) = IM_RESET_FINISHED_OK; + last_scsi_command(host_index)[ldn] = NO_SCSI; return; - ld[ldn].cmd = 0; - + } + /*handling of commands coming from upper level of scsi driver */ + else + { + if (last_scsi_type(host_index)[ldn] == IM_IMM_CMD) + { + /*verify ldn, and may handle rare reset immediate command */ + if ((reset_status(host_index) == IM_RESET_IN_PROGRESS)&& + (last_scsi_command(host_index)[ldn] == IM_RESET_IMM_CMD)) + { + if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) + { + disk_rw_in_progress = 0; + PS2_DISK_LED_OFF (); + reset_status(host_index) = IM_RESET_FINISHED_FAIL; + } + else + { + /*reset disk led counter, turn off disk led */ + disk_rw_in_progress = 0; + PS2_DISK_LED_OFF (); + reset_status(host_index) = IM_RESET_FINISHED_OK; + } + stat_result(host_index) = cmd_result; + last_scsi_command(host_index)[ldn] = NO_SCSI; + return; + } + else if (last_scsi_command(host_index)[ldn] == IM_ABORT_IMM_CMD) + { /* react on SCSI abort command */ +#ifdef IM_DEBUG_PROBE + printk("IBM MCA SCSI: Interrupt from SCSI-abort.\n"); +#endif + disk_rw_in_progress = 0; + PS2_DISK_LED_OFF(); + cmd = ld(host_index)[ldn].cmd; + if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) + cmd->result = DID_NO_CONNECT << 16; + else + cmd->result = DID_ABORT << 16; + stat_result(host_index) = cmd_result; + last_scsi_command(host_index)[ldn] = NO_SCSI; + if (cmd->scsi_done) + (cmd->scsi_done) (cmd); /* should be the internal_done */ + return; + } + else + { + disk_rw_in_progress = 0; + PS2_DISK_LED_OFF (); + reset_status(host_index) = IM_RESET_FINISHED_OK; + stat_result(host_index) = cmd_result; + last_scsi_command(host_index)[ldn] = NO_SCSI; + return; + } + } + last_scsi_command(host_index)[ldn] = NO_SCSI; + cmd = ld(host_index)[ldn].cmd; +#ifdef IM_DEBUG_TIMEOUT + if (cmd) + { + if ((cmd->target == TIMEOUT_PUN)&&(cmd->lun == TIMEOUT_LUN)) + { + printk("IBM MCA SCSI: Ignoring interrupt from pun=%x, lun=%x.\n", + cmd->target, cmd->lun); + return; + } + } +#endif + /*if no command structure, just return, else clear cmd */ + if (!cmd) + return; + ld(host_index)[ldn].cmd = NULL; + #ifdef IM_DEBUG_INT - printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", - cmd->cmnd[0], intr_reg, - ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, - ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error); + printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", + cmd->cmnd[0], intr_reg, + ld(host_index)[ldn].tsb.dev_status, + ld(host_index)[ldn].tsb.cmd_status, + ld(host_index)[ldn].tsb.dev_error, + ld(host_index)[ldn].tsb.cmd_error); #endif + + /*if this is end of media read/write, may turn off PS/2 disk led */ + if ((ld(host_index)[ldn].device_type!=TYPE_NO_LUN)&& + (ld(host_index)[ldn].device_type!=TYPE_NO_DEVICE)) + { /* only access this, if there was a valid device addressed */ + switch (cmd->cmnd[0]) + { + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + if (--disk_rw_in_progress == 0) + PS2_DISK_LED_OFF (); + } + } - /*if this is end of media read/write, may turn off PS/2 disk led */ - if ((ld[ldn].device_type!=TYPE_NO_LUN)&& - (ld[ldn].device_type!=TYPE_NO_DEVICE)) - { /* only access this, if there was a valid device addressed */ - switch (cmd->cmnd[0]) - { - case READ_6: - case WRITE_6: - case READ_10: - case WRITE_10: - case READ_12: - case WRITE_12: - if (--disk_rw_in_progress == 0) - PS2_DISK_LED_OFF (); - } - } - - /*write device status into cmd->result, and call done function */ - if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) - cmd->result = ld[ldn].tsb.dev_status & 0x1e; - else - cmd->result = 0; - (cmd->scsi_done) (cmd); - } -} - -/*--------------------------------------------------------------------*/ - -static void -issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, - unsigned char attn_reg) -{ - unsigned long flags; - /*must wait for attention reg not busy */ - save_flags(flags); - while (1) - { - cli (); - if (!(inb (IM_STAT_REG) & IM_BUSY)) - break; - restore_flags (flags); - } - - /*write registers and enable system interrupts */ - outl (cmd_reg, IM_CMD_REG); - outb (attn_reg, IM_ATTN_REG); - restore_flags (flags); + /* IBM describes the status-mask to be 0x1e, but this is not conform + * with SCSI-defintion, I suppose, it is a printing error in the + * technical reference and assume as mask 0x3e. (ML) */ + cmd->result = (ld(host_index)[ldn].tsb.dev_status & 0x3e); + /* write device status into cmd->result, and call done function */ + if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) + IBM_DS(host_index).total_errors++; + if (interror == NO_SCSI) /* unexpected interrupt :-( */ + cmd->result |= DID_BAD_INTR << 16; + else + cmd->result |= DID_OK << 16; + (cmd->scsi_done) (cmd); + } + if (interror == NO_SCSI) + printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n"); + return; } /*--------------------------------------------------------------------*/ -static void -internal_done (Scsi_Cmnd * cmd) +static void issue_cmd (int host_index, unsigned long cmd_reg, + unsigned char attn_reg) { - cmd->SCp.Status++; + static unsigned long flags; + /* must wait for attention reg not busy */ + while (1) + { +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&issue_lock, flags); +#endif + if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) + break; +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&issue_lock, flags); +#endif + } + /*write registers and enable system interrupts */ + outl (cmd_reg, IM_CMD_REG(host_index)); + outb (attn_reg, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&issue_lock, flags); +#endif } /*--------------------------------------------------------------------*/ -static int ibmmca_getinfo (char *buf, int slot, void *dev) +static void internal_done (Scsi_Cmnd * cmd) { - struct Scsi_Host *shpnt = dev; - int len = 0; - - len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun); - len += sprintf (buf + len, "I/O base address: 0x%lx\n", IM_CMD_REG); - return len; + cmd->SCp.Status++; } /*--------------------------------------------------------------------*/ /* SCSI-SCB-command for device_inquiry */ -static int device_inquiry(struct Scsi_Host *shpnt, int ldn, unsigned char *buf) +static int device_inquiry(int host_index, int ldn) { - struct im_scb scb; - struct im_tsb tsb; - int retries; - - for (retries = 0; retries < 3; retries++) - { - /*fill scb with inquiry command */ - scb.command = IM_DEVICE_INQUIRY_CMD; - scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; - scb.sys_buf_adr = virt_to_bus(buf); - scb.sys_buf_length = 255; - scb.tsb_adr = virt_to_bus(&tsb); - - /*issue scb to passed ldn, and busy wait for interrupt */ - got_interrupt = 0; - issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); - while (!got_interrupt) - barrier (); - - /*if command succesful, break */ - if (stat_result == IM_SCB_CMD_COMPLETED) - break; - } + int retries; + Scsi_Cmnd cmd; + struct im_scb *scb; + struct im_tsb *tsb; + unsigned char *buf; + + scb = &(ld(host_index)[ldn].scb); + tsb = &(ld(host_index)[ldn].tsb); + buf = (unsigned char *)(&(ld(host_index)[ldn].buf)); + ld(host_index)[ldn].tsb.dev_status = 0; /* prepare stusblock */ + + if (bypass_controller) + { /* fill the commonly known field for device-inquiry SCSI cmnd */ + cmd.cmd_len = 6; + memset (&(cmd.cmnd), 0x0, sizeof(char) * cmd.cmd_len); + cmd.cmnd[0] = INQUIRY; /* device inquiry */ + cmd.cmnd[4] = 0xff; /* return buffer size = 255 */ + } + for (retries = 0; retries < 3; retries++) + { + if (bypass_controller) + { /* bypass the hardware integrated command set */ + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->u1.scsi_cmd_length = cmd.cmd_len; + memcpy (scb->u2.scsi_command, &(cmd.cmnd), cmd.cmd_len); + last_scsi_command(host_index)[ldn] = INQUIRY; + last_scsi_type(host_index)[ldn] = IM_SCB; + } + else + { + /*fill scb with inquiry command */ + scb->command = IM_DEVICE_INQUIRY_CMD; + scb->enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + last_scsi_command(host_index)[ldn] = IM_DEVICE_INQUIRY_CMD; + last_scsi_type(host_index)[ldn] = IM_SCB; + } + scb->sys_buf_adr = virt_to_bus(buf); + scb->sys_buf_length = 0xff; /* maximum bufferlength gives max info */ + scb->tsb_adr = virt_to_bus(tsb); + + /*issue scb to passed ldn, and busy wait for interrupt */ + got_interrupt(host_index) = 0; + issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn); + while (!got_interrupt(host_index)) + barrier (); + + /*if command succesful, break */ + if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED)|| + (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES)) + { + return 1; + } + } + + /*if all three retries failed, return "no device at this ldn" */ + if (retries >= 3) + return 0; + else + return 1; +} - /*if all three retries failed, return "no device at this ldn" */ - if (retries >= 3) - return 0; - else - return 1; +static int read_capacity(int host_index, int ldn) +{ + int retries; + Scsi_Cmnd cmd; + struct im_scb *scb; + struct im_tsb *tsb; + unsigned char *buf; + + scb = &(ld(host_index)[ldn].scb); + tsb = &(ld(host_index)[ldn].tsb); + buf = (unsigned char *)(&(ld(host_index)[ldn].buf)); + ld(host_index)[ldn].tsb.dev_status = 0; + + if (bypass_controller) + { /* read capacity in commonly known default SCSI-format */ + cmd.cmd_len = 10; + memset (&(cmd.cmnd), 0x0, sizeof(char) * cmd.cmd_len); + cmd.cmnd[0] = READ_CAPACITY; /* read capacity */ + } + for (retries = 0; retries < 3; retries++) + { + /*fill scb with read capacity command */ + if (bypass_controller) + { /* bypass the SCSI-command */ + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL; + scb->u1.scsi_cmd_length = cmd.cmd_len; + memcpy (scb->u2.scsi_command, &(cmd.cmnd), cmd.cmd_len); + last_scsi_command(host_index)[ldn] = READ_CAPACITY; + last_scsi_type(host_index)[ldn] = IM_SCB; + } + else + { + scb->command = IM_READ_CAPACITY_CMD; + scb->enable = IM_READ_CONTROL; + last_scsi_command(host_index)[ldn] = IM_READ_CAPACITY_CMD; + last_scsi_type(host_index)[ldn] = IM_SCB; + } + scb->sys_buf_adr = virt_to_bus(buf); + scb->sys_buf_length = 8; + scb->tsb_adr = virt_to_bus(tsb); + + /*issue scb to passed ldn, and busy wait for interrupt */ + got_interrupt(host_index) = 0; + issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn); + while (!got_interrupt(host_index)) + barrier (); + + /*if got capacity, get block length and return one device found */ + if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED)|| + (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES)) + { + return 1; + } + } + /*if all three retries failed, return "no device at this ldn" */ + if (retries >= 3) + return 0; + else + return 1; } /* SCSI-immediate-command for assign. This functions maps/unmaps specific - ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the - subsystem and for dynamical remapping od ldns. */ -static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, + ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the + subsystem and for dynamical remapping od ldns. */ +static int immediate_assign(int host_index, unsigned int pun, unsigned int lun, unsigned int ldn, unsigned int operation) { int retries; unsigned long imm_command; - + for (retries=0; retries<3; retries ++) - { - imm_command = inl(IM_CMD_REG); + { + imm_command = inl(IM_CMD_REG(host_index)); imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */ imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD); imm_command |= (unsigned long)((lun & 7) << 24); @@ -1101,14 +949,64 @@ static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, imm_command |= (unsigned long)((pun & 7) << 20); imm_command |= (unsigned long)((ldn & 15) << 16); - got_interrupt = 0; - issue_cmd (shpnt, (unsigned long)(imm_command), IM_IMM_CMD | 0xf); - while (!got_interrupt) - barrier (); - + last_scsi_command(host_index)[0xf] = IM_ASSIGN_IMM_CMD; + last_scsi_type(host_index)[0xf] = IM_IMM_CMD; + got_interrupt(host_index) = 0; + issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | 0xf); + while (!got_interrupt(host_index)) + barrier (); + + /*if command succesful, break */ + if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED) + { + return 1; + } + } + + if (retries >= 3) + return 0; + else + return 1; +} + +#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET +static int immediate_reset(int host_index, unsigned int ldn) +{ + int retries; + int ticks; + unsigned long imm_command; + + for (retries=0; retries<3; retries ++) + { + imm_command = inl(IM_CMD_REG(host_index)); + imm_command &= (unsigned long)(0xFFFF0000); /* keep reserved bits */ + imm_command |= (unsigned long)(IM_RESET_IMM_CMD); + last_scsi_command(host_index)[ldn] = IM_RESET_IMM_CMD; + last_scsi_type(host_index)[ldn] = IM_IMM_CMD; + + got_interrupt(host_index) = 0; + reset_status(host_index) = IM_RESET_IN_PROGRESS; + issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | ldn); + ticks = IM_RESET_DELAY*HZ; + while (reset_status(host_index) == IM_RESET_IN_PROGRESS && --ticks) + { + mdelay(1+999/HZ); + barrier(); + } + /* if reset did not complete, just claim */ + if (!ticks) + { + printk("IBM MCA SCSI: reset did not complete within %d seconds.\n", + IM_RESET_DELAY); + reset_status(host_index) = IM_RESET_FINISHED_OK; + /* did not work, finish */ + return 1; + } /*if command succesful, break */ - if (stat_result == IM_IMMEDIATE_CMD_COMPLETED) - break; + if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED) + { + return 1; + } } if (retries >= 3) @@ -1116,24 +1014,25 @@ static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, else return 1; } +#endif /* type-interpreter for physical device numbers */ static char *ti_p(int value) { switch (value) { - case TYPE_IBM_SCSI_ADAPTER: return("A"); break; - case TYPE_DISK: return("D"); break; - case TYPE_TAPE: return("T"); break; - case TYPE_PROCESSOR: return("P"); break; - case TYPE_WORM: return("W"); break; - case TYPE_ROM: return("R"); break; - case TYPE_SCANNER: return("S"); break; - case TYPE_MOD: return("M"); break; - case TYPE_MEDIUM_CHANGER: return("C"); break; - case TYPE_NO_LUN: return("+"); break; /* show NO_LUN */ - case TYPE_NO_DEVICE: - default: return("-"); break; + case TYPE_IBM_SCSI_ADAPTER: return("A"); break; + case TYPE_DISK: return("D"); break; + case TYPE_TAPE: return("T"); break; + case TYPE_PROCESSOR: return("P"); break; + case TYPE_WORM: return("W"); break; + case TYPE_ROM: return("R"); break; + case TYPE_SCANNER: return("S"); break; + case TYPE_MOD: return("M"); break; + case TYPE_MEDIUM_CHANGER: return("C"); break; + case TYPE_NO_LUN: return("+"); break; /* show NO_LUN */ + case TYPE_NO_DEVICE: + default: return("-"); break; } return("-"); } @@ -1141,9 +1040,9 @@ static char *ti_p(int value) /* interpreter for logical device numbers (ldn) */ static char *ti_l(int value) { - const char hex[16] = ("0123456789abcdef"); + const char hex[16] = "0123456789abcdef"; static char answer[2]; - + answer[1] = (char)(0x0); if (value<=MAX_LOG_DEV) answer[0] = hex[value]; @@ -1154,182 +1053,181 @@ static char *ti_l(int value) } /* - The following routine probes the SCSI-devices in four steps: - 1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter. - 2. ldn 0 is used to go through all possible combinations of pun,lun and - a device_inquiry is done to fiddle out whether there is a device - responding or not. This physical map is stored in get_scsi[][]. - 3. The 15 available ldns (0-14) are mapped to existing pun,lun. - If there are more devices than ldns, it stops at 14 for the boot - time. Dynamical remapping will be done in ibmmca_queuecommand. - 4. If there are less than 15 valid pun,lun, the remaining ldns are - mapped to NON-existing pun,lun to satisfy the adapter. Information - about pun,lun -> ldn is stored as before in get_ldn[][]. - This method leads to the result, that the SCSI-pun,lun shown to Linux - mid-level- and higher-level-drivers is exactly corresponding to the - physical reality on the SCSI-bus. Therefore, it is possible that users - of older releases of this driver have to rewrite their fstab-file, because - the /dev/sdXXX could have changed due to the right pun,lun report, now. - The assignment of ALL ldns avoids dynamical remapping by the adapter - itself. + The following routine probes the SCSI-devices in four steps: + 1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter. + 2. ldn 0 is used to go through all possible combinations of pun,lun and + a device_inquiry is done to fiddle out whether there is a device + responding or not. This physical map is stored in get_scsi[][]. + 3. The 15 available ldns (0-14) are mapped to existing pun,lun. + If there are more devices than ldns, it stops at 14 for the boot + time. Dynamical remapping will be done in ibmmca_queuecommand. + 4. If there are less than 15 valid pun,lun, the remaining ldns are + mapped to NON-existing pun,lun to satisfy the adapter. Information + about pun,lun -> ldn is stored as before in get_ldn[][]. + This method leads to the result, that the SCSI-pun,lun shown to Linux + mid-level- and higher-level-drivers is exactly corresponding to the + physical reality on the SCSI-bus. Therefore, it is possible that users + of older releases of this driver have to rewrite their fstab-file, because + the /dev/sdXXX could have changed due to the right pun,lun report, now. + The assignment of ALL ldns avoids dynamical remapping by the adapter + itself. */ -static void check_devices (struct Scsi_Host *shpnt) +static void check_devices (int host_index) { - int id, lun, ldn; - unsigned char buf[256]; - int count_devices = 0; /* local counter for connected device */ - - /* assign default values to certain variables */ - - IBM_DS.dyn_flag = 0; /* normally no need for dynamical ldn management */ - next_ldn = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/ - last_scsi_command = 0; /* emptify last SCSI-command storage */ - - /* initialize the very important driver-informational arrays/structs */ - memset (ld, 0, sizeof ld); - memset (get_ldn, TYPE_NO_DEVICE, sizeof get_ldn); /* this is essential ! */ - memset (get_scsi, TYPE_NO_DEVICE, sizeof get_scsi); /* this is essential ! */ - - for (lun=0; lun<8; lun++) /* mark the adapter at its pun on all luns*/ - { - get_scsi[subsystem_pun][lun] = TYPE_IBM_SCSI_ADAPTER; - get_ldn[subsystem_pun][lun] = MAX_LOG_DEV; /* make sure, the subsystem - ldn is active for all - luns. */ - } - - /* STEP 1: */ - printk("IBM MCA SCSI: Removing current logical SCSI-device mapping."); - for (ldn=0; ldn<MAX_LOG_DEV; ldn++) - { + int id, lun, ldn, ticks; + int count_devices; /* local counter for connected device */ + + /* assign default values to certain variables */ + + ticks = 0; + count_devices = 0; + IBM_DS(host_index).dyn_flag = 0; /* normally no need for dynamical ldn management */ + IBM_DS(host_index).total_errors = 0; /* set errorcounter to 0 */ + next_ldn(host_index) = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/ + for (ldn=0; ldn<=MAX_LOG_DEV; ldn++) + { + last_scsi_command(host_index)[ldn] = NO_SCSI; /* emptify last SCSI-command storage */ + last_scsi_type(host_index)[ldn] = 0; + } + + /* initialize the very important driver-informational arrays/structs */ + memset (ld(host_index), 0, + sizeof(ld(host_index))); + memset (get_ldn(host_index), TYPE_NO_DEVICE, + sizeof(get_ldn(host_index))); /* this is essential ! */ + memset (get_scsi(host_index), TYPE_NO_DEVICE, + sizeof(get_scsi(host_index))); /* this is essential ! */ + + for (lun=0; lun<8; lun++) /* mark the adapter at its pun on all luns*/ + { + get_scsi(host_index)[subsystem_pun(host_index)][lun] = TYPE_IBM_SCSI_ADAPTER; + get_ldn(host_index)[subsystem_pun(host_index)][lun] = MAX_LOG_DEV; /* make sure, the subsystem + ldn is active for all + luns. */ + } + + /* STEP 1: */ #ifdef IM_DEBUG_PROBE - printk("."); + printk("IBM MCA SCSI: Current SCSI-host index: %d\n",host_index); #endif - immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); /* remove ldn (wherever)*/ - } - - lun = 0; /* default lun is 0 */ + printk("IBM MCA SCSI: Removing default logical SCSI-device mapping."); + for (ldn=0; ldn<MAX_LOG_DEV; ldn++) + { +#ifdef IM_DEBUG_PROBE + printk("."); +#endif + immediate_assign(host_index,0,0,ldn,REMOVE_LDN); /* remove ldn (wherever)*/ + } - /* STEP 2: */ - printk("\nIBM MCA SCSI: Probing SCSI-devices."); - for (id=0; id<8; id++) + lun = 0; /* default lun is 0 */ + + /* STEP 2: */ + printk("\nIBM MCA SCSI: Probing SCSI-devices."); + for (id=0; id<8; id++) #ifdef CONFIG_SCSI_MULTI_LUN - for (lun=0; lun<8; lun++) + for (lun=0; lun<8; lun++) #endif - { + { #ifdef IM_DEBUG_PROBE printk("."); #endif - if (id != subsystem_pun) + if (id != subsystem_pun(host_index)) { /* if pun is not the adapter: */ - immediate_assign(shpnt,id,lun,0,SET_LDN); /*set ldn=0 to pun,lun*/ - if (device_inquiry(shpnt, 0, buf)) /* probe device */ - { - get_scsi[id][lun]=(unsigned char)buf[0]; /* entry, even - for NO_LUN */ - if (buf[0] != TYPE_NO_LUN) - count_devices++; /* a existing device is found */ - } - immediate_assign(shpnt,id,lun,0,REMOVE_LDN); /* remove ldn */ + /*set ldn=0 to pun,lun*/ + immediate_assign(host_index,id,lun,PROBE_LDN,SET_LDN); + if (device_inquiry(host_index, PROBE_LDN)) /* probe device */ + { + get_scsi(host_index)[id][lun]= + (unsigned char)(ld(host_index)[PROBE_LDN].buf[0]); + /* entry, even for NO_LUN */ + if (ld(host_index)[PROBE_LDN].buf[0] != TYPE_NO_LUN) + count_devices++; /* a existing device is found */ + } + /* remove ldn */ + immediate_assign(host_index,id,lun,PROBE_LDN,REMOVE_LDN); } - } - - /* STEP 3: */ - printk("\nIBM MCA SCSI: Mapping SCSI-devices."); + } + + /* STEP 3: */ + printk("\nIBM MCA SCSI: Mapping SCSI-devices."); + + ldn = 0; + lun = 0; - ldn = 0; - lun = 0; - #ifdef CONFIG_SCSI_MULTI_LUN - for (lun=0; lun<8 && ldn<MAX_LOG_DEV; lun++) + for (lun=0; lun<8 && ldn<MAX_LOG_DEV; lun++) #endif - for (id=0; id<8 && ldn<MAX_LOG_DEV; id++) - { + for (id=0; id<8 && ldn<MAX_LOG_DEV; id++) + { #ifdef IM_DEBUG_PROBE printk("."); #endif - if (id != subsystem_pun) + if (id != subsystem_pun(host_index)) { - if (get_scsi[id][lun] != TYPE_NO_LUN && - get_scsi[id][lun] != TYPE_NO_DEVICE) - { - /* Only map if accepted type. Always enter for + if (get_scsi(host_index)[id][lun] != TYPE_NO_LUN && + get_scsi(host_index)[id][lun] != TYPE_NO_DEVICE) + { + /* Only map if accepted type. Always enter for lun == 0 to get no gaps into ldn-mapping for ldn<7. */ - immediate_assign(shpnt,id,lun,ldn,SET_LDN); - get_ldn[id][lun]=ldn; /* map ldn */ - if (device_exists (shpnt, ldn, &ld[ldn].block_length, - &ld[ldn].device_type)) - { + immediate_assign(host_index,id,lun,ldn,SET_LDN); + get_ldn(host_index)[id][lun]=ldn; /* map ldn */ + if (device_exists (host_index, ldn, + &ld(host_index)[ldn].block_length, + &ld(host_index)[ldn].device_type)) + { #ifdef CONFIG_IBMMCA_SCSI_DEV_RESET - int ticks; - printk("(resetting)"); - ticks = IM_RESET_DELAY*HZ; - reset_status = IM_RESET_IN_PROGRESS; - issue_cmd (shpnt, IM_RESET_IMM_CMD, IM_IMM_CMD | ldn); - while (reset_status == IM_RESET_IN_PROGRESS && --ticks) - { - mdelay(1+999/HZ); - barrier(); - } - /* if reset did not complete, just claim */ - if (!ticks) - { - printk("IBM MCA SCSI: reset did not complete within %d seconds.\n", - IM_RESET_DELAY); - reset_status = IM_RESET_FINISHED_OK; - /* did not work, finish */ - } + printk("resetting device at ldn=%x ... ",ldn); + immediate_reset(host_index,ldn); #endif - ldn++; - } - else - { - /* device vanished, probably because we don't know how to - * handle it or because it has problems */ - if (lun > 0) - { - /* remove mapping */ - get_ldn[id][lun]=TYPE_NO_DEVICE; - immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); - } - else ldn++; - } - } - else if (lun == 0) - { - /* map lun == 0, even if no device exists */ - immediate_assign(shpnt,id,lun,ldn,SET_LDN); - get_ldn[id][lun]=ldn; /* map ldn */ - ldn++; - } + ldn++; + } + else + { + /* device vanished, probably because we don't know how to + * handle it or because it has problems */ + if (lun > 0) + { + /* remove mapping */ + get_ldn(host_index)[id][lun]=TYPE_NO_DEVICE; + immediate_assign(host_index,0,0,ldn,REMOVE_LDN); + } + else ldn++; + } + } + else if (lun == 0) + { + /* map lun == 0, even if no device exists */ + immediate_assign(host_index,id,lun,ldn,SET_LDN); + get_ldn(host_index)[id][lun]=ldn; /* map ldn */ + ldn++; + } } - } - + } + /* STEP 4: */ /* map remaining ldns to non-existing devices */ for (lun=1; lun<8 && ldn<MAX_LOG_DEV; lun++) for (id=0; id<8 && ldn<MAX_LOG_DEV; id++) { - if (get_scsi[id][lun] == TYPE_NO_LUN || - get_scsi[id][lun] == TYPE_NO_DEVICE) + if (get_scsi(host_index)[id][lun] == TYPE_NO_LUN || + get_scsi(host_index)[id][lun] == TYPE_NO_DEVICE) { /* Map remaining ldns only to NON-existing pun,lun - combinations to make sure an inquiry will fail. - For MULTI_LUN, it is needed to avoid adapter autonome - SCSI-remapping. */ - immediate_assign(shpnt,id,lun,ldn,SET_LDN); - get_ldn[id][lun]=ldn; + combinations to make sure an inquiry will fail. + For MULTI_LUN, it is needed to avoid adapter autonome + SCSI-remapping. */ + immediate_assign(host_index,id,lun,ldn,SET_LDN); + get_ldn(host_index)[id][lun]=ldn; ldn++; } } - + printk("\n"); -#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD - printk("IBM MCA SCSI: SCSI-access-order: IBM/ANSI.\n"); -#else - printk("IBM MCA SCSI: SCSI-access-order: Linux.\n"); -#endif + if (ibm_ansi_order) + printk("IBM MCA SCSI: Device order: IBM/ANSI (pun=7 is first).\n"); + else + printk("IBM MCA SCSI: Device order: New Industry Standard (pun=0 is first).\n"); #ifdef IM_DEBUG_PROBE /* Show the physical and logical mapping during boot. */ @@ -1338,336 +1236,525 @@ static void check_devices (struct Scsi_Host *shpnt) printk("ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n"); for (id=0; id<8; id++) { - printk("%2d %2s %2s %2s %2s %2s %2s %2s %2s", - id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]), - ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]), - ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]), - ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7])); - - printk(" %2d ",id); + printk("%2d ",id); + for (lun=0; lun<8; lun++) + printk("%2s ",ti_p(get_scsi(host_index)[id][lun])); + printk(" %2d ",id); for (lun=0; lun<8; lun++) - printk("%2s ",ti_l(get_ldn[id][lun])); + printk("%2s ",ti_l(get_ldn(host_index)[id][lun])); printk("\n"); } #endif - + /* assign total number of found SCSI-devices to the statistics struct */ - IBM_DS.total_scsi_devices = count_devices; - + IBM_DS(host_index).total_scsi_devices = count_devices; + /* decide for output in /proc-filesystem, if the configuration of - SCSI-devices makes dynamical reassignment of devices necessary */ + SCSI-devices makes dynamical reassignment of devices necessary */ if (count_devices>=MAX_LOG_DEV) - IBM_DS.dyn_flag = 1; /* dynamical assignment is necessary */ + IBM_DS(host_index).dyn_flag = 1; /* dynamical assignment is necessary */ else - IBM_DS.dyn_flag = 0; /* dynamical assignment is not necessary */ - + IBM_DS(host_index).dyn_flag = 0; /* dynamical assignment is not necessary */ + /* If no SCSI-devices are assigned, return 1 in order to cause message. */ if (ldn == 0) - printk("IBM MCA SCSI: Warning: No SCSI-devices found/assignable!\n"); - - /* reset the counters for statistics on the current adapter */ - IBM_DS.total_accesses = 0; - IBM_DS.total_interrupts = 0; - IBM_DS.dynamical_assignments = 0; - memset (IBM_DS.ldn_access, 0x0, sizeof (IBM_DS.ldn_access)); - memset (IBM_DS.ldn_read_access, 0x0, sizeof (IBM_DS.ldn_read_access)); - memset (IBM_DS.ldn_write_access, 0x0, sizeof (IBM_DS.ldn_write_access)); - memset (IBM_DS.ldn_inquiry_access, 0x0, sizeof (IBM_DS.ldn_inquiry_access)); - memset (IBM_DS.ldn_modeselect_access, 0x0, sizeof (IBM_DS.ldn_modeselect_access)); - memset (IBM_DS.ldn_assignments, 0x0, sizeof (IBM_DS.ldn_assignments)); - - return; + printk("IBM MCA SCSI: Warning: No SCSI-devices found/assigned!\n"); + + /* reset the counters for statistics on the current adapter */ + IBM_DS(host_index).total_accesses = 0; + IBM_DS(host_index).total_interrupts = 0; + IBM_DS(host_index).dynamical_assignments = 0; + memset (IBM_DS(host_index).ldn_access, 0x0, + sizeof (IBM_DS(host_index).ldn_access)); + memset (IBM_DS(host_index).ldn_read_access, 0x0, + sizeof (IBM_DS(host_index).ldn_read_access)); + memset (IBM_DS(host_index).ldn_write_access, 0x0, + sizeof (IBM_DS(host_index).ldn_write_access)); + memset (IBM_DS(host_index).ldn_inquiry_access, 0x0, + sizeof (IBM_DS(host_index).ldn_inquiry_access)); + memset (IBM_DS(host_index).ldn_modeselect_access, 0x0, + sizeof (IBM_DS(host_index).ldn_modeselect_access)); + memset (IBM_DS(host_index).ldn_assignments, 0x0, + sizeof (IBM_DS(host_index).ldn_assignments)); + + return; } /*--------------------------------------------------------------------*/ -static int -device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, - int *device_type) +static int device_exists (int host_index, int ldn, int *block_length, + int *device_type) { - struct im_scb scb; - struct im_tsb tsb; - unsigned char buf[256]; - int retries; - - /* if no valid device found, return immediately with 0 */ - if (!(device_inquiry(shpnt, ldn, buf))) return 0; - - /*if device is CD_ROM, assume block size 2048 and return */ - if (buf[0] == TYPE_ROM) - { - *device_type = TYPE_ROM; - *block_length = 2048; /* (standard blocksize for yellow-/red-book) */ - return 1; - } - - if (buf[0] == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM - therefore, the block_length is also 2048. */ - { - *device_type = TYPE_WORM; - *block_length = 2048; - return 1; - } - - /* if device is disk, use "read capacity" to find its block size */ - if (buf[0] == TYPE_DISK) - { - *device_type = TYPE_DISK; - - for (retries = 0; retries < 3; retries++) - { - /*fill scb with read capacity command */ - scb.command = IM_READ_CAPACITY_CMD; - scb.enable = IM_READ_CONTROL; - scb.sys_buf_adr = virt_to_bus(buf); - scb.sys_buf_length = 8; - scb.tsb_adr = virt_to_bus(&tsb); - - /*issue scb to passed ldn, and busy wait for interrupt */ - got_interrupt = 0; - issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); - while (!got_interrupt) - barrier (); - - /*if got capacity, get block length and return one device found */ - if (stat_result == IM_SCB_CMD_COMPLETED) - { - *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); - return 1; - } - } - - /*if all three retries failed, return "no device at this ldn" */ - if (retries >= 3) - return 0; - } - - /* if this is a magneto-optical drive, treat it like a harddisk */ - if (buf[0] == TYPE_MOD) - { - *device_type = TYPE_MOD; - - for (retries = 0; retries < 3; retries++) - { - /*fill scb with read capacity command */ - scb.command = IM_READ_CAPACITY_CMD; - scb.enable = IM_READ_CONTROL; - scb.sys_buf_adr = virt_to_bus(buf); - scb.sys_buf_length = 8; - scb.tsb_adr = virt_to_bus(&tsb); - - /*issue scb to passed ldn, and busy wait for interrupt */ - got_interrupt = 0; - issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); - while (!got_interrupt) - barrier (); - - /*if got capacity, get block length and return one device found */ - if (stat_result == IM_SCB_CMD_COMPLETED) - { - *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); - return 1; - } - } - - /*if all three retries failed, return "no device at this ldn" */ - if (retries >= 3) - return 0; - } - - if (buf[0] == TYPE_TAPE) /* TAPE-device found */ - { - *device_type = TYPE_TAPE; - *block_length = 0; /* not in use (setting by mt and mtst in op.) */ - return 1; - } - - if (buf[0] == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/ - { - *device_type = TYPE_PROCESSOR; - *block_length = 0; /* they set their stuff on drivers */ - return 1; - } - - if (buf[0] == TYPE_SCANNER) /* other SCSI-scanners */ - { - *device_type = TYPE_SCANNER; - *block_length = 0; /* they set their stuff on drivers */ - return 1; - } - - if (buf[0] == TYPE_MEDIUM_CHANGER) /* Medium-Changer */ - { - *device_type = TYPE_MEDIUM_CHANGER; - *block_length = 0; /* One never knows, what to expect on a medium - changer device. */ - return 1; - } + unsigned char *buf; + + /* if no valid device found, return immediately with 0 */ + if (!(device_inquiry(host_index, ldn))) + return 0; + + buf = (unsigned char *)(&(ld(host_index)[ldn].buf)); - /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are - ignored! MO-drives are now supported and treated as harddisk. */ - return 0; + /*if device is CD_ROM, assume block size 2048 and return */ + if (*buf == TYPE_ROM) + { + *device_type = TYPE_ROM; + *block_length = 2048; /* (standard blocksize for yellow-/red-book) */ + return 1; + } + + if (*buf == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM + therefore, the block_length is also 2048. */ + { + *device_type = TYPE_WORM; + *block_length = 2048; + return 1; + } + + /* if device is disk, use "read capacity" to find its block size */ + if (*buf == TYPE_DISK) + { + *device_type = TYPE_DISK; + if (read_capacity( host_index, ldn)) + { + *block_length = *(buf+7) + (*(buf+6) << 8) + + (*(buf+5) << 16) + (*(buf+4) << 24); + return 1; + } + else + return 0; + } + + /* if this is a magneto-optical drive, treat it like a harddisk */ + if (*buf == TYPE_MOD) + { + *device_type = TYPE_MOD; + if (read_capacity( host_index, ldn)) + { + *block_length = *(buf+7) + (*(buf+6) << 8) + + (*(buf+5) << 16) + (*(buf+4) << 24); + return 1; + } + else + return 0; + } + + if (*buf == TYPE_TAPE) /* TAPE-device found */ + { + *device_type = TYPE_TAPE; + *block_length = 0; /* not in use (setting by mt and mtst in op.) */ + return 1; + } + + if (*buf == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/ + { + *device_type = TYPE_PROCESSOR; + *block_length = 0; /* they set their stuff on drivers */ + return 1; + } + + if (*buf == TYPE_SCANNER) /* other SCSI-scanners */ + { + *device_type = TYPE_SCANNER; + *block_length = 0; /* they set their stuff on drivers */ + return 1; + } + + if (*buf == TYPE_MEDIUM_CHANGER) /* Medium-Changer */ + { + *device_type = TYPE_MEDIUM_CHANGER; + *block_length = 0; /* One never knows, what to expect on a medium + changer device. */ + return 1; + } + + /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are + ignored! MO-drives are now supported and treated as harddisk. */ + return 0; } /*--------------------------------------------------------------------*/ - + #ifdef CONFIG_SCSI_IBMMCA -void -ibmmca_scsi_setup (char *str, int *ints) +void ibmmca_scsi_setup (char *str, int *ints) { - if( str && !strcmp( str, "display" ) ) { - use_display = 1; - } else if( ints ) { - int i; - for (i = 0; i < IM_MAX_HOSTS && 2*i+2 < ints[0]; i++) { - io_port[i] = ints[2*i+2]; - scsi_id[i] = ints[2*i+2]; - } - } + int i, j, io_base, id_base; + char *token; + + io_base = 0; + id_base = 0; + + if (str) + { + token = strtok(str,","); + j = 0; + while (token) + { + if (!strcmp(token,"display")) + { + use_display = 1; + } + if (!strcmp(token,"adisplay")) + { + use_adisplay = 1; + } + if (!strcmp(token,"bypass")) + { + bypass_controller = 1; + } + if (!strcmp(token,"normal")) + { + ibm_ansi_order = 0; + } + if (!strcmp(token,"ansi")) + { + ibm_ansi_order = 1; + } + if ( (*token == '-') || (isdigit(*token)) ) + { + if (!(j%2) && (io_base < IM_MAX_HOSTS)) + { + io_port[io_base++] = simple_strtoul(token,NULL,0); + } + if ((j%2) && (id_base < IM_MAX_HOSTS)) + { + scsi_id[id_base++] = simple_strtoul(token,NULL,0); + } + j++; + } + token = strtok(NULL,","); + } + } + else if (ints) + { + for (i = 0; i < IM_MAX_HOSTS && 2*i+2 < ints[0]; i++) + { + io_port[i] = ints[2*i+2]; + scsi_id[i] = ints[2*i+2]; + } + } + return; } #endif /*--------------------------------------------------------------------*/ -int -ibmmca_detect (Scsi_Host_Template * template) +static int ibmmca_getinfo (char *buf, int slot, void *dev) { - struct Scsi_Host *shpnt; - int port, id, i, list_size, slot; - unsigned pos2, pos3; - - /* if this is not MCA machine, return "nothing found" */ - if (!MCA_bus) - return 0; - - /* get interrupt request level */ - if (request_irq (IM_IRQ, do_interrupt_handler, SA_SHIRQ, "ibmmca", hosts)) - { - printk("IBM MCA SCSI: Unable to get IRQ %d.\n", IM_IRQ); - return 0; - } - - /* if ibmmcascsi setup option was passed to kernel, return "found" */ - for (i = 0; i < IM_MAX_HOSTS; i++) - if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8) - { - printk("IBM MCA SCSI: forced detection, io=0x%x, scsi id=%d.\n", - io_port[i], scsi_id[i]); - ibmmca_register(template, io_port[i], scsi_id[i]); - } - if (found) return found; - - /* - * Patched by ZP Gu to work with the 9556 as well; the 9556 has - * pos2 = 05, but it should be 00, as it should be interfaced - * via port = 0x3540. - */ - - /* first look for the SCSI integrated on the motherboard */ - pos2 = mca_read_stored_pos(MCA_INTEGSCSI, 2); -// if (pos2 != 0xff) { - if ((pos2 & 1) == 0) { - port = IM_IO_PORT + ((pos2 & 0x0e) << 2); - } else { - port = IM_IO_PORT; - } - pos3 = mca_read_stored_pos(MCA_INTEGSCSI, 3); - id = (pos3 & 0xe0) >> 5; - - printk("IBM MCA SCSI: integrated SCSI found, io=0x%x, scsi id=%d.\n", - port, id); - if ((shpnt = ibmmca_register(template, port, id))) - { - mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI"); - mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, - shpnt); - } -// } - - /* now look for other adapters */ - list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct); - for (i = 0; i < list_size; i++) - { - slot = 0; - while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot)) - != MCA_NOTFOUND) - { - pos2 = mca_read_stored_pos(slot, 2); - pos3 = mca_read_stored_pos(slot, 3); - port = IM_IO_PORT + ((pos2 & 0x0e) << 2); - id = (pos3 & 0xe0) >> 5; - printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n", - subsys_list[i].description, slot + 1, port, id); - if ((shpnt = ibmmca_register(template, port, id))) - { - mca_set_adapter_name (slot, subsys_list[i].description); - mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo, - shpnt); - } - slot++; - } - } - - if (!found) { - free_irq (IM_IRQ, hosts); - printk("IBM MCA SCSI: No adapter attached.\n"); - } - - return found; + struct Scsi_Host *shpnt; + int len, special; + unsigned int pos2, pos3; + static unsigned long flags; + +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&info_lock, flags); +#endif + + shpnt = dev; /* assign host-structure to local pointer */ + len = 0; /* set filled text-buffer index to 0 */ + /* get the _special contents of the hostdata structure */ + special = ((struct ibmmca_hostdata *)shpnt->hostdata)->_special; + pos2 = ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2; + pos3 = ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3; + + if (special == FORCED_DETECTION) /* forced detection */ + { + len += sprintf (buf + len, "Adapter cathegory: forced detected\n"); + len += sprintf(buf + len, "***************************************\n"); + len += sprintf(buf + len, "*** Forced detected SCSI Adapter ***\n"); + len += sprintf(buf + len, "*** No chip-information available ***\n"); + len += sprintf(buf + len, "***************************************\n"); + } + else if (special == INTEGRATED_SCSI) + { /* if the integrated subsystem has been found automatically: */ + len += sprintf (buf + len, "Adapter cathegory: integrated\n"); + len += sprintf (buf + len, "Chip revision level: %d\n", + ((pos2 & 0xf0) >> 4)); + len += sprintf (buf + len, "Chip status: %s\n", + (pos2 & 1) ? "enabled" : "disabled"); + len += sprintf (buf + len, "8 kByte NVRAM status: %s\n", + (pos2 & 2) ? "locked" : "accessible"); + } + else if ((special>=0)&& + (special<(sizeof(subsys_list)/sizeof(struct subsys_list_struct)))) + { /* if the subsystem is a slot adapter */ + len += sprintf (buf + len, "Adapter cathegory: slot-card\n"); + len += sprintf (buf + len, "Chip revision level: %d\n", + ((pos2 & 0xf0) >> 4)); + len += sprintf (buf + len, "Chip status: %s\n", + (pos2 & 1) ? "enabled" : "disabled"); + len += sprintf (buf + len, "Port offset: 0x%x\n", + ((pos2 & 0x0e) << 2)); + } + else + { + len += sprintf (buf + len, "Adapter cathegory: unknown\n"); + } + /* common subsystem information to write to the slotn file */ + len += sprintf (buf + len, "Subsystem PUN: %d\n", shpnt->this_id); + len += sprintf (buf + len, "I/O base address range: 0x%x-0x%x", + (unsigned int)(shpnt->io_port), + (unsigned int)(shpnt->io_port+7)); + /* Now make sure, the bufferlength is devideable by 4 to avoid + * paging problems of the buffer. */ + while ( len % sizeof( int ) != ( sizeof ( int ) - 1 ) ) + { + len += sprintf (buf + len, " "); + } + len += sprintf (buf + len, "\n"); + +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&info_lock, flags); +#endif + return len; +} + +int ibmmca_detect (Scsi_Host_Template * scsi_template) +{ + struct Scsi_Host *shpnt; + int port, id, i, j, list_size, slot; + + found = 0; /* make absolutely sure, that found is set to 0 */ + + /* if this is not MCA machine, return "nothing found" */ + if (!MCA_bus) + { + printk("IBM MCA SCSI: No Microchannel-bus support present -> Aborting.\n"); + return 0; + } + else + printk("IBM MCA SCSI: Version %s\n",IBMMCA_SCSI_DRIVER_VERSION); + + /* get interrupt request level */ +#ifdef OLDKERN + if (request_irq (IM_IRQ, interrupt_handler, SA_SHIRQ, "ibmmcascsi", + hosts)) +#else + if (request_irq (IM_IRQ, do_interrupt_handler, SA_SHIRQ, "ibmmcascsi", + hosts)) +#endif + { + printk("IBM MCA SCSI: Unable to get shared IRQ %d.\n", IM_IRQ); + return 0; + } + + /* if ibmmcascsi setup option was passed to kernel, return "found" */ + for (i = 0; i < IM_MAX_HOSTS; i++) + if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8) + { + printk("IBM MCA SCSI: forced detected SCSI Adapter, io=0x%x, scsi id=%d.\n", + io_port[i], scsi_id[i]); + if ((shpnt = ibmmca_register(scsi_template, io_port[i], scsi_id[i], + "forced detected SCSI Adapter"))) + { + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = 0; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = 0; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = + FORCED_DETECTION; + mca_set_adapter_name(MCA_INTEGSCSI, "forced detected SCSI Adapter"); + mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + mca_mark_as_used(MCA_INTEGSCSI); + } + } + if (found) return found; + + /* The POS2-register of all PS/2 model SCSI-subsystems has the following + * interpretation of bits: + * Bit 7 - 4 : Chip Revision ID (Release) + * Bit 3 - 2 : Reserved + * Bit 1 : 8k NVRAM Disabled + * Bit 0 : Chip Enable (EN-Signal) + * The POS3-register is interpreted as follows: + * Bit 7 - 5 : SCSI ID + * Bit 4 : Reserved = 0 + * Bit 3 - 0 : Reserved = 0 + * (taken from "IBM, PS/2 Hardware Interface Technical Reference, Common + * Interfaces (1991)"). + * In short words, this means, that IBM PS/2 machines only support + * 1 single subsystem by default. The slot-adapters must have another + * configuration on pos2. Here, one has to assume the following + * things for POS2-register: + * Bit 7 - 4 : Chip Revision ID (Release) + * Bit 3 - 1 : port offset factor + * Bit 0 : Chip Enable (EN-Signal) + * As I found a patch here, setting the IO-registers to 0x3540 forced, + * as there was a 0x05 in POS2 on a model 56, I assume, that the + * port 0x3540 must be fix for integrated SCSI-controllers. + * Ok, this discovery leads to the following implementation: (M.Lang) */ + + /* first look for the IBM SCSI integrated subsystem on the motherboard */ + for (j=0;j<8;j++) /* read the pos-information */ + pos[j] = mca_read_stored_pos(MCA_INTEGSCSI,j); + /* pos2 = pos3 = 0xff if there is no integrated SCSI-subsystem present */ + if (( pos[2] != 0xff) || (pos[3] != 0xff )) + { + if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */ + { + port = IM_IO_PORT; + } + else + { /* if disabled, no IRQs will be generated, as the chip won't + * listen to the incomming commands and will do really nothing, + * except for listening to the pos-register settings. If this + * happens, I need to hugely think about it, as one has to + * write something to the MCA-Bus pos register in order to + * enable the chip. Normally, IBM-SCSI won't pass the POST, + * when the chip is disabled (see IBM tech. ref.). */ + port = IM_IO_PORT; /* anyway, set the portnumber and warn */ + printk("IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n"); + printk(" SCSI-operations may not work.\n"); + } + id = (pos[3] & 0xe0) >> 5; /* this is correct and represents the PUN */ + + /* give detailed information on the subsystem. This helps me + * additionally during debugging and analyzing bug-reports. */ + printk("IBM MCA SCSI: IBM Integrated SCSI Controller found, io=0x%x, scsi id=%d,\n", + port, id); + printk(" chip rev.=%d, 8K NVRAM=%s, subsystem=%s\n", + ((pos[2] & 0xf0) >> 4), (pos[2] & 2) ? "locked" : "accessible", + (pos[2] & 1) ? "enabled." : "disabled."); + + /* register the found integrated SCSI-subsystem */ + if ((shpnt = ibmmca_register(scsi_template, port, id, + "IBM Integrated SCSI Controller"))) + { + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = pos[2]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = pos[3]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = + INTEGRATED_SCSI; + mca_set_adapter_name(MCA_INTEGSCSI, "IBM Integrated SCSI Controller"); + mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + mca_mark_as_used(MCA_INTEGSCSI); + } + } + + /* now look for other adapters in MCA slots, */ + /* determine the number of known IBM-SCSI-subsystem types */ + /* see the pos[2] dependence to get the adapter port-offset. */ + list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct); + for (i = 0; i < list_size; i++) + { /* scan each slot for a fitting adapter id */ + slot = 0; /* start at slot 0 */ + while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot)) + != MCA_NOTFOUND) + { /* scan through all slots */ + for (j=0;j<8;j++) /* read the pos-information */ + pos[j] = mca_read_stored_pos(slot, j); + if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */ + { /* (explanations see above) */ + port = IM_IO_PORT + ((pos[2] & 0x0e) << 2); + } + else + { /* anyway, set the portnumber and warn */ + port = IM_IO_PORT + ((pos[2] & 0x0e) << 2); + printk("IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n"); + printk(" SCSI-operations may not work.\n"); + } + id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */ + printk("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d,\n", + subsys_list[i].description, slot + 1, port, id); + printk(" chip rev.=%d, port-offset=0x%x, subsystem=%s\n", + ((pos[2] & 0xf0) >> 4), + ((pos[2] & 0x0e) << 2), + (pos[2] & 1) ? "enabled." : "disabled."); + + /* register the hostadapter */ + if ((shpnt = ibmmca_register(scsi_template, port, id, + subsys_list[i].description))) + { + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = pos[2]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = pos[3]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = i; + + mca_set_adapter_name (slot, subsys_list[i].description); + mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + mca_mark_as_used(slot); + } + slot++; /* advance to next slot */ + } /* advance to next adapter id in the list of IBM-SCSI-subsystems*/ + } + + if (!found) + { /* maybe ESDI, or other producers' SCSI-hosts */ + free_irq (IM_IRQ, hosts); + printk("IBM MCA SCSI: No IBM SCSI-subsystem adapter attached.\n"); + } + return found; /* return the number of found SCSI hosts. Should be 1 or 0. */ } static struct Scsi_Host * -ibmmca_register(Scsi_Host_Template * template, int port, int id) +ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, + char *hostname) { - struct Scsi_Host *shpnt; - int i, j; - - /* check I/O region */ - if (check_region(port, IM_N_IO_PORT)) - { - printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x.\n", - port, port + IM_N_IO_PORT); - return NULL; - } - - /* register host */ - shpnt = scsi_register(template, sizeof(struct ibmmca_hostdata)); - if (!shpnt) - { - printk("IBM MCA SCSI: Unable to register host.\n"); - return NULL; - } - - /* request I/O region */ - request_region(port, IM_N_IO_PORT, "ibmmca"); - - hosts[found++] = shpnt; - shpnt->irq = IM_IRQ; - shpnt->io_port = port; - shpnt->n_io_port = IM_N_IO_PORT; - shpnt->this_id = id; - - reset_status = IM_RESET_NOT_IN_PROGRESS; - - for (i = 0; i < 8; i++) - for (j = 0; j < 8; j++) - get_ldn[i][j] = MAX_LOG_DEV; - - /* check which logical devices exist */ - local_checking_phase_flag = 1; - check_devices(shpnt); - local_checking_phase_flag = 0; - - /* an ibm mca subsystem has been detected */ - return shpnt; + struct Scsi_Host *shpnt; + int i, j; + unsigned int ctrl; + + /* check I/O region */ + if (check_region(port, IM_N_IO_PORT)) + { + printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x (%d ports).\n", + port, port + IM_N_IO_PORT - 1, IM_N_IO_PORT); + return NULL; + } + + /* register host */ + shpnt = scsi_register(scsi_template, sizeof(struct ibmmca_hostdata)); + if (!shpnt) + { + printk("IBM MCA SCSI: Unable to register host.\n"); + return NULL; + } + + /* request I/O region */ + request_region(port, IM_N_IO_PORT, hostname); + + hosts[found] = shpnt; /* add new found hostadapter to the list */ + shpnt->irq = IM_IRQ; /* assign necessary stuff for the adapter */ + shpnt->io_port = port; + shpnt->n_io_port = IM_N_IO_PORT; + shpnt->this_id = id; + /* now, the SCSI-subsystem is connected to Linux */ + + ctrl = (unsigned int)(inb(IM_CTR_REG(found))); /* get control-register status */ +#ifdef IM_DEBUG_PROBE + printk("IBM MCA SCSI: Control Register contents: %x, status: %x\n", + ctrl,inb(IM_STAT_REG(found))); + printk("IBM MCA SCSI: This adapters' POS-registers: "); + for (i=0;i<8;i++) + printk("%x ",pos[i]); + printk("\n"); + if (bypass_controller) + printk("IBM MCA SCSI: Subsystem SCSI-commands get bypassed.\n"); +#endif + + reset_status(found) = IM_RESET_NOT_IN_PROGRESS; + + for (i = 0; i < 8; i++) /* reset the tables */ + for (j = 0; j < 8; j++) + get_ldn(found)[i][j] = MAX_LOG_DEV; + + /* check which logical devices exist */ + local_checking_phase_flag(found) = 1; + check_devices(found); /* call by value, using the global variable hosts*/ + local_checking_phase_flag(found) = 0; + + found++; /* now increase index to be prepared for next found subsystem */ + /* an ibm mca subsystem has been detected */ + return shpnt; } /*--------------------------------------------------------------------*/ -int -ibmmca_command (Scsi_Cmnd * cmd) +int ibmmca_command (Scsi_Cmnd * cmd) { ibmmca_queuecommand (cmd, internal_done); cmd->SCp.Status = 0; @@ -1678,8 +1765,7 @@ ibmmca_command (Scsi_Cmnd * cmd) /*--------------------------------------------------------------------*/ -int -ibmmca_release(struct Scsi_Host *shpnt) +int ibmmca_release(struct Scsi_Host *shpnt) { release_region(shpnt->io_port, shpnt->n_io_port); if (!(--found)) @@ -1708,231 +1794,267 @@ ibmmca_release(struct Scsi_Host *shpnt) are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) { - unsigned int ldn; - unsigned int scsi_cmd; - struct im_scb *scb; - struct Scsi_Host *shpnt = cmd->host; - - int current_ldn; - int id,lun; - - /* use industry standard ordering of the IDs */ -#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD - int target = 6 - cmd->target; -#else - int target = cmd->target; -#endif - - /*if (target,lun) is NO LUN or not existing at all, return error */ - if ((get_scsi[target][cmd->lun] == TYPE_NO_LUN)|| - (get_scsi[target][cmd->lun] == TYPE_NO_DEVICE)) + unsigned int ldn; + unsigned int scsi_cmd; + struct im_scb *scb; + struct Scsi_Host *shpnt; + int current_ldn; + int id,lun; + int target; + int host_index; + + if (ibm_ansi_order) + target = 6 - cmd->target; + else + target = cmd->target; + + shpnt = cmd->host; + + /* search for the right hostadapter */ + for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++); + + if (!hosts[host_index]) + { /* invalid hostadapter descriptor address */ + cmd->result = DID_NO_CONNECT << 16; + done (cmd); + return 0; + } + + /*if (target,lun) is NO LUN or not existing at all, return error */ + if ((get_scsi(host_index)[target][cmd->lun] == TYPE_NO_LUN)|| + (get_scsi(host_index)[target][cmd->lun] == TYPE_NO_DEVICE)) { cmd->result = DID_NO_CONNECT << 16; done (cmd); return 0; } - /*if (target,lun) unassigned, do further checks... */ - ldn = get_ldn[target][cmd->lun]; - if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */ - { - if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */ - { - current_ldn = next_ldn; /* stop-value for one circle */ - while (ld[next_ldn].cmd) /* search for a occupied, but not in */ - { /* command-processing ldn. */ - next_ldn ++; - if (next_ldn>=MAX_LOG_DEV) - next_ldn = 7; - if (current_ldn == next_ldn) /* One circle done ? */ - { /* no non-processing ldn found */ - printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n"); - printk(" On ldn 7-14 SCSI-commands everywhere in progress.\n"); - printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n", - target, cmd->lun); - cmd->result = DID_NO_CONNECT << 16;/* return no connect*/ - done (cmd); - return 0; - } - } - - /* unmap non-processing ldn */ - for (id=0; id<8; id ++) - for (lun=0; lun<8; lun++) - { - if (get_ldn[id][lun] == next_ldn) - { - get_ldn[id][lun] = TYPE_NO_DEVICE; /* unmap entry */ - goto DYN_ASSIGN; /* jump out as fast as possible */ - } - } - -DYN_ASSIGN: - /* unassign found ldn (pun,lun does not matter for remove) */ - immediate_assign(shpnt,0,0,next_ldn,REMOVE_LDN); - /* assign found ldn to aimed pun,lun */ - immediate_assign(shpnt,target,cmd->lun,next_ldn,SET_LDN); - /* map found ldn to pun,lun */ - get_ldn[target][cmd->lun] = next_ldn; - /* change ldn to the right value, that is now next_ldn */ - ldn = next_ldn; - /* set reduced interrupt_handler-mode for checking */ - local_checking_phase_flag = 1; - /* get device information for ld[ldn] */ - if (device_exists (shpnt, ldn, &ld[ldn].block_length, - &ld[ldn].device_type)) + /*if (target,lun) unassigned, do further checks... */ + ldn = get_ldn(host_index)[target][cmd->lun]; + if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */ + { + if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */ + { + current_ldn = next_ldn(host_index); /* stop-value for one circle */ + while (ld(host_index)[next_ldn(host_index)].cmd) /* search for a occupied, but not in */ + { /* command-processing ldn. */ + next_ldn(host_index)++; + if (next_ldn(host_index)>=MAX_LOG_DEV) + next_ldn(host_index) = 7; + if (current_ldn == next_ldn(host_index)) /* One circle done ? */ + { /* no non-processing ldn found */ + printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n"); + printk(" On ldn 7-14 SCSI-commands everywhere in progress.\n"); + printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n", + target, cmd->lun); + cmd->result = DID_NO_CONNECT << 16;/* return no connect*/ + done (cmd); + return 0; + } + } + + /* unmap non-processing ldn */ + for (id=0; id<8; id ++) + for (lun=0; lun<8; lun++) + { + if (get_ldn(host_index)[id][lun] == next_ldn(host_index)) + { + get_ldn(host_index)[id][lun] = TYPE_NO_DEVICE; + /* unmap entry */ + } + } + /* set reduced interrupt_handler-mode for checking */ + local_checking_phase_flag(host_index) = 1; + /* unassign found ldn (pun,lun does not matter for remove) */ + immediate_assign(host_index,0,0,next_ldn(host_index),REMOVE_LDN); + /* assign found ldn to aimed pun,lun */ + immediate_assign(host_index,target,cmd->lun,next_ldn(host_index),SET_LDN); + /* map found ldn to pun,lun */ + get_ldn(host_index)[target][cmd->lun] = next_ldn(host_index); + /* change ldn to the right value, that is now next_ldn */ + ldn = next_ldn(host_index); + /* get device information for ld[ldn] */ + if (device_exists (host_index, ldn, + &ld(host_index)[ldn].block_length, + &ld(host_index)[ldn].device_type)) { - ld[ldn].cmd = 0; /* To prevent panic set 0, because - devices that were not assigned, - should have nothing in progress. */ + ld(host_index)[ldn].cmd = 0; /* To prevent panic set 0, because + devices that were not assigned, + should have nothing in progress. */ - /* increase assignment counters for statistics in /proc */ - IBM_DS.dynamical_assignments++; - IBM_DS.ldn_assignments[ldn]++; + /* increase assignment counters for statistics in /proc */ + IBM_DS(host_index).dynamical_assignments++; + IBM_DS(host_index).ldn_assignments[ldn]++; } - else - /* panic here, because a device, found at boottime has - vanished */ - panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", - ldn, target, cmd->lun); - - /* set back to normal interrupt_handling */ - local_checking_phase_flag = 0; - - /* Information on syslog terminal */ - printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", - ldn, target, cmd->lun); - - /* increase next_ldn for next dynamical assignment */ - next_ldn ++; - if (next_ldn>=MAX_LOG_DEV) next_ldn = 7; - } - else - { /* wall against Linux accesses to the subsystem adapter */ - cmd->result = DID_NO_CONNECT << 16; - done (cmd); - return 0; - } - } - - /*verify there is no command already in progress for this log dev */ - if (ld[ldn].cmd) - panic ("IBM MCA SCSI: cmd already in progress for this ldn.\n"); - - /*save done in cmd, and save cmd for the interrupt handler */ - cmd->scsi_done = done; - ld[ldn].cmd = cmd; - - /*fill scb information independent of the scsi command */ - scb = &(ld[ldn].scb); - scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR; - scb->tsb_adr = virt_to_bus(&(ld[ldn].tsb)); - if (cmd->use_sg) - { - int i = cmd->use_sg; - struct scatterlist *sl = (struct scatterlist *) cmd->request_buffer; - if (i > 16) - panic ("IBM MCA SCSI: scatter-gather list too long.\n"); - while (--i >= 0) - { - ld[ldn].sge[i].address = (void *) virt_to_bus(sl[i].address); - ld[ldn].sge[i].byte_length = sl[i].length; - } - scb->enable |= IM_POINTER_TO_LIST; - scb->sys_buf_adr = virt_to_bus(&(ld[ldn].sge[0])); - scb->sys_buf_length = cmd->use_sg * sizeof (struct im_sge); - } - else - { - scb->sys_buf_adr = virt_to_bus(cmd->request_buffer); - scb->sys_buf_length = cmd->request_bufflen; - } - - /*fill scb information dependent on scsi command */ - scsi_cmd = cmd->cmnd[0]; + else + /* panic here, because a device, found at boottime has + vanished */ + panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", + ldn, target, cmd->lun); + + /* set back to normal interrupt_handling */ + local_checking_phase_flag(host_index) = 0; + + /* Information on syslog terminal */ + printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", + ldn, target, cmd->lun); + + /* increase next_ldn for next dynamical assignment */ + next_ldn(host_index)++; + if (next_ldn(host_index)>=MAX_LOG_DEV) + next_ldn(host_index) = 7; + } + else + { /* wall against Linux accesses to the subsystem adapter */ + cmd->result = DID_BAD_TARGET << 16; + done (cmd); + return 0; + } + } + + /*verify there is no command already in progress for this log dev */ + if (ld(host_index)[ldn].cmd) + panic ("IBM MCA SCSI: cmd already in progress for this ldn.\n"); + + /*save done in cmd, and save cmd for the interrupt handler */ + cmd->scsi_done = done; + ld(host_index)[ldn].cmd = cmd; + + /*fill scb information independent of the scsi command */ + scb = &(ld(host_index)[ldn].scb); + ld(host_index)[ldn].tsb.dev_status = 0; + scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR; + scb->tsb_adr = virt_to_bus(&(ld(host_index)[ldn].tsb)); + if (cmd->use_sg) + { + int i = cmd->use_sg; + struct scatterlist *sl = (struct scatterlist *) cmd->request_buffer; + if (i > 16) + panic ("IBM MCA SCSI: scatter-gather list too long.\n"); + while (--i >= 0) + { + ld(host_index)[ldn].sge[i].address = (void *) virt_to_bus(sl[i].address); + ld(host_index)[ldn].sge[i].byte_length = sl[i].length; + } + scb->enable |= IM_POINTER_TO_LIST; + scb->sys_buf_adr = virt_to_bus(&(ld(host_index)[ldn].sge[0])); + scb->sys_buf_length = cmd->use_sg * sizeof (struct im_sge); + } + else + { + scb->sys_buf_adr = virt_to_bus(cmd->request_buffer); + scb->sys_buf_length = cmd->request_bufflen; + } + + /*fill scb information dependent on scsi command */ + scsi_cmd = cmd->cmnd[0]; #ifdef IM_DEBUG_CMD - printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn); + printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn); #endif - - /* for specific device-type debugging: */ + + /* for specific device-type debugging: */ #ifdef IM_DEBUG_CMD_SPEC_DEV - if (ld[ldn].device_type==IM_DEBUG_CMD_DEVICE) + if (ld(host_index)[ldn].device_type==IM_DEBUG_CMD_DEVICE) printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", - ld[ldn].device_type, scsi_cmd, ldn); + ld(host_index)[ldn].device_type, scsi_cmd, ldn); #endif - - /* for possible panics store current command */ - last_scsi_command = scsi_cmd; - - /* update statistical info */ - IBM_DS.total_accesses++; - IBM_DS.ldn_access[ldn]++; - - switch (scsi_cmd) - { - case READ_6: - case WRITE_6: - case READ_10: - case WRITE_10: - case READ_12: - case WRITE_12: - /* statistics for proc_info */ - if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12)) - IBM_DS.ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */ - else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)|| - (scsi_cmd == WRITE_12)) - IBM_DS.ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/ - - /* Distinguish between disk and other devices. Only disks (that are the - most frequently accessed devices) should be supported by the + + /* for possible panics store current command */ + last_scsi_command(host_index)[ldn] = scsi_cmd; + last_scsi_type(host_index)[ldn] = IM_SCB; + + /* update statistical info */ + IBM_DS(host_index).total_accesses++; + IBM_DS(host_index).ldn_access[ldn]++; + + switch (scsi_cmd) + { + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + /* statistics for proc_info */ + if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12)) + IBM_DS(host_index).ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */ + else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)|| + (scsi_cmd == WRITE_12)) + IBM_DS(host_index).ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/ + + /* Distinguish between disk and other devices. Only disks (that are the + most frequently accessed devices) should be supported by the IBM-SCSI-Subsystem commands. */ - switch (ld[ldn].device_type) - { - case TYPE_DISK: /* for harddisks enter here ... */ - case TYPE_MOD: /* ... try it also for MO-drives (send flames as */ - /* you like, if this won't work.) */ - if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || - scsi_cmd == READ_12) - { - scb->command = IM_READ_DATA_CMD; - scb->enable |= IM_READ_CONTROL; - } - else - { - scb->command = IM_WRITE_DATA_CMD; - } - if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) - { - scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | - (((unsigned) cmd->cmnd[2]) << 8) | - ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16); - scb->u2.blk.count = (unsigned) cmd->cmnd[4]; - } - else - { - scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | - (((unsigned) cmd->cmnd[4]) << 8) | - (((unsigned) cmd->cmnd[3]) << 16) | - (((unsigned) cmd->cmnd[2]) << 24); - scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | - (((unsigned) cmd->cmnd[7]) << 8); - } - scb->u2.blk.length = ld[ldn].block_length; - if (++disk_rw_in_progress == 1) - PS2_DISK_LED_ON (shpnt->host_no, target); - break; - - /* for other devices, enter here. Other types are not known by - Linux! TYPE_NO_LUN is forbidden as valid device. */ - case TYPE_ROM: - case TYPE_TAPE: - case TYPE_PROCESSOR: - case TYPE_WORM: - case TYPE_SCANNER: - case TYPE_MEDIUM_CHANGER: - - /* If there is a sequential-device, IBM recommends to use + switch (ld(host_index)[ldn].device_type) + { + case TYPE_DISK: /* for harddisks enter here ... */ + case TYPE_MOD: /* ... try it also for MO-drives (send flames as */ + /* you like, if this won't work.) */ + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || + scsi_cmd == READ_12) + { /* read command preparations */ + if (bypass_controller) + { + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy(scb->u2.scsi_command,cmd->cmnd,cmd->cmd_len); + } + else + { + scb->command = IM_READ_DATA_CMD; + scb->enable |= IM_READ_CONTROL; + } + } + else + { /* write command preparations */ + if (bypass_controller) + { + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy(scb->u2.scsi_command,cmd->cmnd,cmd->cmd_len); + } + else + { + scb->command = IM_WRITE_DATA_CMD; + } + } + + if (!bypass_controller) + { + if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) + { + scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | + (((unsigned) cmd->cmnd[2]) << 8) | + ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16); + scb->u2.blk.count = (unsigned) cmd->cmnd[4]; + } + else + { + scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | + (((unsigned) cmd->cmnd[4]) << 8) | + (((unsigned) cmd->cmnd[3]) << 16) | + (((unsigned) cmd->cmnd[2]) << 24); + scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | + (((unsigned) cmd->cmnd[7]) << 8); + } + scb->u2.blk.length = ld(host_index)[ldn].block_length; + } + if (++disk_rw_in_progress == 1) + PS2_DISK_LED_ON (shpnt->host_no, target); + break; + + /* for other devices, enter here. Other types are not known by + Linux! TYPE_NO_LUN is forbidden as valid device. */ + case TYPE_ROM: + case TYPE_TAPE: + case TYPE_PROCESSOR: + case TYPE_WORM: + case TYPE_SCANNER: + case TYPE_MEDIUM_CHANGER: + + /* If there is a sequential-device, IBM recommends to use IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE. Good/modern CD-ROM-drives are capable of reading sequential AND random-access. This leads to the problem, @@ -1943,241 +2065,422 @@ DYN_ASSIGN: to have a stable state. In addition, data-access on CD-ROMs works faster like that. Strange, but obvious. */ - scb->command = IM_OTHER_SCSI_CMD_CMD; - if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || - scsi_cmd == READ_12) /* enable READ */ - scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; - else - scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /* assume WRITE */ - - scb->u1.scsi_cmd_length = cmd->cmd_len; - memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); - - /* Read/write on this non-disk devices is also displayworthy, + scb->command = IM_OTHER_SCSI_CMD_CMD; + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || + scsi_cmd == READ_12) /* enable READ */ + { + scb->enable |= IM_READ_CONTROL; + } + + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + + /* Read/write on this non-disk devices is also displayworthy, so flash-up the LED/display. */ - if (++disk_rw_in_progress == 1) - PS2_DISK_LED_ON (shpnt->host_no, target); - break; - } - break; - case INQUIRY: - IBM_DS.ldn_inquiry_access[ldn]++; - scb->command = IM_DEVICE_INQUIRY_CMD; - scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; - break; - - case READ_CAPACITY: - scb->command = IM_READ_CAPACITY_CMD; - scb->enable |= IM_READ_CONTROL; - /* the length of system memory buffer must be exactly 8 bytes */ - if (scb->sys_buf_length >= 8) - scb->sys_buf_length = 8; - break; - - /* Commands that need read-only-mode (system <- device): */ - case REQUEST_SENSE: - scb->command = IM_REQUEST_SENSE_CMD; - scb->enable |= IM_READ_CONTROL; - break; - - /* Commands that need write-only-mode (system -> device): */ - case MODE_SELECT: - case MODE_SELECT_10: - IBM_DS.ldn_modeselect_access[ldn]++; - scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/ - scb->u1.scsi_cmd_length = cmd->cmd_len; - memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); - break; - - /* For other commands, read-only is useful. Most other commands are - running without an input-data-block. */ - default: - scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; - scb->u1.scsi_cmd_length = cmd->cmd_len; - memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); - break; - } - - /*issue scb command, and return */ - issue_cmd (shpnt, virt_to_bus(scb), IM_SCB | ldn); - return 0; + if (++disk_rw_in_progress == 1) + PS2_DISK_LED_ON (shpnt->host_no, target); + break; + } + break; + case INQUIRY: + IBM_DS(host_index).ldn_inquiry_access[ldn]++; + if (bypass_controller) + { + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + } + else + { + scb->command = IM_DEVICE_INQUIRY_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + } + break; + + case READ_CAPACITY: + /* the length of system memory buffer must be exactly 8 bytes */ + if (scb->sys_buf_length > 8) + scb->sys_buf_length = 8; + if (bypass_controller) + { + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + } + else + { + scb->command = IM_READ_CAPACITY_CMD; + scb->enable |= IM_READ_CONTROL; + } + break; + + /* Commands that need read-only-mode (system <- device): */ + case REQUEST_SENSE: + if (bypass_controller) + { + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + } + else + { + scb->command = IM_REQUEST_SENSE_CMD; + scb->enable |= IM_READ_CONTROL; + } + break; + + /* Commands that need write-only-mode (system -> device): */ + case MODE_SELECT: + case MODE_SELECT_10: + IBM_DS(host_index).ldn_modeselect_access[ldn]++; + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/ + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + break; + + /* For other commands, read-only is useful. Most other commands are + running without an input-data-block. */ + default: + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + break; + } + + /*issue scb command, and return */ + issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn); + return 0; } /*--------------------------------------------------------------------*/ -int -ibmmca_abort (Scsi_Cmnd * cmd) +int ibmmca_abort (Scsi_Cmnd * cmd) { - /* The code below doesn't work right now, so we tell the upper layer - that we can't abort. This eventually causes a reset. - */ - return SCSI_ABORT_SNOOZE ; + /* Abort does not work, as the adapter never generates an interrupt on + * whatever situation is simulated, even when really pending commands + * are running on the adapters' hardware ! */ + + struct Scsi_Host *shpnt; + unsigned int ldn; + void (*saved_done) (Scsi_Cmnd *); + int target; + int host_index; + static unsigned long flags; + unsigned long imm_command; -#if 0 - struct Scsi_host *shpnt = cmd->host; - unsigned int ldn; - void (*saved_done) (Scsi_Cmnd *); + /* return SCSI_ABORT_SNOOZE ; */ -#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD - int target = 6 - cmd->target; +#ifdef OLDKERN + save_flags(flags); + cli(); #else - int target = cmd->target; -#endif + spin_lock_irqsave(&abort_lock, flags); +#endif + if (ibm_ansi_order) + target = 6 - cmd->target; + else + target = cmd->target; + + shpnt = cmd->host; - /*get logical device number, and disable system interrupts */ - printk ("IBM MCA SCSI: sending abort to device id=%d lun=%d.\n", - target, cmd->lun); - ldn = get_ldn[target][cmd->lun]; - cli (); - - /*if cmd for this ldn has already finished, no need to abort */ - if (!ld[ldn].cmd) - { - /* sti (); */ - return SCSI_ABORT_NOT_RUNNING; - } - - /* Clear ld.cmd, save done function, install internal done, - * send abort immediate command (this enables sys. interrupts), - * and wait until the interrupt arrives. - */ - ld[ldn].cmd = 0; - saved_done = cmd->scsi_done; - cmd->scsi_done = internal_done; - cmd->SCp.Status = 0; - issue_cmd (shpnt, T_IMM_CMD, IM_IMM_CMD | ldn); - while (!cmd->SCp.Status) - barrier (); + /* search for the right hostadapter */ + for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++); + + if (!hosts[host_index]) + { /* invalid hostadapter descriptor address */ + cmd->result = DID_NO_CONNECT << 16; + if (cmd->scsi_done) + (cmd->done) (cmd); + return SCSI_ABORT_SNOOZE; + } + + /*get logical device number, and disable system interrupts */ + printk ("IBM MCA SCSI: Sending abort to device pun=%d, lun=%d.\n", + target, cmd->lun); + ldn = get_ldn(host_index)[target][cmd->lun]; + + /*if cmd for this ldn has already finished, no need to abort */ + if (!ld(host_index)[ldn].cmd) + { +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&abort_lock, flags); +#endif + return SCSI_ABORT_NOT_RUNNING; + } - /*if abort went well, call saved done, then return success or error */ - if (cmd->result == 0) - { - cmd->result |= DID_ABORT << 16; - saved_done (cmd); - return SCSI_ABORT_SUCCESS; - } - else - return SCSI_ABORT_ERROR; + /* Clear ld.cmd, save done function, install internal done, + * send abort immediate command (this enables sys. interrupts), + * and wait until the interrupt arrives. + */ + saved_done = cmd->scsi_done; + cmd->scsi_done = internal_done; + cmd->SCp.Status = 0; + last_scsi_command(host_index)[ldn] = IM_ABORT_IMM_CMD; + last_scsi_type(host_index)[ldn] = IM_IMM_CMD; + imm_command = inl(IM_CMD_REG(host_index)); + imm_command &= (unsigned long)(0xffff0000); /* mask reserved stuff */ + imm_command |= (unsigned long)(IM_ABORT_IMM_CMD); + /* must wait for attention reg not busy */ + while (1) + { + if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) + break; +#ifdef OLDKERN + restore_flags (flags); +#else + spin_unlock_irqrestore(&abort_lock, flags); +#endif +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&abort_lock, flags); +#endif + } + /*write registers and enable system interrupts */ + outl (imm_command, IM_CMD_REG(host_index)); + outb (IM_IMM_CMD | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags (flags); +#else + spin_unlock_irqrestore(&abort_lock, flags); #endif + +#ifdef IM_DEBUG_PROBE + printk("IBM MCA SCSI: Abort submitted, waiting for adapter response...\n"); +#endif + while (!cmd->SCp.Status) + barrier (); + cmd->scsi_done = saved_done; + /*if abort went well, call saved done, then return success or error */ + if (cmd->result == (DID_ABORT << 16)) + { + cmd->result |= DID_ABORT << 16; + if (cmd->scsi_done) + (cmd->scsi_done) (cmd); + ld(host_index)[ldn].cmd = NULL; +#ifdef IM_DEBUG_PROBE + printk("IBM MCA SCSI: Abort finished with success.\n"); +#endif + return SCSI_ABORT_SUCCESS; + } + else + { + cmd->result |= DID_NO_CONNECT << 16; + if (cmd->scsi_done) + (cmd->scsi_done) (cmd); + ld(host_index)[ldn].cmd = NULL; +#ifdef IM_DEBUG_PROBE + printk("IBM MCA SCSI: Abort failed.\n"); +#endif + return SCSI_ABORT_ERROR; + } } /*--------------------------------------------------------------------*/ -int -ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) +int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) { - struct Scsi_Host *shpnt = cmd->host; - int ticks = IM_RESET_DELAY*HZ; - - if (local_checking_phase_flag) { - printk("IBM MCA SCSI: unable to reset while checking devices.\n"); - return SCSI_RESET_SNOOZE; - } - - /* issue reset immediate command to subsystem, and wait for interrupt */ - printk("IBM MCA SCSI: resetting all devices.\n"); - cli (); - reset_status = IM_RESET_IN_PROGRESS; - issue_cmd (shpnt, IM_RESET_IMM_CMD, IM_IMM_CMD | 0xf); - while (reset_status == IM_RESET_IN_PROGRESS && --ticks) { - mdelay(1+999/HZ); - barrier(); - } - /* if reset did not complete, just return an error*/ - if (!ticks) { - printk("IBM MCA SCSI: reset did not complete within %d seconds.\n", - IM_RESET_DELAY); - reset_status = IM_RESET_FINISHED_FAIL; - return SCSI_RESET_ERROR; - } - - /* if reset failed, just return an error */ - if (reset_status == IM_RESET_FINISHED_FAIL) { - printk("IBM MCA SCSI: reset failed.\n"); - return SCSI_RESET_ERROR; - } - - /* so reset finished ok - call outstanding done's, and return success */ - printk ("IBM MCA SCSI: reset completed without error.\n"); - { - int i; - for (i = 0; i < MAX_LOG_DEV; i++) - { - Scsi_Cmnd *cmd = ld[i].cmd; - if (cmd && cmd->scsi_done) - { - ld[i].cmd = 0; - cmd->result = DID_RESET; - (cmd->scsi_done) (cmd); - } - } - } - return SCSI_RESET_SUCCESS; + struct Scsi_Host *shpnt; + Scsi_Cmnd *cmd_aid; + int ticks,i; + int host_index; + static unsigned long flags; + unsigned long imm_command; + +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&reset_lock, flags); +#endif + ticks = IM_RESET_DELAY*HZ; + shpnt = cmd->host; + /* search for the right hostadapter */ + for (host_index = 0; hosts[host_index] && hosts[host_index]->host_no != shpnt->host_no; host_index++); + + if (!hosts[host_index]) + { /* invalid hostadapter descriptor address */ + if (!local_checking_phase_flag(host_index)) + { + cmd->result = DID_NO_CONNECT << 16; + if (cmd->scsi_done) + (cmd->done) (cmd); + } + return SCSI_ABORT_SNOOZE; + } + + if (local_checking_phase_flag(host_index)) + { + printk("IBM MCA SCSI: unable to reset while checking devices.\n"); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&reset_lock, flags); +#endif + return SCSI_RESET_SNOOZE; + } + + /* issue reset immediate command to subsystem, and wait for interrupt */ + printk("IBM MCA SCSI: resetting all devices.\n"); + reset_status(host_index) = IM_RESET_IN_PROGRESS; + last_scsi_command(host_index)[0xf] = IM_RESET_IMM_CMD; + last_scsi_type(host_index)[0xf] = IM_IMM_CMD; + imm_command = inl(IM_CMD_REG(host_index)); + imm_command &= (unsigned long)(0xffff0000); /* mask reserved stuff */ + imm_command |= (unsigned long)(IM_RESET_IMM_CMD); + /* must wait for attention reg not busy */ + while (1) + { + if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) + break; +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&reset_lock, flags); +#endif +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&reset_lock, flags); +#endif + } + /*write registers and enable system interrupts */ + outl (imm_command, IM_CMD_REG(host_index)); + outb (IM_IMM_CMD | 0xf, IM_ATTN_REG(host_index)); + /* wait for interrupt finished or intr_stat register to be set, as the + * interrupt will not be executed, while we are in here! */ + while (reset_status(host_index) == IM_RESET_IN_PROGRESS && --ticks + && ((inb(IM_INTR_REG(host_index)) & 0x8f)!=0x8f)) { + mdelay(1+999/HZ); + barrier(); + } + /* if reset did not complete, just return an error*/ + if (!ticks) { + printk("IBM MCA SCSI: reset did not complete within %d seconds.\n", + IM_RESET_DELAY); + reset_status(host_index) = IM_RESET_FINISHED_FAIL; +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&reset_lock, flags); +#endif + return SCSI_RESET_ERROR; + } + + if ((inb(IM_INTR_REG(host_index)) & 0x8f)==0x8f) + { /* analysis done by this routine and not by the intr-routine */ + if (inb(IM_INTR_REG(host_index))==0xaf) + reset_status(host_index) = IM_RESET_FINISHED_OK_NO_INT; + else if (inb(IM_INTR_REG(host_index))==0xcf) + reset_status(host_index) = IM_RESET_FINISHED_FAIL; + else /* failed, 4get it */ + reset_status(host_index) = IM_RESET_NOT_IN_PROGRESS_NO_INT; + outb (IM_EOI | 0xf, IM_ATTN_REG(host_index)); + } + + /* if reset failed, just return an error */ + if (reset_status(host_index) == IM_RESET_FINISHED_FAIL) { + printk("IBM MCA SCSI: reset failed.\n"); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&reset_lock, flags); +#endif + return SCSI_RESET_ERROR; + } + + /* so reset finished ok - call outstanding done's, and return success */ + printk ("IBM MCA SCSI: Reset completed without known error.\n"); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&reset_lock, flags); +#endif + for (i = 0; i < MAX_LOG_DEV; i++) + { + cmd_aid = ld(host_index)[i].cmd; + if (cmd_aid && cmd_aid->scsi_done) + { + ld(host_index)[i].cmd = NULL; + cmd_aid->result = DID_RESET << 16; + (cmd_aid->scsi_done) (cmd_aid); + } + } + return SCSI_RESET_SUCCESS; } /*--------------------------------------------------------------------*/ -int -ibmmca_biosparam (Disk * disk, kdev_t dev, int *info) +int ibmmca_biosparam (Disk * disk, kdev_t dev, int *info) { - info[0] = 64; - info[1] = 32; - info[2] = disk->capacity / (info[0] * info[1]); - if (info[2] >= 1024) - { - info[0] = 128; - info[1] = 63; - info[2] = disk->capacity / (info[0] * info[1]); - if (info[2] >= 1024) - { - info[0] = 255; - info[1] = 63; - info[2] = disk->capacity / (info[0] * info[1]); - if (info[2] >= 1024) - info[2] = 1023; - } - } - return 0; + info[0] = 64; + info[1] = 32; + info[2] = disk->capacity / (info[0] * info[1]); + if (info[2] >= 1024) + { + info[0] = 128; + info[1] = 63; + info[2] = disk->capacity / (info[0] * info[1]); + if (info[2] >= 1024) + { + info[0] = 255; + info[1] = 63; + info[2] = disk->capacity / (info[0] * info[1]); + if (info[2] >= 1024) + info[2] = 1023; + } + } + return 0; } /* calculate percentage of total accesses on a ldn */ -static int ldn_access_load(struct Scsi_Host *shpnt, int ldn) +static int ldn_access_load(int host_index, int ldn) { - if (IBM_DS.total_accesses == 0) return (0); - if (IBM_DS.ldn_access[ldn] == 0) return (0); - return (IBM_DS.ldn_access[ldn] * 100) / IBM_DS.total_accesses; + if (IBM_DS(host_index).total_accesses == 0) return (0); + if (IBM_DS(host_index).ldn_access[ldn] == 0) return (0); + return (IBM_DS(host_index).ldn_access[ldn] * 100) / IBM_DS(host_index).total_accesses; } /* calculate total amount of r/w-accesses */ -static int ldn_access_total_read_write(struct Scsi_Host *shpnt) +static int ldn_access_total_read_write(int host_index) { - int a = 0; + int a; int i; + a = 0; for (i=0; i<=MAX_LOG_DEV; i++) - a+=IBM_DS.ldn_read_access[i]+IBM_DS.ldn_write_access[i]; + a+=IBM_DS(host_index).ldn_read_access[i]+IBM_DS(host_index).ldn_write_access[i]; return(a); } -static int ldn_access_total_inquiry(struct Scsi_Host *shpnt) +static int ldn_access_total_inquiry(int host_index) { - int a = 0; + int a; int i; + a = 0; for (i=0; i<=MAX_LOG_DEV; i++) - a+=IBM_DS.ldn_inquiry_access[i]; + a+=IBM_DS(host_index).ldn_inquiry_access[i]; return(a); } -static int ldn_access_total_modeselect(struct Scsi_Host *shpnt) +static int ldn_access_total_modeselect(int host_index) { - int a = 0; + int a; int i; + a = 0; for (i=0; i<=MAX_LOG_DEV; i++) - a+=IBM_DS.ldn_modeselect_access[i]; + a+=IBM_DS(host_index).ldn_modeselect_access[i]; return(a); } @@ -2186,28 +2489,30 @@ int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) { int len=0; - int i,id,lun; + int i,id,lun,host_index; struct Scsi_Host *shpnt; unsigned long flags; +#ifdef OLDKERN + save_flags(flags); + cli(); +#else + spin_lock_irqsave(&proc_lock, flags); +#endif + for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++); shpnt = hosts[i]; + host_index = i; if (!shpnt) { len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno); return len; } - save_flags(flags); - cli(); - len += sprintf(buffer+len, "\n IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n", IBMMCA_SCSI_DRIVER_VERSION); len += sprintf(buffer+len, " SCSI Access-Statistics:\n"); -#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD - len += sprintf(buffer+len, " ANSI-SCSI-standard order.: Yes\n"); -#else - len += sprintf(buffer+len, " ANSI-SCSI-standard order.: No\n"); -#endif + len += sprintf(buffer+len, " Device Scanning Order....: %s\n", + (ibm_ansi_order) ? "IBM/ANSI" : "New Industry Standard"); #ifdef CONFIG_SCSI_MULTI_LUN len += sprintf(buffer+len, " Multiple LUN probing.....: Yes\n"); #else @@ -2215,72 +2520,74 @@ int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, #endif len += sprintf(buffer+len, " This Hostnumber..........: %d\n", hostno); - len += sprintf(buffer+len, " Base I/O-Port............: 0x%lx\n", - IM_CMD_REG); + len += sprintf(buffer+len, " Base I/O-Port............: 0x%x\n", + (unsigned int)(IM_CMD_REG(host_index))); len += sprintf(buffer+len, " (Shared) IRQ.............: %d\n", IM_IRQ); + len += sprintf(buffer+len, " SCSI-command set used....: %s\n", + (bypass_controller) ? "software" : "hardware integrated"); len += sprintf(buffer+len, " Total Interrupts.........: %d\n", - IBM_DS.total_interrupts); + IBM_DS(host_index).total_interrupts); len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n", - IBM_DS.total_accesses); + IBM_DS(host_index).total_accesses); len += sprintf(buffer+len, " Total SCSI READ/WRITE..: %d\n", - ldn_access_total_read_write(shpnt)); + ldn_access_total_read_write(host_index)); len += sprintf(buffer+len, " Total SCSI Inquiries...: %d\n", - ldn_access_total_inquiry(shpnt)); + ldn_access_total_inquiry(host_index)); len += sprintf(buffer+len, " Total SCSI Modeselects.: %d\n", - ldn_access_total_modeselect(shpnt)); - len += sprintf(buffer+len, " Total SCSI other cmds..: %d\n\n", - IBM_DS.total_accesses - ldn_access_total_read_write(shpnt) - - ldn_access_total_modeselect(shpnt) - - ldn_access_total_inquiry(shpnt)); - + ldn_access_total_modeselect(host_index)); + len += sprintf(buffer+len, " Total SCSI other cmds..: %d\n", + IBM_DS(host_index).total_accesses - ldn_access_total_read_write(host_index) + - ldn_access_total_modeselect(host_index) + - ldn_access_total_inquiry(host_index)); + len += sprintf(buffer+len, " Total SCSI command fails.: %d\n\n", + IBM_DS(host_index).total_errors); len += sprintf(buffer+len, " Logical-Device-Number (LDN) Access-Statistics:\n"); len += sprintf(buffer+len, " LDN | Accesses [%%] | READ | WRITE | ASSIGNMENTS\n"); len += sprintf(buffer+len, " -----|--------------|-----------|-----------|--------------\n"); for (i=0; i<=MAX_LOG_DEV; i++) len += sprintf(buffer+len, " %2X | %3d | %8d | %8d | %8d\n", - i, ldn_access_load(shpnt, i), IBM_DS.ldn_read_access[i], - IBM_DS.ldn_write_access[i], IBM_DS.ldn_assignments[i]); + i, ldn_access_load(host_index, i), IBM_DS(host_index).ldn_read_access[i], + IBM_DS(host_index).ldn_write_access[i], IBM_DS(host_index).ldn_assignments[i]); len += sprintf(buffer+len, " -----------------------------------------------------------\n\n"); len += sprintf(buffer+len, " Dynamical-LDN-Assignment-Statistics:\n"); len += sprintf(buffer+len, " Number of physical SCSI-devices..: %d (+ Adapter)\n", - IBM_DS.total_scsi_devices); + IBM_DS(host_index).total_scsi_devices); len += sprintf(buffer+len, " Dynamical Assignment necessaray..: %s\n", - IBM_DS.dyn_flag ? "Yes" : "No "); + IBM_DS(host_index).dyn_flag ? "Yes" : "No "); len += sprintf(buffer+len, " Next LDN to be assigned..........: 0x%x\n", - next_ldn); + next_ldn(host_index)); len += sprintf(buffer+len, " Dynamical assignments done yet...: %d\n", - IBM_DS.dynamical_assignments); + IBM_DS(host_index).dynamical_assignments); len += sprintf(buffer+len, "\n Current SCSI-Device-Mapping:\n"); len += sprintf(buffer+len, " Physical SCSI-Device Map Logical SCSI-Device Map\n"); len += sprintf(buffer+len, " ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n"); for (id=0; id<=7; id++) { - len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s", - id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]), - ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]), - ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]), - ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7])); - - len += sprintf(buffer+len, " %2d ",id); + len += sprintf(buffer+len, " %2d ",id); for (lun=0; lun<8; lun++) - len += sprintf(buffer+len,"%2s ",ti_l(get_ldn[id][lun])); + len += sprintf(buffer+len,"%2s ",ti_p(get_scsi(host_index)[id][lun])); + len += sprintf(buffer+len, " %2d ",id); + for (lun=0; lun<8; lun++) + len += sprintf(buffer+len,"%2s ",ti_l(get_ldn(host_index)[id][lun])); len += sprintf(buffer+len,"\n"); } len += sprintf(buffer+len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n"); len += sprintf(buffer+len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n"); - len += sprintf(buffer+len, " - = nothing found)\n\n"); + len += sprintf(buffer+len, " - = nothing found, nothing assigned or unprobed LUN)\n\n"); *start = buffer + offset; len -= offset; if (len > length) len = length; - +#ifdef OLDKERN restore_flags(flags); - +#else + spin_unlock_irqrestore(&proc_lock, flags); +#endif return len; } @@ -2293,5 +2600,3 @@ Scsi_Host_Template driver_template = IBMMCA; /*--------------------------------------------------------------------*/ - - |