diff options
Diffstat (limited to 'drivers/scsi/ibmmca.c')
-rw-r--r-- | drivers/scsi/ibmmca.c | 1917 |
1 files changed, 1367 insertions, 550 deletions
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index 393cf909e..aa4cca390 100644 --- a/drivers/scsi/ibmmca.c +++ b/drivers/scsi/ibmmca.c @@ -7,7 +7,7 @@ * 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. + * updates, info and ADF-files for adapters supported by this driver. */ /******************* HEADER FILE INCLUDES ************************************/ @@ -16,9 +16,12 @@ #endif /* choose adaption for Kernellevel */ -#define local_LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,65) +#define OLDKERN +#else +#undef OLDKERN +#endif -#include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/ctype.h> @@ -31,8 +34,13 @@ #include <linux/stat.h> #include <linux/mca.h> #include <asm/system.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17) #include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include <asm/spinlock.h> +#endif #include <asm/io.h> +#include <linux/init.h> #include "sd.h" #include "scsi.h" #include "hosts.h" @@ -42,6 +50,7 @@ /******************* LOCAL DEFINES *******************************************/ +/* milliseconds of delay for timing out reset. */ #ifndef mdelay #define mdelay(a) udelay((a) * 1000) #endif @@ -49,7 +58,7 @@ /*--------------------------------------------------------------------*/ /* current version of this driver-source: */ -#define IBMMCA_SCSI_DRIVER_VERSION "3.1e" +#define IBMMCA_SCSI_DRIVER_VERSION "3.2" /*--------------------------------------------------------------------*/ @@ -60,17 +69,17 @@ /* driver debugging - #undef all for normal operation */ /* if defined: count interrupts and ignore this special one: */ -#undef IM_DEBUG_TIMEOUT 50 +#undef IM_DEBUG_TIMEOUT 50 #define TIMEOUT_PUN 0 #define TIMEOUT_LUN 0 /* verbose interrupt: */ -#undef IM_DEBUG_INT +#undef IM_DEBUG_INT /* verbose queuecommand: */ -#undef IM_DEBUG_CMD +#undef IM_DEBUG_CMD /* verbose queucommand for specific SCSI-device type: */ -#undef IM_DEBUG_CMD_SPEC_DEV +#undef IM_DEBUG_CMD_SPEC_DEV /* verbose device probing */ -#undef IM_DEBUG_PROBE +#define IM_DEBUG_PROBE /* device type that shall be displayed on syslog (only during debugging): */ #define IM_DEBUG_CMD_DEVICE TYPE_TAPE @@ -103,6 +112,7 @@ /*note: the lower nibble specifies the device(0-14), or subsystem(15) */ #define IM_SCB_CMD_COMPLETED 0x10 #define IM_SCB_CMD_COMPLETED_WITH_RETRIES 0x50 +#define IM_LOOP_SCATTER_BUFFER_FULL 0x60 #define IM_ADAPTER_HW_FAILURE 0x70 #define IM_IMMEDIATE_CMD_COMPLETED 0xa0 #define IM_CMD_COMPLETED_WITH_FAILURE 0xc0 @@ -146,7 +156,7 @@ struct im_scb unsigned short length; /*block length, on SCSI device */ } blk; - unsigned char scsi_command[12]; /*other scsi command */ + unsigned char scsi_command[12]; /*other scsi command */ } u2; }; @@ -158,6 +168,31 @@ struct im_sge unsigned long byte_length; }; +/*structure returned by a get_pos_info command: */ +struct im_pos_info + { + unsigned short pos_id; /* adapter id */ + unsigned char pos_3a; /* pos 3 (if pos 6 = 0) */ + unsigned char pos_2; /* pos 2 */ + unsigned char int_level; /* interrupt level IRQ 11 or 14 */ + unsigned char pos_4a; /* pos 4 (if pos 6 = 0) */ + unsigned short connector_size; /* MCA connector size: 16 or 32 Bit */ + unsigned char num_luns; /* number of supported luns per device */ + unsigned char num_puns; /* number of supported puns */ + unsigned char pacing_factor; /* pacing factor */ + unsigned char num_ldns; /* number of ldns available */ + unsigned char eoi_off; /* time EOI and interrupt inactive */ + unsigned char max_busy; /* time between reset and busy on */ + unsigned short cache_stat; /* ldn cachestat. Bit=1 = not cached */ + unsigned short retry_stat; /* retry status of ldns. Bit=1=disabled */ + unsigned char pos_4b; /* pos 4 (if pos 6 = 1) */ + unsigned char pos_3b; /* pos 3 (if pos 6 = 1) */ + unsigned char pos_6; /* pos 6 */ + unsigned char pos_5; /* pos 5 */ + unsigned short max_overlap; /* maximum overlapping requests */ + unsigned short num_bus; /* number of SCSI-busses */ + }; + /*values for SCB command word */ #define IM_NO_SYNCHRONOUS 0x0040 /*flag for any command */ #define IM_NO_DISCONNECT 0x0080 /*flag for any command */ @@ -168,6 +203,7 @@ struct im_sge #define IM_REQUEST_SENSE_CMD 0x1c08 #define IM_READ_CAPACITY_CMD 0x1c09 #define IM_DEVICE_INQUIRY_CMD 0x1c0b +#define IM_READ_LOGICAL_CMD 0x1c2a #define IM_OTHER_SCSI_CMD_CMD 0x241f /* unused, but supported, SCB commands */ @@ -183,6 +219,7 @@ struct im_sge #define IM_RETRY_ENABLE 0x2000 #define IM_POINTER_TO_LIST 0x1000 #define IM_SUPRESS_EXCEPTION_SHORT 0x0400 +#define IM_BYPASS_BUFFER 0x0200 #define IM_CHAIN_ON_NO_ERROR 0x0001 /*TSB (Termination Status Block) structure */ @@ -204,15 +241,17 @@ struct im_tsb }; /*subsystem uses interrupt request level 14 */ -#define IM_IRQ 14 +#define IM_IRQ 14 +/*SCSI-2 F/W may evade to interrupt 11 */ +#define IM_IRQ_FW 11 /*--------------------------------------------------------------------*/ /* The model 95 doesn't have a standard activity light. Instead it - has a row of LEDs on the front. We use the last one as the activity - indicator if we think we're on a model 95. I suspect the model id - check will be either too narrow or too general, and some machines - won't have an activity indicator. Oh well... + has a row of alphanumerial LEDs on the front. We use the last one + as the activity indicator if we think we're on a model 95. I suspect + the model id check will be either too narrow or too general, and some + machines won't have an activity indicator. Oh well... The regular PS/2 disk led is turned on/off by bits 6,7 of system control port. @@ -222,6 +261,12 @@ struct im_tsb #define MOD95_LED_PORT 0x108 /* system-control-register of PS/2s with diskindicator */ #define PS2_SYS_CTR 0x92 +/* activity displaying methods */ +#define LED_DISP 1 +#define LED_ADISP 2 +#define LED_ACTIVITY 4 + +#define CMD_FAIL 255 /* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED displays. ldn is no longer displayed here, because the ldn mapping is now @@ -230,30 +275,39 @@ struct im_tsb interest, debugging or just for having fun. The left number gives the host-adapter number and the right shows the accessed SCSI-ID. */ -/* 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; - +/* display_mode is set by the ibmmcascsi= command line arg */ +static int display_mode = 0; +/* set default adapter timeout */ +static unsigned int adapter_timeout = 45; +/* for probing on feature-command: */ +static unsigned int global_command_error_excuse = 0; +/* global setting by command line for adapter_speed */ +static int global_adapter_speed = 0; /* full speed by default */ + +/* Panel / LED on, do it right for F/W addressin, too. adisplay will + * just ignore ids>7, as the panel has only 7 digits available */ #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); \ + if (display_mode & LED_DISP) { \ + if (id>9) \ + outw((ad+48)|((id+55)<<8), MOD95_LED_PORT ); \ + else \ + outw((ad+48)|((id+48)<<8), MOD95_LED_PORT ); } \ + else if (display_mode & LED_ADISP) { \ + if (id<7) outb((char)(id+48),MOD95_LED_PORT+1+id); \ + outb((char)(ad+48), MOD95_LED_PORT); } \ + if ((display_mode & LED_ACTIVITY)||(!display_mode)) \ + outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \ } - +/* Panel / LED off */ /* bug fixed, Dec 15, 1997, where | was replaced by & here */ #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); \ + if (display_mode & LED_DISP) \ + outw(0x2020, MOD95_LED_PORT ); \ + else if (display_mode & LED_ADISP) { \ + outl(0x20202020,MOD95_LED_PORT); \ + outl(0x20202020,MOD95_LED_PORT+4); } \ + if ((display_mode & LED_ACTIVITY)||(!display_mode)) \ + outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); \ } /*--------------------------------------------------------------------*/ @@ -265,15 +319,37 @@ struct subsys_list_struct char *description; }; +/* types of different supported hardware that goes to hostdata special */ +#define IBM_SCSI2_FW 0 +#define IBM_7568_WCACHE 1 +#define IBM_EXP_UNIT 2 +#define IBM_SCSI_WCACHE 3 +#define IBM_SCSI 4 + +/* other special flags for hostdata structure */ +#define FORCED_DETECTION 100 +#define INTEGRATED_SCSI 101 + /* List of possible IBM-SCSI-adapters */ struct subsys_list_struct subsys_list[] = { - {0x8efc, "IBM Fast SCSI-2 Adapter"}, /* special = 0 */ - {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/cache"}, /* special = 1 */ + {0x8efc, "IBM SCSI-2 F/W 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, only valid in older kernel releases */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,27) +struct proc_dir_entry proc_scsi_ibmmca = +{ + PROC_SCSI_IBMMCA, 6, "ibmmca", + S_IFDIR | S_IRUGO | S_IXUGO, 2, + 0, 0, 0, NULL, NULL, NULL, NULL, + NULL, NULL, NULL }; +#endif /* Max number of logical devices (can be up from 0 to 14). 15 is the address of the adapter itself. */ @@ -286,11 +362,12 @@ struct logical_device 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 */ - + Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */ int device_type; /* type of the SCSI-device. See include/scsi/scsi.h for interpretation of the possible values */ int block_length;/* blocksize of a particular logical SCSI-device */ + int cache_flag; /* 1 if this is uncached, 0 if cache is present for ldn */ + int retry_flag; /* 1 if adapter retry is disabled, 0 if enabled */ }; /* statistics of the driver during operations (for proc_info) */ @@ -302,6 +379,8 @@ struct Driver_Statistics int ldn_write_access[MAX_LOG_DEV+1]; /* total write-access on a ldn */ int ldn_inquiry_access[MAX_LOG_DEV+1]; /* total inquiries on a ldn */ int ldn_modeselect_access[MAX_LOG_DEV+1]; /* total mode selects on ldn */ + int scbs; /* short SCBs queued */ + int long_scbs; /* long SCBs queued */ int total_accesses; /* total accesses on all ldns */ int total_interrupts; /* total interrupts (should be same as total_accesses) */ @@ -318,12 +397,12 @@ struct Driver_Statistics struct ibmmca_hostdata { /* array of logical devices: */ - struct logical_device _ld[MAX_LOG_DEV+1]; + struct logical_device _ld[MAX_LOG_DEV+1]; /* array to convert (pun, lun) into logical device number: */ - unsigned char _get_ldn[8][8]; + unsigned char _get_ldn[16][8]; /*array that contains the information about the physical SCSI-devices attached to this host adapter: */ - unsigned char _get_scsi[8][8]; + unsigned char _get_scsi[16][8]; /* used only when checking logical devices: */ int _local_checking_phase_flag; /* report received interrupt: */ @@ -336,21 +415,30 @@ struct ibmmca_hostdata 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 + /* last blockcount */ + int _last_scsi_blockcount[MAX_LOG_DEV+1]; + /* last locgical block address */ + unsigned long _last_scsi_logical_block[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; + /* This hostadapters pos-registers pos2 until pos6 */ + unsigned _pos2, _pos3, _pos4, _pos5, _pos6; /* assign a special variable, that contains dedicated info about the adaptertype */ int _special; + /* connector size on the MCA bus */ + int _connector_size; + /* synchronous SCSI transfer rate bitpattern */ + int _adapter_speed; }; /* macros to access host data structure */ #define subsystem_pun(hi) (hosts[(hi)]->this_id) +#define subsystem_maxid(hi) (hosts[(hi)]->max_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) @@ -360,13 +448,21 @@ struct ibmmca_hostdata #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 last_scsi_blockcount(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_blockcount) +#define last_scsi_logical_block(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_last_scsi_logical_block) +#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 subsystem_connector_size(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_connector_size) +#define adapter_speed(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_adapter_speed) #define pos2(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos2) #define pos3(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos3) +#define pos4(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos4) +#define pos5(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos5) +#define pos6(hi) (((struct ibmmca_hostdata *) hosts[(hi)]->hostdata)->_pos6) -/* Define a arbitrary number as subsystem-marker-type. This number is, as +/* Define a arbitrary number as subsystem-marker-type. This number is, as described in the ANSI-SCSI-standard, not occupied by other device-types. */ #define TYPE_IBM_SCSI_ADAPTER 0x2F @@ -396,10 +492,6 @@ struct ibmmca_hostdata #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 @@ -409,22 +501,40 @@ struct ibmmca_hostdata static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 }; +/* fill module-parameters only, when this define is present. + (that is kernel version 2.1.x) */ +#if defined(MODULE) +static char *boot_options = NULL; +#include <linux/module.h> +MODULE_PARM(boot_options, "s"); +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 */ +static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL }; +static unsigned int pos[8]; /* whole pos register-line for diagnosis */ /* 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 */ @@ -439,32 +549,40 @@ static char ibm_ansi_order = 0; /******************* 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 (int); -static int immediate_assign(int, unsigned int, unsigned int, unsigned int, +static void check_devices (int, int); +static int immediate_assign(int, unsigned int, unsigned int, unsigned int, unsigned int); +static int immediate_feature(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 int get_pos_info(int); static char *ti_p(int); static char *ti_l(int); +static char *ibmrate(unsigned int, int); +static int probe_display(int); +static int probe_bus_mode(int); static int device_exists (int, int, int *, int *); static struct Scsi_Host *ibmmca_register(Scsi_Host_Template *, - int, int, char *); - + int, int, int, char *); /* local functions needed for proc_info */ 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 */ + /*--------------------------------------------------------------------*/ /******************* 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) { @@ -473,8 +591,9 @@ static void do_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) spin_lock_irqsave(&io_request_lock, flags); interrupt_handler(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags); - return; + return; } +#endif static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) { @@ -482,20 +601,17 @@ static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) unsigned int intr_reg; unsigned int cmd_result; unsigned int ldn; - static unsigned long flags; + 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 :-((( .... */ + int lastSCSI; + + host_index = 0; /* make sure, host_index is 0 */ /* 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; @@ -507,88 +623,126 @@ static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) 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; - + /*must wait for attention reg not busy, then send EOI to subsystem */ - while (1) + while (1) { +#ifdef OLDKERN + save_flags(flags); + cli(); +#else spin_lock_irqsave(&intr_lock, flags); - if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) +#endif + /* if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) */ + if ((inb(IM_STAT_REG(host_index)) & 0xf) == (IM_CMD_REG_EMPTY | IM_INTR_REQUEST)) break; +#ifdef OLDKERN + restore_flags(flags); +#else spin_unlock_irqrestore(&intr_lock, flags); +#endif } - outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); + /*get command result and logical device */ + intr_reg = (unsigned char)(inb (IM_INTR_REG(host_index))); + cmd_result = intr_reg & 0xf0; + ldn = intr_reg & 0x0f; + /* get the last_scsi_command here */ - interror = last_scsi_command(host_index)[ldn]; - spin_unlock_irqrestore(&intr_lock, flags); - errorflag = 0; /* no errors by default */ + lastSCSI = last_scsi_command(host_index)[ldn]; + /*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 (!global_command_error_excuse) + { + switch (cmd_result) + { /* Prevent from Ooopsing on error to show the real reason */ + case IM_ADAPTER_HW_FAILURE: + case IM_SOFTWARE_SEQUENCING_ERROR: + case IM_CMD_ERROR: + printk("\nIBM MCA SCSI: Fatal Subsystem ERROR!\n"); + printk(" Last cmd=0x%x, ena=%x, len=",lastSCSI, + ld(host_index)[ldn].scb.enable); + if (ld(host_index)[ldn].cmd) + printk("%ld/%ld",(long)(ld(host_index)[ldn].cmd->request_bufflen), + (long)(ld(host_index)[ldn].scb.sys_buf_length)); + else + printk("none"); + printk(", "); + if (ld(host_index)[ldn].cmd) + printk("Blocksize=%d",ld(host_index)[ldn].scb.u2.blk.length); + else + printk("Blocksize=none"); + printk(", host=0x%x, ldn=0x%x\n",host_index, ldn); + if (ld(host_index)[ldn].cmd) + { + printk("Blockcount=%d/%d\n",last_scsi_blockcount(host_index)[ldn], + ld(host_index)[ldn].scb.u2.blk.count); + printk("Logical block=%lx/%lx\n",last_scsi_logical_block(host_index)[ldn], + ld(host_index)[ldn].scb.u1.log_blk_adr); + } + printk("Reason given: %s\n", + (cmd_result==IM_ADAPTER_HW_FAILURE) ? "HARDWARE FAILURE" : + (cmd_result==IM_SOFTWARE_SEQUENCING_ERROR) ? "SOFTWARE SEQUENCING ERROR" : + (cmd_result==IM_CMD_ERROR) ? "COMMAND ERROR" : "UNKNOWN"); + /* 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-Command.................: %x\n", + ld(host_index)[ldn].scb.command); + printk(" SCB-Enable..................: %x\n", + ld(host_index)[ldn].scb.enable); + printk(" SCB-logical block address...: %lx\n", + ld(host_index)[ldn].scb.u1.log_blk_adr); + printk(" SCB-system buffer address...: %lx\n", + ld(host_index)[ldn].scb.sys_buf_adr); + printk(" SCB-system buffer length....: %lx\n", + ld(host_index)[ldn].scb.sys_buf_length); + printk(" SCB-tsb address.............: %lx\n", + ld(host_index)[ldn].scb.tsb_adr); + printk(" SCB-Chain address...........: %lx\n", + ld(host_index)[ldn].scb.scb_chain_adr); + printk(" SCB-block count.............: %x\n", + ld(host_index)[ldn].scb.u2.blk.count); + printk(" SCB-block length............: %x\n", + ld(host_index)[ldn].scb.u2.blk.length); + } + printk(" Send this report to the maintainer.\n"); + panic("IBM MCA SCSI: Fatal errormessage from the subsystem (0x%X,0x%X)!\n", + lastSCSI,cmd_result); + break; + } } - 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)) + else + { /* The command error handling is made silent, but we tell the + * calling function, that there is a reported error from the + * adapter. */ + switch (cmd_result) { - 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); + case IM_ADAPTER_HW_FAILURE: + case IM_SOFTWARE_SEQUENCING_ERROR: + case IM_CMD_ERROR: + global_command_error_excuse = CMD_FAIL; + break; + default: + global_command_error_excuse = 0; + break; } - 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++; - + /*only for local checking phase */ if (local_checking_phase_flag(host_index)) { @@ -596,8 +750,15 @@ static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) got_interrupt(host_index) = 1; reset_status(host_index) = IM_RESET_FINISHED_OK; last_scsi_command(host_index)[ldn] = NO_SCSI; + + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif return; - } + } /*handling of commands coming from upper level of scsi driver */ else { @@ -622,24 +783,39 @@ static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) } stat_result(host_index) = cmd_result; last_scsi_command(host_index)[ldn] = NO_SCSI; + last_scsi_type(host_index)[ldn] = 0; + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif 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 +#endif disk_rw_in_progress = 0; PS2_DISK_LED_OFF(); cmd = ld(host_index)[ldn].cmd; + ld(host_index)[ldn].cmd = NULL; 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; + last_scsi_type(host_index)[ldn] = 0; + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif if (cmd->scsi_done) - (cmd->scsi_done) (cmd); /* should be the internal_done */ + (cmd->scsi_done)(cmd); /* should be the internal_done */ return; } else @@ -649,36 +825,59 @@ static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) reset_status(host_index) = IM_RESET_FINISHED_OK; stat_result(host_index) = cmd_result; last_scsi_command(host_index)[ldn] = NO_SCSI; + + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif return; - } + } } last_scsi_command(host_index)[ldn] = NO_SCSI; + last_scsi_type(host_index)[ldn] = 0; cmd = ld(host_index)[ldn].cmd; + ld(host_index)[ldn].cmd = NULL; #ifdef IM_DEBUG_TIMEOUT if (cmd) { - if ((cmd->target == TIMEOUT_PUN)&&(cmd->lun == TIMEOUT_LUN)) + 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); + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif return; } } #endif /*if no command structure, just return, else clear cmd */ if (!cmd) - return; - ld(host_index)[ldn].cmd = NULL; - + { + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif + return; + } + #ifdef IM_DEBUG_INT - 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, + 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.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)) @@ -697,41 +896,73 @@ static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) } /* 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 */ + * with SCSI-defintion, I suppose, the reason for it is that IBM + * adapters do not support CMD_TERMINATED, TASK_SET_FULL and + * ACA_ACTIVE as returning statusbyte information. (ML) */ 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; + { + cmd->result = (unsigned char)(ld(host_index)[ldn].tsb.dev_status & 0x1e); + IBM_DS(host_index).total_errors++; + } else + cmd->result = 0; + /* write device status into cmd->result, and call done function */ + if (lastSCSI == NO_SCSI) /* unexpected interrupt :-( */ + cmd->result |= DID_BAD_INTR << 16; + else /* things went right :-) */ cmd->result |= DID_OK << 16; - (cmd->scsi_done) (cmd); - } - if (interror == NO_SCSI) + outb (IM_EOI | ldn, IM_ATTN_REG(host_index)); +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&intr_lock, flags); +#endif + /* This is for Kernel 2.2.x. Something weired happens here. + * Between the command got queued and the interrupt is released, + * the flags sometimes contain different values, which must + * be a strange thing. E.g. it appears when cold-booting with a + * tape drive at id0. */ + cmd->flags &= 0x3f; + if (cmd->scsi_done) + (cmd->scsi_done)(cmd); + } + if (lastSCSI == NO_SCSI) printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n"); return; } /*--------------------------------------------------------------------*/ -static void issue_cmd (int host_index, unsigned long cmd_reg, +static void issue_cmd (int host_index, unsigned long cmd_reg, unsigned char attn_reg) { static unsigned long flags; - /* must wait for attention reg not busy */ + /* must wait for attention reg not busy */ while (1) { +#ifdef OLDKERN + save_flags(flags); + cli(); +#else spin_lock_irqsave(&issue_lock, flags); - if (!(inb (IM_STAT_REG(host_index)) & IM_BUSY)) +#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 + return; } /*--------------------------------------------------------------------*/ @@ -739,75 +970,70 @@ static void issue_cmd (int host_index, unsigned long cmd_reg, static void internal_done (Scsi_Cmnd * cmd) { cmd->SCp.Status++; + return; } /*--------------------------------------------------------------------*/ /* SCSI-SCB-command for device_inquiry */ static int device_inquiry(int host_index, int ldn) -{ +{ int retries; - Scsi_Cmnd *cmd; + 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 */ - cmd = kmalloc(sizeof(*cmd), GFP_KERNEL|GFP_DMA); - if(cmd==NULL) - { - printk(KERN_ERR "ibmmca: out of memory for inquiry.\n"); - return 0; - } 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 */ - } + 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); + scb->command = IM_OTHER_SCSI_CMD_CMD | IM_NO_DISCONNECT; + scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; + 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; + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; } else { /*fill scb with inquiry command */ - scb->command = IM_DEVICE_INQUIRY_CMD; - scb->enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->command = IM_DEVICE_INQUIRY_CMD | IM_NO_DISCONNECT; + scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | 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; + 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); + if ((scb->command & IM_OTHER_SCSI_CMD_CMD) == IM_OTHER_SCSI_CMD_CMD) + issue_cmd (host_index, virt_to_bus(scb), IM_LONG_SCB | ldn); + else + 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; - } + return 1; } - kfree(cmd); - + /*if all three retries failed, return "no device at this ldn" */ if (retries >= 3) return 0; @@ -822,25 +1048,25 @@ static int read_capacity(int host_index, int ldn) 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->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; 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; @@ -849,26 +1075,27 @@ static int read_capacity(int host_index, int ldn) else { scb->command = IM_READ_CAPACITY_CMD; - scb->enable = IM_READ_CONTROL; + scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; last_scsi_command(host_index)[ldn] = IM_READ_CAPACITY_CMD; - last_scsi_type(host_index)[ldn] = IM_SCB; + 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); + if ((scb->command & IM_OTHER_SCSI_CMD_CMD) == IM_OTHER_SCSI_CMD_CMD) + issue_cmd (host_index, virt_to_bus(scb), IM_LONG_SCB | ldn); + else + 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 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; - } + return 1; } /*if all three retries failed, return "no device at this ldn" */ if (retries >= 3) @@ -877,41 +1104,141 @@ static int read_capacity(int host_index, int ldn) return 1; } +static int get_pos_info(int host_index) +{ + int retries; + struct im_scb *scb; + struct im_tsb *tsb; + unsigned char *buf; + + scb = &(ld(host_index)[MAX_LOG_DEV].scb); + tsb = &(ld(host_index)[MAX_LOG_DEV].tsb); + buf = (unsigned char *)(&(ld(host_index)[MAX_LOG_DEV].buf)); + ld(host_index)[MAX_LOG_DEV].tsb.dev_status = 0; + + for (retries = 0; retries < 3; retries++) + { + /*fill scb with get_pos_info command */ + scb->command = IM_GET_POS_INFO_CMD; + scb->enable = IM_READ_CONTROL | IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE | IM_BYPASS_BUFFER | IM_SUPRESS_EXCEPTION_SHORT; + last_scsi_command(host_index)[MAX_LOG_DEV] = IM_GET_POS_INFO_CMD; + last_scsi_type(host_index)[MAX_LOG_DEV] = IM_SCB; + + scb->sys_buf_adr = virt_to_bus(buf); + if (special(host_index)==IBM_SCSI2_FW) + scb->sys_buf_length = 256; /* get all info from F/W adapter */ + else + scb->sys_buf_length = 18; /* get exactly 18 bytes for other SCSI */ + + scb->tsb_adr = virt_to_bus(tsb); + + /*issue scb to ldn=15, and busy wait for interrupt */ + got_interrupt(host_index) = 0; + issue_cmd (host_index, virt_to_bus(scb), IM_SCB | MAX_LOG_DEV); + while (!got_interrupt(host_index)) + barrier (); + + /*if got POS-stuff, 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(int host_index, unsigned int pun, - unsigned int lun, unsigned int ldn, +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(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); - imm_command |= (unsigned long)((operation & 1) << 23); - imm_command |= (unsigned long)((pun & 7) << 20); - imm_command |= (unsigned long)((ldn & 15) << 16); - - last_scsi_command(host_index)[0xf] = IM_ASSIGN_IMM_CMD; - last_scsi_type(host_index)[0xf] = IM_IMM_CMD; + /* select mutation level of the SCSI-adapter */ + switch (special(host_index)) + { + case IBM_SCSI2_FW: + imm_command = (unsigned long)(IM_ASSIGN_IMM_CMD); + imm_command |= (unsigned long)((lun & 7) << 24); + imm_command |= (unsigned long)((operation & 1) << 23); + imm_command |= (unsigned long)((pun & 7)<< 20)|((pun & 8)<< 24); + imm_command |= (unsigned long)((ldn & 15) << 16); + break; + default: + 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); + imm_command |= (unsigned long)((operation & 1) << 23); + imm_command |= (unsigned long)((pun & 7) << 20); + imm_command |= (unsigned long)((ldn & 15) << 16); + break; + } + last_scsi_command(host_index)[MAX_LOG_DEV] = IM_ASSIGN_IMM_CMD; + last_scsi_type(host_index)[MAX_LOG_DEV] = IM_IMM_CMD; got_interrupt(host_index) = 0; - issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | 0xf); + issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | MAX_LOG_DEV); 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; +} + +static int immediate_feature(int host_index, unsigned int speed, + unsigned int timeout) +{ + int retries; + unsigned long imm_command; + + for (retries=0; retries<3; retries ++) + { + /* select mutation level of the SCSI-adapter */ + switch (special(host_index)) { - return 1; + default: + imm_command = IM_FEATURE_CTR_IMM_CMD; + imm_command |= (unsigned long)((speed & 0x7) << 29); + imm_command |= (unsigned long)((timeout & 0x1fff) << 16); + break; + } + last_scsi_command(host_index)[MAX_LOG_DEV] = IM_FEATURE_CTR_IMM_CMD; + last_scsi_type(host_index)[MAX_LOG_DEV] = IM_IMM_CMD; + got_interrupt(host_index) = 0; + /* we need to run into command errors in order to probe for the + * right speed! */ + global_command_error_excuse = 1; + issue_cmd (host_index, (unsigned long)(imm_command), IM_IMM_CMD | MAX_LOG_DEV); + while (!got_interrupt(host_index)) + barrier (); + if (global_command_error_excuse == CMD_FAIL) + { + global_command_error_excuse = 0; + return 2; } + else + global_command_error_excuse = 0; + + /*if command succesful, break */ + if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED) + return 1; } - - if (retries >= 3) + + if (retries >= 3) return 0; else return 1; @@ -923,7 +1250,7 @@ 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)); @@ -933,31 +1260,29 @@ static int immediate_reset(int host_index, unsigned int ldn) last_scsi_type(host_index)[ldn] = IM_IMM_CMD; got_interrupt(host_index) = 0; - reset_status(host_index) = IM_RESET_IN_PROGRESS; + 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) + 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) + 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; + reset_status(host_index) = IM_RESET_FINISHED_OK; /* did not work, finish */ return 1; } /*if command succesful, break */ if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED) - { - return 1; - } + return 1; } - - if (retries >= 3) + + if (retries >= 3) return 0; else return 1; @@ -990,17 +1315,108 @@ static char *ti_l(int value) { const char hex[16] = "0123456789abcdef"; static char answer[2]; - + answer[1] = (char)(0x0); if (value<=MAX_LOG_DEV) answer[0] = hex[value]; else answer[0] = '-'; - + return (char *)&answer; } -/* +/* transfers bitpattern of the feature command to values in MHz */ +static char *ibmrate(unsigned int speed, int adaptertype) +{ + int i; + i=adaptertype; + switch (speed) + { + case 0: if (i) return "5.00"; else return "10.00"; break; + case 1: if (i) return "4.00"; else return "8.00"; break; + case 2: if (i) return "3.33"; else return "6.66"; break; + case 3: if (i) return "2.86"; else return "5.00"; break; + case 4: if (i) return "2.50"; else return "4.00"; break; + case 5: if (i) return "2.22"; else return "3.10"; break; + case 6: if (i) return "2.00"; else return "2.50"; break; + case 7: if (i) return "1.82"; else return "2.00"; break; + } + return "---"; +} + +static int probe_display(int what) +{ + static int rotator = 0; + const char rotor[] = "|/-\\"; + + if (!(display_mode & LED_DISP)) + return 0; + if (!what) + { + outl(0x20202020,MOD95_LED_PORT); + outl(0x20202020,MOD95_LED_PORT+4); + } + else + { + outb('S',MOD95_LED_PORT+7); + outb('C',MOD95_LED_PORT+6); + outb('S',MOD95_LED_PORT+5); + outb('I',MOD95_LED_PORT+4); + outb('i',MOD95_LED_PORT+3); + outb('n',MOD95_LED_PORT+2); + outb('i',MOD95_LED_PORT+1); + outb((char)(rotor[rotator]),MOD95_LED_PORT); + rotator++; + if (rotator>3) + rotator=0; + } + return 0; +} + +static int probe_bus_mode(int host_index) +{ + struct im_pos_info *info; + int num_bus = 0; + int ldn; + + info = (struct im_pos_info *)(&(ld(host_index)[MAX_LOG_DEV].buf)); + + if (get_pos_info(host_index)) + { + if (info->connector_size & 0xf000) + subsystem_connector_size(host_index)=16; + else + subsystem_connector_size(host_index)=32; + num_bus |= (info->pos_4b & 8) >> 3; + for (ldn=0; ldn<=MAX_LOG_DEV; ldn++) + { + if ((special(host_index)==IBM_SCSI_WCACHE)|| + (special(host_index)==IBM_7568_WCACHE)) + { + if (!((info->cache_stat >> ldn) & 1)) + ld(host_index)[ldn].cache_flag = 0; + } + if (!((info->retry_stat >> ldn) & 1)) + ld(host_index)[ldn].retry_flag = 0; + } +#ifdef IM_DEBUG_PROBE + printk("IBM MCA SCSI: SCSI-Cache bits: "); + for (ldn=0; ldn<=MAX_LOG_DEV; ldn++) + { + printk("%d",ld(host_index)[ldn].cache_flag); + } + printk("\nIBM MCA SCSI: SCSI-Retry bits: "); + for (ldn=0; ldn<=MAX_LOG_DEV; ldn++) + { + printk("%d",ld(host_index)[ldn].retry_flag); + } + printk("\n"); +#endif + } + return num_bus; +} + +/* 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 @@ -1020,32 +1436,36 @@ static char *ti_l(int value) The assignment of ALL ldns avoids dynamical remapping by the adapter itself. */ -static void check_devices (int host_index) +static void check_devices (int host_index, int adaptertype) { int id, lun, ldn, ticks; int count_devices; /* local counter for connected device */ - + int max_pun; + int num_bus; + int speedrun; /* local adapter_speed check variable */ + /* 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'*/ + + /* initialize the very important driver-informational arrays/structs */ + memset (ld(host_index), 0, + sizeof(ld(host_index))); 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; + ld(host_index)[ldn].cache_flag = 1; + ld(host_index)[ldn].retry_flag = 1; } - - /* 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, + 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; @@ -1054,13 +1474,58 @@ static void check_devices (int host_index) luns. */ } + probe_display(0); /* Supercool display usage during SCSI-probing. */ + /* This makes sense, when booting without any */ + /* monitor connected on model XX95. */ + /* STEP 1: */ + adapter_speed(host_index) = global_adapter_speed; + speedrun = adapter_speed(host_index); + while (immediate_feature(host_index,speedrun,adapter_timeout)==2) + { + probe_display(1); + if (speedrun==7) + panic("IBM MCA SCSI: Cannot set Synchronous-Transfer-Rate!\n"); + speedrun++; + if (speedrun>7) + speedrun=7; + } + adapter_speed(host_index) = speedrun; + /* Get detailed information about the current adapter, necessary for + * device operations: */ + num_bus=probe_bus_mode(host_index); + + /* num_bus contains only valid data for the F/W adapter! */ + if (adaptertype==IBM_SCSI2_FW) /* F/W SCSI adapter: */ + { + /* F/W adapter PUN-space extension evaluation: */ + if (num_bus) + { + printk("IBM MCA SCSI: Seperate bus mode (wide-addressing enabled)\n"); + subsystem_maxid(host_index) = 16; + } + else + { + printk("IBM MCA SCSI: Combined bus mode (wide-addressing disabled)\n"); + subsystem_maxid(host_index) = 8; + } + printk("IBM MCA SCSI: Sync.-Rate (F/W: 20, Int.: 10, Ext.: %s) MBytes/s\n", + ibmrate(speedrun,adaptertype)); + } + else /* all other IBM SCSI adapters: */ + printk("IBM MCA SCSI: Synchronous-SCSI-Transfer-Rate: %s MBytes/s\n", + ibmrate(speedrun,adaptertype)); + + /* assign correct PUN device space */ + max_pun = subsystem_maxid(host_index); + #ifdef IM_DEBUG_PROBE printk("IBM MCA SCSI: Current SCSI-host index: %d\n",host_index); #endif printk("IBM MCA SCSI: Removing default logical SCSI-device mapping."); for (ldn=0; ldn<MAX_LOG_DEV; ldn++) { + probe_display(1); #ifdef IM_DEBUG_PROBE printk("."); #endif @@ -1068,14 +1533,20 @@ static void check_devices (int host_index) } lun = 0; /* default lun is 0 */ - + /* STEP 2: */ - printk("\nIBM MCA SCSI: Probing SCSI-devices."); - for (id=0; id<8; id++) + printk("\nIBM MCA SCSI: Probing SCSI-devices"); +#ifndef IM_DEBUG_PROBE + printk(" (this can take up to 2 minutes)"); +#endif + printk("."); + + for (id=0; id<max_pun ; id++) #ifdef CONFIG_SCSI_MULTI_LUN for (lun=0; lun<8; lun++) #endif { + probe_display(1); #ifdef IM_DEBUG_PROBE printk("."); #endif @@ -1095,31 +1566,32 @@ static void check_devices (int host_index) immediate_assign(host_index,id,lun,PROBE_LDN,REMOVE_LDN); } } - - /* STEP 3: */ + + /* STEP 3: */ printk("\nIBM MCA SCSI: Mapping SCSI-devices."); - + ldn = 0; lun = 0; - -#ifdef CONFIG_SCSI_MULTI_LUN + +#ifdef CONFIG_SCSI_MULTI_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<max_pun && ldn<MAX_LOG_DEV; id++) { + probe_display(1); #ifdef IM_DEBUG_PROBE printk("."); #endif if (id != subsystem_pun(host_index)) { - if (get_scsi(host_index)[id][lun] != TYPE_NO_LUN && + 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 + /* Only map if accepted type. Always enter for lun == 0 to get no gaps into ldn-mapping for ldn<7. */ immediate_assign(host_index,id,lun,ldn,SET_LDN); get_ldn(host_index)[id][lun]=ldn; /* map ldn */ - if (device_exists (host_index, ldn, + if (device_exists (host_index, ldn, &ld(host_index)[ldn].block_length, &ld(host_index)[ldn].device_type)) { @@ -1149,18 +1621,19 @@ static void check_devices (int host_index) 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++) + for (id=0; id<max_pun && ldn<MAX_LOG_DEV; id++) { if (get_scsi(host_index)[id][lun] == TYPE_NO_LUN || get_scsi(host_index)[id][lun] == TYPE_NO_DEVICE) { + probe_display(1); /* 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 @@ -1169,20 +1642,20 @@ static void check_devices (int host_index) get_ldn(host_index)[id][lun]=ldn; ldn++; } - } - + } + printk("\n"); 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. */ printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n"); printk(" Physical SCSI-Device Map Logical SCSI-Device Map\n"); 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++) + for (id=0; id<max_pun; id++) { printk("%2d ",id); for (lun=0; lun<8; lun++) @@ -1193,52 +1666,54 @@ static void check_devices (int host_index) printk("\n"); } #endif - + /* assign total number of found SCSI-devices to the statistics struct */ 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 */ - if (count_devices>=MAX_LOG_DEV) + if (count_devices>=MAX_LOG_DEV) IBM_DS(host_index).dyn_flag = 1; /* dynamical assignment is necessary */ - else + else 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/assigned!\n"); - + /* reset the counters for statistics on the current adapter */ + IBM_DS(host_index).scbs = 0; + IBM_DS(host_index).long_scbs = 0; 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, + memset (IBM_DS(host_index).ldn_access, 0x0, sizeof (IBM_DS(host_index).ldn_access)); - memset (IBM_DS(host_index).ldn_read_access, 0x0, + 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, + 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, + 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, + 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, + memset (IBM_DS(host_index).ldn_assignments, 0x0, sizeof (IBM_DS(host_index).ldn_assignments)); - + probe_display(0); return; } /*--------------------------------------------------------------------*/ -static int device_exists (int host_index, int ldn, int *block_length, +static int device_exists (int host_index, int ldn, int *block_length, int *device_type) { 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)); /*if device is CD_ROM, assume block size 2048 and return */ @@ -1248,64 +1723,64 @@ static int device_exists (int host_index, int ldn, int *block_length, *block_length = 2048; /* (standard blocksize for yellow-/red-book) */ return 1; } - - if (*buf == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM + + 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) + + *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) + + *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; + 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; @@ -1313,60 +1788,54 @@ static int device_exists (int host_index, int ldn, int *block_length, 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. */ + 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 internal_ibmmca_scsi_setup (char *str, int *ints) { 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,"activity")) + display_mode |= LED_ACTIVITY; if (!strcmp(token,"display")) - { - use_display = 1; - } + display_mode |= LED_DISP; if (!strcmp(token,"adisplay")) - { - use_adisplay = 1; - } + display_mode |= LED_ADISP; if (!strcmp(token,"bypass")) - { - bypass_controller = 1; - } + bypass_controller = 1; if (!strcmp(token,"normal")) - { - ibm_ansi_order = 0; - } + ibm_ansi_order = 0; if (!strcmp(token,"ansi")) - { - ibm_ansi_order = 1; - } + ibm_ansi_order = 1; + if (!strcmp(token,"fast")) + global_adapter_speed = 0; + if (!strcmp(token,"medium")) + global_adapter_speed = 4; + if (!strcmp(token,"slow")) + global_adapter_speed = 7; if ( (*token == '-') || (isdigit(*token)) ) { if (!(j%2) && (io_base < IM_MAX_HOSTS)) - { - io_port[io_base++] = simple_strtoul(token,NULL,0); - } + 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); - } + scsi_id[id_base++] = simple_strtoul(token,NULL,0); j++; } token = strtok(NULL,","); @@ -1374,7 +1843,7 @@ void ibmmca_scsi_setup (char *str, int *ints) } else if (ints) { - for (i = 0; i < IM_MAX_HOSTS && 2*i+2 < ints[0]; 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]; @@ -1390,28 +1859,34 @@ void ibmmca_scsi_setup (char *str, int *ints) static int ibmmca_getinfo (char *buf, int slot, void *dev) { struct Scsi_Host *shpnt; - int len, special; + int len, speciale,connectore; 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; + speciale = ((struct ibmmca_hostdata *)shpnt->hostdata)->_special; + connectore = ((struct ibmmca_hostdata *)shpnt->hostdata)->_connector_size; pos2 = ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2; pos3 = ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3; - - if (special == FORCED_DETECTION) /* forced detection */ + + if (speciale == FORCED_DETECTION) /* forced detection */ { - len += sprintf (buf + len, "Adapter cathegory: forced detected\n"); + len += sprintf (buf + len, "Adapter category: 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) + else if (speciale == INTEGRATED_SCSI) { /* if the integrated subsystem has been found automatically: */ len += sprintf (buf + len, "Adapter category: integrated\n"); len += sprintf (buf + len, "Chip revision level: %d\n", @@ -1421,15 +1896,19 @@ static int ibmmca_getinfo (char *buf, int slot, void *dev) 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 */ + else if ((speciale>=0)&& + (speciale<(sizeof(subsys_list)/sizeof(struct subsys_list_struct)))) + { /* if the subsystem is a slot adapter */ len += sprintf (buf + len, "Adapter category: slot-card\n"); - len += sprintf (buf + len, "Chip revision level: %d\n", - ((pos2 & 0xf0) >> 4)); + len += sprintf (buf + len, "ROM Segment Address: "); + if ((pos2 & 0xf0) == 0xf0) + len += sprintf (buf + len, "off\n"); + else + len += sprintf (buf + len, "0x%x\n", + ((pos2 & 0xf0) << 13) + 0xc0000); len += sprintf (buf + len, "Chip status: %s\n", (pos2 & 1) ? "enabled" : "disabled"); - len += sprintf (buf + len, "Port offset: 0x%x\n", + len += sprintf (buf + len, "Adapter I/O Offset: 0x%x\n", ((pos2 & 0x0e) << 2)); } else @@ -1438,10 +1917,11 @@ static int ibmmca_getinfo (char *buf, int slot, void *dev) } /* 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", + len += sprintf (buf + len, "I/O base address range: 0x%x-0x%x\n", (unsigned int)(shpnt->io_port), (unsigned int)(shpnt->io_port+7)); - /* Now make sure, the bufferlength is divisible by 4 to avoid + len += sprintf (buf + len, "MCA-slot size: %d bits",connectore); + /* Now make sure, the bufferlength is devidable by 4 to avoid * paging problems of the buffer. */ while ( len % sizeof( int ) != ( sizeof ( int ) - 1 ) ) { @@ -1449,34 +1929,59 @@ static int ibmmca_getinfo (char *buf, int slot, void *dev) } 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; - + int devices_on_irq_11 = 0; + int devices_on_irq_14 = 0; + int IRQ14_registered = 0; + int IRQ11_registered = 0; + found = 0; /* make absolutely sure, that found is set to 0 */ + /* First of all, print the version number of the driver. This is + * important to allow better user bugreports in case of already + * having problems with the MCA_bus probing. */ + printk("IBM MCA SCSI: Version %s\n",IBMMCA_SCSI_DRIVER_VERSION); /* if this is not MCA machine, return "nothing found" */ if (!MCA_bus) { - printk("IBM MCA SCSI: No Microchannel-bus support present -> Aborting.\n"); + printk("IBM MCA SCSI: No Microchannel-bus present --> Aborting.\n"); + printk(" This machine does not have any IBM MCA-bus\n"); + printk(" or the MCA-Kernel-support is not enabled!\n"); return 0; } - else - printk("IBM MCA SCSI: Version %s\n",IBMMCA_SCSI_DRIVER_VERSION); +#ifdef MODULE + /* If the driver is run as module, read from conf.modules or cmd-line */ + if (boot_options) + option_setup(boot_options); +#endif + /* 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; } - + else + IRQ14_registered++; + /* 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) @@ -1484,20 +1989,25 @@ int ibmmca_detect (Scsi_Host_Template * scsi_template) 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_DETECTION, "forced detected SCSI Adapter"))) { ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos2 = 0; ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos3 = 0; - ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos4 = 0; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = 0; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = 0; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_special = FORCED_DETECTION; - mca_set_adapter_name(MCA_INTEGSCSI, "forcibly detected SCSI Adapter"); + 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); - } + devices_on_irq_14++; + } } 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) @@ -1509,16 +2019,16 @@ int ibmmca_detect (Scsi_Host_Template * scsi_template) * 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 + * 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 + * 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) */ @@ -1526,13 +2036,15 @@ int ibmmca_detect (Scsi_Host_Template * scsi_template) 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] != 0xff) || (pos[3] != 0xff )) */ + /* Previous if-arguments do fail! Therefore, we use now the following to + * make sure, we see a real integrated onboard SCSI-interface: */ + if ((!pos[0] && !pos[1] && pos[2]>0 && pos[3]>0 && !pos[4] && !pos[5] && !pos[6] && !pos[7]) || + (pos[0]==0xff && pos[1]==0xff && pos[2]<0xff && pos[3]<0xff && pos[4]==0xff && pos[5]==0xff && pos[6]==0xff && pos[7]==0xff)) { if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */ - { - port = IM_IO_PORT; - } - else + 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 @@ -1543,10 +2055,10 @@ int ibmmca_detect (Scsi_Host_Template * scsi_template) 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 + + /* 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); @@ -1556,20 +2068,25 @@ int ibmmca_detect (Scsi_Host_Template * scsi_template) /* register the found integrated SCSI-subsystem */ if ((shpnt = ibmmca_register(scsi_template, port, id, + INTEGRATED_SCSI, "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 = + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos4 = pos[4]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = pos[5]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = pos[6]; + ((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); + devices_on_irq_14++; } } - - /* now look for other adapters in MCA slots, */ + + /* 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); @@ -1584,54 +2101,189 @@ int ibmmca_detect (Scsi_Host_Template * scsi_template) if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */ { /* (explanations see above) */ port = IM_IO_PORT + ((pos[2] & 0x0e) << 2); - } - else + } + else { /* anyway, set the portnumber and warn */ - port = IM_IO_PORT + ((pos[2] & 0x0e) << 2); + 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"); + } + if ((i==IBM_SCSI2_FW)&&(pos[6]!=0)) + { + printk("IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n"); + printk(" Impossible to determine adapter PUN!\n"); + printk(" Guessing adapter PUN = 7.\n"); + id = 7; + } + else + { + id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */ + if (i==IBM_SCSI2_FW) + { + id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit + * for F/W adapters */ + } + } + if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0)) + { /* IRQ11 is used by SCSI-2 F/W Adapter/A */ + printk("IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n"); + /* get interrupt request level */ +#ifdef OLDKERN + if (request_irq (IM_IRQ_FW, interrupt_handler, SA_SHIRQ, + "ibmmcascsi", hosts)) +#else + if (request_irq (IM_IRQ_FW, do_interrupt_handler, SA_SHIRQ, + "ibmmcascsi", hosts)) +#endif + { + printk("IBM MCA SCSI: Unable to get shared IRQ %d.\n", + IM_IRQ_FW); + } + else + IRQ11_registered++; } - 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), + if ((pos[2] & 0xf0) == 0xf0) + printk(" ROM Addr.=off,"); + else + printk(" ROM Addr.=0x%x,", + ((pos[2] & 0xf0) << 13) + 0xc0000); + printk(" port-offset=0x%x, subsystem=%s\n", ((pos[2] & 0x0e) << 2), (pos[2] & 1) ? "enabled." : "disabled."); - + /* register the hostadapter */ - if ((shpnt = ibmmca_register(scsi_template, port, id, + if ((shpnt = ibmmca_register(scsi_template, port, id, i, 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)->_pos4 = pos[4]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = pos[5]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = pos[6]; ((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); + if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0)) + devices_on_irq_11++; + else + devices_on_irq_14++; } 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"); + + /* now look for SCSI-adapters, by bugs mapped to the integrated SCSI + * area. E.g. a W/Cache in MCA-slot 9 ???? Arrrrgh!! */ + 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 = mca_find_adapter(subsys_list[i].mca_id, MCA_INTEGSCSI); + if (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"); + } + if ((i==IBM_SCSI2_FW)&&(pos[6]!=0)) + { + printk("IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n"); + printk(" Impossible to determine adapter PUN!\n"); + printk(" Guessing adapter PUN = 7.\n"); + id = 7; + } + else + { + id = (pos[3] & 0xe0) >> 5; /* get subsystem PUN */ + if (i==IBM_SCSI2_FW) + { + id |= (pos[3] & 0x10) >> 1; /* get subsystem PUN high-bit + * for F/W adapters */ + } + } + if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0)) + { /* IRQ11 is used by SCSI-2 F/W Adapter/A */ + printk("IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n"); + /* get interrupt request level */ +#ifdef OLDKERN + if (request_irq (IM_IRQ_FW, interrupt_handler, SA_SHIRQ, + "ibmmcascsi", hosts)) +#else + if (request_irq (IM_IRQ_FW, do_interrupt_handler, SA_SHIRQ, + "ibmmcascsi", hosts)) +#endif + { + printk("IBM MCA SCSI: Unable to get shared IRQ %d.\n", + IM_IRQ_FW); + } + else + IRQ11_registered++; + } + 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 ((pos[2] & 0xf0) == 0xf0) + printk(" ROM Addr.=off,"); + else + printk(" ROM Addr.=0x%x,", + ((pos[2] & 0xf0) << 13) + 0xc0000); + printk(" port-offset=0x%x, subsystem=%s\n", + ((pos[2] & 0x0e) << 2), + (pos[2] & 1) ? "enabled." : "disabled."); + + /* register the hostadapter */ + if ((shpnt = ibmmca_register(scsi_template, port, id, i, + 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)->_pos4 = pos[4]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos5 = pos[5]; + ((struct ibmmca_hostdata *)shpnt->hostdata)->_pos6 = pos[6]; + ((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); + if ((i==IBM_SCSI2_FW)&&(pos[4] & 0x01)&&(pos[6]==0)) + devices_on_irq_11++; + else + devices_on_irq_14++; + } + slot++; /* advance to next slot */ + } /* advance to next adapter id in the list of IBM-SCSI-subsystems*/ } + + if ( IRQ11_registered && !devices_on_irq_11 ) + free_irq(IM_IRQ_FW, hosts); /* no devices on IRQ 11 */ + if ( IRQ14_registered && !devices_on_irq_14 ) + free_irq(IM_IRQ, hosts); /* no devices on IRQ 14 */ + if ( !devices_on_irq_11 && !devices_on_irq_14 ) + 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 * scsi_template, int port, int id, - char *hostname) +ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, + int adaptertype, char *hostname) { struct Scsi_Host *shpnt; int i, j; unsigned int ctrl; - + /* check I/O region */ if (check_region(port, IM_N_IO_PORT)) { @@ -1639,7 +2291,7 @@ ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, 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) @@ -1647,18 +2299,21 @@ ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, 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 */ + special(found) = adaptertype; /* important assignment or else crash! */ + subsystem_connector_size(found) = 0; /* preset slot-size */ 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; + shpnt->max_id = 8; /* 8 PUNs are default */ /* now, the SCSI-subsystem is connected to Linux */ - - ctrl = (unsigned int)(inb(IM_CTR_REG(found))); /* get control-register status */ + + 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))); @@ -1669,18 +2324,19 @@ ibmmca_register(Scsi_Host_Template * scsi_template, int port, int id, 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 (i = 0; i < 16; 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*/ + /* after this line, local interrupting is possible: */ + local_checking_phase_flag(found) = 1; + check_devices(found,adaptertype); /* 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; @@ -1710,21 +2366,21 @@ int ibmmca_release(struct Scsi_Host *shpnt) /*--------------------------------------------------------------------*/ /* The following routine is the SCSI command queue. The old edition is - now improved by dynamical reassignment of ldn numbers that are + now improved by dynamical reassignment of ldn numbers that are currently not assigned. The mechanism works in a way, that first the physical structure is checked. If at a certain pun,lun a device should be present, the routine proceeds to the ldn check from get_ldn. An answer of 0xff would show-up, that the aimed device is - currently not assigned any ldn. At this point, the dynamical + currently not assigned any ldn. At this point, the dynamical remapping algorithm is called. It works in a way, that it goes in cyclic order through the ldns from 7 to 14. If a ldn is assigned, it takes 8 dynamical reassignment calls, until a device looses its - ldn again. With this method it is assured, that while doing + ldn again. With this method it is assured, that while doing intense I/O between up to eight devices, no dynamical remapping is done there. ldns 0 through 6(!) are left untouched, which means, that puns 0 through 7(!) on lun=0 are always accessible without remapping. - These ldns are statically assigned by this driver. The subsystem always - occupies at least one pun, therefore 7 ldns (at lun=0) for other devices + These ldns are statically assigned by this driver. The subsystem always + occupies at least one pun, therefore 7 ldns (at lun=0) for other devices are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) { @@ -1736,33 +2392,46 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) int id,lun; int target; int host_index; - - if (ibm_ansi_order) - target = 6 - cmd->target; - else - target = cmd->target; - + int max_pun; + int i; + struct scatterlist *sl; + 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); + if (done) + done (cmd); return 0; } - + + max_pun = subsystem_maxid(host_index); + + if (ibm_ansi_order) + { + target = max_pun - 1 - cmd->target; + if ((target <= subsystem_pun(host_index))&&(cmd->target <= subsystem_pun(host_index))) + target--; + else if ((target >= subsystem_pun(host_index))&&(cmd->target >= subsystem_pun(host_index))) + target++; + } + else + target = cmd->target; + /*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); + if (done) + done (cmd); return 0; } - + /*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 */ @@ -1773,7 +2442,7 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) 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) + 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 */ @@ -1782,18 +2451,19 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n", target, cmd->lun); cmd->result = DID_NO_CONNECT << 16;/* return no connect*/ - done (cmd); + if (done) + done (cmd); return 0; } } - + /* unmap non-processing ldn */ - for (id=0; id<8; id ++) + for (id=0; id<max_pun; 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; + get_ldn(host_index)[id][lun] = TYPE_NO_DEVICE; /* unmap entry */ } } @@ -1808,66 +2478,69 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) /* 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, + if (device_exists (host_index, ldn, &ld(host_index)[ldn].block_length, &ld(host_index)[ldn].device_type)) { 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(host_index).dynamical_assignments++; IBM_DS(host_index).ldn_assignments[ldn]++; } else - /* panic here, because a device, found at boottime has + /* 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 */ + + /* increase next_ldn for next dynamical assignment */ next_ldn(host_index)++; - if (next_ldn(host_index)>=MAX_LOG_DEV) + if (next_ldn(host_index)>=MAX_LOG_DEV) next_ldn(host_index) = 7; - } + } else - { /* wall against Linux accesses to the subsystem adapter */ + { /* wall against Linux accesses to the subsystem adapter */ cmd->result = DID_BAD_TARGET << 16; - done (cmd); + if (done) + 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->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE; scb->tsb_adr = virt_to_bus(&(ld(host_index)[ldn].tsb)); + scsi_cmd = cmd->cmnd[0]; + if (cmd->use_sg) { - int i = cmd->use_sg; - struct scatterlist *sl = (struct scatterlist *) cmd->request_buffer; + i = cmd->use_sg; + 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].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; @@ -1877,31 +2550,45 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) else { scb->sys_buf_adr = virt_to_bus(cmd->request_buffer); - scb->sys_buf_length = cmd->request_bufflen; + /* recent Linux midlevel SCSI places 1024 byte for inquiry + * command. Far too much for old PS/2 hardware. */ + switch (scsi_cmd) + { /* avoid command errors by setting bufferlengths to + * ANSI-standard. */ + case INQUIRY: + case REQUEST_SENSE: + case MODE_SENSE: + case MODE_SELECT: + scb->sys_buf_length = 255; + break; + case TEST_UNIT_READY: + scb->sys_buf_length = 0; + break; + default: + scb->sys_buf_length = cmd->request_bufflen; + break; + } } - /*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); #endif - + /* for specific device-type debugging: */ #ifdef IM_DEBUG_CMD_SPEC_DEV 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", + printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", ld(host_index)[ldn].device_type, scsi_cmd, ldn); #endif - + /* for possible panics store current command */ - last_scsi_command(host_index)[ldn] = scsi_cmd; - last_scsi_type(host_index)[ldn] = IM_SCB; - + 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: @@ -1909,54 +2596,48 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) 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.*/ - + case WRITE_12: /* Distinguish between disk and other devices. Only disks (that are the - most frequently accessed devices) should be supported by the - IBM-SCSI-Subsystem commands. */ + most frequently accessed devices) should be supported by the + IBM-SCSI-Subsystem commands. */ 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 || + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12) { /* read command preparations */ + scb->enable |= IM_READ_CONTROL; + IBM_DS(host_index).ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */ if (bypass_controller) { scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_READ_CONTROL; + scb->enable |= IM_BYPASS_BUFFER; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy(scb->u2.scsi_command,cmd->cmnd,cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; } else - { - scb->command = IM_READ_DATA_CMD; - scb->enable |= IM_READ_CONTROL; - } + scb->command = IM_READ_DATA_CMD | IM_NO_DISCONNECT; } else { /* write command preparations */ + IBM_DS(host_index).ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/ if (bypass_controller) { scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_BYPASS_BUFFER; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy(scb->u2.scsi_command,cmd->cmnd,cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; } else - { - scb->command = IM_WRITE_DATA_CMD; - } + scb->command = IM_WRITE_DATA_CMD | IM_NO_DISCONNECT; } - + if (!bypass_controller) - { + { if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) { scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | @@ -1973,12 +2654,14 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | (((unsigned) cmd->cmnd[7]) << 8); } + last_scsi_logical_block(host_index)[ldn] = scb->u1.log_blk_adr; + last_scsi_blockcount(host_index)[ldn] = scb->u2.blk.count; 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: @@ -1986,31 +2669,29 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) case TYPE_PROCESSOR: case TYPE_WORM: case TYPE_SCANNER: - case TYPE_MEDIUM_CHANGER: - + case TYPE_MEDIUM_CHANGER: /* If there is a sequential-device, IBM recommends to use - IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE. + 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, - that random-accesses are covered by the subsystem, but + that random-accesses are covered by the subsystem, but sequentials are not, as like for tape-drives. Therefore, it is the easiest way to use IM_OTHER_SCSI_CMD_CMD for all read-ops on CD-ROM-drives in order not to run into timing problems and 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 || + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12) /* enable READ */ - { - scb->enable |= IM_READ_CONTROL; - } - + scb->enable |= IM_READ_CONTROL; + scb->enable |= IM_BYPASS_BUFFER; 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. */ + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; + + /* 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; @@ -2021,17 +2702,27 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) if (bypass_controller) { scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; + scb->u1.log_blk_adr = 0; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; } else { scb->command = IM_DEVICE_INQUIRY_CMD; scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->u1.log_blk_adr = 0; } break; - + case TEST_UNIT_READY: + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; + scb->u1.log_blk_adr = 0; + scb->u1.scsi_cmd_length = 6; + memcpy (scb->u2.scsi_command, cmd->cmnd, 6); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; + break; case READ_CAPACITY: /* the length of system memory buffer must be exactly 8 bytes */ if (scb->sys_buf_length > 8) @@ -2039,55 +2730,67 @@ int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) if (bypass_controller) { scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_READ_CONTROL; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; } else { scb->command = IM_READ_CAPACITY_CMD; - scb->enable |= IM_READ_CONTROL; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; } break; - - /* Commands that need read-only-mode (system <- device): */ + + /* Commands that need read-only-mode (system <- device): */ case REQUEST_SENSE: - if (bypass_controller) + if (bypass_controller) { scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_READ_CONTROL; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; } else { scb->command = IM_REQUEST_SENSE_CMD; - scb->enable |= IM_READ_CONTROL; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; } break; - - /* Commands that need write-only-mode (system -> device): */ + + /* 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->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; /*Select needs WRITE-enabled*/ scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; break; - - /* For other commands, read-only is useful. Most other commands are + + /* 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->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + last_scsi_type(host_index)[ldn] = IM_LONG_SCB; break; } - /*issue scb command, and return */ - issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn); + if (last_scsi_type(host_index)[ldn] == IM_LONG_SCB) + { + issue_cmd (host_index, virt_to_bus(scb), IM_LONG_SCB | ldn); + IBM_DS(host_index).long_scbs++; + } + else + { + issue_cmd (host_index, virt_to_bus(scb), IM_SCB | ldn); + IBM_DS(host_index).scbs++; + } return 0; } @@ -2098,36 +2801,50 @@ int ibmmca_abort (Scsi_Cmnd * cmd) /* 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; + int max_pun; static unsigned long flags; unsigned long imm_command; /* return SCSI_ABORT_SNOOZE ; */ +#ifdef OLDKERN + save_flags(flags); + cli(); +#else spin_lock_irqsave(&abort_lock, flags); - if (ibm_ansi_order) - target = 6 - cmd->target; - else - target = cmd->target; - +#endif 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++); - + 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); + (cmd->scsi_done) (cmd); return SCSI_ABORT_SNOOZE; } - + + max_pun = subsystem_maxid(host_index); + + if (ibm_ansi_order) + { + target = max_pun - 1 - cmd->target; + if ((target <= subsystem_pun(host_index))&&(cmd->target <= subsystem_pun(host_index))) + target--; + else if ((target >= subsystem_pun(host_index))&&(cmd->target >= subsystem_pun(host_index))) + target++; + } + else + target = cmd->target; + /*get logical device number, and disable system interrupts */ printk ("IBM MCA SCSI: Sending abort to device pun=%d, lun=%d.\n", target, cmd->lun); @@ -2136,13 +2853,17 @@ int ibmmca_abort (Scsi_Cmnd * cmd) /*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; } - /* Clear ld.cmd, save done function, install internal done, - * send abort immediate command (this enables sys. interrupts), - * and wait until the interrupt arrives. + /* 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; @@ -2157,22 +2878,34 @@ int ibmmca_abort (Scsi_Cmnd * cmd) { 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 +#endif while (!cmd->SCp.Status) - barrier (); - cmd->scsi_done = saved_done; - /*if abort went well, call saved done, then return success or error */ + 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; @@ -2181,7 +2914,7 @@ int ibmmca_abort (Scsi_Cmnd * cmd) ld(host_index)[ldn].cmd = NULL; #ifdef IM_DEBUG_PROBE printk("IBM MCA SCSI: Abort finished with success.\n"); -#endif +#endif return SCSI_ABORT_SUCCESS; } else @@ -2192,7 +2925,7 @@ int ibmmca_abort (Scsi_Cmnd * cmd) ld(host_index)[ldn].cmd = NULL; #ifdef IM_DEBUG_PROBE printk("IBM MCA SCSI: Abort failed.\n"); -#endif +#endif return SCSI_ABORT_ERROR; } } @@ -2208,7 +2941,17 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) static unsigned long flags; unsigned long imm_command; + if (cmd == NULL) + { + printk("IBM MCA SCSI: Reset called with NULL-command!\n"); + return(SCSI_RESET_SNOOZE); + } +#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 */ @@ -2218,9 +2961,19 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) { /* invalid hostadapter descriptor address */ if (!local_checking_phase_flag(host_index)) { - cmd->result = DID_NO_CONNECT << 16; - if (cmd->scsi_done) - (cmd->done) (cmd); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132) + if (flags & SCSI_RESET_SYNCHRONOUS) + { +#ifdef OLDKERN + restore_flags(flags); +#else + spin_unlock_irqrestore(&reset_lock, flags); +#endif + cmd->result = DID_NO_CONNECT << 16; + if (cmd->scsi_done) + (cmd->scsi_done) (cmd); + } +#endif } return SCSI_ABORT_SNOOZE; } @@ -2228,7 +2981,11 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) 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; } @@ -2245,8 +3002,17 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) { 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)); @@ -2263,10 +3029,14 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) 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) @@ -2276,18 +3046,26 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) 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"); + printk ("IBM MCA SCSI: Reset successfully completed.\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; @@ -2295,10 +3073,22 @@ int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) { ld(host_index)[i].cmd = NULL; cmd_aid->result = DID_RESET << 16; - (cmd_aid->scsi_done) (cmd_aid); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132) + if (flags & SCSI_RESET_SYNCHRONOUS) + { + cmd_aid->result = DID_BUS_BUSY << 16; + if (cmd_aid->scsi_done) + (cmd_aid->scsi_done) (cmd_aid); + } +#endif } } - return SCSI_RESET_SUCCESS; + if (flags & SCSI_RESET_SUGGEST_HOST_RESET) + return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET); + else if (flags & SCSI_RESET_SUGGEST_BUS_RESET) + return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); + else + return SCSI_RESET_SUCCESS; } /*--------------------------------------------------------------------*/ @@ -2338,7 +3128,7 @@ static int ldn_access_total_read_write(int host_index) { int a; int i; - + a = 0; for (i=0; i<=MAX_LOG_DEV; i++) a+=IBM_DS(host_index).ldn_read_access[i]+IBM_DS(host_index).ldn_write_access[i]; @@ -2349,7 +3139,7 @@ static int ldn_access_total_inquiry(int host_index) { int a; int i; - + a = 0; for (i=0; i<=MAX_LOG_DEV; i++) a+=IBM_DS(host_index).ldn_inquiry_access[i]; @@ -2360,7 +3150,7 @@ static int ldn_access_total_modeselect(int host_index) { int a; int i; - + a = 0; for (i=0; i<=MAX_LOG_DEV; i++) a+=IBM_DS(host_index).ldn_modeselect_access[i]; @@ -2375,17 +3165,24 @@ int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, int i,id,lun,host_index; struct Scsi_Host *shpnt; unsigned long flags; - + int max_pun; + +#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); + len += sprintf(buffer+len, "\nIBM MCA SCSI: Can't find adapter for host number %d\n", hostno); return len; } - + max_pun = subsystem_maxid(host_index); + 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"); @@ -2406,8 +3203,12 @@ int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, (bypass_controller) ? "software" : "hardware integrated"); len += sprintf(buffer+len, " Total Interrupts.........: %d\n", IBM_DS(host_index).total_interrupts); - len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n", + len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n", IBM_DS(host_index).total_accesses); + len += sprintf(buffer+len, " Total short SCBs.........: %d\n", + IBM_DS(host_index).scbs); + len += sprintf(buffer+len, " Total long SCBs..........: %d\n", + IBM_DS(host_index).long_scbs); len += sprintf(buffer+len, " Total SCSI READ/WRITE..: %d\n", ldn_access_total_read_write(host_index)); len += sprintf(buffer+len, " Total SCSI Inquiries...: %d\n", @@ -2428,11 +3229,11 @@ int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, 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(host_index).total_scsi_devices); - len += sprintf(buffer+len, " Dynamical Assignment necessaray..: %s\n", + len += sprintf(buffer+len, " Dynamical Assignment necessary...: %s\n", IBM_DS(host_index).dyn_flag ? "Yes" : "No "); len += sprintf(buffer+len, " Next LDN to be assigned..........: 0x%x\n", next_ldn(host_index)); @@ -2442,47 +3243,63 @@ int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, 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++) + for (id=0; id<max_pun; id++) { len += sprintf(buffer+len, " %2d ",id); for (lun=0; lun<8; lun++) - len += sprintf(buffer+len,"%2s ",ti_p(get_scsi(host_index)[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, nothing assigned or unprobed LUN)\n\n"); - + *start = buffer + offset; len -= offset; - if (len > length) + if (len > length) len = length; +#ifdef OLDKERN + restore_flags(flags); +#else spin_unlock_irqrestore(&proc_lock, flags); +#endif return len; } +void ibmmca_scsi_setup (char *str, int *ints) +{ + internal_ibmmca_scsi_setup (str, ints); +} + +static int option_setup(char *str) +{ + int ints[IM_MAX_HOSTS]; + char *cur = str; + int i = 1; + + while (cur && isdigit(*cur) && i <= IM_MAX_HOSTS) { + ints[i++] = simple_strtoul(cur, NULL, 0); + if ((cur = strchr(cur,',')) != NULL) cur++; + } + ints[0] = i - 1; + internal_ibmmca_scsi_setup(cur, ints); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,18) +__setup("ibmmcascsi=", option_setup); +#endif + #ifdef MODULE /* Eventually this will go into an include file, but this will be later */ Scsi_Host_Template driver_template = IBMMCA; #include "scsi_module.c" - -/* - * Module parameters - */ - -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 /*--------------------------------------------------------------------*/ + |