summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/aic7xxx.c')
-rw-r--r--drivers/scsi/aic7xxx.c7361
1 files changed, 4356 insertions, 3005 deletions
diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c
index 96e66defd..005c889d7 100644
--- a/drivers/scsi/aic7xxx.c
+++ b/drivers/scsi/aic7xxx.c
@@ -1,5 +1,3 @@
-#define EXPERIMENTAL_FLAGS 0
-
/*+M*************************************************************************
* Adaptec AIC7xxx device driver for Linux.
*
@@ -29,21 +27,73 @@
* the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
* ANSI SCSI-2 specification (draft 10c), ...
*
- * ----------------------------------------------------------------
- * Modified to include support for wide and twin bus adapters,
- * DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * --------------------------------------------------------------------------
+ *
+ * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ * Substantially modified to include support for wide and twin bus
+ * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
* SCB paging, and other rework of the code.
*
- * Parts of this driver are based on the FreeBSD driver by Justin
- * T. Gibbs.
+ * Parts of this driver were also based on the FreeBSD driver by
+ * Justin T. Gibbs. His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ * Thanks also go to (in alphabetical order) the following:
+ *
+ * Rory Bolt - Sequencer bug fixes
+ * Jay Estabrook - Initial DEC Alpha support
+ * Doug Ledford - Much needed abort/reset bug fixes
+ * Kai Makisara - DMAing of SCBs
*
* A Boot time option was also added for not resetting the scsi bus.
*
- * Form: aic7xxx=extended,no_reset
+ * Form: aic7xxx=extended
+ * aic7xxx=no_reset
+ * aic7xxx=ultra
+ * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level
+ * aic7xxx=verbose
*
- * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
*
- * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
*-M*************************************************************************/
#ifdef MODULE
@@ -67,7 +117,11 @@
#include "scsi.h"
#include "hosts.h"
#include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
#include "aic7xxx_reg.h"
+#include "aic7xxx_seq.h"
#include <linux/stat.h>
#include <linux/malloc.h> /* for kmalloc() */
@@ -79,16 +133,20 @@
*/
#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
-static struct proc_dir_entry proc_scsi_aic7xxx = {
+struct proc_dir_entry proc_scsi_aic7xxx = {
PROC_SCSI_AIC7XXX, 7, "aic7xxx",
- S_IFDIR | S_IRUGO | S_IXUGO, 2
+ S_IFDIR | S_IRUGO | S_IXUGO, 2,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
-#define AIC7XXX_C_VERSION "$Revision: 4.0 $"
+#define AIC7XXX_C_VERSION "$Revision: 4.1 $"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b) ((a < b) ? a : b)
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define ALL_TARGETS -1
+#define ALL_CHANNELS '\0'
+#define ALL_LUNS -1
#ifndef TRUE
# define TRUE 1
#endif
@@ -107,11 +165,8 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
* support because all PCI dependent code is bracketed with
* "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
*
- * o Twin bus support - this has been tested and does work.
- *
- * o DMAing of SCBs - thanks to Kai Makisara, this now works.
- * This define is now taken out and DMAing of SCBs is always
- * performed (8/12/95 - DE).
+ * o Twin bus support - this has been tested and does work. It is
+ * not an option anymore.
*
* o Tagged queueing - this driver is capable of tagged queueing
* but I am unsure as to how well the higher level driver implements
@@ -140,16 +195,16 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
* LUN using its own heuristic based on the number of available
* SCBs.
*
- * o 3985 support - The 3985 adapter is much like the 3940, but
- * has three 7870 controllers as opposed to two for the 3940.
- * It will get probed and recognized as three different adapters,
- * but all three controllers can share the same external bank of
- * 255 SCBs. If you enable AIC7XXX_SHARE_SCBS, then the driver
- * will attempt to share the common bank of SCBs between the three
- * controllers of the 3985. This is experimental and hasn't
- * been tested. By default, we do not share the bank of SCBs,
- * and force the controllers to use their own internal bank of
- * 16 SCBs. Please let us know if sharing the SCB array works.
+ * o 3985 support - The 3985 adapter is much like the 3940, but has
+ * three 7870 controllers as opposed to two for the 3940. It will
+ * be probed and recognized as three different adapters, but all
+ * three controllers can share the same external bank of 255 SCBs.
+ * If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
+ * to use and share the common bank of SCBs between the three
+ * controllers of the 3985. This is experimental and hasn't been
+ * been tested. By default, we do not use external SCB RAM, and
+ * force the controllers to use their own internal bank of 16 SCBs.
+ * Please let us know if using the external SCB array works.
*
* o SCB paging support - SCB paging is enabled by defining
* AIC7XXX_PAGE_ENABLE. Support for this was taken from the
@@ -162,43 +217,54 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
* Note that sharing of IRQs is not an option any longer. Linux supports
* it so we support it.
*
- * Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
*/
-/* Uncomment this for testing twin bus support. */
-#define AIC7XXX_TWIN_SUPPORT
-
/* Uncomment this for tagged queueing. */
-/* #define AIC7XXX_TAGGED_QUEUEING */
+#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
+#define AIC7XXX_TAGGED_QUEUEING
+#endif
/*
* You can try raising me if tagged queueing is enabled, or lowering
* me if you only have 4 SCBs.
*/
-/* #define AIC7XXX_CMDS_PER_LUN 8 */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
/* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
#define AIC7XXX_RESET_DELAY 15
+#endif
/*
- * Uncomment the following define for collection of SCSI transfer statistics
- * for the /proc filesystem.
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
*
* NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
* NOTE: This does affect performance since it has to maintain statistics.
*/
-/* #define AIC7XXX_PROC_STATS */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
/*
- * Uncomment the following to enable SCB paging.
+ * Enable SCB paging.
*/
-/* #define AIC7XXX_PAGE_ENABLE */
+#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
+#define AIC7XXX_PAGE_ENABLE
+#endif
/*
- * Uncomment the following to enable sharing of the external bank
- * of 255 SCBs for the 3985.
+ * Uncomment the following to enable use of the external bank
+ * of 255 SCBs. For 3985 adapters, this will also enable sharing
+ * of the SCB array across all three controllers.
*/
-#define AIC7XXX_SHARE_SCBS
+#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
+#define AIC7XXX_USE_EXT_SCBRAM
+#endif
/*
* For debugging the abort/reset code.
@@ -211,6 +277,87 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
#define AIC7XXX_DEBUG
/*
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis. The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on. These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases. The tag_commands is an array of
+ * 16 to allow for wide and twin adapters. Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ *
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN. If SCB paging is
+ * enabled, the commands/LUN is 8. When SCB paging is not enabled,
+ * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
+ * and 4 commands/LUN for adapters with 3 or 4 SCBs.
+ *
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+typedef struct
+{
+ char tag_commands[16]; /* Allow for wide/twin channel adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver to use it's own algorithm
+ * for determining commands/LUN (see Determining commands per LUN
+ * above).
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system. By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above). When -1, the driver will
+ * not enable tagged queueing for that particular device. When positive
+ * (> 0) the values in the array are used for the queue_depth. Note
+ * that the maximum value for an entry is 127.
+ *
+ * In this example, the first line will enable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter and tells the driver
+ * to use it's own algorithm for determining commands/LUN.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+ {DEFAULT_TAG_COMMANDS},
+ {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+#endif
+
+/*
+ * Don't define this unless you have problems with the driver
+ * interrupt handler. The old method would register the drivers
+ * interrupt handler as a "fast" type interrupt handler that would
+ * lock out other interrupts. Since this driver can spend a lot
+ * of time in the interrupt handler, this is _not_ a good idea.
+ * It also conflicts with some of the more common ethernet drivers
+ * that don't use fast interrupts. Currently, Linux does not allow
+ * IRQ sharing unless both drivers can agree on the type of interrupt
+ * handler.
+ */
+/* #define AIC7XXX_OLD_ISR_TYPE */
+
+
+/*
* Controller type and options
*/
typedef enum {
@@ -232,14 +379,15 @@ typedef enum {
AIC_7882, /* PCI aic7882 on 3940 Ultra */
AIC_7883, /* PCI aic7883 on 3985 Ultra */
AIC_7884 /* PCI aic7884 on 294x Ultra Differential */
-} aha_type;
+} aha_chip_type;
typedef enum {
AIC_777x, /* AIC-7770 based */
- AIC_785x, /* AIC-7850 based */
+ AIC_785x, /* AIC-7850 based (3 SCBs)*/
+ AIC_786x, /* AIC-7860 based (7850 ultra) */
AIC_787x, /* AIC-7870 based */
- AIC_788x /* AIC-7880 based */
-} aha_chip_type;
+ AIC_788x /* AIC-7880 based (ultra) */
+} aha_chip_class_type;
typedef enum {
AIC_SINGLE, /* Single Channel */
@@ -269,24 +417,24 @@ typedef enum {
* Don't forget to change this when changing the types!
*/
static const char *board_names[] = {
- "<AIC-7xxx Unknown>", /* AIC_NONE */
- "AIC-7770", /* AIC_7770 */
- "AHA-2740", /* AIC_7771 */
- "AHA-2840", /* AIC_284x */
- "AIC-7850", /* AIC_7850 */
- "AIC-7855", /* AIC_7855 */
- "AIC-7850 Ultra", /* AIC_7860 */
- "AHA-2940A Ultra", /* AIC_7861 */
- "AIC-7870", /* AIC_7870 */
- "AHA-2940", /* AIC_7871 */
- "AHA-3940", /* AIC_7872 */
- "AHA-3985", /* AIC_7873 */
- "AHA-2940 Differential", /* AIC_7874 */
- "AIC-7880 Ultra", /* AIC_7880 */
- "AHA-2940 Ultra", /* AIC_7881 */
- "AHA-3940 Ultra", /* AIC_7882 */
- "AHA-3985 Ultra", /* AIC_7883 */
- "AHA-2940 Ultra Differential" /* AIC_7884 */
+ "AIC-7xxx Unknown", /* AIC_NONE */
+ "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
+ "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
+ "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
+ "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
+ "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
+ "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
+ "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
+ "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
+ "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
+ "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
+ "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
+ "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
+ "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
+ "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
+ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
+ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
+ "Adaptec AHA-2944 Ultra SCSI host adapter" /* AIC_7884 */
};
/*
@@ -310,12 +458,17 @@ static const char *board_names[] = {
*/
#define DID_RETRY_COMMAND DID_ERROR
+#define HSCSIID 0x07
+#define HWSCSIID 0x0F
+#define SCSI_RESET 0x040
+
/*
* EISA/VL-bus stuff
*/
#define MINSLOT 1
#define MAXSLOT 15
#define SLOTBASE(x) ((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
/*
* Standard EISA Host ID regs (Offset from slot base)
@@ -334,14 +487,6 @@ static const char *board_names[] = {
#define INTDEF 0x5C /* Interrupt Definition Register */
/*
- * Some defines for the HCNTRL register.
- */
-#define REQ_PAUSE IRQMS | INTEN | PAUSE
-#define UNPAUSE_274X IRQMS | INTEN
-#define UNPAUSE_284X INTEN
-#define UNPAUSE_294X IRQMS | INTEN
-
-/*
* AIC-78X0 PCI registers
*/
#define CLASS_PROGIF_REVID 0x08
@@ -377,7 +522,7 @@ static const char *board_names[] = {
* each word, while the C56 and C66 (4096 bits) use 8 bits to
* address each word.
*/
-typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type;
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
/*
*
@@ -417,15 +562,15 @@ struct seeprom_config {
/*
* Host Adapter Control Bits
*/
-/* UNUSED 0x0001 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
-#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
#define CFSPARITY 0x0010 /* SCSI parity */
#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
-#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
/* UNUSED 0xFF80 */
unsigned short adapter_control; /* word 17 */
@@ -448,36 +593,17 @@ struct seeprom_config {
unsigned short checksum; /* word 31 */
};
+#define SELBUS_MASK 0x0a
+#define SELNARROW 0x00
+#define SELBUSB 0x08
+#define SINGLE_BUS 0x00
-#define SCSI_RESET 0x040
-
-/*
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- */
-#define PAUSE_SEQUENCER(p) \
- synchronize_irq(); \
- outb(p->pause, HCNTRL + p->base); \
- while ((inb(HCNTRL + p->base) & PAUSE) == 0) \
- ; \
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
- outb(p->unpause, HCNTRL + p->base)
-
-/*
- * Restart the sequencer program from address zero
- */
-#define RESTART_SEQUENCER(p) \
- do { \
- outb(SEQRESET | FASTMODE, SEQCTL + p->base); \
- } while (inb(SEQADDR0 + p->base) != 0 && \
- inb(SEQADDR1 + p->base) != 0); \
- UNPAUSE_SEQUENCER(p);
+#define SCB_TARGET(scb) \
+ (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb) \
+ ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb) \
+ (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
/*
* If an error occurs during a data transfer phase, run the command
@@ -541,12 +667,6 @@ static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1];
static int aic7xxx_spurious_count;
/*
- * The driver keeps up to four scb structures per card in memory. Only the
- * first 25 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookkeeping.
- */
-
-/*
* As of Linux 2.1, the mid-level SCSI code uses virtual addresses
* in the scatter-gather lists. We need to convert the virtual
* addresses to physical addresses.
@@ -559,20 +679,28 @@ struct hw_scatterlist {
/*
* Maximum number of SG segments these cards can support.
*/
-#define MAX_SG 256
+#define AIC7XXX_MAX_SG 27
-struct aic7xxx_scb {
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 255
+
+
+struct aic7xxx_hwscb {
/* ------------ Begin hardware supported fields ---------------- */
/* 0*/ unsigned char control;
/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
/* 2*/ unsigned char target_status;
/* 3*/ unsigned char SG_segment_count;
-/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 4*/ unsigned int SG_list_pointer;
/* 8*/ unsigned char residual_SG_segment_count;
-/* 9*/ unsigned char residual_data_count[3] __attribute__ ((packed));
-/*12*/ unsigned char data_pointer[4] __attribute__ ((packed));
-/*16*/ unsigned int data_count __attribute__ ((packed)); /* must be 32 bits */
-/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/* 9*/ unsigned char residual_data_count[3];
+/*12*/ unsigned int data_pointer;
+/*16*/ unsigned int data_count;
+/*20*/ unsigned int SCSI_cmd_pointer;
/*24*/ unsigned char SCSI_cmd_length;
/*25*/ u_char tag; /* Index into our kernel SCB array.
* Also used as the tag for tagged I/O
@@ -580,29 +708,47 @@ struct aic7xxx_scb {
#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
* via PIO to initialize a transaction.
*/
-/*26*/ u_char next; /* Used to thread SCBs awaiting selection
+/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
* or disconnected down in the sequencer.
*/
- /*-----------------end of hardware supported fields----------------*/
- Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
- struct aic7xxx_scb *q_next; /* next scb in queue */
-#define SCB_FREE 0x00
-#define SCB_ACTIVE 0x01
-#define SCB_ABORTED 0x02
-#define SCB_DEVICE_RESET 0x04
-#define SCB_IMMED 0x08
-#define SCB_SENSE 0x10
-#define SCB_QUEUED_FOR_DONE 0x40
-#define SCB_PAGED_OUT 0x80
-#define SCB_WAITINGQ 0x100
-#define SCB_ASSIGNEDQ 0x200
-#define SCB_SENTORDEREDTAG 0x400
-#define SCB_IN_PROGRESS (SCB_ACTIVE | SCB_PAGED_OUT | \
- SCB_WAITINGQ | SCB_ASSIGNEDQ)
- int state; /* current state of scb */
- unsigned int position; /* Position in scb array */
- struct hw_scatterlist sg_list[MAX_SG]; /* SG list in adapter format */
- unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */
+/*27*/ unsigned char prev;
+/*28*/ unsigned int pad; /*
+ * Unused by the kernel, but we require
+ * the padding so that the array of
+ * hardware SCBs is alligned on 32 byte
+ * boundaries so the sequencer can index
+ */
+};
+
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_ACTIVE = 0x0001,
+ SCB_ABORTED = 0x0002,
+ SCB_DEVICE_RESET = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_TIMEDOUT = 0x0010,
+ SCB_QUEUED_FOR_DONE = 0x0020,
+ SCB_RECOVERY_SCB = 0x0040,
+ SCB_WAITINGQ = 0x0080,
+ SCB_ASSIGNEDQ = 0x0100,
+ SCB_SENTORDEREDTAG = 0x0200,
+ SCB_MSGOUT_SDTR = 0x0400,
+ SCB_MSGOUT_WDTR = 0x0800,
+ SCB_ABORT = 0x1000,
+ SCB_QUEUED_ABORT = 0x2000
+} scb_flag_type;
+
+struct aic7xxx_scb {
+ struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */
+ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
+ struct aic7xxx_scb *q_next; /* next scb in queue */
+ scb_flag_type flags; /* current state of scb */
+ struct hw_scatterlist *sg_list; /* SG list in adapter format */
+ unsigned char sg_count;
+ unsigned char sense_cmd[6]; /*
+ * Allocate 6 characters for
+ * sense command.
+ */
};
/*
@@ -627,20 +773,17 @@ static unsigned char
generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
typedef struct {
+ struct aic7xxx_hwscb *hscbs;
scb_queue_type free_scbs; /*
* SCBs assigned to free slot on
* card (no paging required)
*/
- int numscbs; /* current number of scbs */
- int activescbs; /* active scbs */
-} scb_usage_type;
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB 255
+ unsigned char numscbs; /* current number of scbs */
+ unsigned char maxhscbs; /* hardware scbs */
+ unsigned char maxscbs; /* max scbs including pageable scbs */
+ struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB];
+ unsigned int reserve[100];
+} scb_data_type;
/*
* Define a structure used for each host adapter, only one per IRQ.
@@ -648,17 +791,26 @@ typedef struct {
struct aic7xxx_host {
struct Scsi_Host *host; /* pointer to scsi host */
int host_no; /* SCSI host number */
+ int instance; /* aic7xxx instance number */
+ int scsi_id; /* host adapter SCSI ID */
+ int scsi_id_b; /* channel B for twin adapters */
+ int irq; /* IRQ for this adapter */
int base; /* card base address */
- int maxhscbs; /* hardware SCBs */
- int maxscbs; /* max SCBs (including pageable) */
-#define A_SCANNED 0x0001
-#define B_SCANNED 0x0002
-#define EXTENDED_TRANSLATION 0x0004
-#define HAVE_SEEPROM 0x0008
-#define ULTRA_ENABLED 0x0010
-#define PAGE_ENABLED 0x0020
-#define IN_ISR 0x0040
-#define USE_DEFAULTS 0x0080
+ unsigned int mbase; /* I/O memory address */
+ volatile unsigned char *maddr; /* memory mapped address */
+#define A_SCANNED 0x0001
+#define B_SCANNED 0x0002
+#define EXTENDED_TRANSLATION 0x0004
+#define FLAGS_CHANNEL_B_PRIMARY 0x0008
+#define MULTI_CHANNEL 0x0010
+#define ULTRA_ENABLED 0x0020
+#define PAGE_ENABLED 0x0040
+#define USE_DEFAULTS 0x0080
+#define BIOS_ENABLED 0x0100
+#define IN_ISR 0x0200
+#define IN_TIMEOUT 0x0400
+#define SHARED_SCBDATA 0x0800
+#define HAVE_SEEPROM 0x1000
unsigned int flags;
unsigned int isr_count; /* Interrupt count */
unsigned short needsdtr_copy; /* default config */
@@ -669,36 +821,22 @@ struct aic7xxx_host {
unsigned short wdtr_pending;
unsigned short orderedtag;
unsigned short discenable; /* Targets allowed to disconnect */
- aha_type type; /* card type */
- aha_chip_type chip_type; /* chip base type */
+ aha_chip_type chip_type; /* card type */
+ aha_chip_class_type chip_class;
aha_bus_type bus_type; /* normal/twin/wide bus */
- char * mbase; /* I/O memory address */
- unsigned char chan_num; /* for 3940/3985, channel number */
+ unsigned char chan_num; /* for 39xx, channel number */
unsigned char unpause; /* unpause value for HCNTRL */
unsigned char pause; /* pause value for HCNTRL */
unsigned char qcntmask;
- struct seeprom_config seeprom;
+ unsigned char qfullcount;
+ unsigned char curqincnt;
struct Scsi_Host *next; /* allow for multiple IRQs */
- struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; /* active commands */
- struct aic7xxx_scb *pagedout_ntscbs[16]; /*
- * paged-out, non-tagged scbs
- * indexed by target.
- */
- scb_queue_type page_scbs; /*
- * SCBs that will require paging
- * before use (no assigned slot)
- */
+ unsigned char activescbs; /* active scbs */
scb_queue_type waiting_scbs; /*
- * SCBs waiting to be paged and
- * started.
+ * SCBs waiting for space in
+ * the QINFIFO.
*/
- scb_queue_type assigned_scbs; /*
- * SCBs that were waiting but have
- * have now been assigned a slot
- * by aic7xxx_free_scb
- */
- scb_usage_type scb_usage;
- scb_usage_type *scb_link;
+ scb_data_type *scb_data;
struct aic7xxx_cmd_queue {
Scsi_Cmnd *head;
@@ -710,6 +848,7 @@ struct aic7xxx_host {
#define BUS_DEVICE_RESET_PENDING 0x02
int flags;
int commands_sent;
+ int active_cmds;
} device_status[16];
#ifdef AIC7XXX_PROC_STATS
/*
@@ -735,34 +874,10 @@ struct aic7xxx_host {
#endif /* AIC7XXX_PROC_STATS */
};
-struct aic7xxx_host_config {
- int irq; /* IRQ number */
- int mbase; /* memory base address*/
- int base; /* I/O base address*/
- int maxhscbs; /* hardware SCBs */
- int maxscbs; /* max SCBs (including pageable) */
- int unpause; /* unpause value for HCNTRL */
- int pause; /* pause value for HCNTRL */
- int scsi_id; /* host SCSI ID */
- int scsi_id_b; /* host SCSI ID B channel for twin cards */
- unsigned int flags; /* used the same as struct aic7xxx_host flags */
- int chan_num; /* for 3940/3985, channel number */
- unsigned char busrtime; /* bus release time */
- unsigned char bus_speed; /* bus speed */
- unsigned char qcntmask;
- aha_type type; /* card type */
- aha_chip_type chip_type; /* chip base type */
- aha_bus_type bus_type; /* normal/twin/wide bus */
- aha_status_type bios; /* BIOS is enabled/disabled */
- aha_status_type parity; /* bus parity enabled/disabled */
- aha_status_type low_term; /* bus termination low byte */
- aha_status_type high_term; /* bus termination high byte (wide cards only) */
-};
-
/*
* Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
*/
static struct {
short period;
@@ -771,17 +886,17 @@ static struct {
short rate;
const char *english;
} aic7xxx_syncrates[] = {
- { 50, 0x100, "20.0" },
- { 62, 0x110, "16.0" },
- { 75, 0x120, "13.4" },
- { 100, 0x000, "10.0" },
- { 125, 0x010, "8.0" },
- { 150, 0x020, "6.67" },
- { 175, 0x030, "5.7" },
- { 200, 0x040, "5.0" },
- { 225, 0x050, "4.4" },
- { 250, 0x060, "4.0" },
- { 275, 0x070, "3.6" }
+ { 12, 0x100, "20.0" },
+ { 15, 0x110, "16.0" },
+ { 18, 0x120, "13.4" },
+ { 25, 0x000, "10.0" },
+ { 31, 0x010, "8.0" },
+ { 37, 0x020, "6.67" },
+ { 43, 0x030, "5.7" },
+ { 50, 0x040, "5.0" },
+ { 56, 0x050, "4.4" },
+ { 62, 0x060, "4.0" },
+ { 68, 0x070, "3.6" }
};
static int num_aic7xxx_syncrates =
@@ -790,166 +905,51 @@ static int num_aic7xxx_syncrates =
#ifdef CONFIG_PCI
static int number_of_3940s = 0;
static int number_of_3985s = 0;
-#ifdef AIC7XXX_SHARE_SCBS
-static scb_usage_type *shared_3985_scbs = NULL;
-#endif
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
#ifdef AIC7XXX_DEBUG
-static void
-debug_config(struct aic7xxx_host_config *p)
-{
- int scsi_conf;
- unsigned char brelease;
- unsigned char dfthresh;
-
- static int DFT[] = { 0, 50, 75, 100 };
- static int SST[] = { 256, 128, 64, 32 };
- static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
-
- scsi_conf = inb(SCSICONF + p->base);
-
- /*
- * Scale the Data FIFO Threshhold and the Bus Release Time; they are
- * stored in formats compatible for writing to sequencer registers.
- */
- dfthresh = p->bus_speed >> 6;
-
- if (p->chip_type == AIC_777x)
- {
- brelease = p->busrtime >> 2;
- }
- else
- {
- brelease = p->busrtime;
- }
- if (brelease == 0)
- {
- brelease = 2;
- }
-
- switch (p->type)
- {
- case AIC_7770:
- case AIC_7771:
- printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
- p->base >> 12);
- break;
-
- case AIC_284x:
- printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
- p->base >> 12);
- break;
-
- case AIC_7850:
- case AIC_7855:
- case AIC_7860:
- case AIC_7861:
- case AIC_7870:
- case AIC_7871:
- case AIC_7872:
- case AIC_7873:
- case AIC_7874:
- case AIC_7880:
- case AIC_7881:
- case AIC_7882:
- case AIC_7883:
- case AIC_7884:
- printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type],
- BUSW[p->bus_type], p->base, p->mbase);
- break;
-
- default:
- panic("aic7xxx: (debug_config) internal error.\n");
- }
-
- printk(" irq %d\n"
- " bus release time %d bclks\n"
- " data fifo threshold %d%%\n",
- p->irq,
- brelease,
- DFT[dfthresh]);
-
- printk(" SCSI CHANNEL A:\n"
- " scsi id %d\n"
- " scsi selection timeout %d ms\n"
- " scsi bus reset at power-on %sabled\n",
- scsi_conf & 0x07,
- SST[(scsi_conf >> 3) & 0x03],
- (scsi_conf & 0x40) ? "en" : "dis");
-
- if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
- {
- /*
- * Set the parity for 7770 based cards.
- */
- p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
- }
- if (p->parity != AIC_UNKNOWN)
- {
- printk(" scsi bus parity %sabled\n",
- (p->parity == AIC_ENABLED) ? "en" : "dis");
- }
-
- if ((p->type == AIC_7770) || (p->type == AIC_7771))
- {
- p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
- }
- if (p->low_term != AIC_UNKNOWN)
- {
- printk(" scsi bus termination (low byte) %sabled\n",
- (p->low_term == AIC_ENABLED) ? "en" : "dis");
- }
- if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
- {
- printk(" scsi bus termination (high byte) %sabled\n",
- (p->high_term == AIC_ENABLED) ? "en" : "dis");
- }
-}
-
#if 0
static void
debug_scb(struct aic7xxx_scb *scb)
{
- printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
- scb->control, scb->target_channel_lun, scb->SG_segment_count,
- (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
- (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
- (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
- (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
- scb->SCSI_cmd_length);
- printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
- (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
- scb->residual_SG_segment_count,
- ((scb->residual_data_count[2] << 16) |
- (scb->residual_data_count[1] << 8) |
- (scb->residual_data_count[0]));
- printk("data ptr 0x%x, data count %d, next waiting %d\n",
- (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
- (scb->data_pointer[1] << 8) | scb->data_pointer[0],
- scb->data_count, scb->next_waiting);
- printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
- (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
- scb->position);
+ struct aic7xxx_hwscb *hscb = scb->hscb;
+
+ printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+ scb,
+ hscb->control,
+ hscb->target_channel_lun,
+ hscb->SCSI_cmd_length,
+ hscb->SCSI_cmd_pointer );
+ printk(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+ hscb->data_count,
+ hscb->data_pointer,
+ hscb->SG_segment_count,
+ hscb->SG_list_pointer);
+ printk(" sg_addr:%lx sg_len:%ld\n",
+ hscb->sg_list[0].address,
+ hscb->sg_list[0].length);
}
#endif
#else
-# define debug_config(x)
# define debug_scb(x)
#endif AIC7XXX_DEBUG
-#define TCL_OF_SCB(x) (((x)->target_channel_lun >> 4) & 0xf), \
- (((x)->target_channel_lun >> 3) & 0x01), \
- ((x)->target_channel_lun & 0x07)
+#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+ (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+ ((scb->hscb)->target_channel_lun & 0x07)
+
+#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+ (((scb->hscb)->target_channel_lun >> 3) & 0x01)
-#define TARGET_INDEX(x) ((x)->target | ((x)->channel << 3))
+#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+
+#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
/*
* XXX - these options apply unilaterally to _all_ 274x/284x/294x
- * cards in the system. This should be fixed, but then,
- * does anyone really have more than one in a machine?
+ * cards in the system. This should be fixed.
*/
static unsigned int aic7xxx_extended = 0; /* extended translation on? */
static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */
@@ -959,6 +959,53 @@ static int aic7xxx_irq_trigger = -1; /*
* 1 use level triggered
*/
static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */
+static int aic7xxx_verbose = 0; /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * These functions are not used yet, but when we do memory mapped
+ * IO, we'll use them then.
+ *
+ ***************************************************************************/
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+ if (p->maddr != NULL)
+ return (p->maddr[port]);
+ else
+ return (inb(p->base + port));
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+ if (p->maddr != NULL)
+ p->maddr[port] = val;
+ else
+ outb(val, p->base + port);
+}
+
+static inline void
+aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
+{
+ if (p->maddr != NULL)
+ {
+ __asm __volatile("
+ cld;
+ 1: lodsb;
+ movb %%al,(%0);
+ loop 1b" :
+ :
+ "r" ((p)->maddr + (port)),
+ "S" ((valp)), "c" ((size)) :
+ "%esi", "%ecx", "%eax");
+ }
+ else
+ {
+ outsb(p->base + port, valp, size);
+ }
+}
/*+F*************************************************************************
* Function:
@@ -983,6 +1030,7 @@ aic7xxx_setup(char *s, int *dummy)
{ "no_reset", &aic7xxx_no_reset },
{ "irq_trigger", &aic7xxx_irq_trigger },
{ "ultra", &aic7xxx_enable_ultra },
+ { "verbose", &aic7xxx_verbose },
{ NULL, NULL }
};
@@ -1008,58 +1056,230 @@ aic7xxx_setup(char *s, int *dummy)
/*+F*************************************************************************
* Function:
+ * pause_sequencer
+ *
+ * Description:
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ *-F*************************************************************************/
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
+{
+ outb(p->pause, p->base + HCNTRL);
+ while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+ {
+ ;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * unpause_sequencer
+ *
+ * Description:
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ *-F*************************************************************************/
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
+{
+ if (unpause_always ||
+ ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
+ {
+ outb(p->unpause, p->base + HCNTRL);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * restart_sequencer
+ *
+ * Description:
+ * Restart the sequencer program from address zero. This assumes
+ * that the sequencer is already paused.
+ *-F*************************************************************************/
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
+{
+ /* Set the sequencer address to 0. */
+ outb(0, p->base + SEQADDR0);
+ outb(0, p->base + SEQADDR1);
+
+ /*
+ * Reset and unpause the sequencer. The reset is suppose to
+ * start the sequencer running, but we do an unpause to make
+ * sure.
+ */
+ outb(SEQRESET | FASTMODE, p->base + SEQCTL);
+
+ unpause_sequencer(p, /*unpause_always*/ TRUE);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_next_patch
+ *
+ * Description:
+ * Find the next patch to download.
+ *-F*************************************************************************/
+static struct patch *
+aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+{
+ while (cur_patch != NULL)
+ {
+ if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
+ || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
+ || (instrptr >= cur_patch->end))
+ {
+ /*
+ * Either we want to keep this section of code, or we have consumed
+ * this patch. Skip to the next patch.
+ */
+ cur_patch++;
+ if (cur_patch->options == 0)
+ {
+ /* Out of patches. */
+ cur_patch = NULL;
+ }
+ }
+ else
+ {
+ /* Found an OK patch. */
+ break;
+ }
+ }
+ return (cur_patch);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_download_instr
+ *
+ * Description:
+ * Find the next patch to download.
+ *-F*************************************************************************/
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
+{
+ unsigned char opcode;
+ struct ins_format3 *instr;
+
+ instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+ /* Pull the opcode */
+ opcode = instr->opcode_addr >> 1;
+ switch (opcode)
+ {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ int address_offset;
+ struct ins_format3 new_instr;
+ unsigned int address;
+ struct patch *patch;
+ int i;
+
+ address_offset = 0;
+ new_instr = *instr; /* Strucure copy */
+ address = new_instr.address;
+ address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+ for (i = 0; i < NUMBER(patches); i++)
+ {
+ patch = &patches[i];
+ if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
+ (((patch->options & options) != 0) && (patch->negative == TRUE)))
+ {
+ if (address >= patch->end)
+ {
+ address_offset += patch->end - patch->begin;
+ }
+ }
+ }
+ address -= address_offset;
+ new_instr.address = address &0xFF;
+ new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
+ new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+ outsb(p->base + SEQRAM, &new_instr.immediate, 4);
+ break;
+ }
+
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_ROL:
+ outsb(p->base + SEQRAM, &instr->immediate, 4);
+ break;
+
+ default:
+ panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+ break;
+ }
+}
+
+
+/*+F*************************************************************************
+ * Function:
* aic7xxx_loadseq
*
* Description:
* Load the sequencer code into the controller memory.
*-F*************************************************************************/
static void
-aic7xxx_loadseq(int base)
+aic7xxx_loadseq(struct aic7xxx_host *p)
{
- static unsigned char seqprog[] = {
- /*
- * Each sequencer instruction is 29 bits
- * long (fill in the excess with zeroes)
- * and has to be loaded from least -> most
- * significant byte, so this table has the
- * byte ordering reversed.
- */
-# include "aic7xxx_seq.h"
- };
+ int options;
+ struct patch *cur_patch;
+ int i;
+ int downloaded;
- /*
- * When the AIC-7770 is paused (as on chip reset), the
- * sequencer address can be altered and a sequencer
- * program can be loaded by writing it, byte by byte, to
- * the sequencer RAM port - the Adaptec documentation
- * recommends using REP OUTSB to do this, hence the inline
- * assembly. Since the address autoincrements as we load
- * the program, reset it back to zero afterward. Disable
- * sequencer RAM parity error detection while loading, and
- * make sure the LOADRAM bit is enabled for loading.
- */
- outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+ }
+ options = 1; /* Code for all options. */
+ downloaded = 0;
+ if ((p->flags & ULTRA_ENABLED) != 0)
+ options |= ULTRA;
+ if (p->bus_type == AIC_TWIN)
+ options |= TWIN_CHANNEL;
+ if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+ options |= SCB_PAGING;
- outsb(SEQRAM + base, seqprog, sizeof(seqprog));
+ cur_patch = patches;
+ outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
+ outb(0, p->base + SEQADDR0);
+ outb(0, p->base + SEQADDR1);
- /*
- * WARNING! This is a magic sequence! After extensive
- * experimentation, it seems that you MUST turn off the
- * LOADRAM bit before you play with SEQADDR again, else
- * you will end up with parity errors being flagged on
- * your sequencer program. (You would also think that
- * turning off LOADRAM and setting SEQRESET to reset the
- * address to zero would work, but you need to do it twice
- * for it to take effect on the address. Timing problem?)
- */
- do {
- /*
- * Actually, reset it until
- * the address shows up as
- * zero just to be safe..
- */
- outb(SEQRESET | FASTMODE, SEQCTL + base);
- } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+ for (i = 0; i < sizeof(seqprog) / 4; i++)
+ {
+ cur_patch = aic7xxx_next_patch(cur_patch, options, i);
+ if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+ {
+ /* Skip this instruction for this configuration. */
+ continue;
+ }
+ aic7xxx_download_instr(p, options, i);
+ downloaded++;
+ }
+
+ outb(FASTMODE, p->base + SEQCTL);
+ outb(0, p->base + SEQADDR0);
+ outb(0, p->base + SEQADDR1);
+
+ if (aic7xxx_verbose)
+ {
+ printk(" %d instructions downloaded\n", downloaded);
+ }
}
/*+F*************************************************************************
@@ -1067,18 +1287,22 @@ aic7xxx_loadseq(int base)
* aic7xxx_delay
*
* Description:
- * Delay for specified amount of time.
+ * Delay for specified amount of time. We use udelay because the timer
+ * interrupt is not guaranteed to be enabled. This will cause an
+ * infinite loop since jiffies (clock ticks) is not updated.
*-F*************************************************************************/
static void
aic7xxx_delay(int seconds)
{
- unsigned long i;
-
- i = jiffies + (seconds * HZ); /* compute time to stop */
+ int i;
- while (jiffies < i)
+ /*
+ * Call udelay() for 1 millisecond inside a loop for
+ * the requested amount of seconds.
+ */
+ for (i=0; i < seconds*1000; i++)
{
- ; /* Do nothing! */
+ udelay(1000); /* Delay for 1 millisecond. */
}
}
@@ -1090,7 +1314,7 @@ aic7xxx_delay(int seconds)
* Return a string containing just the RCS version number from either
* an Id or Revision RCS clause.
*-F*************************************************************************/
-static const char *
+const char *
rcs_version(const char *version_info)
{
static char buf[10];
@@ -1150,8 +1374,10 @@ aic7xxx_info(struct Scsi_Host *notused)
strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
strcat(buffer, "/");
strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+#if 0
strcat(buffer, "/");
strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+#endif
return buffer;
}
@@ -1161,9 +1387,12 @@ aic7xxx_info(struct Scsi_Host *notused)
* aic7xxx_length
*
* Description:
- * How much data should be transferred for this SCSI command? Stop
- * at segment sg_last if it's a scatter-gather command so we can
- * compute underflow easily.
+ * How much data should be transferred for this SCSI command? Assume
+ * all segments are to be transferred except for the last sg_last
+ * segments. This will allow us to compute underflow easily. To
+ * calculate the total length of the command, use sg_last = 0. To
+ * calculate the length of all but the last 2 SG segments, use
+ * sg_last = 2.
*-F*************************************************************************/
static unsigned
aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
@@ -1177,7 +1406,7 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
if (cmd->use_sg)
{
- for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+ for (i = length = 0; i < segments; i++)
{
length += sg[i].length;
}
@@ -1199,9 +1428,9 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
*-F*************************************************************************/
static void
aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
- short period, unsigned char offset, int target, char channel)
+ unsigned char *period, unsigned char *offset, int target, char channel)
{
- int i;
+ int i = num_aic7xxx_syncrates;
unsigned long ultra_enb_addr;
unsigned char ultra_enb, sxfrctl0;
@@ -1209,11 +1438,11 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
* If the offset is 0, then the device is requesting asynchronous
* transfers.
*/
- if (offset != 0)
+ if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
{
for (i = 0; i < num_aic7xxx_syncrates; i++)
{
- if ((aic7xxx_syncrates[i].period - period) >= 0)
+ if (*period <= aic7xxx_syncrates[i].period)
{
/*
* Watch out for Ultra speeds when ultra is not enabled and
@@ -1229,99 +1458,57 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
*/
continue;
}
- *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+ *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
+ *period = aic7xxx_syncrates[i].period;
- /*
- * Ensure Ultra mode is set properly for this target.
- */
- ultra_enb_addr = ULTRA_ENB;
- if ((channel == 'B') || (target > 7))
- {
- ultra_enb_addr++;
- }
- ultra_enb = inb(p->base + ultra_enb_addr);
- sxfrctl0 = inb(p->base + SXFRCTL0);
- if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
+ if (aic7xxx_verbose)
{
- ultra_enb |= 0x01 << (target & 0x07);
- sxfrctl0 |= ULTRAEN;
+ printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
+ "offset %d.\n", p->host_no, target, channel,
+ aic7xxx_syncrates[i].english, *offset);
}
- else
- {
- ultra_enb &= ~(0x01 << (target & 0x07));
- sxfrctl0 &= ~ULTRAEN;
- }
- outb(ultra_enb, p->base + ultra_enb_addr);
- outb(sxfrctl0, p->base + SXFRCTL0);
-
- printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
- "offset %d.\n", p->host_no, target, channel,
- aic7xxx_syncrates[i].english, offset);
- return;
+ break;
}
}
}
- /*
- * Default to asynchronous transfer
- */
- *scsirate = 0;
- printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
- p->host_no, target, channel);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_putscb
- *
- * Description:
- * Transfer a SCB to the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- int base = p->base;
-
- outb(SCBAUTO, SCBCNT + base);
+ if (i >= num_aic7xxx_syncrates)
+ {
+ /*
+ * Use asynchronous transfers.
+ */
+ *scsirate = 0;
+ *period = 0;
+ *offset = 0;
+ if (aic7xxx_verbose)
+ {
+ printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
+ p->host_no, target, channel);
+ }
+ }
/*
- * By turning on the SCB auto increment, any reference
- * to the SCB I/O space postincrements the SCB address
- * we're looking at. So turn this on and dump the relevant
- * portion of the SCB to the card.
- *
- * We can do 16bit transfers on all but 284x.
+ * Ensure Ultra mode is set properly for this target.
*/
- if (p->type == AIC_284x)
+ ultra_enb_addr = ULTRA_ENB;
+ if ((channel == 'B') || (target > 7))
+ {
+ ultra_enb_addr++;
+ }
+ ultra_enb = inb(p->base + ultra_enb_addr);
+ sxfrctl0 = inb(p->base + SXFRCTL0);
+ if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
{
- outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+ ultra_enb |= 0x01 << (target & 0x07);
+ sxfrctl0 |= FAST20;
}
else
{
- outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+ ultra_enb &= ~(0x01 << (target & 0x07));
+ sxfrctl0 &= ~FAST20;
}
-
- outb(0, SCBCNT + base);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_getscb
- *
- * Description:
- * Get a SCB from the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- int base = p->base;
-
- /*
- * This is almost identical to aic7xxx_putscb().
- */
- outb(SCBAUTO, SCBCNT + base);
- insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
- outb(0, SCBCNT + base);
+ outb(ultra_enb, p->base + ultra_enb_addr);
+ outb(sxfrctl0, p->base + SXFRCTL0);
}
/*+F*************************************************************************
@@ -1375,6 +1562,47 @@ scbq_remove_head(scb_queue_type *queue)
/*+F*************************************************************************
* Function:
+ * scbq_remove
+ *
+ * Description:
+ * Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+ if (queue->head == scb)
+ {
+ /* At beginning of queue, remove from head. */
+ scbq_remove_head(queue);
+ }
+ else
+ {
+ struct aic7xxx_scb *curscb = queue->head;
+
+ /*
+ * Search until the next scb is the one we're looking for, or
+ * we run out of queue.
+ */
+ while ((curscb != NULL) && (curscb->q_next != scb))
+ {
+ curscb = curscb->q_next;
+ }
+ if (curscb != NULL)
+ {
+ /* Found it. */
+ curscb->q_next = scb->q_next;
+ if (scb->q_next == NULL)
+ {
+ /* Update the tail when removing the tail. */
+ queue->tail = curscb;
+ }
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
* scbq_insert_tail
*
* Description:
@@ -1404,23 +1632,87 @@ scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
* to be reset and all devices on that channel must be aborted.
*-F*************************************************************************/
static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
+ int lun, unsigned char tag)
{
- int targ = (scb->target_channel_lun >> 4) & 0x0F;
- char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+ char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int slun = scb->hscb->target_channel_lun & 0x07;
+ int match;
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
- target, channel, targ, chan);
+ printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
+ scb->cmd->device->host->host_no, target, channel, targ, chan);
#endif
- if (target == ALL_TARGETS)
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == ALL_TARGETS));
+ if (match != 0)
+ match = ((lun == slun) || (lun == ALL_LUNS));
+ if (match != 0)
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+ return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ * Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+ /*
+ * Invalidate the tag so that aic7xxx_find_scb doesn't think
+ * it's active
+ */
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
+
+ outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
+ outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ * Removes the current SCB from the disconnected list and adds it
+ * to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+ unsigned char next;
+ unsigned char prev;
+
+ outb(scbptr, p->base + SCBPTR);
+ next = inb(p->base + SCB_NEXT);
+ prev = inb(p->base + SCB_PREV);
+
+ outb(0, p->base + SCB_CONTROL);
+
+ aic7xxx_add_curscb_to_free_list(p);
+
+ if (prev != SCB_LIST_NULL)
{
- return (chan == channel);
+ outb(prev, p->base + SCBPTR);
+ outb(next, p->base + SCB_NEXT);
}
else
{
- return ((chan == channel) && (targ == target));
+ outb(next, p->base + DISCONNECTED_SCBH);
}
+
+ if (next != SCB_LIST_NULL)
+ {
+ outb(next, p->base + SCBPTR);
+ outb(prev, p->base + SCB_PREV);
+ }
+ return next;
}
/*+F*************************************************************************
@@ -1428,51 +1720,93 @@ aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
* aic7xxx_busy_target
*
* Description:
- * Set the specified target active.
+ * Set the specified target busy.
*-F*************************************************************************/
static void
-aic7xxx_busy_target(unsigned char target, char channel, int base)
+aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
+ char channel, unsigned char scbid)
{
- unsigned char active;
- unsigned long active_port = ACTIVE_A + base;
+ unsigned char active_scb;
+ unsigned char info_scb;
+ unsigned int scb_offset;
+
+ info_scb = target / 4;
+ if (channel == 'B')
+ info_scb = info_scb + 2;
+
+ active_scb = inb(p->base + SCBPTR);
+ outb(info_scb, p->base + SCBPTR);
+ scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+ outb(scbid, p->base + scb_offset);
+ outb(active_scb, p->base + SCBPTR);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_index_busy_target
+ *
+ * Description:
+ * Returns the index of the busy target, and optionally sets the
+ * target inactive.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
+ char channel, int unbusy)
+{
+ unsigned char active_scb;
+ unsigned char info_scb;
+ unsigned char busy_scbid;
+ unsigned int scb_offset;
+
+ info_scb = target / 4;
+ if (channel == 'B')
+ info_scb = info_scb + 2;
- if ((target > 0x07) || (channel == 'B'))
+ active_scb = inb(p->base + SCBPTR);
+ outb(info_scb, p->base + SCBPTR);
+ scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+ busy_scbid = inb(p->base + scb_offset);
+ if (unbusy)
{
- /*
- * targets on the Second channel or above id 7 store info in byte two
- * of ACTIVE
- */
- active_port++;
+ outb(SCB_LIST_NULL, p->base + scb_offset);
}
- active = inb(active_port);
- active |= (0x01 << (target & 0x07));
- outb(active, active_port);
+ outb(active_scb, p->base + SCBPTR);
+ return (busy_scbid);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_unbusy_target
+ * aic7xxx_find_scb
*
* Description:
- * Set the specified target inactive.
+ * Look through the SCB array of the card and attempt to find the
+ * hardware SCB that corresponds to the passed in SCB. Return
+ * SCB_LIST_NULL if unsuccessful. This routine assumes that the
+ * card is already paused.
*-F*************************************************************************/
-static void
-aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- unsigned char active;
- unsigned long active_port = ACTIVE_A + base;
+ unsigned char saved_scbptr;
+ unsigned char curindex;
- if ((target > 0x07) || (channel == 'B'))
+ saved_scbptr = inb(p->base + SCBPTR);
+ curindex = 0;
+ for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
{
- /*
- * targets on the Second channel or above id 7 store info in byte two
- * of ACTIVE
- */
- active_port++;
+ outb(curindex, p->base + SCBPTR);
+ if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+ {
+ break;
+ }
}
- active = inb(active_port);
- active &= ~(0x01 << (target & 0x07));
- outb(active, active_port);
+ outb(saved_scbptr, p->base + SCBPTR);
+ if (curindex >= p->scb_data->maxhscbs)
+ {
+ curindex = SCB_LIST_NULL;
+ }
+
+ return (curindex);
}
/*+F*************************************************************************
@@ -1480,68 +1814,60 @@ aic7xxx_unbusy_target(unsigned char target, char channel, int base)
* aic7xxx_allocate_scb
*
* Description:
- * Get a free SCB either from one already assigned to a hardware
- * slot, or one that will require an SCB to be paged out before
- * use. If there are none, attempt to allocate a new one.
+ * Get an SCB from the free list or by allocating a new one.
*-F*************************************************************************/
static struct aic7xxx_scb *
aic7xxx_allocate_scb(struct aic7xxx_host *p)
{
- struct aic7xxx_scb *scbp = NULL;
- int maxscbs;
+ struct aic7xxx_scb *scbp = NULL;
+ struct aic7xxx_hwscb *hscbp = NULL;
+#ifdef AGRESSIVE
+ long processor_flags;
- scbp = p->scb_link->free_scbs.head;
+ save_flags(processor_flags);
+ cli();
+#endif
+
+ scbp = p->scb_data->free_scbs.head;
if (scbp != NULL)
{
- scbq_remove_head(&p->scb_link->free_scbs);
+ scbq_remove_head(&p->scb_data->free_scbs);
}
else
{
- /*
- * This should always be NULL if paging is not enabled.
- */
- scbp = p->page_scbs.head;
- if (scbp != NULL)
+ if (p->scb_data->numscbs < p->scb_data->maxscbs)
{
- scbq_remove_head(&p->page_scbs);
- }
- else
- {
- /*
- * Set limit the SCB allocation to the maximum number of
- * hardware SCBs if paging is not enabled; otherwise use
- * the maximum (255).
- */
- if (p->flags & PAGE_ENABLED)
- maxscbs = p->maxscbs;
- else
- maxscbs = p->maxhscbs;
- if (p->scb_link->numscbs < maxscbs)
- {
- int scb_index = p->scb_link->numscbs;
- int scb_size = sizeof(struct aic7xxx_scb);
+ int scb_index = p->scb_data->numscbs;
+ int scb_size = sizeof(struct aic7xxx_scb) +
+ sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
- p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
- scbp = (p->scb_array[scb_index]);
- if (scbp != NULL)
- {
- memset(scbp, 0, sizeof(*scbp));
- scbp->tag = scb_index;
- if (scb_index < p->maxhscbs)
- scbp->position = scb_index;
- else
- scbp->position = SCB_LIST_NULL;
- p->scb_link->numscbs++;
- }
+ scbp = kmalloc(scb_size, GFP_ATOMIC);
+ if (scbp != NULL)
+ {
+ memset(scbp, 0, sizeof(struct aic7xxx_scb));
+ hscbp = &p->scb_data->hscbs[scb_index];
+ scbp->hscb = hscbp;
+ scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
+ memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
+ hscbp->tag = scb_index;
+ p->scb_data->numscbs++;
+ /*
+ * Place in the scb array; never is removed
+ */
+ p->scb_data->scb_array[scb_index] = scbp;
}
}
}
+#ifdef AIC7XXX_DEBUG
if (scbp != NULL)
{
-#ifdef AIC7XXX_DEBUG
- p->scb_link->activescbs++;
-#endif
+ p->activescbs++;
}
+#endif
+
+#ifdef AGRESSIVE
+ restore_flags(processor_flags);
+#endif
return (scbp);
}
@@ -1581,6 +1907,7 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
cmd = p->completeq.head;
p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
cmd->host_scribble = NULL;
+ p->device_status[TARGET_INDEX(cmd)].active_cmds--;
cmd->scsi_done(cmd);
}
p->completeq.tail = NULL;
@@ -1591,53 +1918,29 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
* aic7xxx_free_scb
*
* Description:
- * Free the scb and update the page, waiting, free scb lists.
+ * Free the scb and insert into the free scb list.
*-F*************************************************************************/
static void
aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- struct aic7xxx_scb *wscb;
+ struct aic7xxx_hwscb *hscb;
+ long flags;
+
+ hscb = scb->hscb;
+ save_flags(flags);
+ cli();
- scb->state = SCB_FREE;
+ scb->flags = SCB_FREE;
scb->cmd = NULL;
- scb->control = 0;
- scb->state = 0;
+ hscb->control = 0;
+ hscb->target_status = 0;
- if (scb->position == SCB_LIST_NULL)
- {
- scbq_insert_head(&p->page_scbs, scb);
- }
- else
- {
- /*
- * If there are any SCBS on the waiting queue, assign the slot of this
- * "freed" SCB to the first one. We'll run the waiting queues after
- * all command completes for a particular interrupt are completed or
- * when we start another command.
- */
- wscb = p->waiting_scbs.head;
- if (wscb != NULL)
- {
- scbq_remove_head(&p->waiting_scbs);
- wscb->position = scb->position;
- scbq_insert_tail(&p->assigned_scbs, wscb);
- wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ;
-
- /*
- * The "freed" SCB will need to be assigned a slot before being
- * used, so put it in the page_scbs queue.
- */
- scb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->page_scbs, scb);
- }
- else
- {
- scbq_insert_head(&p->scb_link->free_scbs, scb);
- }
+ scbq_insert_head(&p->scb_data->free_scbs, scb);
#ifdef AIC7XXX_DEBUG
- p->scb_link->activescbs--; /* For debugging purposes. */
+ p->activescbs--; /* For debugging purposes. */
#endif
- }
+
+ restore_flags(flags);
}
/*+F*************************************************************************
@@ -1652,68 +1955,113 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
Scsi_Cmnd *cmd = scb->cmd;
+ if (scb->flags & SCB_RECOVERY_SCB)
+ {
+ p->flags &= ~IN_TIMEOUT;
+ }
+ if (cmd->result == DID_OK)
+ {
+ if (scb->flags & SCB_ABORTED)
+ {
+ cmd->result = (DID_RESET << 16);
+ }
+ }
+ if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+ {
+ unsigned short mask;
+
+ mask = 0x01 << TARGET_INDEX(scb->cmd);
+ if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ p->wdtr_pending &= ~mask;
+ }
+ if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ p->sdtr_pending &= ~mask;
+ }
+ }
aic7xxx_free_scb(p, scb);
aic7xxx_queue_cmd_complete(p, cmd);
+#ifdef AIC7XXX_PROC_STATS
+ {
+ int actual;
+
+ /*
+ * XXX: we should actually know how much actually transferred
+ * XXX: for each command, but apparently that's too difficult.
+ */
+ actual = aic7xxx_length(cmd, 0);
+ if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
+ && (aic7xxx_error(cmd) == 0))
+ {
+ struct aic7xxx_xferstats *sp;
+ long *ptr;
+ int x;
+
+ sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+ sp->xfers++;
+
+ if (cmd->request.cmd == WRITE)
+ {
+ sp->w_total++;
+ sp->w_total512 += (actual >> 9);
+ ptr = sp->w_bins;
+ }
+ else
+ {
+ sp->r_total++;
+ sp->r_total512 += (actual >> 9);
+ ptr = sp->r_bins;
+ }
+ for (x = 9; x <= 17; x++)
+ {
+ if (actual < (1 << x))
+ {
+ ptr[x - 9]++;
+ break;
+ }
+ }
+ if (x > 17)
+ {
+ ptr[x - 9]++;
+ }
+ }
+ }
+#endif /* AIC7XXX_PROC_STATS */
}
/*+F*************************************************************************
* Function:
- * aic7xxx_done_aborted_scbs
+ * aic7xxx_run_done_queue
*
* Description:
- * Calls the scsi_done() for the Scsi_Cmnd of each scb in the
- * aborted list, and adds each scb to the free list.
+ * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ * aborted list, and adds each scb to the free list. If complete
+ * is TRUE, we also process the commands complete list.
*-F*************************************************************************/
static void
-aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
{
- Scsi_Cmnd *cmd;
struct aic7xxx_scb *scb;
int i;
- for (i = 0; i < p->scb_link->numscbs; i++)
+ for (i = 0; i < p->scb_data->numscbs; i++)
{
- scb = (p->scb_array[i]);
- if (scb->state & SCB_QUEUED_FOR_DONE)
+ scb = p->scb_data->scb_array[i];
+ if (scb->flags & SCB_QUEUED_FOR_DONE)
{
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
- scb->position, TCL_OF_SCB(scb));
+ printk("(scsi%d:%d:%d) Aborting scb %d\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
#endif
- /*
- * Process the command after marking the scb as free
- * and adding it to the free list.
- */
- cmd = scb->cmd;
- p->device_status[TARGET_INDEX(cmd)].flags = 0;
- aic7xxx_free_scb(p, scb);
- cmd->scsi_done(cmd); /* call the done function */
+ aic7xxx_done(p, scb);
}
}
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_add_waiting_scb
- *
- * Description:
- * Add this SCB to the head of the "waiting for selection" list.
- *-F*************************************************************************/
-static void
-aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
-{
- unsigned char next;
- unsigned char curscb;
-
- curscb = inb(SCBPTR + base);
- next = inb(WAITING_SCBH + base);
-
- outb(scb->position, SCBPTR + base);
- outb(next, SCB_NEXT + base);
- outb(scb->position, WAITING_SCBH + base);
-
- outb(curscb, SCBPTR + base);
+ if (complete)
+ {
+ aic7xxx_done_cmds_complete(p);
+ }
}
/*+F*************************************************************************
@@ -1726,26 +2074,23 @@ aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
*-F*************************************************************************/
static unsigned char
aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
- unsigned char prev)
+ unsigned char scbpos, unsigned char prev)
{
unsigned char curscb, next;
- int target = (scb->target_channel_lun >> 4) & 0x0F;
- char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
- int base = p->base;
/*
* Select the SCB we want to abort and pull the next pointer out of it.
*/
- curscb = inb(SCBPTR + base);
- outb(scb->position, SCBPTR + base);
- next = inb(SCB_NEXT + base);
+ curscb = inb(p->base + SCBPTR);
+ outb(scbpos, p->base + SCBPTR);
+ next = inb(p->base + SCB_NEXT);
/*
* Clear the necessary fields
*/
- outb(0, SCB_CONTROL + base);
- outb(SCB_LIST_NULL, SCB_NEXT + base);
- aic7xxx_unbusy_target(target, channel, base);
+ outb(0, p->base + SCB_CONTROL);
+
+ aic7xxx_add_curscb_to_free_list(p);
/*
* Update the waiting list
@@ -1755,22 +2100,23 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
/*
* First in the list
*/
- outb(next, WAITING_SCBH + base);
+ outb(next, p->base + WAITING_SCBH);
}
else
{
/*
* Select the scb that pointed to us and update its next pointer.
*/
- outb(prev, SCBPTR + base);
- outb(next, SCB_NEXT + base);
+ outb(prev, p->base + SCBPTR);
+ outb(next, p->base + SCB_NEXT);
}
/*
* Point us back at the original scb position and inform the SCSI
* system that the command has been aborted.
*/
- outb(curscb, SCBPTR + base);
- scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ outb(curscb, p->base + SCBPTR);
+ scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scb->flags &= ~SCB_ACTIVE;
scb->cmd->result = (DID_RESET << 16);
return (next);
@@ -1778,6 +2124,75 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
/*+F*************************************************************************
* Function:
+ * aic7xxx_search_qinfifo
+ *
+ * Description:
+ * Search the queue-in FIFO for matching SCBs and conditionally
+ * requeue. Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
+ int lun, unsigned char tag, int flags, int requeue)
+{
+ unsigned char saved_queue[AIC7XXX_MAXSCB];
+ int queued = inb(p->base + QINCNT) & p->qcntmask;
+ int i;
+ int found;
+ struct aic7xxx_scb *scbp;
+ scb_queue_type removed_scbs;
+
+ found = 0;
+ scbq_init (&removed_scbs);
+ for (i = 0; i < (queued - found); i++)
+ {
+ saved_queue[i] = inb(p->base + QINFIFO);
+ scbp = p->scb_data->scb_array[saved_queue[i]];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ /*
+ * We found an scb that needs to be removed.
+ */
+ if (requeue)
+ {
+ scbq_insert_head(&removed_scbs, scbp);
+ }
+ else
+ {
+ scbp->flags = flags;
+ scbp->flags &= ~SCB_ACTIVE;
+ /*
+ * XXX - Don't know what error to use here.
+ */
+ aic7xxx_error(scbp->cmd) = DID_RESET;
+ }
+ i--;
+ found++;
+ }
+ }
+ /* Now put the saved scbs back. */
+ for (queued = 0; queued < i; queued++)
+ outb(saved_queue[queued], p->base + QINFIFO);
+
+ if (requeue)
+ {
+ scbp = removed_scbs.head;
+ while (scbp != NULL)
+ {
+ scbq_remove_head(&removed_scbs);
+ /*
+ * XXX - Shouldn't we be adding this to the free list?
+ */
+ scbq_insert_head(&p->waiting_scbs, scbp);
+ scbp->flags |= SCB_WAITINGQ;
+ scbp = removed_scbs.head;
+ }
+ }
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
* aic7xxx_reset_device
*
* Description:
@@ -1785,131 +2200,280 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
* all active and queued scbs for that target/channel.
*-F*************************************************************************/
static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+ int lun, unsigned char tag)
{
- int base = p->base;
- struct aic7xxx_scb *scb;
+ struct aic7xxx_scb *scbp;
unsigned char active_scb;
int i = 0;
- int found = 0;
+ int found;
/*
* Restore this when we're done
*/
- active_scb = inb(SCBPTR + base);
+ active_scb = inb(p->base + SCBPTR);
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
- target, channel, active_scb);
+ printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
+ p->host_no, target, CHAN_TO_INT(channel), active_scb);
#endif
+
/*
- * Search the QINFIFO.
+ * Deal with the busy target and linked next issues.
*/
{
- int saved_queue[AIC7XXX_MAXSCB];
- int queued = inb(QINCNT + base) & p->qcntmask;
+ int min_target, max_target;
+ unsigned char busy_scbid;
- for (i = 0; i < (queued - found); i++)
+ /* Make all targets 'relative' to bus A. */
+ if (target == ALL_TARGETS)
{
- saved_queue[i] = inb(QINFIFO + base);
- outb(saved_queue[i], SCBPTR + base);
- scb = (p->scb_array[inb(SCB_TAG + base)]);
- if (aic7xxx_match_scb(scb, target, channel))
+ switch (channel)
{
- /*
- * We found an scb that needs to be aborted.
- */
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n",
- saved_queue[i], TCL_OF_SCB(scb));
-#endif
- scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
- scb->cmd->result = (DID_RESET << 16);
- outb(0, SCB_CONTROL + base);
- i--;
- found++;
+ case 'A':
+ min_target = 0;
+ max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+ break;
+ case 'B':
+ min_target = 8;
+ max_target = 15;
+ break;
+ case ALL_CHANNELS:
+ default:
+ min_target = 0;
+ max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+ break;
}
}
- /*
- * Now put the saved scbs back.
- */
- for (queued = 0; queued < i; queued++)
+ else
+ {
+ min_target = target + channel == 'B' ? 8 : 0;
+ max_target = min_target;
+ }
+
+ for (i = min_target; i <= max_target; i++)
{
- outb(saved_queue[queued], QINFIFO + base);
+ busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
+ if (busy_scbid < p->scb_data->numscbs)
+ {
+ struct aic7xxx_scb *busy_scb;
+ struct aic7xxx_scb *next_scb;
+ unsigned char next_scbid;
+
+ busy_scb = p->scb_data->scb_array[busy_scbid];
+
+ next_scbid = busy_scb->hscb->data_count >> 24;
+
+ if (next_scbid == SCB_LIST_NULL)
+ {
+ busy_scbid = aic7xxx_find_scb(p, busy_scb);
+
+ if (busy_scbid != SCB_LIST_NULL)
+ {
+ outb(busy_scbid, p->base + SCBPTR);
+ next_scbid = inb(p->base + SCB_LINKED_NEXT);
+ }
+ }
+
+ if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
+ {
+ aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
+ }
+
+ if (next_scbid != SCB_LIST_NULL)
+ {
+ next_scb = p->scb_data->scb_array[next_scbid];
+ if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+ {
+ continue;
+ }
+ /* Requeue for later processing */
+ scbq_insert_head(&p->waiting_scbs, next_scb);
+ next_scb->flags |= SCB_WAITINGQ;
+ }
+ }
}
}
+ found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+ SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
+
/*
* Search waiting for selection list.
*/
{
- unsigned char next, prev;
+ unsigned char next, prev, scb_index;
- next = inb(WAITING_SCBH + base); /* Start at head of list. */
+ next = inb(p->base + WAITING_SCBH); /* Start at head of list. */
prev = SCB_LIST_NULL;
while (next != SCB_LIST_NULL)
{
- outb(next, SCBPTR + base);
- scb = (p->scb_array[inb(SCB_TAG + base)]);
- /*
- * Select the SCB.
- */
- if (aic7xxx_match_scb(scb, target, channel))
+ outb(next, p->base + SCBPTR);
+ scb_index = inb(p->base + SCB_TAG);
+ if (scb_index >= p->scb_data->numscbs)
+ {
+ panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
+ scb_index, p->scb_data->numscbs);
+ }
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
{
- next = aic7xxx_abort_waiting_scb(p, scb, prev);
+ unsigned char linked_next;
+
+ next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+ linked_next = inb(p->base + SCB_LINKED_NEXT);
+ if (linked_next != SCB_LIST_NULL)
+ {
+ struct aic7xxx_scb *next_scb;
+ /*
+ * Requeue the waiting SCB via the waiting list.
+ */
+ next_scb = p->scb_data->scb_array[linked_next];
+ if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+ {
+ scbq_insert_head(&p->waiting_scbs, next_scb);
+ next_scb->flags |= SCB_WAITINGQ;
+ }
+ }
found++;
}
else
{
prev = next;
- next = inb(SCB_NEXT + base);
+ next = inb(p->base + SCB_NEXT);
+ }
+ }
+ }
+
+ /*
+ * Go through disconnected list and remove any entries we have queued
+ * for completion, zeroing their control byte too.
+ */
+ {
+ unsigned char next, prev, scb_index;
+
+ next = inb(p->base + DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL)
+ {
+ outb(next, p->base + SCBPTR);
+ scb_index = inb(p->base + SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
+ "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
+ }
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
+ }
+ else
+ {
+ prev = next;
+ next = inb(p->base + SCB_NEXT);
+ }
+ }
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list.
+ */
+ for (i = 0; i < p->scb_data->maxhscbs; i++)
+ {
+ unsigned char scbid;
+
+ outb(i, p->base + SCBPTR);
+ scbid = inb(p->base + SCB_TAG);
+ if (scbid < p->scb_data->numscbs)
+ {
+ scbp = p->scb_data->scb_array[scbid];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ aic7xxx_add_curscb_to_free_list(p);
}
}
}
/*
* Go through the entire SCB array now and look for commands for
- * for this target that are active. These are other (most likely
+ * for this target that are stillactive. These are other (most likely
* tagged) commands that were disconnected when the reset occurred.
*/
- for (i = 0; i < p->scb_link->numscbs; i++)
+ for (i = 0; i < p->scb_data->numscbs; i++)
{
- scb = (p->scb_array[i]);
- if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+ scbp = p->scb_data->scb_array[i];
+ if (((scbp->flags & SCB_ACTIVE) != 0) &&
+ aic7xxx_match_scb(scbp, target, channel, lun, tag))
{
- /*
- * Ensure the target is "free"
- */
- aic7xxx_unbusy_target(target, channel, base);
- if (! (scb->state & SCB_PAGED_OUT))
+ scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scbp->flags &= ~SCB_ACTIVE;
+ aic7xxx_error(scbp->cmd) = DID_RESET;
+
+ found++;
+
+ if ((scbp->flags & SCB_WAITINGQ) != 0)
{
- outb(scb->position, SCBPTR + base);
- outb(0, SCB_CONTROL + base);
+ scbq_remove(&p->waiting_scbs, scbp);
+ scbp->flags &= ~SCB_WAITINGQ;
}
- scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
- scb->cmd->result = (DID_RESET << 16);
- found++;
}
}
- outb(active_scb, SCBPTR + base);
+ outb(active_scb, p->base + SCBPTR);
return (found);
}
/*+F*************************************************************************
* Function:
+ * aic7xxx_clear_intstat
+ *
+ * Description:
+ * Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+ /* Clear any interrupt conditions this may have caused. */
+ outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
+ outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+ CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
* aic7xxx_reset_current_bus
*
* Description:
* Reset the current SCSI bus.
*-F*************************************************************************/
static void
-aic7xxx_reset_current_bus(int base)
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
{
- outb(SCSIRSTO, SCSISEQ + base);
+ unsigned char scsiseq;
+
+ /* Disable reset interrupts. */
+ outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+
+ /* Turn on the bus reset. */
+ scsiseq = inb(p->base + SCSISEQ);
+ outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+
+ udelay(1000);
+
+ /* Turn off the bus reset. */
+ outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+
+ aic7xxx_clear_intstat(p);
+
+ /* Re-enable reset interrupts. */
+ outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+
udelay(1000);
- outb(0, SCSISEQ + base);
}
/*+F*************************************************************************
@@ -1922,25 +2486,24 @@ aic7xxx_reset_current_bus(int base)
static int
aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
{
- int base = p->base;
- unsigned char sblkctl;
- char cur_channel;
unsigned long offset, offset_max;
int found;
+ unsigned char sblkctl;
+ char cur_channel;
+ pause_sequencer(p);
/*
- * Clean up all the state information for the
- * pending transactions on this bus.
+ * Clean up all the state information for the pending transactions
+ * on this bus.
*/
- found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
+ found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
if (channel == 'B')
{
p->needsdtr |= (p->needsdtr_copy & 0xFF00);
p->sdtr_pending &= 0x00FF;
- outb(0, ACTIVE_B + base);
- offset = TARG_SCRATCH + base + 8;
- offset_max = TARG_SCRATCH + base + 16;
+ offset = TARG_SCRATCH + 8;
+ offset_max = TARG_SCRATCH + 16;
}
else
{
@@ -1950,132 +2513,100 @@ aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
p->needwdtr = p->needwdtr_copy;
p->sdtr_pending = 0x0;
p->wdtr_pending = 0x0;
- outb(0, ACTIVE_A + base);
- outb(0, ACTIVE_B + base);
- offset = TARG_SCRATCH + base;
- offset_max = TARG_SCRATCH + base + 16;
+ offset = TARG_SCRATCH;
+ offset_max = TARG_SCRATCH + 16;
}
else
{
+ /* Channel A */
p->needsdtr |= (p->needsdtr_copy & 0x00FF);
p->sdtr_pending &= 0xFF00;
- outb(0, ACTIVE_A + base);
- offset = TARG_SCRATCH + base;
- offset_max = TARG_SCRATCH + base + 8;
+ offset = TARG_SCRATCH;
+ offset_max = TARG_SCRATCH + 8;
}
}
+
while (offset < offset_max)
{
/*
- * Revert to async/narrow transfers
- * until we renegotiate.
+ * Revert to async/narrow transfers until we renegotiate.
*/
u_char targ_scratch;
- targ_scratch = inb(offset);
+
+ targ_scratch = inb(p->base + offset);
targ_scratch &= SXFR;
- outb(targ_scratch, offset);
+ outb(targ_scratch, p->base + offset);
offset++;
}
/*
* Reset the bus and unpause/restart the controller
*/
-
- /*
- * Case 1: Command for another bus is active
- */
- sblkctl = inb(SBLKCTL + base);
+ sblkctl = inb(p->base + SBLKCTL);
cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
if (cur_channel != channel)
{
+ /*
+ * Case 1: Command for another bus is active
+ */
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
- channel);
+ printk("scsi%d: Stealthily resetting channel %c\n",
+ p->host_no, channel);
#endif
/*
- * Stealthily reset the other bus without upsetting the current bus
+ * Stealthily reset the other bus without upsetting the current bus.
*/
- outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+ outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
+ outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
if (initiate_reset)
{
- aic7xxx_reset_current_bus(base);
+ aic7xxx_reset_current_bus(p);
+ /*
+ * Cause the mid-level SCSI code to delay any further
+ * queueing by the bus settle time for us.
+ */
+ p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
}
- outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
- outb(sblkctl, SBLKCTL + base);
-
- UNPAUSE_SEQUENCER(p);
+ outb(0, p->base + SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ outb(sblkctl, p->base + SBLKCTL);
+ unpause_sequencer(p, /* unpause_always */ FALSE);
}
- /*
- * Case 2: A command from this bus is active or we're idle
- */
else
{
+ /*
+ * Case 2: A command from this bus is active or we're idle.
+ */
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
- channel);
+ printk("scsi%d: Resetting current channel %c\n",
+ p->host_no, channel);
#endif
+ outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
if (initiate_reset)
{
- aic7xxx_reset_current_bus(base);
+ aic7xxx_reset_current_bus(p);
+ /*
+ * Cause the mid-level SCSI code to delay any further
+ * queueing by the bus settle time for us.
+ */
+#if 0
+ p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
+#endif
}
- outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
- RESTART_SEQUENCER(p);
+ outb(0, p->base + SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ restart_sequencer(p);
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+ printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
#endif
}
/*
- * Cause the mid-level SCSI code to delay any further
- * queueing by the bus settle time for us.
- */
- p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-
- /*
* Now loop through all the SCBs that have been marked for abortion,
* and call the scsi_done routines.
*/
- aic7xxx_done_aborted_scbs(p);
- return found;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_page_scb
- *
- * Description:
- * Swap in_scbp for out_scbp down in the cards SCB array.
- * We assume that the SCB for out_scbp is already selected in SCBPTR.
- *
- *-F*************************************************************************/
-static inline void
-aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp,
- struct aic7xxx_scb *in_scbp)
-{
- int index;
-
- /* Page-out */
-#if 0
-printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
- out_scbp->cmd->target, in_scbp->cmd->target);
-#endif
- aic7xxx_getscb(p, out_scbp);
- out_scbp->state |= SCB_PAGED_OUT;
- if (!(out_scbp->control & TAG_ENB))
- {
- /* Stick in non-tagged array */
- index = (out_scbp->target_channel_lun >> 4) |
- (out_scbp->target_channel_lun & SELBUSB);
- p->pagedout_ntscbs[index] = out_scbp;
- }
-
- /* Page-in */
- in_scbp->position = out_scbp->position;
- out_scbp->position = SCB_LIST_NULL;
- aic7xxx_putscb(p, in_scbp);
- in_scbp->state &= ~SCB_PAGED_OUT;
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+ return (found);
}
/*+F*************************************************************************
@@ -2083,1159 +2614,1326 @@ printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
* aic7xxx_run_waiting_queues
*
* Description:
- * Scan the assigned_scbs and waiting_scbs queues. For scbs in the
- * assigned_scbs queue, we download and start them. For scbs in the
- * waiting_scbs queue, we page in as many as we can being careful
- * not to cause a deadlock for a reconnecting target.
- *
+ * Scan the awaiting_scbs queue downloading and starting as many
+ * scbs as we can.
*-F*************************************************************************/
static inline void
aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
{
struct aic7xxx_scb *scb;
- u_char cur_scb, intstat;
- u_long base = p->base;
- long flags;
- if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
+ if (p->waiting_scbs.head == NULL)
return;
- save_flags(flags);
- cli();
-
- PAUSE_SEQUENCER(p);
- cur_scb = inb(SCBPTR + base);
- intstat = inb(INTSTAT + base);
-
+ pause_sequencer(p);
/*
* First handle SCBs that are waiting but have been assigned a slot.
*/
- scb = p->assigned_scbs.head;
- while (scb != NULL)
- {
- scbq_remove_head(&(p->assigned_scbs));
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- /* Mark this as an active command. */
- scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE;
- outb(scb->position, QINFIFO + base);
- scb = p->assigned_scbs.head;
- }
-
- /* Now deal with SCBs that require paging. */
scb = p->waiting_scbs.head;
- if (scb != NULL)
+ while (scb != NULL)
{
- u_char disc_scb = inb(DISCONNECTED_SCBH + base);
- u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN);
- int count = 0;
- u_char next_scb;
-
- while (scb != NULL)
+ if (p->curqincnt >= p->qfullcount)
{
- /* Attempt to page this SCB in */
- if (disc_scb == SCB_LIST_NULL)
- break;
-
- /*
- * Advance disc_scb to the next one in the list.
- */
- outb(disc_scb, SCBPTR + base);
- next_scb = inb(SCB_NEXT + base);
-
- /*
- * We have to be careful about when we allow an SCB to be paged out.
- * There must always be at least one slot availible for a reconnecting
- * target in case it references an SCB that has been paged out. Our
- * heuristic is that either the disconnected list has at least two
- * entries in it or there is one entry and the sequencer is activily
- * working on an SCB which implies that it will either complete or
- * disconnect before another reconnection can occur.
- */
- if ((next_scb != SCB_LIST_NULL) || active)
+ p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
+ if (p->curqincnt >= p->qfullcount)
{
- u_char out_scbi;
- struct aic7xxx_scb *out_scbp;
-
- scbq_remove_head(&(p->waiting_scbs));
-
- /*
- * Find the in-core SCB for the one we're paging out.
- */
- out_scbi = inb(SCB_TAG + base);
- out_scbp = (p->scb_array[out_scbi]);
-
- /* Do the page out and mark the paged in SCB as active. */
- aic7xxx_page_scb(p, out_scbp, scb);
-
- /* Mark this as an active command. */
- scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE;
-
- /* Queue the command */
- outb(scb->position, QINFIFO + base);
- count++;
-
- /* Advance to the next disconnected SCB */
- disc_scb = next_scb;
- scb = p->waiting_scbs.head;
+ break;
}
- else
- scb = NULL;
}
- if (count)
+ /*
+ * We have some space.
+ */
+ scbq_remove_head(&(p->waiting_scbs));
+ scb->flags &= ~SCB_WAITINGQ;
+
+ outb(scb->hscb->tag, p->base + QINFIFO);
+
+ if ((p->flags & PAGE_ENABLED) != 0)
{
- /*
- * Update the head of the disconnected list.
+ /*
+ * We only care about this statistic when paging
+ * since it's impossible to overflow the qinfifo
+ * in the non-paging case.
*/
- outb(disc_scb, DISCONNECTED_SCBH + base);
- if (disc_scb != SCB_LIST_NULL)
- {
- outb(disc_scb, SCBPTR + base);
- outb(SCB_LIST_NULL, SCB_PREV + base);
- }
+ p->curqincnt++;
}
+ scb = p->waiting_scbs.head;
}
- /* Restore old position */
- outb(cur_scb, SCBPTR + base);
- /*
- * Guard against unpausing the sequencer if there is an interrupt
- * waiting to happen.
- */
- if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
- {
- UNPAUSE_SEQUENCER(p);
- }
+ unpause_sequencer(p, FALSE);
+}
- restore_flags(flags);
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_sdtr
+ *
+ * Description:
+ * Constucts a synchronous data transfer message in the message
+ * buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
+ unsigned char period, unsigned char offset)
+{
+ outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte);
+ outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+ outb(MSG_EXT_SDTR, p->base + MSG_OUT + 2 + start_byte);
+ outb(period, p->base + MSG_OUT + 3 + start_byte);
+ outb(offset, p->base + MSG_OUT + 4 + start_byte);
+ outb(start_byte + 5, p->base + MSG_LEN);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_isr
+ * aic7xxx_construct_wdtr
*
* Description:
- * SCSI controller interrupt handler.
+ * Constucts a wide data transfer message in the message buffer
+ * on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
+ unsigned char bus_width)
+{
+ outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte);
+ outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+ outb(MSG_EXT_WDTR, p->base + MSG_OUT + 2 + start_byte);
+ outb(bus_width, p->base + MSG_OUT + 3 + start_byte);
+ outb(start_byte + 4, p->base + MSG_LEN);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_calc_residual
*
- * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- * be disabled all through this function unless we say otherwise.
+ * Description:
+ * Calculate the residual data not yet transferred.
*-F*************************************************************************/
static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
- struct aic7xxx_host *p;
- struct aic7xxx_scb *scb = NULL;
- short transfer;
- unsigned char ha_flags, scsi_id, bus_width;
- unsigned char offset, rate, scratch, scratch_offset;
- unsigned char max_offset, rej_byte;
- unsigned short target_mask;
- char channel;
- unsigned int addr; /* must be 32 bits */
+ struct aic7xxx_hwscb *hscb;
Scsi_Cmnd *cmd;
+ int actual;
- p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+ cmd = scb->cmd;
+ hscb = scb->hscb;
/*
- * Search for the host with a pending interrupt. If we can't find
- * one, then we've encountered a spurious interrupt.
+ * Don't destroy valid residual information with
+ * residual coming from a check sense operation.
*/
- while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+ if (((scb->hscb->control & DISCONNECTED) == 0) &&
+ (scb->flags & SCB_SENSE) == 0)
{
- if (p->next == NULL)
- {
- p = NULL;
- }
- else
+ /*
+ * We had an underflow. At this time, there's only
+ * one other driver that bothers to check for this,
+ * and cmd->underflow seems to be set rather half-
+ * heartedly in the higher-level SCSI code.
+ */
+ actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
+
+ actual -= (hscb->residual_data_count[2] << 16) |
+ (hscb->residual_data_count[1] << 8) |
+ hscb->residual_data_count[0];
+
+ if (actual < cmd->underflow)
{
- p = (struct aic7xxx_host *) p->next->hostdata;
+ printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+ "Wanted at least %u, got %u, residual SG count %d.\n",
+ p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+ hscb->residual_SG_segment_count);
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ aic7xxx_status(cmd) = hscb->target_status;
}
}
- if (p == NULL)
- return;
-
/*
- * Keep track of interrupts for /proc/scsi
+ * Clean out the residual information in the SCB for the
+ * next consumer.
*/
- p->isr_count++;
+ hscb->residual_data_count[2] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_SG_segment_count = 0;
+}
- if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_device_reset
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+{
+ unsigned short targ_mask;
+ unsigned char targ_scratch;
+ int scratch_offset = target;
+ int found;
+
+ if (channel == 'B')
{
- /*
- * We must only have one card at this IRQ and it must have been
- * added to the board data before the spurious interrupt occurred.
- * It is sufficient that we check isr_count and not the spurious
- * interrupt count.
- */
- printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
- return;
+ scratch_offset += 8;
}
-
- base = p->base;
+ targ_mask = (0x01 << scratch_offset);
/*
- * Handle all the interrupt sources - especially for SCSI
- * interrupts, we won't get a second chance at them.
+ * Go back to async/narrow transfers and renegotiate.
*/
- intstat = inb(INTSTAT + base);
+ p->needsdtr |= p->needsdtr_copy & targ_mask;
+ p->needwdtr |= p->needwdtr_copy & targ_mask;
+ p->sdtr_pending &= ~targ_mask;
+ p->wdtr_pending &= ~targ_mask;
+ targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ targ_scratch &= SXFR;
+ outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
+ "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
- /*
- * Indicate that we're in the interrupt handler.
- */
- p->flags |= IN_ISR;
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_seqint
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ struct aic7xxx_scb *scb;
+ unsigned short target_mask;
+ unsigned char target, scratch_offset;
+ char channel;
- if (intstat & BRKADRINT)
+ if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
{
- int i;
- unsigned char errno = inb(ERROR + base);
-
- printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
- for (i = 0; i < NUMBER(hard_error); i++)
- {
- if (errno & hard_error[i].errno)
- {
- printk(KERN_ERR " %s\n", hard_error[i].errmesg);
- }
- }
- panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
- inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+ target = (inb(p->base + SELID) >> 4) & 0x0F;
}
+ else
+ {
+ target = (inb(p->base + SCSIID) >> 4) & 0x0F;
+ }
+ scratch_offset = target;
+ channel = 'A';
+ if (inb(p->base + SBLKCTL) & SELBUSB)
+ {
+ channel = 'B';
+ scratch_offset += 8;
+ }
+ target_mask = (0x01 << scratch_offset);
- if (intstat & SEQINT)
+ switch (intstat & SEQINT_MASK)
{
- /*
- * Although the sequencer is paused immediately on
- * a SEQINT, an interrupt for a SCSIINT condition will
- * unpaused the sequencer before this point.
- */
- PAUSE_SEQUENCER(p);
+ case NO_MATCH:
+ {
+ /*
+ * This could be for a normal abort request. Figure out
+ * which SCB we were trying to find and only give an error
+ * if we didn't ask for this to happen.
+ */
+ unsigned char scb_index;
+ unsigned char busy_scbid;
+ unsigned char arg1;
- scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
- scratch_offset = scsi_id;
- channel = 'A';
- if (inb(SBLKCTL + base) & SELBUSB)
- {
- channel = 'B';
- scratch_offset += 8;
- }
- target_mask = (0x01 << scratch_offset);
+ busy_scbid = aic7xxx_index_busy_target(p, target, channel,
+ /*unbusy*/ FALSE);
+ arg1 = inb(p->base + ARG_1);
- switch (intstat & SEQINT_MASK)
- {
- case NO_MATCH:
- if (p->flags & PAGE_ENABLED)
+ if (arg1 == SCB_LIST_NULL)
{
- /* SCB Page-in request */
- struct aic7xxx_scb *outscb;
- u_char arg_1 = inb(ARG_1 + base);
- int use_disconnected = FALSE;
-
- /*
- * The sequencer expects this value upon return. Assume
- * we will find the paged out SCB and set the value now.
- * If we don't, and one of the methods used to acquire an
- * SCB calls aic7xxx_done(), we will end up in our queue
- * routine and unpause the sequencer without giving it the
- * correct return value, which causes a hang.
- */
- outb(SCB_PAGEDIN, RETURN_1 + base);
- if (arg_1 == SCB_LIST_NULL)
- {
- /* Non-tagged command */
- int index = scsi_id;
- if (channel == 'B')
- {
- index |= SELBUSB;
- }
- scb = p->pagedout_ntscbs[index];
- }
- else
- scb = (p->scb_array[arg_1]);
+ /* untagged request */
+ scb_index = busy_scbid;
+ }
+ else
+ {
+ scb_index = arg1;
+ }
- if (!(scb->state & SCB_PAGED_OUT))
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if (scb->hscb->control & ABORT_SCB)
{
- printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
- "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(0, RETURN_1 + base);
+ /*
+ * We expected this. Let the busfree handler take care
+ * of this when we the abort is finially sent. Set
+ * IDENTIFY_SEEN so that the busfree handler knows that
+ * there is an SCB to cleanup.
+ */
+ outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
+ printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
+ p->host_no, TC_OF_SCB(scb));
break;
}
+ }
+ printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+ "target - Issuing BUS DEVICE RESET.\n",
+ p->host_no, target, CHAN_TO_INT(channel));
+
+ printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+ inb(p->base + SAVED_TCL), arg1,
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ aic7xxx_handle_device_reset(p, target, channel);
+ }
+ break;
- /*
- * Now to pick the SCB to page out. Either take a free SCB, an
- * assigned SCB, an SCB that just completed, or the first one
- * on the disconnected SCB list.
- */
- if (p->scb_link->free_scbs.head != NULL)
- {
- outscb = p->scb_link->free_scbs.head;
- scbq_remove_head(&p->scb_link->free_scbs);
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->page_scbs, outscb);
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
- }
- else if (p->assigned_scbs.head != NULL)
- {
- outscb = p->assigned_scbs.head;
- scbq_remove_head(&p->assigned_scbs);
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->waiting_scbs, outscb);
- outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
- }
- else if (intstat & CMDCMPLT)
- {
- int scb_index;
+ case NO_MATCH_BUSY:
+ {
+ /*
+ * XXX - Leave this as a panic for the time being since it
+ * indicates a bug in the timeout code for this to happen.
+ */
+ unsigned char scb_index;
- outb(CLRCMDINT, CLRINT + base);
- scb_index = inb(QOUTFIFO + base);
- if (!(inb(QOUTCNT + base) & p->qcntmask))
- {
- intstat &= ~CMDCMPLT;
- }
- outscb = (p->scb_array[scb_index]);
- if (!(outscb->state & SCB_ACTIVE))
+ scb_index = inb(p->base + CUR_SCBID);
+ scb = p->scb_data->scb_array[scb_index];
+
+ panic("scsi%d: Target %d, channel %c, Target busy link failure, "
+ "but busy SCB exists!\n",
+ p->host_no, target, channel);
+ }
+ break;
+
+ case SEND_REJECT:
+ {
+ unsigned char rej_byte;
+
+ rej_byte = inb(p->base + REJBYTE);
+ printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
+ "received from target, SEQ_FLAGS=0x%x\n",
+ p->host_no, target, CHAN_TO_INT(channel), rej_byte,
+ inb(p->base + SEQ_FLAGS));
+ }
+ break;
+
+ case NO_IDENT:
+ {
+ /*
+ * The reconnecting target either did not send an identify
+ * message, or did, but we didn't find and SCB to match and
+ * before it could respond to our ATN/abort, it hit a dataphase.
+ * The only safe thing to do is to blow it away with a bus
+ * reset.
+ */
+ int found;
+
+ printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
+ "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+ p->host_no, target, CHAN_TO_INT(channel),
+ inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+
+ found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+
+ printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
+ "%d SCBs aborted\n", p->host_no, channel, found);
+ }
+ break;
+
+ case BAD_PHASE:
+ if (inb(p->base + LASTPHASE) == P_BUSFREE)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
+ p->host_no, CHAN_TO_INT(channel), target);
+ restart_sequencer(p);
+ }
+ else
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
+ "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+ }
+ break;
+
+ case EXTENDED_MSG:
+ {
+ unsigned char message_length;
+ unsigned char message_code;
+ unsigned char scb_index;
+
+ message_length = inb(p->base + MSGIN_EXT_LEN);
+ message_code = inb(p->base + MSGIN_EXT_OPCODE);
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+
+ switch (message_code)
+ {
+ case MSG_EXT_SDTR:
+ {
+ unsigned char period;
+ unsigned char offset;
+ unsigned char saved_offset;
+ unsigned char targ_scratch;
+ unsigned char max_offset;
+ unsigned char rate;
+
+ if (message_length != MSG_EXT_SDTR_LEN)
{
- printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
- "during NO_MATCH interrupt\n", scb_index, p->host_no);
- use_disconnected = TRUE;
+ outb(SEND_REJ, p->base + RETURN_1);
+ break;
}
+
+ period = inb(p->base + MSGIN_EXT_BYTES);
+ saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
+ targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+ if (targ_scratch & WIDEXFER)
+ max_offset = MAX_OFFSET_16BIT;
else
+ max_offset = MAX_OFFSET_8BIT;
+ offset = MIN(saved_offset, max_offset);
+
+ aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+
+ /*
+ * Preserve the WideXfer flag.
+ */
+ targ_scratch = rate | (targ_scratch & WIDEXFER);
+
+ /*
+ * Update both the target scratch area and current SCSIRATE.
+ */
+ outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ outb(targ_scratch, p->base + SCSIRATE);
+
+ /*
+ * See if we initiated Sync Negotiation and didn't have
+ * have to fall down to async transfers.
+ */
+ if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
{
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
- outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
- if ((outscb->cmd->flags & WAS_SENSE) &&
- !(outscb->cmd->flags & ASKED_FOR_SENSE))
+ /* We started it. */
+ if (saved_offset == offset)
{
- /*
- * Got sense information.
- */
- outscb->cmd->flags &= ASKED_FOR_SENSE;
+ /*
+ * Don't send an SDTR back to the target.
+ */
+ outb(0, p->base + RETURN_1);
+ }
+ else
+ {
+ /* We went too low - force async. */
+ outb(SEND_REJ, p->base + RETURN_1);
}
- p->device_status[TARGET_INDEX(outscb->cmd)].flags
- |= DEVICE_SUCCESS;
- aic7xxx_done(p, outscb);
}
+ else
+ {
+ /*
+ * Send our own SDTR in reply.
+ *
+ * We want to see this message as we don't expect a target
+ * to send us a SDTR request first.
+ */
+ printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+ aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
+ outb(SEND_MSG, p->base + RETURN_1);
+ }
+ /*
+ * Clear the flags.
+ */
+ p->needsdtr &= ~target_mask;
+ break;
}
- else
- {
- use_disconnected = TRUE;
- }
- if (use_disconnected)
+
+ case MSG_EXT_WDTR:
{
- u_char tag;
- u_char next;
- u_char disc_scb = inb(DISCONNECTED_SCBH + base);
- if (disc_scb != SCB_LIST_NULL)
+ unsigned char scratch, bus_width;
+
+ if (message_length != MSG_EXT_WDTR_LEN)
{
- outb(disc_scb, SCBPTR + base);
- tag = inb(SCB_TAG + base);
- outscb = (p->scb_array[tag]);
- next = inb(SCB_NEXT + base);
- if (next != SCB_LIST_NULL)
- {
- outb(next, SCBPTR + base);
- outb(SCB_LIST_NULL, SCB_PREV + base);
- outb(disc_scb, SCBPTR + base);
- }
- outb(next, DISCONNECTED_SCBH + base);
- aic7xxx_page_scb(p, outscb, scb);
- }
- else if (inb(QINCNT + base) & p->qcntmask)
+ outb(SEND_REJ, p->base + RETURN_1);
+ break;
+ }
+
+ bus_width = inb(p->base + MSGIN_EXT_BYTES);
+ scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+ if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
{
- /* Pull one of our queued commands as a last resort. */
- disc_scb = inb(QINFIFO + base);
- outb(disc_scb, SCBPTR + base);
- tag = inb(SCB_TAG + base);
- outscb = (p->scb_array[tag]);
- if ((outscb->control & 0x23) != TAG_ENB)
+ /*
+ * Don't send an WDTR back to the target, since we asked first.
+ */
+ outb(0, p->base + RETURN_1);
+ switch (bus_width)
{
- /*
- * This is not a simple tagged command so its position
- * in the queue matters. Take the command at the end of
- * the queue instead.
- */
- int i;
- int saved_queue[AIC7XXX_MAXSCB];
- int queued = inb(QINCNT + base) & p->qcntmask;
-
- /* Count the command we removed already */
- saved_queue[0] = disc_scb;
- queued++;
-
- /* Empty the input queue. */
- for (i = 1; i < queued; i++)
- {
- saved_queue[i] = inb(QINFIFO + base);
- }
-
- /* Put everyone back but the last entry. */
- queued--;
- for (i = 0; i < queued; i++)
- {
- outb(saved_queue[i], QINFIFO + base);
- }
-
- outb(saved_queue[queued], SCBPTR + base);
- tag = inb(SCB_TAG + base);
- outscb = (p->scb_array[tag]);
+ case BUS_8_BIT:
+ scratch &= 0x7F;
+ break;
+
+ case BUS_16_BIT:
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+ "bit transfers.\n", p->host_no, target, channel);
+ }
+ scratch |= WIDEXFER;
+ break;
+
+ case BUS_32_BIT:
+ outb(SEND_REJ, p->base + RETURN_1);
+ /* No verbose here! We want to see this condition. */
+ printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+ "requesting 32 bit transfers, rejecting...\n",
+ p->host_no, target, channel);
+ break;
+
+ default:
+ break;
}
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->waiting_scbs, outscb);
- outscb->state |= SCB_WAITINGQ;
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
}
else
{
- printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
- "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(0, RETURN_1 + base);
+ /*
+ * Send our own WDTR in reply.
+ */
+ switch (bus_width)
+ {
+ case BUS_8_BIT:
+ scratch &= 0x7F;
+ break;
+
+ case BUS_32_BIT:
+ case BUS_16_BIT:
+ if (p->bus_type == AIC_WIDE)
+ {
+ printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+ "bit transfers.\n", p->host_no, target, channel);
+ bus_width = BUS_16_BIT;
+ scratch |= WIDEXFER;
+ }
+ else
+ {
+ bus_width = BUS_8_BIT;
+ scratch &= 0x7F; /* XXX - FreeBSD doesn't do this. */
+ }
+ break;
+
+ default:
+ break;
+ }
+ aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+ outb(SEND_MSG, p->base + RETURN_1);
}
- }
- }
- else
- {
- printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
- "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(0, SCB_CONTROL + base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(0, RETURN_1 + base);
- }
- break;
-
- case BAD_PHASE:
- panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
- break;
-
- case SEND_REJECT:
- rej_byte = inb(REJBYTE + base);
- if ((rej_byte & 0xF0) == 0x20)
- {
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- printk(KERN_WARNING "scsi%d: Tagged message received without identify."
- "Disabling tagged commands for target %d channel %c.\n",
- p->host_no, scsi_id, channel);
- scb->cmd->device->tagged_supported = 0;
- scb->cmd->device->tagged_queue = 0;
- }
- else
- {
- printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
- "from target %d channel %c.\n",
- p->host_no, rej_byte, scsi_id, channel);
- }
- break;
+ p->needwdtr &= ~target_mask;
+ outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
+ outb(scratch, p->base + SCSIRATE);
+ break;
+ } /* case MSG_EXT_WDTR */
- case NO_IDENT:
- panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
- "message. SAVED_TCL 0x%x.\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- break;
+ default:
+ /*
+ * Unknown extended message - reject it.
+ */
+ outb(SEND_REJ, p->base + RETURN_1);
+ break;
+ } /* switch (message_code) */
+ } /* case EXTENDED_MSG */
+ break;
- case SDTR_MSG:
- /*
- * Help the sequencer to translate the negotiated
- * transfer rate. Transfer is 1/4 the period
- * in ns as is returned by the sync negotiation
- * message. So, we must multiply by four.
- */
- transfer = (inb(ARG_1 + base) << 2);
- offset = inb(ACCUM + base);
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
+ case REJECT_MSG:
+ {
/*
- * The maximum offset for a wide device is 0x08; for a
- * 8-bit bus device the maximum offset is 0x0F.
+ * What we care about here is if we had an outstanding SDTR
+ * or WDTR message for this target. If we did, this is a
+ * signal that the target is refusing negotiation.
*/
- if (scratch & WIDEXFER)
+ unsigned char targ_scratch;
+ unsigned char scb_index;
+
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+ if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
{
- max_offset = 0x08;
+ /*
+ * note 8bit xfers and clear flag
+ */
+ targ_scratch &= 0x7F;
+ p->needwdtr &= ~target_mask;
+ printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+ "negotiation; using 8 bit transfers.\n",
+ p->host_no, target, channel);
}
else
{
- max_offset = 0x0F;
- }
- aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
- scsi_id, channel);
- /*
- * Preserve the wide transfer flag.
- */
- scratch = rate | (scratch & WIDEXFER);
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- outb(scratch, SCSIRATE + base);
- if ((scratch & 0x0F) == 0)
- {
- /*
- * One of two things happened. Either the device requested
- * asynchronous data transfers, or it requested a synchronous
- * data transfer rate that was so low that asynchronous
- * transfers are faster (not to mention the controller won't
- * support them). In both cases the synchronous data transfer
- * rate and the offset are set to 0 indicating asynchronous
- * transfers.
- *
- * If the device requested an asynchronous transfer, then
- * accept the request. If the device is being forced to
- * asynchronous data transfers and this is the first time
- * we've seen the request, accept the request. If we've
- * already seen the request, then attempt to force
- * asynchronous data transfers by rejecting the message.
- */
- if ((offset == 0) || (p->sdtr_pending & target_mask))
+ if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
{
/*
- * Device requested asynchronous transfers or we're
- * forcing asynchronous transfers for the first time.
+ * note asynch xfers and clear flag
*/
- outb(0, RETURN_1 + base);
+ targ_scratch &= 0xF0;
+ p->needsdtr &= ~target_mask;
+ printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+ "synchronous negotiation; using asynchronous transfers.\n",
+ p->host_no, target, channel);
}
- else
- {
- /*
- * The first time in forcing asynchronous transfers
- * failed, so we try sending a reject message.
- */
- outb(SEND_REJ, RETURN_1 + base);
- }
- }
- else
- {
- /*
- * See if we initiated Sync Negotiation
- */
- if (p->sdtr_pending & target_mask)
- {
- /*
- * Don't send an SDTR back to the target.
- */
- outb(0, RETURN_1 + base);
- }
- else
- {
- /*
- * Send our own SDTR in reply.
- */
- printk("aic7xxx: Sending SDTR!!\n");
- outb(SEND_SDTR, RETURN_1 + base);
- }
- }
- /*
- * Clear the flags.
- */
- p->needsdtr &= ~target_mask;
- p->sdtr_pending &= ~target_mask;
- break;
-
- case WDTR_MSG:
- {
- bus_width = inb(ARG_1 + base);
- printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
- "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
- if (p->wdtr_pending & target_mask)
- {
- /*
- * Don't send an WDTR back to the target, since we asked first.
- */
- outb(0, RETURN_1 + base);
- switch (bus_width)
- {
- case BUS_8_BIT:
- scratch &= 0x7F;
- break;
-
- case BUS_16_BIT:
- printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
- "transfers.\n", p->host_no, scsi_id, channel);
- scratch |= 0x80;
- break;
-
- case BUS_32_BIT:
- outb(SEND_REJ, RETURN_1 + base);
- printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
- "transfers, rejecting...\n", p->host_no, scsi_id, channel);
- break;
- }
- }
- else
- {
- /*
- * Send our own WDTR in reply.
- */
- printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
- switch (bus_width)
- {
- case BUS_8_BIT:
- scratch &= 0x7F;
- break;
-
- case BUS_32_BIT:
- /*
- * Negotiate 16 bits.
- */
- bus_width = BUS_16_BIT;
- /* Yes, we mean to fall thru here. */
-
- case BUS_16_BIT:
- printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
- "transfers.\n", p->host_no, scsi_id, channel);
- scratch |= 0x80;
- break;
- }
- outb(bus_width | SEND_WDTR, RETURN_1 + base);
+ /*
+ * Otherwise, we ignore it.
+ */
}
- p->needwdtr &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- outb(scratch, SCSIRATE + base);
- break;
+ outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ outb(targ_scratch, p->base + SCSIRATE);
}
+ break;
- case REJECT_MSG:
+ case BAD_STATUS:
{
- /*
- * What we care about here is if we had an
- * outstanding SDTR or WDTR message for this
- * target. If we did, this is a signal that
- * the target is refusing negotiation.
+ unsigned char scb_index;
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+
+ /* The sequencer will notify us when a command has an error that
+ * would be of interest to the kernel. This allows us to leave
+ * the sequencer running in the common case of command completes
+ * without error. The sequencer will have DMA'd the SCB back
+ * up to us, so we can reference the drivers SCB array.
*/
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ hscb = scb->hscb;
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
- if (p->wdtr_pending & target_mask)
- {
- /*
- * note 8bit xfers and clear flag
- */
- scratch &= 0x7F;
- p->needwdtr &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
- "negotiation; using 8 bit transfers.\n",
- p->host_no, scsi_id, channel);
- }
- else
- {
- if (p->sdtr_pending & target_mask)
- {
- /*
- * note asynch xfers and clear flag
- */
- scratch &= 0xF0;
- p->needsdtr &= ~target_mask;
- p->sdtr_pending &= ~target_mask;
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
- "synchronous negotiation; using asynchronous transfers.\n",
- p->host_no, scsi_id, channel);
- }
- /*
- * Otherwise, we ignore it.
- */
- }
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- outb(scratch, SCSIRATE + base);
- break;
- }
-
- case BAD_STATUS:
- /* The sequencer will notify us when a command has an error that
- * would be of interest to the kernel. This allows us to leave
- * the sequencerrunning in the common case of command completes
- * without error.
- */
-
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ /*
+ * Set the default return value to 0 indicating not to send
+ * sense. The sense code will change this if needed and this
+ * reduces code duplication.
+ */
+ outb(0, p->base + RETURN_1);
+ if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+ printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+ "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
+ intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
}
else
{
- cmd = scb->cmd;
- scb->target_status = inb(SCB_TARGET_STATUS + base);
- aic7xxx_status(cmd) = scb->target_status;
+ cmd = scb->cmd;
+ hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+ aic7xxx_status(cmd) = hscb->target_status;
- cmd->result |= scb->target_status;
+ cmd->result |= hscb->target_status;
- switch (status_byte(scb->target_status))
- {
- case GOOD:
- printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
- break;
-
- case CHECK_CONDITION:
- if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
- {
- unsigned char tcl;
- unsigned int req_buf; /* must be 32 bits */
+ switch (status_byte(hscb->target_status))
+ {
+ case GOOD:
+ printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
+ "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+ break;
- tcl = scb->target_channel_lun;
+ case CHECK_CONDITION:
+ if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+ {
+ unsigned int addr; /* must be 32 bits */
+ /*
+ * XXX - How do we save the residual (if there is one).
+ */
+ aic7xxx_calculate_residual(p, scb);
+
+ /*
+ * Send a sense command to the requesting target.
+ * XXX - revisit this and get rid of the memcopys.
+ */
+ memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+ sizeof(generic_sense));
+
+ scb->sense_cmd[1] = (cmd->lun << 5);
+ scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+ scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
+ scb->sg_list[0].length = sizeof(cmd->sense_buffer);
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
- /*
- * Send a sense command to the requesting target.
+ /*
+ * XXX - We should allow disconnection, but can't as it
+ * might allow overlapped tagged commands.
*/
- cmd->flags |= WAS_SENSE;
- memcpy((void *) scb->sense_cmd, (void *) generic_sense,
- sizeof(generic_sense));
-
- scb->sense_cmd[1] = (cmd->lun << 5);
- scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
- scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
- scb->sg_list[0].length = sizeof(cmd->sense_buffer);
- req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
- cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
- scb->control = scb->control & DISCENB;
- scb->target_channel_lun = tcl;
- addr = VIRT_TO_BUS(scb->sense_cmd);
- scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
- memcpy(scb->SCSI_cmd_pointer, &addr,
- sizeof(scb->SCSI_cmd_pointer));
- scb->SG_segment_count = 1;
- memcpy(scb->SG_list_pointer, &req_buf,
- sizeof(scb->SG_list_pointer));
- scb->data_count = scb->sg_list[0].length;
- memcpy(scb->data_pointer, &(scb->sg_list[0].address),
- sizeof(scb->data_pointer));
-
- aic7xxx_putscb(p, scb);
+ /* hscb->control &= DISCENB; */
+ hscb->control = 0;
+ hscb->target_status = 0;
+ hscb->SG_segment_count = 1;
+
+ addr = VIRT_TO_BUS(&scb->sg_list[0]);
+ memcpy(&hscb->SG_list_pointer, &addr,
+ sizeof(hscb->SG_list_pointer));
+
+ memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
+ sizeof(hscb->data_pointer));
+ /* Maintain SCB_LINKED_NEXT */
+ hscb->data_count &= 0xFF000000;
+ hscb->data_count |= scb->sg_list[0].length;
+
+ addr = VIRT_TO_BUS(scb->sense_cmd);
+ memcpy(&hscb->SCSI_cmd_pointer, &addr,
+ sizeof(hscb->SCSI_cmd_pointer));
+ hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+
+ scb->sg_count = hscb->SG_segment_count;
+ scb->flags |= SCB_SENSE;
/*
- * Ensure that the target is "BUSY" so we don't get overlapping
- * commands if we happen to be doing tagged I/O.
+ * Ensure the target is busy since this will be an
+ * an untagged request.
*/
- aic7xxx_busy_target(scsi_id, channel, base);
+ aic7xxx_busy_target(p, target, channel, hscb->tag);
+ outb(SEND_SENSE, p->base + RETURN_1);
+ } /* first time sense, no errors */
+ else
+ {
+ if (aic7xxx_error(cmd) == 0)
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ }
+ break;
- aic7xxx_add_waiting_scb(base, scb);
- outb(SEND_SENSE, RETURN_1 + base);
- } /* first time sense, no errors */
- else
+ case QUEUE_FULL:
+#ifdef NOT_YET
+ if (scb->hscb->control & TAG_ENB)
{
- cmd->flags &= ~ASKED_FOR_SENSE;
- if (aic7xxx_error(cmd) == 0)
- {
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
+ if (cmd->device->queue_depth > 2)
+ {
+ cmd->device->queue_depth--; /* Not correct */
+ printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
+ "reduced to %d\n", p->host_no,
+ TC_OF_SCB(scb), cmd->device->queue_depth);
+ }
+ /*
+ * XXX - Requeue this unconditionally?
+ */
+
+ /*
+ * We'd like to be able to give the SCB some more time
+ * (untimeout, then timeout).
+ */
+ break;
}
- break;
-
- case BUSY:
- printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
- p->host_no, scb->target_channel_lun);
- if (!aic7xxx_error(cmd))
- {
- /* The error code here used to be DID_BUS_BUSY,
- * but after extensive testing, it has been determined
- * that a DID_BUS_BUSY return is a waste of time. If
- * the problem is something that will go away, then it
- * will, if it isn't, then you don't want the endless
- * looping that you get with a DID_BUS_BUSY. Better
- * to be on the safe side and specify an error condition
- * that will eventually lead to a reset or abort of some
- * sort instead of an endless loop.
- */
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
- break;
-
- case QUEUE_FULL:
- printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
- scb->state |= SCB_ASSIGNEDQ;
- scbq_insert_tail(&p->assigned_scbs, scb);
- break;
-
- default:
- printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
- p->host_no, scb->target_status);
- if (!aic7xxx_error(cmd))
- {
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
- break;
- } /* end switch */
+#endif
+ printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+ "queue depth %d, active %d\n", p->host_no,
+ TC_OF_SCB(scb), cmd->device->queue_depth,
+ p->device_status[TARGET_INDEX(cmd)].active_cmds);
+
+ /* Else treat this as if it was a BUSY condition. */
+ scb->hscb->target_status = (BUSY << 1) |
+ (scb->hscb->target_status & 0x01);
+ /* Fall through to the BUSY case. */
+
+ case BUSY:
+ printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
+ p->host_no, TC_OF_SCB(scb));
+ if (!aic7xxx_error(cmd))
+ {
+ /*
+ * The mid-level SCSI code should be fixed to
+ * retry the command at a later time instead of
+ * trying right away.
+ */
+ aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
+ }
+ udelay(1000); /* A small pause (1ms) to help the drive */
+ break;
+
+ default:
+ printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+ "status 0x%x.\n", p->host_no,
+ TC_OF_SCB(scb), scb->hscb->target_status);
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+ } /* end switch */
} /* end else of */
- break;
+ }
+ break;
- case RESIDUAL:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
- }
- else
- {
- cmd = scb->cmd;
- /*
- * Don't destroy valid residual information with
- * residual coming from a check sense operation.
- */
- if (!(cmd->flags & WAS_SENSE))
- {
- /*
- * We had an underflow. At this time, there's only
- * one other driver that bothers to check for this,
- * and cmd->underflow seems to be set rather half-
- * heartedly in the higher-level SCSI code.
- */
- actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
-
- actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
- (inb(SCB_RESID_DCNT1 + base) << 8) |
- inb(SCB_RESID_DCNT0 + base);
-
- if (actual < cmd->underflow)
- {
- printk(KERN_WARNING "scsi%d: Target %d underflow - "
- "Wanted at least %u, got %u, residual SG count %d.\n",
- p->host_no, cmd->target, cmd->underflow, actual,
- inb(SCB_RESID_SGCNT + base));
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- aic7xxx_status(cmd) = scb->target_status;
- }
- }
- }
- break;
+ case AWAITING_MSG:
+ {
+ unsigned char scb_index;
+ unsigned char message_offset;
- case ABORT_TAG:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
- }
- else
- {
- cmd = scb->cmd;
- /*
- * We didn't receive a valid tag back from the target
- * on a reconnect.
- */
- printk("scsi%d: Invalid tag received on target %d, channel %c, "
- "lun %d - Sending ABORT_TAG.\n", p->host_no,
- scsi_id, channel, cmd->lun & 0x07);
-
- cmd->result = (DID_RETRY_COMMAND << 16);
- aic7xxx_done(p, scb);
- }
- break;
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
- case AWAITING_MSG:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ /*
+ * This SCB had a MK_MESSAGE set in its control byte informing
+ * the sequencer that we wanted to send a special message to
+ * this target.
+ */
+ message_offset = inb(p->base + MSG_LEN);
+ if (scb->flags & SCB_DEVICE_RESET)
{
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+ outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+ outb(1, p->base + MSG_LEN);
+ printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
+ p->host_no, TC_OF_SCB(scb));
}
- else
+ else if (scb->flags & SCB_ABORT)
+ {
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+ }
+ else
+ {
+ outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+ }
+ outb(message_offset + 1, p->base + MSG_LEN);
+ printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
+ p->host_no, TC_OF_SCB(scb));
+ }
+ else if (scb->flags & SCB_MSGOUT_WDTR)
{
- /*
- * This SCB had a zero length command, informing the sequencer
- * that we wanted to send a special message to this target.
- * We only do this for BUS_DEVICE_RESET messages currently.
- */
- if (scb->state & SCB_DEVICE_RESET)
- {
-#ifdef AIC7XXX_DEBUG_ABORT
- printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
- scsi_id);
-#endif
- outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
- outb(1, MSG_LEN + base);
- }
- else
- {
- panic("scsi%d: AWAITING_SCB for an SCB that does "
- "not have a waiting message.\n", p->host_no);
- }
- }
- break;
-
- case IMMEDDONE:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
- scsi_id, scb_index, scb->state);
-#endif
- if (scb->state & SCB_DEVICE_RESET)
+ aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
{
- int found;
+ unsigned char target_scratch;
+ unsigned short ultra_enable;
+ int i, sxfr;
/*
- * Go back to async/narrow transfers and renegotiate.
+ * Pull the user defined setting from scratch RAM.
*/
- aic7xxx_unbusy_target(scsi_id, channel, base);
- p->needsdtr |= (p->needsdtr_copy & target_mask);
- p->needwdtr |= (p->needwdtr_copy & target_mask);
- p->sdtr_pending &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
- scratch &= SXFR;
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- found = aic7xxx_reset_device(p, (int) scsi_id, channel);
- printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
- "aborted.\n", p->host_no, found);
- /* Indicate that we want to call aic7xxx_done_aborted_scbs() */
- run_aborted_queue = TRUE;
+ target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ sxfr = target_scratch & SXFR;
+ ultra_enable = inb(p->base + ULTRA_ENB) |
+ (inb(p->base + ULTRA_ENB + 1) << 8);
+ if (ultra_enable & target_mask)
+ {
+ sxfr |= 0x100;
+ }
+ for (i = 0; i < num_aic7xxx_syncrates; i++)
+ {
+ if (sxfr == aic7xxx_syncrates[i].rate)
+ break;
+ }
+ aic7xxx_construct_sdtr(p, message_offset,
+ aic7xxx_syncrates[i].period,
+ target_scratch & WIDEXFER ?
+ MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
}
- else
+ else
{
- panic("scsi%d: Immediate complete for unknown operation.\n",
- p->host_no);
- }
- break;
+ panic("aic7xxx: AWAITING_MSG for an SCB that does "
+ "not have a waiting message.");
+ }
+ }
+ break;
- case DATA_OVERRUN:
+ case DATA_OVERRUN:
{
- unsigned int overrun;
-
- scb = (p->scb_array[inb(base + SCB_TAG)]);
- overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
- (inb(base + STCNT2) << 16);
- overrun =0x00FFFFFF - overrun;
- printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
- "a retry.\n", p->host_no, overrun);
- aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
- break;
+ unsigned char scb_index = inb(p->base + SCB_TAG);
+ unsigned char lastphase = inb(p->base + LASTPHASE);
+ unsigned int i, overrun;
+
+ scb = (p->scb_data->scb_array[scb_index]);
+ overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
+ (inb(p->base + STCNT + 2) << 16);
+ overrun = 0x00FFFFFF - overrun;
+ printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
+ "in %s phase, tag %d; forcing a retry.\n",
+ p->host_no, TC_OF_SCB(scb), overrun,
+ lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+ scb->hscb->tag);
+ printk(KERN_WARNING "%s seen Data Phase. Length = %d, NumSGs = %d.\n",
+ inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ aic7xxx_length(scb->cmd, 0), scb->sg_count);
+ for (i = 0; i < scb->sg_count; i++)
+ {
+ printk(KERN_INFO " sg[%d] - Addr 0x%x : Length %d\n",
+ i, scb->sg_list[i].address, scb->sg_list[i].length);
+ }
+ /*
+ * XXX - What do we really want to do on an overrun? The
+ * mid-level SCSI code should handle this, but for now,
+ * we'll just indicate that the command should retried.
+ */
+ aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
}
+ break;
-#if AIC7XXX_NOT_YET
- /* XXX Fill these in later */
- case MESG_BUFFER_BUSY:
- break;
- case MSGIN_PHASEMIS:
- break;
-#endif
+/* #if AIC7XXX_NOT_YET */
+ /* XXX Fill these in later */
+ case MSG_BUFFER_BUSY:
+ printk("aic7xxx: Message buffer busy.\n");
+ break;
+ case MSGIN_PHASEMIS:
+ printk("aic7xxx: Message-in phasemis.\n");
+ break;
+/*#endif */
+
+ case ABORT_CMDCMPLT:
+ /* This interrupt serves to pause the sequencer until we can clean
+ * up the QOUTFIFO allowing us to handle any abort SCBs that may
+ * completed yet still have an SCB in the QINFIFO or waiting for
+ * selection queue. By the time we get here, we should have
+ * already cleaned up the queues, so all we need to do is unpause
+ * the sequencer.
+ */
+ break;
+
+ default: /* unknown */
+ printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+ p->host_no, intstat, inb(p->base + SCSISIGI));
+ break;
+ }
+
+ /*
+ * Clear the sequencer interrupt and unpause the sequencer.
+ */
+ outb(CLRSEQINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+}
- default: /* unknown */
- printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
- p->host_no, intstat, inb(SCSISIGI + base));
- break;
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_scsiint
+ *
+ * Description:
+ * Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ unsigned char scb_index;
+ unsigned char status;
+ struct aic7xxx_scb *scb;
+
+ scb_index = inb(p->base + SCB_TAG);
+ status = inb(p->base + SSTAT1);
+
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
}
+ }
+ else
+ {
+ scb = NULL;
+ }
+
+ if ((status & SCSIRSTI) != 0)
+ {
+ char channel;
+ channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+
+ printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
+ p->host_no, channel);
/*
- * Clear the sequencer interrupt and unpause the sequencer.
+ * Go through and abort all commands for the channel, but do not
+ * reset the channel again.
*/
- outb(CLRSEQINT, CLRINT + base);
- UNPAUSE_SEQUENCER(p);
+ aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+ scb = NULL;
}
-
- if (intstat & SCSIINT)
+ else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
{
- int status = inb(SSTAT1 + base);
- scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
- channel = 'A';
- if (inb(SBLKCTL + base) & SELBUSB)
- {
- channel = 'B';
- }
+ /*
+ * First look at what phase we were last in. If it's message-out,
+ * chances are pretty good that the bus free was in response to
+ * one of our abort requests.
+ */
+ unsigned char lastphase = inb(p->base + LASTPHASE);
+ unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
+ char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+ int printerror = TRUE;
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (status & SCSIRSTI)
- {
- PAUSE_SEQUENCER(p);
- printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
- p->host_no, channel);
- /*
- * Go through and abort all commands for the channel, but do not
- * reset the channel again.
- */
- aic7xxx_reset_channel(p, channel, FALSE);
- run_aborted_queue = TRUE;
- }
- else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ outb(0, p->base + SCSISEQ);
+ if (lastphase == P_MESGOUT)
{
- printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
- /*
- * Turn off the interrupt and set status to zero, so that it
- * falls through the rest of the SCSIINT code.
- */
- outb(status, CLRSINT1 + base);
- UNPAUSE_SEQUENCER(p);
- outb(CLRSCSIINT, CLRINT + base);
- scb = NULL;
+ unsigned char sindex;
+ unsigned char message;
+
+ sindex = inb(p->base + SINDEX);
+ message = inb(p->base + sindex - 1);
+
+ if (message == MSG_ABORT)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
+ aic7xxx_run_done_queue(p, /* complete */ TRUE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_ABORT_TAG)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
+ aic7xxx_run_done_queue(p, /* complete */ TRUE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_BUS_DEV_RESET)
+ {
+ aic7xxx_handle_device_reset(p, target, channel);
+ scb = NULL;
+ printerror = 0;
+ }
}
- else if (status & SCSIPERR)
+ if (printerror != 0)
{
- char *phase;
- unsigned char mesg_out = MSG_NOP;
- unsigned char lastphase = inb(LASTPHASE + base);
+ if (scb != NULL)
+ {
+ unsigned char tag;
- cmd = scb->cmd;
- switch (lastphase)
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ tag = scb->hscb->tag;
+ }
+ else
+ {
+ tag = SCB_LIST_NULL;
+ }
+ aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+ }
+ else
{
- case P_DATAOUT:
- phase = "Data-Out";
- break;
- case P_DATAIN:
- phase = "Data-In";
- mesg_out = MSG_INITIATOR_DET_ERROR;
- break;
- case P_COMMAND:
- phase = "Command";
- break;
- case P_MESGOUT:
- phase = "Message-Out";
- break;
- case P_STATUS:
- phase = "Status";
- mesg_out = MSG_INITIATOR_DET_ERROR;
- break;
- case P_MESGIN:
- phase = "Message-In";
- mesg_out = MSG_MSG_PARITY_ERROR;
- break;
- default:
- phase = "unknown";
- break;
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
}
+ printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
+ "SEQADDR = 0x%x\n", p->host_no, lastphase,
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ }
+ outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+ outb(CLRBUSFREE, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ restart_sequencer(p);
+ }
+ else if ((status & SELTO) != 0)
+ {
+ unsigned char scbptr;
+ unsigned char nextscb;
+ Scsi_Cmnd *cmd;
- /*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
- */
- printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
- "channel %d, lun %d.\n", p->host_no, phase,
- cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+ scbptr = inb(p->base + WAITING_SCBH);
+ outb(scbptr, p->base + SCBPTR);
+ scb_index = inb(p->base + SCB_TAG);
- /*
- * We've set the hardware to assert ATN if we get a parity
- * error on "in" phases, so all we need to do is stuff the
- * message buffer with the appropriate message. In phases
- * have set mesg_out to something other than MSG_NOP.
- */
- if (mesg_out != MSG_NOP)
- {
- outb(mesg_out, MSG0 + base);
- outb(1, MSG_LEN + base);
- cmd->result = DID_PARITY << 16;
- }
- else
+ scb = NULL;
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
{
- /*
- * Should we allow the target to make this decision for us?
- */
- cmd->result = DID_RETRY_COMMAND << 16;
+ scb = NULL;
}
- aic7xxx_done(p, scb);
}
- else if (status & SELTO)
+ if (scb == NULL)
{
- unsigned char waiting;
-
+ printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
+ p->host_no, scb_index);
+ printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+ "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
+ inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+ inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+ }
+ else
+ {
+ /*
+ * XXX - If we queued an abort tag, go clean up the disconnected list.
+ */
cmd = scb->cmd;
-
cmd->result = (DID_TIME_OUT << 16);
+
/*
* Clear an pending messages for the timed out
* target and mark the target as free.
*/
- ha_flags = inb(FLAGS + base);
- outb(0, MSG_LEN + base);
- aic7xxx_unbusy_target(scsi_id, channel, base);
+ outb(0, p->base + MSG_LEN);
+ aic7xxx_index_busy_target(p, cmd->target,
+ cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
+ outb(0, p->base + SCB_CONTROL);
+
/*
- * Stop the selection.
+ * Shift the waiting for selection queue forward
*/
- outb(0, SCSISEQ + base);
- outb(0, SCB_CONTROL + base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
+ nextscb = inb(p->base + SCB_NEXT);
+ outb(nextscb, p->base + WAITING_SCBH);
/*
- * Shift the waiting for selection queue forward
+ * Put this SCB back on the free list.
*/
- waiting = inb(WAITING_SCBH + base);
- outb(waiting, SCBPTR + base);
- waiting = inb(SCB_NEXT + base);
- outb(waiting, WAITING_SCBH + base);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ /*
+ * Stop the selection.
+ */
+ outb(0, p->base + SCSISEQ);
+ outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ restart_sequencer(p);
+ }
+ else if (scb == NULL)
+ {
+ printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+ "during scsiint 0x%x scb(%d)\n"
+ " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+ p->host_no, status, scb_index, inb(p->base + SIMODE0),
+ inb(p->base + SIMODE1), inb(p->base + SSTAT0),
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ /*
+ * Turn off the interrupt and set status to zero, so that it
+ * falls through the rest of the SCSIINT code.
+ */
+ outb(status, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ else if (status & SCSIPERR)
+ {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ */
+ char *phase;
+ Scsi_Cmnd *cmd;
+ unsigned char mesg_out = MSG_NOOP;
+ unsigned char lastphase = inb(p->base + LASTPHASE);
- RESTART_SEQUENCER(p);
- aic7xxx_done(p, scb);
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
+ p->host_no, TC_OF_SCB(scb), phase);
+
+ /*
+ * We've set the hardware to assert ATN if we get a parity
+ * error on "in" phases, so all we need to do is stuff the
+ * message buffer with the appropriate message. "In" phases
+ * have set mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP)
+ {
+ outb(mesg_out, p->base + MSG_OUT);
+ outb(1, p->base + MSG_LEN);
+ scb = NULL;
}
- else if (!(status & BUSFREE))
+ else
{
/*
- * We don't know what's going on. Turn off the
- * interrupt source and try to continue.
+ * Should we allow the target to make this decision for us?
*/
- printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
- outb(status, CLRSINT1 + base);
- UNPAUSE_SEQUENCER(p);
- outb(CLRSCSIINT, CLRINT + base);
+ cmd->result = DID_RETRY_COMMAND << 16;
+ }
+ outb(CLRSCSIPERR, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ }
+ else
+ {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
+ outb(status, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ if (scb != NULL)
+ {
+ aic7xxx_done(p, scb);
+ aic7xxx_done_cmds_complete(p);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_isr
+ *
+ * Description:
+ * SCSI controller interrupt handler.
+ *
+ * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ * be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aic7xxx_host *p;
+ unsigned char intstat;
+ unsigned long flags;
+
+ p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+
+ /*
+ * Search for the host with a pending interrupt. If we can't find
+ * one, then we've encountered a spurious interrupt.
+ */
+ while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
+ {
+ if (p->next == NULL)
+ {
+ p = NULL;
+ }
+ else
+ {
+ p = (struct aic7xxx_host *) p->next->hostdata;
+ }
+ }
+
+ if (p == NULL)
+ return;
+
+ /*
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
+ */
+ intstat = inb(p->base + INTSTAT);
+
+ /*
+ * Keep track of interrupts for /proc/scsi
+ */
+ p->isr_count++;
+
+ if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+ {
+ /*
+ * We must only have one card at this IRQ and it must have been
+ * added to the board data before the spurious interrupt occurred.
+ * It is sufficient that we check isr_count and not the spurious
+ * interrupt count.
+ */
+ printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
+ if (intstat)
+ {
+ /* Try clearing all interrupts. */
+ outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
}
+ return;
}
- if (run_aborted_queue)
- aic7xxx_done_aborted_scbs(p);
+ if (p->flags & IN_ISR)
+ {
+ printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+ p->host_no);
+ return;
+ }
+
+ /*
+ * Indicate that we're in the interrupt handler.
+ */
+ save_flags(flags);
+ cli();
+ p->flags |= IN_ISR;
if (intstat & CMDCMPLT)
{
- int complete;
+ struct aic7xxx_scb *scb = NULL;
+ Scsi_Cmnd *cmd;
+ unsigned char qoutcnt;
+ unsigned char scb_index;
+ int i, interrupts_cleared = 0;
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
- do {
- complete = inb(QOUTFIFO + base);
+ qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
- scb = (p->scb_array[complete]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
- " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
- "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
- inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
- scb->position);
- outb(CLRCMDINT, CLRINT + base);
- continue;
- }
- cmd = scb->cmd;
- cmd->result |= (aic7xxx_error(cmd) << 16);
- if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+#if 1
+ if (qoutcnt >= p->qfullcount - 1)
+ printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
+ "qoutcnt = %d.\n", qoutcnt);
+#endif
+ while (qoutcnt > 0)
+ {
+ for (i = 0; i < qoutcnt; i++)
{
- /*
- * Got sense information.
- */
- cmd->flags &= ASKED_FOR_SENSE;
+ scb_index = inb(p->base + QOUTFIFO);
+ scb = p->scb_data->scb_array[scb_index];
+ if (scb == NULL)
+ {
+ printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
+ "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+ inb(p->base + QOUTCNT), inb(p->base + QINCNT));
+ continue;
+ }
+ else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
+ "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+ p->host_no, scb_index, inb(p->base + QOUTCNT),
+ inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
+ continue;
+ }
+ cmd = scb->cmd;
+ if (scb->hscb->residual_SG_segment_count != 0)
+ {
+ aic7xxx_calculate_residual(p, scb);
+ }
+ if ((scb->flags & SCB_QUEUED_ABORT) != 0)
+ {
+ /*
+ * Have to clean up any possible entries in the
+ * waiting queue and the QINFIFO.
+ */
+ int target;
+ char channel;
+ int lun;
+ unsigned char tag;
+
+ tag = SCB_LIST_NULL;
+ target = cmd->target;
+ lun = cmd->lun;
+ channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ if (scb->hscb->control & TAG_ENB)
+ {
+ tag = scb->hscb->tag;
+ }
+ aic7xxx_reset_device(p, target, channel, lun, tag);
+ /*
+ * Run the done queue, but don't complete the commands; we
+ * do this once at the end of the loop.
+ */
+ aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+ }
+ cmd->result |= (aic7xxx_error(cmd) << 16);
+ p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+ aic7xxx_done(p, scb);
}
- p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-
/*
* Clear interrupt status before checking the output queue again.
* This eliminates a race condition whereby a command could
@@ -3243,56 +3941,152 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
* so notification of the command being complete never made it
* back up to the kernel.
*/
- outb(CLRCMDINT, CLRINT + base);
- aic7xxx_done(p, scb);
+ outb(CLRCMDINT, p->base + CLRINT);
+ interrupts_cleared++;
+ qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+ }
-#ifdef AIC7XXX_PROC_STATS
- /*
- * XXX: we should actually know how much actually transferred
- * XXX: for each command, but apparently that's too difficult.
- */
- actual = aic7xxx_length(cmd, 0);
- if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+ if (interrupts_cleared == 0)
+ {
+ outb(CLRCMDINT, p->base + CLRINT);
+ }
+
+ aic7xxx_done_cmds_complete(p);
+ }
+
+ if (intstat & BRKADRINT)
+ {
+ int i;
+ unsigned char errno = inb(p->base + ERROR);
+
+ printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+ for (i = 0; i < NUMBER(hard_error); i++)
+ {
+ if (errno & hard_error[i].errno)
{
- struct aic7xxx_xferstats *sp;
- long *ptr;
- int x;
+ printk(KERN_ERR " %s\n", hard_error[i].errmesg);
+ }
+ }
+ printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
+ inb(p->base + ERROR),
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+ }
+
+ if (intstat & SEQINT)
+ {
+ aic7xxx_handle_seqint(p, intstat);
+ }
- sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
- sp->xfers++;
+ if (intstat & SCSIINT)
+ {
+ aic7xxx_handle_scsiint(p, intstat);
+ }
- if (cmd->request.cmd == WRITE)
+ if (p->waiting_scbs.head != NULL)
+ {
+ aic7xxx_run_waiting_queues(p);
+ }
+
+ p->flags &= ~IN_ISR;
+ restore_flags(flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_device_queue_depth
+ *
+ * Description:
+ * Determines the queue depth for a given device. There are two ways
+ * a queue depth can be obtained for a tagged queueing device. One
+ * way is the default queue depth which is determined by whether
+ * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used
+ * as the default queue depth. Otherwise, we use either 4 or 8 as the
+ * default queue depth (dependent on the number of hardware SCBs).
+ * The other way we determine queue depth is through the use of the
+ * aic7xxx_tag_info array which is enabled by defining
+ * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized
+ * with queue depths for individual devices. It also allows tagged
+ * queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+ int default_depth = 2;
+
+ device->queue_depth = default_depth;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+ if (device->tagged_supported)
+ {
+ unsigned short target_mask;
+ int tag_enabled = TRUE;
+
+ target_mask = (1 << (device->id | (device->channel << 3)));
+
+#ifdef AIC7XXX_CMDS_PER_LUN
+ default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+ if (p->scb_data->maxhscbs <= 4)
+ {
+ default_depth = 4; /* Not many SCBs to work with. */
+ }
+ else
+ {
+ default_depth = 8;
+ }
+#endif
+
+ if (!(p->discenable & target_mask))
+ {
+ printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+ "enable tagged queueing.\n",
+ p->host_no, device->id, device->channel);
+ }
+ else
+ {
+#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+ device->queue_depth = default_depth;
+#else
+ if (p->instance > NUMBER(aic7xxx_tag_info))
+ {
+ device->queue_depth = default_depth;
+ }
+ else
+ {
+ unsigned char tindex;
+
+ tindex = device->id | (device->channel << 3);
+ if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
{
- sp->w_total++;
- sp->w_total512 += (actual >> 9);
- ptr = sp->w_bins;
+ tag_enabled = FALSE;
+ device->queue_depth = 2; /* Tagged queueing is disabled. */
}
- else
+ else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
{
- sp->r_total++;
- sp->r_total512 += (actual >> 9);
- ptr = sp->r_bins;
+ device->queue_depth = default_depth;
}
- for (x = 9; x <= 17; x++)
+ else
{
- if (actual < (1 << x))
- {
- ptr[x - 9]++;
- break;
- }
+ device->queue_depth =
+ aic7xxx_tag_info[p->instance].tag_commands[tindex];
}
- if (x > 17)
+ }
+#endif
+ if ((device->tagged_queue == 0) && tag_enabled)
+ {
+ if (aic7xxx_verbose)
{
- ptr[x - 9]++;
+ printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+ "queue depth %d.\n", p->host_no,
+ device->id, device->channel, device->queue_depth);
}
+ device->tagged_queue = 1;
+ device->current_tag = SCB_LIST_NULL;
}
-#endif /* AIC7XXX_PROC_STATS */
-
- } while (inb(QOUTCNT + base) & p->qcntmask);
+ }
}
- aic7xxx_done_cmds_complete(p);
- p->flags &= ~IN_ISR;
- aic7xxx_run_waiting_queues(p);
+#endif
}
/*+F*************************************************************************
@@ -3307,59 +4101,18 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
* algorithm for determining the queue depth based on the maximum
* SCBs for the controller.
*-F*************************************************************************/
-static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
Scsi_Device *scsi_devs)
{
- Scsi_Device *device = scsi_devs;
- int tq_depth = 2;
+ Scsi_Device *device;
struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
-#ifdef AIC7XXX_CMDS_PER_LUN
- tq_depth = AIC7XXX_CMDS_PER_LUN;
-#else
- {
- if (p->maxhscbs <= 4)
- {
- tq_depth = 4; /* Not many SCBs to work with. */
- }
- else
- {
- tq_depth = 8;
- }
- }
-#endif
-
for (device = scsi_devs; device != NULL; device = device->next)
{
if (device->host == host)
{
- device->queue_depth = 2;
-#ifdef AIC7XXX_TAGGED_QUEUEING
- if (device->tagged_supported)
- {
- unsigned short target_mask = (1 << device->id) | device->channel;
-
- if (!(p->discenable & target_mask))
- {
- printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable "
- "tagged queueing for target %d, channel %d, LUN %d.\n",
- host->host_no, device->id, device->channel, device->lun);
- }
- else
- {
- device->queue_depth = tq_depth;
- if (device->tagged_queue == 0)
- {
- printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, "
- "channel %d, LUN %d, queue depth %d.\n", host->host_no,
- device->id, device->channel, device->lun,
- device->queue_depth);
- device->tagged_queue = 1;
- device->current_tag = SCB_LIST_NULL;
- }
- }
- }
-#endif
+ aic7xxx_device_queue_depth(p, device);
}
}
}
@@ -3386,7 +4139,7 @@ static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
* The fourth byte's lowest bit seems to be an enabled/disabled
* flag (rest of the bits are reserved?).
*-F*************************************************************************/
-static aha_type
+static aha_chip_type
aic7xxx_probe(int slot, int base, aha_status_type *bios)
{
int i;
@@ -3395,7 +4148,7 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
static struct {
int n;
unsigned char signature[sizeof(buf)];
- aha_type type;
+ aha_chip_type type;
int bios_disabled;
} AIC7xxx[] = {
{ 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
@@ -3434,7 +4187,8 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
return (AIC7xxx[i].type);
}
- printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+ printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+ "disabled at slot %d, ignored.\n", slot);
}
}
@@ -3461,10 +4215,9 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
* useful in that it gives us an 800 nsec timer. After a read from the
* SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
* later.
- *
*-F*************************************************************************/
static int
-read_2840_seeprom(int base, struct seeprom_config *sc)
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
{
int i = 0, k = 0;
unsigned char temp;
@@ -3477,11 +4230,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \
+ while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \
{ \
; /* Do nothing */ \
} \
- (void) inb(SEECTL_2840 + base);
+ (void) inb(p->base + SEECTL_2840);
/*
* Read the first 32 registers of the seeprom. For the 2840,
@@ -3494,8 +4247,8 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
/*
* Send chip select for one clock cycle.
*/
- outb(CK_2840 | CS_2840, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
/*
* Now we're ready to send the read command followed by the
@@ -3504,11 +4257,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
for (i = 0; i < seeprom_read.len; i++)
{
temp = CS_2840 | seeprom_read.bits[i];
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
/*
* Send the 6 bit address (MSB first, LSB last).
@@ -3518,11 +4271,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
temp = k;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = CS_2840 | temp;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
/*
@@ -3534,12 +4287,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
for (i = 0; i <= 16; i++)
{
temp = CS_2840;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
/*
* The serial EEPROM has a checksum in the last word. Keep a
@@ -3555,12 +4308,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
/*
* Reset the chip select for the next command cycle.
*/
- outb(0, SEECTL_2840 + base);
- CLOCK_PULSE(base);
- outb(CK_2840, SEECTL_2840 + base);
- CLOCK_PULSE(base);
- outb(0, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(0, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
+ outb(CK_2840, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
+ outb(0, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
#if 0
@@ -3589,6 +4342,53 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
/*+F*************************************************************************
* Function:
+ * acquire_seeprom
+ *
+ * Description:
+ * Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+ int wait;
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the 7870 chip reset, there
+ * should be no contention.
+ */
+ outb(SEEMS, p->base + SEECTL);
+ wait = 1000; /* 1000 msec = 1 second */
+ while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+ {
+ wait--;
+ udelay(1000); /* 1 msec */
+ }
+ if ((inb(p->base + SEECTL) & SEERDY) == 0)
+ {
+ outb(0, p->base + SEECTL);
+ return (0);
+ }
+ return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * release_seeprom
+ *
+ * Description:
+ * Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline void
+release_seeprom(struct aic7xxx_host *p)
+{
+ outb(0, p->base + SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
* read_seeprom
*
* Description:
@@ -3626,7 +4426,7 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
* first). The clock cycling from low to high initiates the next data
* bit to be sent from the chip.
*
- * The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
* register. After successful arbitration for the memory port, the
* SEECS bit of the SEECTL register is connected to the chip select.
* The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
@@ -3636,17 +4436,14 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
* to this is when we first request access to the memory port. The
* SEERDY goes high to signify that access has been granted and, for
* this case, has no implied timing.
- *
*-F*************************************************************************/
static int
-read_seeprom(int base, int offset, struct seeprom_config *sc,
- seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
+ unsigned int len, seeprom_chip_type chip)
{
int i = 0, k;
- unsigned long timeout;
unsigned char temp;
unsigned short checksum = 0;
- unsigned short *seeprom = (unsigned short *) sc;
struct seeprom_cmd {
unsigned char len;
unsigned char bits[3];
@@ -3654,43 +4451,33 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(SEECTL + base) & SEERDY) == 0) \
+ while ((inb(p->base + SEECTL) & SEERDY) == 0) \
{ \
; /* Do nothing */ \
}
/*
- * Request access of the memory port. When access is
- * granted, SEERDY will go high. We use a 1 second
- * timeout which should be near 1 second more than
- * is needed. Reason: after the 7870 chip reset, there
- * should be no contention.
+ * Request access of the memory port.
*/
- outb(SEEMS, SEECTL + base);
- timeout = jiffies + 100; /* 1 second timeout */
- while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
- {
- ; /* Do nothing! Wait for access to be granted. */
- }
- if ((inb(SEECTL + base) & SEERDY) == 0)
+ if (acquire_seeprom(p) == 0)
{
- outb(0, SEECTL + base);
return (0);
}
/*
- * Read the first 32 registers of the seeprom. For the 7870,
- * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
- * but only the first 32 are used by Adaptec BIOS. The loop
- * will range from 0 to 31.
+ * Read 'len' registers of the seeprom. For the 7870, the 93C46
+ * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+ * the first 32 are used by Adaptec BIOS. Some adapters use the
+ * 93C56 SEEPROM which is a 2048-bit device. The loop will range
+ * from 0 to 'len' - 1.
*/
- for (k = 0; k < (sizeof(*sc) / 2); k++)
+ for (k = 0; k < len; k++)
{
/*
* Send chip select for one clock cycle.
*/
- outb(SEEMS | SEECK | SEECS, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+ CLOCK_PULSE(p);
/*
* Now we're ready to send the read command followed by the
@@ -3699,25 +4486,25 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
for (i = 0; i < seeprom_read.len; i++)
{
temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
temp = temp ^ SEECK;
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
- * Send the 6 bit address (MSB first, LSB last).
+ * Send the 6 or 8 bit address (MSB first, LSB last).
*/
for (i = ((int) chip - 1); i >= 0; i--)
{
temp = k + offset;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = SEEMS | SEECS | (temp << 1);
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
temp = temp ^ SEECK;
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
@@ -3729,56 +4516,57 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
for (i = 0; i <= 16; i++)
{
temp = SEEMS | SEECS;
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
temp = temp ^ SEECK;
- seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
- * The serial EEPROM has a checksum in the last word. Keep a
- * running checksum for all words read except for the last
- * word. We'll verify the checksum after all words have been
- * read.
+ * The serial EEPROM should have a checksum in the last word.
+ * Keep a running checksum for all words read except for the
+ * last word. We'll verify the checksum after all words have
+ * been read.
*/
- if (k < (sizeof(*sc) / 2) - 1)
+ if (k < (len - 1))
{
- checksum = checksum + seeprom[k];
+ checksum = checksum + scarray[k];
}
/*
* Reset the chip select for the next command cycle.
*/
- outb(SEEMS, SEECTL + base);
- CLOCK_PULSE(base);
- outb(SEEMS | SEECK, SEECTL + base);
- CLOCK_PULSE(base);
- outb(SEEMS, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(SEEMS, p->base + SEECTL);
+ CLOCK_PULSE(p);
+ outb(SEEMS | SEECK, p->base + SEECTL);
+ CLOCK_PULSE(p);
+ outb(SEEMS, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
* Release access to the memory port and the serial EEPROM.
*/
- outb(0, SEECTL + base);
+ release_seeprom(p);
#if 0
- printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+ printk("Computed checksum 0x%x, checksum read 0x%x\n",
+ checksum, scarray[len - 1]);
printk("Serial EEPROM:");
- for (k = 0; k < (sizeof(*sc) / 2); k++)
+ for (k = 0; k < len; k++)
{
if (((k % 8) == 0) && (k != 0))
{
printk("\n ");
}
- printk(" 0x%x", seeprom[k]);
+ printk(" 0x%x", scarray[k]);
}
printk("\n");
#endif
- if (checksum != sc->checksum)
+ if (checksum != scarray[len - 1])
{
return (0);
}
@@ -3789,563 +4577,452 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
/*+F*************************************************************************
* Function:
- * detect_maxscb
+ * write_brdctl
*
* Description:
- * Detects the maximum number of SCBs for the controller and returns
- * the count and a mask in config (config->maxscbs, config->qcntmask).
+ * Writes a value to the BRDCTL register.
*-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host_config *config)
+static inline void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
{
- unsigned char sblkctl_reg;
- int base, i;
-
-#ifdef AIC7XXX_PAGE_ENABLE
- config->flags |= PAGE_ENABLED;
-#endif
- base = config->base;
- switch (config->type)
- {
- case AIC_7770:
- case AIC_7771:
- case AIC_284x:
- /*
- * Check for Rev C or E boards. Rev E boards can supposedly have
- * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
- * It's still not clear extactly what is different about the Rev E
- * boards, but we think it allows 8 bit entries in the QOUTFIFO to
- * support "paging" SCBs (more than 4 commands can be active at once).
- *
- * The Rev E boards have a read/write autoflush bit in the
- * SBLKCTL register, while in the Rev C boards it is read only.
- */
- sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
- outb(sblkctl_reg, SBLKCTL + base);
- if (inb(SBLKCTL + base) == sblkctl_reg)
- {
- /*
- * We detected a Rev E board, we allow paging on this board.
- */
- printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n",
- board_names[config->type]);
- outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
- }
- else
- {
- /* Do not allow paging. */
- config->flags &= ~PAGE_ENABLED;
- printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n",
- board_names[config->type]);
- }
- break;
-
- default:
- break;
- }
-
- /*
- * Walk the SCBs to determine how many there are.
- */
- i = 1;
- outb(0, SCBPTR + base);
- outb(0, SCBARRAY + base);
-
- while (i < AIC7XXX_MAXSCB)
- {
- outb(i, SCBPTR + base);
- outb(i, SCBARRAY + base);
- if (inb(SCBARRAY + base) != i)
- break;
- outb(0, SCBPTR + base);
- if (inb(SCBARRAY + base) != 0)
- break;
-
- outb(i, SCBPTR + base); /* Clear the control byte. */
- outb(0, SCBARRAY + base);
-
- config->qcntmask |= i; /* Update the count mask. */
- i++;
- }
- outb(i, SCBPTR + base); /* Ensure we clear the control bytes. */
- outb(0, SCBARRAY + base);
- outb(0, SCBPTR + base);
- outb(0, SCBARRAY + base);
-
- config->maxhscbs = i;
- config->qcntmask |= i;
- if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB))
- {
- config->maxscbs = AIC7XXX_MAXSCB;
- }
- else
- {
- config->flags &= ~PAGE_ENABLED; /* Disable paging if we have 255 SCBs!. */
- config->maxscbs = config->maxhscbs;
- }
-
- printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs);
- if (config->flags & PAGE_ENABLED)
- printk(", %d page-enabled SCBs.\n", config->maxscbs);
- else
- printk(", paging not enabled.\n");
-
+ unsigned char brdctl;
+
+ brdctl = BRDCS | BRDSTB;
+ outb(brdctl, p->base + BRDCTL);
+ brdctl |= value;
+ outb(brdctl, p->base + BRDCTL);
+ brdctl &= ~BRDSTB;
+ outb(brdctl, p->base + BRDCTL);
+ brdctl &= ~BRDCS;
+ outb(brdctl, p->base + BRDCTL);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_register
+ * read_brdctl
*
* Description:
- * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ * Reads the BRDCTL register.
*-F*************************************************************************/
-static int
-aic7xxx_register(Scsi_Host_Template *template,
- struct aic7xxx_host_config *config)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
{
- int i;
- unsigned char sblkctl, flags = 0;
- int max_targets;
- int found = 1;
- unsigned int sram, base;
- unsigned char target_settings;
- unsigned char scsi_conf, host_conf;
- unsigned short ultraenable = 0;
- int have_seeprom = FALSE;
- struct Scsi_Host *host;
- struct aic7xxx_host *p;
- struct seeprom_config sc;
-
- base = config->base;
-
- /*
- * Lock out other contenders for our i/o space.
- */
- request_region(base, MAXREG - MINREG, "aic7xxx");
+ outb(BRDRW | BRDCS, p->base + BRDCTL);
+ return (inb(p->base + BRDCTL));
+}
- switch (config->type)
+/*+F*************************************************************************
+ * Function:
+ * configure_termination
+ *
+ * Description:
+ * Configures the termination settings on PCI adapters that have
+ * SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
+ unsigned short adapter_control, unsigned char max_targ)
+{
+ unsigned char brdctl_int, brdctl_ext;
+ int internal50_present;
+ int internal68_present = 0;
+ int external_present = 0;
+ int eprom_present;
+ int high_on;
+ int low_on;
+ int old_verbose;
+
+ if (acquire_seeprom(p))
{
- case AIC_7770:
- case AIC_7771:
- /*
- * Use the boot-time option for the interrupt trigger type. If not
- * supplied (-1), then we use BIOS settings to determine the interrupt
- * trigger type (level or edge) and use this value for pausing and
- * unpausing the sequencer.
- */
- switch (aic7xxx_irq_trigger)
- {
- case 0: config->unpause = INTEN; /* Edge */
- break;
- case 1: config->unpause = IRQMS | INTEN; /* Level */
- break;
- case -1:
- default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN;
- break;
- }
- config->pause = config->unpause | PAUSE;
+ if (adapter_control & CFAUTOTERM)
+ {
+ old_verbose = aic7xxx_verbose;
+ printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please "
+ "verify driver");
+ printk(KERN_INFO " detected settings and use manual termination "
+ "if necessary.");
+
+ /* Configure auto termination. */
+ outb(SEECS | SEEMS, p->base + SEECTL);
/*
- * For some 274x boards, we must clear the CHIPRST bit and pause
- * the sequencer. For some reason, this makes the driver work.
- * For 284x boards, we give it a CHIPRST just like the 294x boards.
+ * First read the status of our cables. Set the rom bank to
+ * 0 since the bank setting serves as a multiplexor for the
+ * cable detection logic. BRDDAT5 controls the bank switch.
*/
- outb(config->pause | CHIPRST, HCNTRL + base);
- aic7xxx_delay(1);
- if (inb(HCNTRL + base) & CHIPRST)
- {
- printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
- }
- outb(config->pause, HCNTRL + base);
+ write_brdctl(p, 0);
/*
- * Just to be on the safe side with the 274x, we will re-read the irq
- * since there was some issue about resetting the board.
+ * Now read the state of the internal connectors. The
+ * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
+ * set when cables are not present (BRDDAT6 is INT50 and
+ * BRDDAT7 is INT68).
*/
- config->irq = inb(INTDEF + base) & 0x0F;
- if ((config->type == AIC_7771) &&
- (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
+ brdctl_int = read_brdctl(p);
+ internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+ if (max_targ > 8)
{
- config->bios = AIC_DISABLED;
- config->flags |= USE_DEFAULTS;
- }
- else
- {
- host_conf = inb(HOSTCONF + base);
- config->bus_speed = host_conf & DFTHRSH;
- config->busrtime = (host_conf << 2) & BOFF;
+ internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
}
/*
- * Setup the FIFO threshold and the bus off time
+ * Set the rom bank to 1 and determine
+ * the other signals.
*/
- outb(config->bus_speed & DFTHRSH, BUSSPD + base);
- outb(config->busrtime, BUSTIME + base);
+ write_brdctl(p, BRDDAT5);
/*
- * A reminder until this can be detected automatically.
+ * Now read the state of the external connectors. BRDDAT6 is
+ * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
+ * set when the eprom is present.
*/
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
- break;
-
- case AIC_284x:
- outb(CHIPRST, HCNTRL + base);
- config->unpause = UNPAUSE_284X;
- config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
- aic7xxx_delay(1);
- outb(config->pause, HCNTRL + base);
-
- config->parity = AIC_ENABLED;
- config->irq = inb(INTDEF + base) & 0x0F;
- host_conf = inb(HOSTCONF + base);
-
- printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
- have_seeprom = read_2840_seeprom(base, &sc);
- if (!have_seeprom)
+ brdctl_ext = read_brdctl(p);
+ external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
+ eprom_present = brdctl_ext & BRDDAT7;
+ if (aic7xxx_verbose)
{
- printk("aic7xxx: Unable to read SEEPROM.\n");
- }
- else
- {
- printk("done.\n");
- config->flags |= HAVE_SEEPROM;
- if (sc.bios_control & CF284XEXTEND)
- config->flags |= EXTENDED_TRANSLATION;
- if (!(sc.bios_control & CFBIOSEN))
+ if (max_targ > 8)
{
- /*
- * The BIOS is disabled; the values left over in scratch
- * RAM are still valid. Do not use defaults as in the
- * AIC-7770 case.
- */
- config->bios = AIC_DISABLED;
+ printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+ "Ext-68 %s)\n",
+ internal50_present ? "YES" : "NO",
+ internal68_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
}
else
{
- config->parity = (sc.adapter_control & CFSPARITY) ?
- AIC_ENABLED : AIC_DISABLED;
- config->low_term = (sc.adapter_control & CF284XSTERM) ?
- AIC_ENABLED : AIC_DISABLED;
- /*
- * XXX - Adaptec *does* make 284x wide controllers, but the
- * documents do not say where the high byte termination
- * enable bit is located.
- */
+ printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+ internal50_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
}
+ printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
+ "brdctl_ext=0x%x\n",
+ eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
}
- host_conf = inb(HOSTCONF + base);
- config->bus_speed = host_conf & DFTHRSH;
- config->busrtime = (host_conf << 2) & BOFF;
-
- /*
- * Setup the FIFO threshold and the bus off time
- */
- outb(config->bus_speed & DFTHRSH, BUSSPD + base);
- outb(config->busrtime, BUSTIME + base);
-
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
- break;
-
- case AIC_7860:
- case AIC_7861:
- case AIC_7880:
- case AIC_7881:
- case AIC_7882:
- case AIC_7883:
- case AIC_7884:
- /*
- * Remember if Ultra was enabled in case there is no SEEPROM.
- * Fall through to the rest of the AIC_78xx code.
- */
- if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra)
- config->flags |= ULTRA_ENABLED;
-
- case AIC_7850:
- case AIC_7855:
- case AIC_7870:
- case AIC_7871:
- case AIC_7872:
- case AIC_7873:
- case AIC_7874:
/*
- * Grab the SCSI ID before chip reset in case there is no SEEPROM.
+ * Now set the termination based on what we found. BRDDAT6
+ * controls wide termination enable.
*/
- config->scsi_id = inb(SCSIID + base) & OID;
- outb(CHIPRST, HCNTRL + base);
- config->unpause = UNPAUSE_294X;
- config->pause = config->unpause | PAUSE;
- aic7xxx_delay(1);
- outb(config->pause, HCNTRL + base);
-
- config->parity = AIC_ENABLED;
+ high_on = FALSE;
+ low_on = FALSE;
+ if ((max_targ > 8) &&
+ ((external_present == 0) || (internal68_present == 0)))
+ {
+ high_on = TRUE;
+ }
- printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
- if ((config->type == AIC_7873) || (config->type == AIC_7883))
+ if ((internal50_present + internal68_present + external_present) <= 1)
{
- have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
- &sc, c56_66);
+ low_on = TRUE;
}
- else
+
+ if (internal50_present && internal68_present && external_present)
{
- have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
- &sc, c46);
+ printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
+ " Only two connectors on the adapter may be "
+ "used at a time!\n");
}
- if (!have_seeprom)
+
+ if (high_on == TRUE)
+ write_brdctl(p, BRDDAT6);
+ else
+ write_brdctl(p, 0);
+
+ if (low_on == TRUE)
+ *sxfrctl1 |= STPWEN;
+
+ if (aic7xxx_verbose)
{
- for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
+ if (max_targ > 8)
{
- if (inb(sram) != 0x00)
- break;
- }
- if (sram == base + TARG_SCRATCH)
- {
- for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
- {
- if (inb(sram) != 0xFF)
- break;
- }
- }
- if ((sram != base + 0x60) && (config->scsi_id != 0))
- {
- config->flags &= ~USE_DEFAULTS;
- printk("\naic7xxx: Unable to read SEEPROM; "
- "using leftover BIOS values.\n");
+ printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+ low_on ? "ON" : "OFF",
+ high_on ? "ON" : "OFF");
}
else
{
- printk("\n");
- printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default "
- "settings.\n");
- config->flags |= USE_DEFAULTS;
- config->flags &= ~ULTRA_ENABLED;
- config->scsi_id = 7;
+ printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
}
- scsi_conf = ENSPCHK | RESET_SCSI;
+ }
+ aic7xxx_verbose = old_verbose;
+ }
+ else
+ {
+ if (adapter_control & CFSTERM)
+ {
+ *sxfrctl1 |= STPWEN;
+ }
+ outb(SEEMS | SEECS, p->base + SEECTL);
+ /*
+ * Configure high byte termination.
+ */
+ if (adapter_control & CFWSTERM)
+ {
+ write_brdctl(p, BRDDAT6);
}
else
{
- printk("done.\n");
- config->flags |= HAVE_SEEPROM;
- if (!(sc.bios_control & CFBIOSEN))
- {
- /*
- * The BIOS is disabled; the values left over in scratch
- * RAM are still valid. Do not use defaults as in the
- * AIC-7770 case.
- */
- config->bios = AIC_DISABLED;
- scsi_conf = ENSPCHK | RESET_SCSI;
- }
- else
- {
- scsi_conf = 0;
- if (sc.adapter_control & CFRESETB)
- scsi_conf |= RESET_SCSI;
- if (sc.adapter_control & CFSPARITY)
- scsi_conf |= ENSPCHK;
- if (sc.bios_control & CFEXTEND)
- config->flags |= EXTENDED_TRANSLATION;
- config->scsi_id = (sc.brtime_id & CFSCSIID);
- config->parity = (sc.adapter_control & CFSPARITY) ?
- AIC_ENABLED : AIC_DISABLED;
- config->low_term = (sc.adapter_control & CFSTERM) ?
- AIC_ENABLED : AIC_DISABLED;
- config->high_term = (sc.adapter_control & CFWSTERM) ?
- AIC_ENABLED : AIC_DISABLED;
- config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
- if (((config->type == AIC_7880) || (config->type == AIC_7881) ||
- (config->type == AIC_7882) || (config->type == AIC_7883) ||
- (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN))
- {
- printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI "
- "speed.\n");
- config->flags |= ULTRA_ENABLED;
- }
- }
+ write_brdctl(p, 0);
}
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+ (adapter_control & CFSTERM) ? "ON" : "OFF",
+ (adapter_control & CFWSTERM) ? "ON" : "OFF");
+ }
+ }
+ release_seeprom(p);
+ }
+}
- outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
- config->bus_speed = DFTHRSH_100;
- outb(config->bus_speed, DSPCISTATUS + base);
+/*+F*************************************************************************
+ * Function:
+ * detect_maxscb
+ *
+ * Description:
+ * Detects the maximum number of SCBs for the controller and returns
+ * the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+ int i;
+ unsigned char max_scbid = 255;
- /*
- * In case we are a wide card...
- */
- outb(config->scsi_id, SCSICONF + base + 1);
+ /*
+ * It's possible that we've already done this for multichannel
+ * adapters.
+ */
+ if (p->scb_data->maxhscbs == 0)
+ {
+ /*
+ * We haven't initialized the SCB settings yet. Walk the SCBs to
+ * determince how many there are.
+ */
+ outb(0, p->base + FREE_SCBH);
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
- break;
+ for (i = 0; i < AIC7XXX_MAXSCB; i++)
+ {
+ outb(i, p->base + SCBPTR);
+ outb(i, p->base + SCB_CONTROL);
+ if (inb(p->base + SCB_CONTROL) != i)
+ break;
+ outb(0, p->base + SCBPTR);
+ if (inb(p->base + SCB_CONTROL) != 0)
+ break;
- default:
- panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
+ outb(i, p->base + SCBPTR);
+ outb(0, p->base + SCB_CONTROL); /* Clear the control byte. */
+ outb(i + 1, p->base + SCB_NEXT); /* Set the next pointer. */
+ outb(SCB_LIST_NULL, p->base + SCB_TAG); /* Make the tag invalid. */
+
+ /* Make the non-tagged targets not busy. */
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+ }
+
+ /* Make sure the last SCB terminates the free list. */
+ outb(i - 1, p->base + SCBPTR);
+ outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+
+ /* Ensure we clear the first (0) SCBs control byte. */
+ outb(0, p->base + SCBPTR);
+ outb(0, p->base + SCB_CONTROL);
+
+ p->scb_data->maxhscbs = i;
}
- detect_maxscb(config);
+ if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
+ {
+ /* Determine the number of valid bits in the FIFOs. */
+ outb(max_scbid, p->base + QINFIFO);
+ max_scbid = inb(p->base + QINFIFO);
+ p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
+ }
+ else
+ {
+ p->scb_data->maxscbs = p->scb_data->maxhscbs;
+ }
+ if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
+ {
+ /*
+ * Disable paging if the QINFIFO doesn't allow more SCBs than
+ * we have in hardware.
+ */
+ p->flags &= ~PAGE_ENABLED;
+ }
- if (config->chip_type == AIC_777x)
+ /*
+ * Set the Queue Full Count. Some cards have more queue space than
+ * SCBs.
+ */
+ switch (p->chip_class)
{
- if (config->pause & IRQMS)
- {
- printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
- }
- else
- {
- printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
- }
+ case AIC_777x:
+ p->qfullcount = 4;
+ p->qcntmask = 0x07;
+ break;
+ case AIC_785x:
+ case AIC_786x:
+ p->qfullcount = 8;
+ p->qcntmask = 0x0f;
+ break;
+ case AIC_787x:
+ case AIC_788x:
+ if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
+ {
+ p->qfullcount = AIC7XXX_MAXSCB;
+ p->qcntmask = 0xFF;
+ }
+ else
+ {
+ p->qfullcount = 16;
+ p->qcntmask = 0x1F;
+ }
+ break;
}
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_register
+ *
+ * Description:
+ * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+{
+ int i;
+ unsigned char sblkctl, flags = 0;
+ int max_targets;
+ int found = 1;
+ char channel_ids[] = {'A', 'B', 'C'};
+ unsigned char target_settings;
+ unsigned char scsi_conf, sxfrctl1;
+ unsigned short ultraenable = 0;
+ struct Scsi_Host *host;
+
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ request_region(p->base, MAXREG - MINREG, "aic7xxx");
/*
* Read the bus type from the SBLKCTL register. Set the FLAGS
* register in the sequencer for twin and wide bus cards.
*/
- sblkctl = inb(SBLKCTL + base);
- if (config->flags & PAGE_ENABLED)
+ sblkctl = inb(p->base + SBLKCTL);
+ if (p->flags & PAGE_ENABLED)
flags = PAGESCBS;
switch (sblkctl & SELBUS_MASK)
{
case SELNARROW: /* narrow/normal bus */
- config->scsi_id = inb(SCSICONF + base) & 0x07;
- config->bus_type = AIC_SINGLE;
- outb(flags | SINGLE_BUS, FLAGS + base);
+ p->scsi_id = inb(p->base + SCSICONF) & 0x07;
+ p->bus_type = AIC_SINGLE;
+ p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+ if (p->flags & MULTI_CHANNEL)
+ {
+ printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
+ channel_ids[p->chan_num], p->scsi_id);
+ }
+ else
+ {
+ printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
+ p->scsi_id);
+ }
+ outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
break;
case SELWIDE: /* Wide bus */
- config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
- config->bus_type = AIC_WIDE;
- printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
- board_names[config->type]);
- outb(flags | WIDE_BUS, FLAGS + base);
+ p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
+ p->bus_type = AIC_WIDE;
+ p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+ if (p->flags & MULTI_CHANNEL)
+ {
+ printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
+ channel_ids[p->chan_num], p->scsi_id);
+ }
+ else
+ {
+ printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
+ p->scsi_id);
+ }
+ outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
break;
case SELBUSB: /* Twin bus */
- config->scsi_id = inb(SCSICONF + base) & 0x07;
-#ifdef AIC7XXX_TWIN_SUPPORT
- config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
- config->bus_type = AIC_TWIN;
- printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n",
- board_names[config->type]);
- outb(flags | TWIN_BUS, FLAGS + base);
-#else
- config->bus_type = AIC_SINGLE;
- printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n",
- board_names[config->type]);
- outb(flags, FLAGS + base);
-#endif
+ p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
+ p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
+ p->bus_type = AIC_TWIN;
+ printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+ p->scsi_id, p->scsi_id_b);
+ outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
break;
default:
printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
- "mail deang@teleport.com\n", inb(SBLKCTL + base));
- outb(0, FLAGS + base);
+ "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
+ outb(0, p->base + SEQ_FLAGS);
return (0);
}
/*
- * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
- * take the card out of diagnostic mode and make the host adapter
- * LED follow bus activity (will not always be on).
+ * Detect SCB parameters and initialize the SCB array.
*/
- outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
+ detect_maxscb(p);
+ printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
+ p->scb_data->maxhscbs, p->scb_data->maxscbs,
+ p->qfullcount, p->qcntmask);
- /*
- * The IRQ level in i/o port 4 maps directly onto the real
- * IRQ number. If it's ok, register it with the kernel.
- *
- * NB. the Adaptec documentation says the IRQ number is only
- * in the lower four bits; the ECU information shows the
- * high bit being used as well. Which is correct?
- *
- * The PCI cards get their interrupt from PCI BIOS.
- */
- if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
- {
- printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, "
- "ignoring.\n");
- return (0);
- }
+ host = p->host;
- /*
- * Print out debugging information before re-enabling
- * the card - a lot of registers on it can't be read
- * when the sequencer is active.
- */
- debug_config(config);
-
- /*
- * Register each "host" and fill in the returned Scsi_Host
- * structure as best we can. Some of the parameters aren't
- * really relevant for bus types beyond ISA, and none of the
- * high-level SCSI code looks at it anyway. Why are the fields
- * there? Also save the pointer so that we can find the
- * information when an IRQ is triggered.
- */
- host = scsi_register(template, sizeof(struct aic7xxx_host));
- host->can_queue = config->maxscbs;
+ host->can_queue = p->scb_data->maxscbs;
host->cmd_per_lun = 2;
+ host->sg_tablesize = AIC7XXX_MAX_SG;
host->select_queue_depths = aic7xxx_select_queue_depth;
- host->this_id = config->scsi_id;
- host->io_port = config->base;
+ host->this_id = p->scsi_id;
+ host->io_port = p->base;
host->n_io_port = 0xFF;
- host->base = (unsigned char *)config->mbase;
- host->irq = config->irq;
- if (config->bus_type == AIC_WIDE)
+ host->base = (unsigned char *) p->mbase;
+ host->irq = p->irq;
+ if (p->bus_type == AIC_WIDE)
{
host->max_id = 16;
}
- if (config->bus_type == AIC_TWIN)
+ if (p->bus_type == AIC_TWIN)
{
host->max_channel = 1;
}
- p = (struct aic7xxx_host *) host->hostdata;
-
p->host = host;
- p->host_no = (int)host->host_no;
+ p->host_no = host->host_no;
p->isr_count = 0;
- p->base = base;
- p->maxscbs = config->maxscbs;
- p->maxhscbs = config->maxhscbs;
- p->qcntmask = config->qcntmask;
- p->mbase = (char *)config->mbase;
- p->type = config->type;
- p->chip_type = config->chip_type;
- p->flags = config->flags;
- p->chan_num = config->chan_num;
- p->scb_link = &(p->scb_usage);
-#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS)
- if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883)))
- {
- shared_3985_scbs = &(p->scb_usage);
- p->scb_link = &(p->scb_usage);
- }
-#endif
- p->scb_link->numscbs = 0;
- p->bus_type = config->bus_type;
- p->seeprom = sc;
p->next = NULL;
p->completeq.head = NULL;
p->completeq.tail = NULL;
- scbq_init(&p->scb_link->free_scbs);
- scbq_init(&p->page_scbs);
+ scbq_init(&p->scb_data->free_scbs);
scbq_init(&p->waiting_scbs);
- scbq_init(&p->assigned_scbs);
- p->unpause = config->unpause;
- p->pause = config->pause;
-
- for (i = 0; i <= 15; i++)
+ for (i = 0; i <= NUMBER(p->device_status); i++)
{
p->device_status[i].commands_sent = 0;
p->device_status[i].flags = 0;
+ p->device_status[i].active_cmds = 0;
p->device_status[i].last_reset = 0;
}
- if (aic7xxx_boards[config->irq] == NULL)
+ if (aic7xxx_boards[p->irq] == NULL)
{
+ int result;
+ int irq_flags = 0;
+
+#ifdef AIC7XXX_OLD_ISR_TYPE
+ irg_flags = SA_INTERRUPT;
+#endif
/*
* Warning! This must be done before requesting the irq. It is
* possible for some boards to raise an interrupt as soon as
@@ -4353,17 +5030,26 @@ aic7xxx_register(Scsi_Host_Template *template,
* kernel, an interrupt is triggered immediately. Therefore, we
* must ensure the board data is correctly set before the request.
*/
- aic7xxx_boards[config->irq] = host;
+ aic7xxx_boards[p->irq] = host;
/*
- * Register IRQ with the kernel.
+ * Register IRQ with the kernel. Only allow sharing IRQs with
+ * PCI devices.
*/
- if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
- "aic7xxx", NULL))
+ if (p->chip_class == AIC_777x)
+ {
+ result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+ }
+ else
+ {
+ result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+ "aic7xxx", NULL));
+ }
+ if (result < 0)
{
printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
- config->irq);
- aic7xxx_boards[config->irq] = NULL;
+ p->irq);
+ aic7xxx_boards[p->irq] = NULL;
return (0);
}
}
@@ -4374,79 +5060,74 @@ aic7xxx_register(Scsi_Host_Template *template,
* registered host adapter. Add this host adapter's Scsi_Host
* to the beginning of the linked list of hosts at the same IRQ.
*/
- p->next = aic7xxx_boards[config->irq];
- aic7xxx_boards[config->irq] = host;
- }
-
- /*
- * Load the sequencer program, then re-enable the board -
- * resetting the AIC-7770 disables it, leaving the lights
- * on with nobody home. On the PCI bus you *may* be home,
- * but then your mailing address is dynamically assigned
- * so no one can find you anyway :-)
- */
- printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
- aic7xxx_loadseq(base);
-
- /*
- * Set Fast Mode and Enable the board
- */
- outb(FASTMODE, SEQCTL + base);
-
- if (p->chip_type == AIC_777x)
- {
- outb(ENABLE, BCTL + base);
+ p->next = aic7xxx_boards[p->irq];
+ aic7xxx_boards[p->irq] = host;
}
- printk("done.\n");
-
/*
* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
*/
if (p->bus_type == AIC_TWIN)
{
/*
- * Select Channel B.
+ * The controller is gated to channel B after a chip reset; set
+ * bus B values first.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
-
- outb(config->scsi_id_b, SCSIID + base);
- scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
- outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
- outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
- outb(ENSELTIMO, SIMODE1 + base);
-#endif
+ outb(p->scsi_id_b, p->base + SCSIID);
+ scsi_conf = inb(p->base + SCSICONF + 1);
+ sxfrctl1 = inb(p->base + SXFRCTL1);
+ outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) |
+ ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+ outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
if (p->flags & ULTRA_ENABLED)
{
- outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
}
else
{
- outb(DFON | SPIOEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN, p->base + SXFRCTL0);
}
- /*
- * Select Channel A
- */
- outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+ if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+ {
+ /* Reset SCSI bus B. */
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+
+ aic7xxx_reset_current_bus(p);
+ }
+
+ /* Select channel A */
+ outb(SELNARROW, p->base + SBLKCTL);
}
- outb(config->scsi_id, SCSIID + base);
- scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
- outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
- outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
- outb(ENSELTIMO, SIMODE1 + base);
-#endif
+
+ outb(p->scsi_id, p->base + SCSIID);
+ scsi_conf = inb(p->base + SCSICONF);
+ sxfrctl1 = inb(p->base + SXFRCTL1);
+ outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) |
+ ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+ outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
if (p->flags & ULTRA_ENABLED)
{
- outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
}
else
{
- outb(DFON | SPIOEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN, p->base + SXFRCTL0);
+ }
+
+ if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+ {
+ /* Reset SCSI bus A. */
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+
+ aic7xxx_reset_current_bus(p);
+
+ /*
+ * Delay for the reset delay.
+ */
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
}
/*
@@ -4473,67 +5154,47 @@ aic7xxx_register(Scsi_Host_Template *template,
/*
* Grab the disconnection disable table and invert it for our needs
*/
- if (have_seeprom)
+ if (p->flags & USE_DEFAULTS)
{
- p->discenable = 0x0;
+ printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
+ "device parameters.\n");
+ p->discenable = 0xFFFF;
}
else
{
- if (config->bios == AIC_DISABLED)
- {
- printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI "
- "device parameters.\n");
- p->discenable = 0xFFFF;
- }
- else
- {
- p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
- inb(DISC_DSB + base));
- }
+ p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
+ inb(p->base + DISC_DSB));
}
for (i = 0; i < max_targets; i++)
{
- if (config->flags & USE_DEFAULTS)
+ if (p->flags & USE_DEFAULTS)
{
- target_settings = 0; /* 10 MHz */
+ target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */
p->needsdtr_copy |= (0x01 << i);
p->needwdtr_copy |= (0x01 << i);
+ if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ ultraenable |= (0x01 << i);
}
else
{
- if (have_seeprom)
+ target_settings = inb(p->base + TARG_SCRATCH + i);
+ if (target_settings & 0x0F)
{
- target_settings = ((sc.device_flags[i] & CFXFER) << 4);
- if (sc.device_flags[i] & CFSYNCH)
- {
- p->needsdtr_copy |= (0x01 << i);
- }
- if (sc.device_flags[i] & CFWIDEB)
- {
- p->needwdtr_copy |= (0x01 << i);
- }
- if (sc.device_flags[i] & CFDISC)
- {
- p->discenable |= (0x01 << i);
- }
+ p->needsdtr_copy |= (0x01 << i);
+ /*
+ * Default to asynchronous transfers (0 offset)
+ */
+ target_settings &= 0xF0;
}
- else
+ if (target_settings & 0x80)
{
- target_settings = inb(TARG_SCRATCH + base + i);
- if (target_settings & 0x0F)
- {
- p->needsdtr_copy |= (0x01 << i);
- /*
- * Default to asynchronous transfers (0 offset)
- */
- target_settings &= 0xF0;
- }
- if (target_settings & 0x80)
- {
- p->needwdtr_copy |= (0x01 << i);
- target_settings &= 0x7F;
- }
+ p->needwdtr_copy |= (0x01 << i);
+ /*
+ * Clear the wide flag. When wide negotiation is successful,
+ * we'll enable it.
+ */
+ target_settings &= 0x7F;
}
if (p->flags & ULTRA_ENABLED)
{
@@ -4544,7 +5205,7 @@ aic7xxx_register(Scsi_Host_Template *template,
case 0x20:
ultraenable |= (0x01 << i);
break;
- case 0x40:
+ case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */
target_settings &= ~(0x70);
break;
default:
@@ -4552,7 +5213,7 @@ aic7xxx_register(Scsi_Host_Template *template,
}
}
}
- outb(target_settings, (TARG_SCRATCH + base + i));
+ outb(target_settings, p->base + TARG_SCRATCH + i);
}
/*
@@ -4567,103 +5228,448 @@ aic7xxx_register(Scsi_Host_Template *template,
p->needsdtr = p->needsdtr_copy;
p->needwdtr = p->needwdtr_copy;
p->orderedtag = 0;
-#if 0
- printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
- printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif
- outb(ultraenable & 0xFF, ULTRA_ENB + base);
- outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1);
+ outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
+ outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
/*
- * Set the number of available SCBs.
+ * Set the number of available hardware SCBs.
*/
- outb(config->maxhscbs, SCBCOUNT + base);
+ outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
/*
* 2s compliment of maximum tag value.
*/
- i = p->maxscbs;
- outb(-i & 0xFF, COMP_SCBCOUNT + base);
+ i = p->scb_data->maxscbs;
+ outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
/*
- * Set the QCNT (queue count) mask to deal with broken aic7850s that
- * sporatically get garbage in the upper bits of their QCNT registers.
+ * Allocate enough hardware scbs to handle the maximum number of
+ * concurrent transactions we can have. We have to make sure that
+ * the allocated memory is contiguous memory. The Linux kmalloc
+ * routine should only allocate contiguous memory, but note that
+ * this could be a problem if kmalloc() is changed.
*/
- outb(config->qcntmask, QCNTMASK + base);
+ if (p->scb_data->hscbs == NULL)
+ {
+ size_t array_size;
+ unsigned int hscb_physaddr;
+
+ array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+ p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+ if (p->scb_data->hscbs == NULL)
+ {
+ printk("aic7xxx: Unable to allocate hardware SCB array; "
+ "failing detection.\n");
+ release_region(p->base, MAXREG - MINREG);
+ /*
+ * Ensure that we only free the IRQ when there is _not_ another
+ * aic7xxx adapter sharing this IRQ. The adapters are always
+ * added to the beginning of the list, so we can grab the next
+ * pointer and place it back in the board array.
+ */
+ if (p->next == NULL)
+ {
+ free_irq(p->irq, aic7xxx_isr);
+ }
+ aic7xxx_boards[p->irq] = p->next;
+ return(0);
+ }
+ /* At least the control byte of each SCB needs to be 0. */
+ memset(p->scb_data->hscbs, 0, array_size);
+
+ /* Tell the sequencer where it can find the hardware SCB array. */
+ hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+ outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
+ outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
+ outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
+ outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
+ }
/*
- * Clear the active flags - no targets are busy.
- */
- outb(0, ACTIVE_A + base);
- outb(0, ACTIVE_B + base);
+ * QCount mask to deal with broken aic7850s that sporadically get
+ * garbage in the upper bits of their QCNT registers.
+ */
+ outb(p->qcntmask, p->base + QCNTMASK);
/*
* We don't have any waiting selections or disconnected SCBs.
*/
- outb(SCB_LIST_NULL, WAITING_SCBH + base);
- outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
+ outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+ outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
/*
* Message out buffer starts empty
*/
- outb(0, MSG_LEN + base);
+ outb(0, p->base + MSG_LEN);
/*
- * Reset the SCSI bus. Is this necessary?
- * There may be problems for a warm boot without resetting
- * the SCSI bus. Either BIOS settings in scratch RAM
- * will not get reinitialized, or devices may stay at
- * previous negotiated settings (SDTR and WDTR) while
- * the driver will think that no negotiations have been
- * performed.
- *
- * Some devices need a long time to "settle" after a SCSI
- * bus reset.
+ * Load the sequencer program, then re-enable the board -
+ * resetting the AIC-7770 disables it, leaving the lights
+ * on with nobody home. On the PCI bus you *may* be home,
+ * but then your mailing address is dynamically assigned
+ * so no one can find you anyway :-)
+ */
+ aic7xxx_loadseq(p);
+
+ if (p->chip_class == AIC_777x)
+ {
+ outb(ENABLE, p->base + BCTL); /* Enable the boards BUS drivers. */
+ }
+
+ /*
+ * Unpause the sequencer before returning and enable
+ * interrupts - we shouldn't get any until the first
+ * command is sent to us by the high-level SCSI code.
+ */
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_chip_reset
+ *
+ * Description:
+ * Perform a chip reset on the aic7xxx SCSI controller. The controller
+ * is paused upon return.
+ *-F*************************************************************************/
+static void
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+ unsigned char hcntrl;
+ int wait;
+
+ /* Retain the IRQ type across the chip reset. */
+ hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+
+ /*
+ * For some 274x boards, we must clear the CHIPRST bit and pause
+ * the sequencer. For some reason, this makes the driver work.
+ */
+ outb(PAUSE | CHIPRST, p->base + HCNTRL);
+
+ /*
+ * In the future, we may call this function as a last resort for
+ * error handling. Let's be nice and not do any unecessary delays.
+ */
+ wait = 1000; /* 1 second (1000 * 1000 usec) */
+ while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+ {
+ udelay(1000); /* 1 msec = 1000 usec */
+ wait = wait - 1;
+ }
+
+ if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+ {
+ printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+ }
+
+ outb(hcntrl | PAUSE, p->base + HCNTRL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_alloc
+ *
+ * Description:
+ * Allocate and initialize a host structure. Returns NULL upon error
+ * and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
+ aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+{
+ struct aic7xxx_host *p = NULL;
+ struct Scsi_Host *host;
+
+ /*
+ * Allocate a storage area by registering us with the mid-level
+ * SCSI layer.
*/
- if (!aic7xxx_no_reset)
+ host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+ if (host != NULL)
{
- printk("aic7xxx: Resetting the SCSI bus...");
- if (p->bus_type == AIC_TWIN)
+ p = (struct aic7xxx_host *) host->hostdata;
+ memset(p, 0, sizeof(struct aic7xxx_host));
+ p->host = host;
+
+ if (scb_data != NULL)
+ {
+ /*
+ * We are sharing SCB data areas; use the SCB data pointer
+ * provided.
+ */
+ p->scb_data = scb_data;
+ p->flags |= SHARED_SCBDATA;
+ }
+ else
{
/*
- * Select Channel B.
+ * We are not sharing SCB data; allocate one.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+ p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+ if (p->scb_data != NULL)
+ {
+ memset(p->scb_data, 0, sizeof(scb_data_type));
+ scbq_init (&p->scb_data->free_scbs);
+ }
+ else
+ {
+ /*
+ * For some reason we don't have enough memory. Free the
+ * allocated memory for the aic7xxx_host struct, and return NULL.
+ */
+ scsi_unregister(host);
+ p = NULL;
+ }
+ }
+ if (p != NULL)
+ {
+ p->host_no = host->host_no;
+ p->base = base;
+ p->mbase = mbase;
+ p->maddr = NULL;
+ p->flags = flags;
+ p->chip_type = chip_type;
+ p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+ p->pause = p->unpause | PAUSE;
+ }
+ }
+ return (p);
+}
- outb(SCSIRSTO, SCSISEQ + base);
- udelay(1000);
- outb(0, SCSISEQ + base);
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_free
+ *
+ * Description:
+ * Frees and releases all resources associated with an instance of
+ * the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free (struct aic7xxx_host *p)
+{
+ int i;
- /* Ensure we don't get a RSTI interrupt from this. */
- outb(CLRSCSIRSTI, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
+ /*
+ * We should be careful in freeing the scb_data area. For those
+ * adapters sharing external SCB RAM(398x), there will be only one
+ * scb_data area allocated. The flag SHARED_SCBDATA indicates if
+ * one adapter is sharing anothers SCB RAM.
+ */
+ if (!(p->flags & SHARED_SCBDATA))
+ {
+ /*
+ * Free the allocated hardware SCB space.
+ */
+ if (p->scb_data->hscbs != NULL)
+ {
+ kfree(p->scb_data->hscbs);
+ }
+ /*
+ * Free the driver SCBs. These were allocated on an as-need
+ * basis.
+ */
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ kfree(p->scb_data->scb_array[i]);
+ }
+ /*
+ * Free the hardware SCBs.
+ */
+ if (p->scb_data->hscbs != NULL)
+ {
+ kfree(p->scb_data->hscbs);
+ }
- /*
- * Select Channel A.
+ /*
+ * Free the SCB data area.
+ */
+ kfree(p->scb_data);
+ }
+ /*
+ * Free the instance of the device structure.
+ */
+ scsi_unregister(p->host);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_load_seeprom
+ *
+ * Description:
+ * Load the seeprom and configure adapter and target settings.
+ * Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static int
+load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+ int have_seeprom = 0;
+ int i, max_targets;
+ unsigned char target_settings, scsi_conf;
+ unsigned short scarray[128];
+ struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+ }
+ switch (p->chip_type)
+ {
+ case AIC_7770: /* None of these adapters have seeproms. */
+ case AIC_7771:
+ case AIC_7850:
+ case AIC_7855:
+ break;
+
+ case AIC_284x:
+ have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+ break;
+
+ case AIC_7861:
+ case AIC_7870:
+ case AIC_7871:
+ case AIC_7872:
+ case AIC_7874:
+ case AIC_7881:
+ case AIC_7882:
+ case AIC_7884:
+ have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+ scarray, sizeof(*sc)/2, C46);
+ break;
+
+ case AIC_7860: /* Motherboard Ultra controllers might have RAID port. */
+ case AIC_7880:
+ have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
+ if (!have_seeprom)
+ {
+ have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
+ }
+ break;
+
+ case AIC_7873: /* The 3985 adapters use the 93c56 serial EEPROM. */
+ case AIC_7883:
+ have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+ scarray, sizeof(scarray)/2, C56_66);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!have_seeprom)
+ {
+ if (aic7xxx_verbose)
+ {
+ printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+ }
+ p->flags |= USE_DEFAULTS;
+ }
+ else
+ {
+ if (aic7xxx_verbose)
+ {
+ printk("done\n");
+ }
+ p->flags |= HAVE_SEEPROM;
+
+ /*
+ * Update the settings in sxfrctl1 to match the termination settings.
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * First process the settings that are different between the VLB
+ * and PCI adapter seeproms.
+ */
+ if (p->chip_class == AIC_777x)
+ {
+ /* VLB adapter seeproms */
+ if (sc->bios_control & CF284XEXTEND)
+ p->flags |= EXTENDED_TRANSLATION;
+
+ if (sc->adapter_control & CF284XSTERM)
+ *sxfrctl1 |= STPWEN;
+ /*
+ * The 284x SEEPROM doesn't have a max targets field. We
+ * set it to 16 to make sure we take care of the 284x-wide
+ * adapters. For narrow adapters, going through the extra
+ * 8 target entries will not cause any harm since they will
+ * will not be used.
+ *
+ * XXX - We should probably break out the bus detection
+ * from the register function so we can use it here
+ * to tell us how many targets there really are.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+ max_targets = 16;
}
+ else
+ {
+ /* PCI adapter seeproms */
+ if (sc->bios_control & CFEXTEND)
+ p->flags |= EXTENDED_TRANSLATION;
- outb(SCSIRSTO, SCSISEQ + base);
- udelay(1000);
- outb(0, SCSISEQ + base);
+ if (sc->adapter_control & CFSTERM)
+ *sxfrctl1 |= STPWEN;
- /* Ensure we don't get a RSTI interrupt from this. */
- outb(CLRSCSIRSTI, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
+ /* Limit to 16 targets just in case. */
+ max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
+ }
- aic7xxx_delay(AIC7XXX_RESET_DELAY);
+ for (i = 0; i < max_targets; i++)
+ {
+ target_settings = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFSYNCH)
+ target_settings |= SOFS;
+ if (sc->device_flags[i] & CFWIDEB)
+ target_settings |= WIDEXFER;
+ if (sc->device_flags[i] & CFDISC)
+ p->discenable |= (0x01 << i);
+ outb(target_settings, p->base + TARG_SCRATCH + i);
+ }
+ outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
+ outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
- printk("done.\n");
- }
+ p->scsi_id = sc->brtime_id & CFSCSIID;
- /*
- * Unpause the sequencer before returning and enable
- * interrupts - we shouldn't get any until the first
- * command is sent to us by the high-level SCSI code.
- */
- UNPAUSE_SEQUENCER(p);
- return (found);
+ scsi_conf = (p->scsi_id & 0x7);
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc->adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ {
+ /*
+ * We allow the operator to override ultra enable through
+ * the boot prompt.
+ */
+ if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
+ {
+ /* Treat us as a non-ultra card */
+ p->flags &= ~ULTRA_ENABLED;
+ }
+ }
+
+ /* Set the host ID */
+ outb(scsi_conf, p->base + SCSICONF);
+ /* In case we are a wide card */
+ outb(p->scsi_id, p->base + SCSICONF + 1);
+
+ if (p->chip_class != AIC_777x)
+ {
+ /*
+ * Update the settings in sxfrctl1 to match the termination
+ * settings.
+ */
+ *sxfrctl1 = 0;
+ configure_termination(p, sxfrctl1, sc->adapter_control,
+ (unsigned char) sc->max_targets & CFMAXTARG);
+ }
+ }
+ return (have_seeprom);
}
/*+F*************************************************************************
@@ -4672,17 +5678,24 @@ aic7xxx_register(Scsi_Host_Template *template,
*
* Description:
* Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe(). A sequence of
+ * probe(), attach()/detach(), and init() makes more sense than
+ * one do-it-all function. This may be useful when (and if) the
+ * mid-level SCSI code is overhauled.
*-F*************************************************************************/
int
aic7xxx_detect(Scsi_Host_Template *template)
{
- int found = 0, slot, base;
- unsigned char irq = 0;
+ int found = 0;
+ aha_status_type adapter_bios;
+ aha_chip_class_type chip_class;
+ aha_chip_type chip_type;
+ int slot, base;
+ int chan_num = 0;
+ unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
int i;
- struct aic7xxx_host_config config;
-
- template->proc_dir = &proc_scsi_aic7xxx;
- config.chan_num = 0;
+ struct aic7xxx_host *p;
/*
* Since we may allow sharing of IRQs, it is imperative
@@ -4696,6 +5709,10 @@ aic7xxx_detect(Scsi_Host_Template *template)
aic7xxx_boards[i] = NULL;
}
+ template->proc_dir = &proc_scsi_aic7xxx;
+ template->name = aic7xxx_info(NULL);
+ template->sg_tablesize = AIC7XXX_MAX_SG;
+
/*
* Initialize the spurious count to 0.
*/
@@ -4717,33 +5734,174 @@ aic7xxx_detect(Scsi_Host_Template *template)
continue;
}
- config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
- if (config.type != AIC_NONE)
+ chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
+ if (chip_type != AIC_NONE)
{
+
+ switch (chip_type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[chip_type], slot);
+ break;
+ case AIC_284x:
+ printk("aic7xxx: <%s> at VLB %d\n",
+ board_names[chip_type], slot);
+ break;
+ default:
+ break;
+ }
+
/*
* We found a card, allow 1 spurious interrupt.
*/
aic7xxx_spurious_count = 1;
/*
- * We "find" a AIC-7770 if we locate the card
- * signature and we can set it up and register
- * it with the kernel without incident.
+ * Pause the card preserving the IRQ type. Allow the operator
+ * to override the IRQ trigger.
*/
- config.chip_type = AIC_777x;
- config.base = base;
- config.mbase = 0;
- config.irq = irq;
- config.parity = AIC_ENABLED;
- config.low_term = AIC_UNKNOWN;
- config.high_term = AIC_UNKNOWN;
- config.flags = 0;
- if (aic7xxx_extended)
- config.flags |= EXTENDED_TRANSLATION;
- config.bus_speed = DFTHRSH_100;
- config.busrtime = BOFF;
- found += aic7xxx_register(template, &config);
+ if (aic7xxx_irq_trigger == 1)
+ hcntrl = IRQMS; /* Level */
+ else if (aic7xxx_irq_trigger == 0)
+ hcntrl = 0; /* Edge */
+ else
+ hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
+ outb(hcntrl | PAUSE, base + HCNTRL);
+
+ irq = inb(INTDEF + base) & 0x0F;
+ switch (irq)
+ {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
+
+ default:
+ printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+ "level, ignoring.\n");
+ irq = 0;
+ break;
+ }
+
+ if (irq != 0)
+ {
+ p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+ if (p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ continue;
+ }
+ p->irq = irq & 0x0F;
+ p->chip_class = AIC_777x;
+#ifdef AIC7XXX_PAGE_ENABLE
+ p->flags |= PAGE_ENABLED;
+#endif
+ p->instance = found;
+ if (aic7xxx_extended)
+ {
+ p->flags |= EXTENDED_TRANSLATION;
+ }
+ aic7xxx_chip_reset(p);
+
+ switch (p->chip_type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ {
+ unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
+
+ /*
+ * Get the primary channel information. Right now we don't
+ * do anything with this, but someday we will be able to inform
+ * the mid-level SCSI code which channel is primary.
+ */
+ if (biosctrl & CHANNEL_B_PRIMARY)
+ {
+ p->flags |= FLAGS_CHANNEL_B_PRIMARY;
+ }
+
+ if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+ {
+ p->flags |= USE_DEFAULTS;
+ }
+ break;
+ }
+
+ case AIC_284x:
+ if (!load_seeprom(p, &sxfrctl1))
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+ }
+ break;
+
+ default: /* Won't get here. */
+ break;
+ }
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
+ (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
+ (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+ /*
+ * Check for Rev C or E boards. Rev E boards can supposedly have
+ * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+ * It's still not clear extactly what is different about the Rev E
+ * boards, but we think it allows 8 bit entries in the QOUTFIFO to
+ * support "paging" SCBs (more than 4 commands can be active at once).
+ *
+ * The Rev E boards have a read/write autoflush bit in the
+ * SBLKCTL register, while in the Rev C boards it is read only.
+ */
+ sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
+ outb(sblkctl, p->base + SBLKCTL);
+ if (inb(p->base + SBLKCTL) == sblkctl)
+ {
+ /*
+ * We detected a Rev E board, we allow paging on this board.
+ */
+ printk("Revision >= E\n");
+ outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+ }
+ else
+ {
+ /* Do not allow paging. */
+ p->flags &= ~PAGE_ENABLED;
+ printk("Revision <= C\n");
+ }
+
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+ /*
+ * Set the FIFO threshold and the bus off time.
+ */
+ hostconf = inb(p->base + HOSTCONF);
+ outb(hostconf & DFTHRSH, p->base + BUSSPD);
+ outb((hostconf << 2) & BOFF, p->base + BUSTIME);
+ /*
+ * Try to initialize the card and register it with the kernel.
+ */
+ if (aic7xxx_register(template, p))
+ {
+ /*
+ * We successfully found a board and registered it.
+ */
+ found = found + 1;
+ }
+ else
+ {
+ /*
+ * Something went wrong; release and free all resources.
+ */
+ aic7xxx_free(p);
+ }
+ }
/*
* Disallow spurious interrupts.
*/
@@ -4759,15 +5917,15 @@ aic7xxx_detect(Scsi_Host_Template *template)
{
struct
{
- unsigned short vendor_id;
- unsigned short device_id;
- aha_type card_type;
- aha_chip_type chip_type;
+ unsigned short vendor_id;
+ unsigned short device_id;
+ aha_chip_type chip_type;
+ aha_chip_class_type chip_class;
} const aic7xxx_pci_devices[] = {
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
@@ -4780,14 +5938,14 @@ aic7xxx_detect(Scsi_Host_Template *template)
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
};
- int error;
+ int error, flags;
int done = 0;
unsigned int iobase, mbase;
unsigned short index = 0;
unsigned char pci_bus, pci_device_fn;
- unsigned int csize_lattime;
- unsigned int class_revid;
- unsigned int devconfig;
+ unsigned char ultra_enb = 0;
+ unsigned int devconfig, class_revid;
+ scb_data_type *shared_scb_data = NULL;
char rev_id[] = {'B', 'C', 'D'};
for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
@@ -4804,36 +5962,33 @@ aic7xxx_detect(Scsi_Host_Template *template)
}
else /* Found an Adaptec PCI device. */
{
- config.type = aic7xxx_pci_devices[i].card_type;
- config.chip_type = aic7xxx_pci_devices[i].chip_type;
- config.chan_num = 0;
- config.bios = AIC_ENABLED; /* Assume bios is enabled. */
- config.flags = 0;
- config.busrtime = 40;
- switch (config.type)
+ chip_class = aic7xxx_pci_devices[i].chip_class;
+ chip_type = aic7xxx_pci_devices[i].chip_type;
+ chan_num = 0;
+ flags = 0;
+ switch (aic7xxx_pci_devices[i].chip_type)
{
case AIC_7850:
case AIC_7855:
- case AIC_7860:
- case AIC_7861:
- config.bios = AIC_DISABLED;
- config.flags |= USE_DEFAULTS;
- config.bus_speed = DFTHRSH_100;
+ flags |= USE_DEFAULTS;
break;
case AIC_7872: /* 3940 */
case AIC_7882: /* 3940-Ultra */
- config.chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
+ flags |= MULTI_CHANNEL;
+ chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
number_of_3940s++;
break;
case AIC_7873: /* 3985 */
case AIC_7883: /* 3985-Ultra */
- config.chan_num = number_of_3985s; /* Has 3 controllers */
+ chan_num = number_of_3985s; /* Has 3 controllers */
+ flags |= MULTI_CHANNEL;
number_of_3985s++;
if (number_of_3985s == 3)
{
number_of_3985s = 0;
+ shared_scb_data = NULL;
}
break;
@@ -4850,39 +6005,165 @@ aic7xxx_detect(Scsi_Host_Template *template)
PCI_INTERRUPT_LINE, &irq);
error += pcibios_read_config_dword(pci_bus, pci_device_fn,
PCI_BASE_ADDRESS_1, &mbase);
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, &devconfig);
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CLASS_PROGIF_REVID, &class_revid);
+
+ printk("aic7xxx: <%s> at PCI %d\n",
+ board_names[chip_type], PCI_SLOT(pci_device_fn));
/*
- * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+ * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
* we mask it off.
*/
iobase &= PCI_BASE_ADDRESS_IO_MASK;
+ p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
+ shared_scb_data);
+
+ if (p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ continue;
+ }
+
+ /* Remember to set the channel number, irq, and chip class. */
+ p->chan_num = chan_num;
+ p->irq = irq;
+ p->chip_class = chip_class;
+#ifdef AIC7XXX_PAGE_ENABLE
+ p->flags |= PAGE_ENABLED;
+#endif
+ p->instance = found;
+
/*
- * Read the PCI burst size and latency timer.
+ * Remember how the card was setup in case there is no seeprom.
*/
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- CSIZE_LATTIME, &csize_lattime);
- printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
- "PCLKS\n", (int) (csize_lattime & CACHESIZE),
- (csize_lattime >> 8) & 0x000000ff);
+ p->scsi_id = inb(p->base + SCSIID) & OID;
+ if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ {
+ p->flags |= ULTRA_ENABLED;
+ ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+ }
+ sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- CLASS_PROGIF_REVID, &class_revid);
- if ((class_revid & DEVREVID) < 3)
+ aic7xxx_chip_reset(p);
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
+ if (devconfig & RAMPSM)
{
- printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
- rev_id[class_revid & DEVREVID]);
+ printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
+ "access.\n");
+ /*
+ * XXX - Assume 9 bit SRAM and enable parity checking.
+ */
+ devconfig |= EXTSCBPEN;
+
+ /*
+ * XXX - Assume fast SRAM and only enable 2 cycle access if we
+ * are sharing the SRAM across multiple adapters (398x).
+ */
+ if ((devconfig & MPORTMODE) == 0)
+ {
+ devconfig |= EXTSCBTIME;
+ }
+ devconfig &= ~SCBRAMSEL;
+ pcibios_write_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, devconfig);
}
+#endif
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- DEVCONFIG, &devconfig);
- if (error)
+ if ((p->flags & USE_DEFAULTS) == 0)
{
- panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
- error);
+ load_seeprom(p, &sxfrctl1);
+ }
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ sblkctl = inb(p->base + SBLKCTL);
+ outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+
+ /*
+ * We don't know where this is set in the SEEPROM or by the
+ * BIOS, so we default to 100%.
+ */
+ outb(DFTHRSH_100, p->base + DSPCISTATUS);
+
+ if (p->flags & USE_DEFAULTS)
+ {
+ int j;
+ /*
+ * Default setup; should only be used if the adapter does
+ * not have a SEEPROM.
+ */
+ /*
+ * Check the target scratch area to see if someone set us
+ * up already. We are previously set up if the scratch
+ * area contains something other than all zeroes and ones.
+ */
+ for (j = TARG_SCRATCH; j < 0x60; j++)
+ {
+ if (inb(p->base + j) != 0x00) /* Check for all zeroes. */
+ break;
+ }
+ if (j == TARG_SCRATCH)
+ {
+ for (j = TARG_SCRATCH; j < 0x60; j++)
+ {
+ if (inb(p->base + 1) != 0xFF) /* Check for all ones. */
+ break;
+ }
+ }
+ if ((j != 0x60) && (p->scsi_id != 0))
+ {
+ p->flags &= ~USE_DEFAULTS;
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
+ }
+ }
+ else
+ {
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: No BIOS found; using default "
+ "settings.\n");
+ }
+ /*
+ * Assume only one connector and always turn on
+ * termination.
+ */
+ sxfrctl1 = STPWEN;
+ p->scsi_id = 7;
+ }
+ outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+ p->base + SCSICONF);
+ /* In case we are a wide card. */
+ outb(p->scsi_id, p->base + SCSICONF + 1);
+ if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
+ {
+ /*
+ * If there wasn't a BIOS or the board wasn't in this mode
+ * to begin with, turn off Ultra.
+ */
+ p->flags &= ~ULTRA_ENABLED;
+ }
}
- printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
+ /*
+ * Print some additional information about the adapter.
+ */
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
+ "IO Mem 0x%x, IRQ %d",
+ (p->flags & USE_DEFAULTS) ? "dis" : "en",
+ p->base, p->mbase, p->irq);
+ if ((class_revid & DEVREVID) < 3)
+ {
+ printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+ }
+ printk("\n");
/*
* I don't think we need to bother with allowing
@@ -4891,58 +6172,57 @@ aic7xxx_detect(Scsi_Host_Template *template)
*/
aic7xxx_spurious_count = 1;
- config.base = iobase;
- config.mbase = mbase;
- config.irq = irq;
- config.parity = AIC_ENABLED;
- config.low_term = AIC_UNKNOWN;
- config.high_term = AIC_UNKNOWN;
if (aic7xxx_extended)
- config.flags |= EXTENDED_TRANSLATION;
-#ifdef AIC7XXX_SHARE_SCBs
- if (devconfig & RAMPSM)
-#else
- if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
- (config.type != AIC_7883))
-#endif
+ p->flags |= EXTENDED_TRANSLATION;
+
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+ /*
+ * Put our termination setting into sxfrctl1 now that the
+ * generic initialization is complete.
+ */
+ sxfrctl1 |= inb(p->base + SXFRCTL1);
+ outb(sxfrctl1, p->base + SXFRCTL1);
+
+ if (aic7xxx_register(template, p) == 0)
{
+ aic7xxx_free(p);
+ }
+ else
+ {
+ found = found + 1;
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
/*
- * External SRAM present. The probe will walk the SCBs to see
- * how much SRAM we have and set the number of SCBs accordingly.
- * We have to turn off SCBRAMSEL to access the external SCB
- * SRAM.
- *
- * It seems that early versions of the aic7870 didn't use these
- * bits, hence the hack for the 3940 above. I would guess that
- * recent 3940s using later aic7870 or aic7880 chips do actually
- * set RAMPSM.
+ * Set the shared SCB data once we've successfully probed a
+ * 398x adapter.
*
- * The documentation isn't clear, but it sounds like the value
- * written to devconfig must not have RAMPSM set. The second
- * sixteen bits of the register are R/O anyway, so it shouldn't
- * affect RAMPSM either way.
+ * Note that we can only do this if the use of external
+ * SCB RAM is enabled.
*/
- printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
- "access.\n");
- devconfig &= ~(RAMPSM | SCBRAMSEL);
- pcibios_write_config_dword(pci_bus, pci_device_fn,
- DEVCONFIG, devconfig);
+ if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+ {
+ if (shared_scb_data == NULL)
+ {
+ shared_scb_data = p->scb_data;
+ }
+ }
+#endif
}
- found += aic7xxx_register(template, &config);
+ index++;
/*
* Disable spurious interrupts.
*/
aic7xxx_spurious_count = 0;
-
- index++;
} /* Found an Adaptec PCI device. */
}
}
}
#endif CONFIG_PCI
- template->name = aic7xxx_info(NULL);
return (found);
}
@@ -4958,45 +6238,45 @@ static void
aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
struct aic7xxx_scb *scb)
{
- unsigned int addr; /* must be 32 bits */
unsigned short mask;
+ struct aic7xxx_hwscb *hscb;
mask = (0x01 << TARGET_INDEX(cmd));
+ hscb = scb->hscb;
+
/*
* Setup the control byte if we need negotiation and have not
* already requested it.
*/
-#ifdef AIC7XXX_TAGGED_QUEUEING
- if (cmd->device->tagged_queue)
+ if (p->discenable & mask)
{
- cmd->tag = scb->tag;
- cmd->device->current_tag = scb->tag;
- scb->control |= TAG_ENB;
- p->device_status[TARGET_INDEX(cmd)].commands_sent++;
- if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200)
- {
- scb->control |= 0x02;
- p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
- }
-#if 0
- if (p->orderedtag & mask)
+ hscb->control |= DISCENB;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+ if (cmd->device->tagged_queue)
{
- scb->control |= 0x02;
- p->orderedtag = p->orderedtag & ~mask;
+ cmd->tag = hscb->tag;
+ p->device_status[TARGET_INDEX(cmd)].commands_sent++;
+ if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+ {
+ hscb->control |= MSG_SIMPLE_Q_TAG;
+ }
+ else
+ {
+ hscb->control |= MSG_ORDERED_Q_TAG;
+ p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+ }
}
-#endif
- }
-#endif
- if (p->discenable & mask)
- {
- scb->control |= DISCENB;
+#endif /* Tagged queueing */
}
+
if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
{
p->wdtr_pending |= mask;
- scb->control |= NEEDWDTR;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR;
#if 0
- printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+ printk("scsi%d: Sending WDTR request to target %d.\n",
+ p->host_no, cmd->target);
#endif
}
else
@@ -5004,19 +6284,20 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
{
p->sdtr_pending |= mask;
- scb->control |= NEEDSDTR;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
#if 0
- printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+ printk("scsi%d: Sending SDTR request to target %d.\n",
+ p->host_no, cmd->target);
#endif
}
}
-
#if 0
printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
"mask(0x%x).\n",
cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
#endif
- scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+ hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
/*
@@ -5030,9 +6311,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
* XXX - this relies on the host data being stored in a
* little-endian format.
*/
- addr = VIRT_TO_BUS(cmd->cmnd);
- scb->SCSI_cmd_length = cmd->cmd_len;
- memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+ hscb->SCSI_cmd_length = cmd->cmd_len;
+ hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
if (cmd->use_sg)
{
@@ -5052,15 +6332,16 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
scb->sg_list[i].length = (unsigned int) sg[i].length;
}
- scb->SG_segment_count = cmd->use_sg;
- addr = VIRT_TO_BUS(scb->sg_list);
- memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
- memcpy(scb->data_pointer, &(scb->sg_list[0].address),
- sizeof(scb->data_pointer));
- scb->data_count = scb->sg_list[0].length;
+ hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+ hscb->SG_segment_count = cmd->use_sg;
+ scb->sg_count = hscb->SG_segment_count;
+
+ /* Copy the first SG into the data pointer area. */
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
#if 0
printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
- cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+ cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
#endif
}
else
@@ -5069,28 +6350,23 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
(unsigned long) cmd->request_buffer, cmd->request_bufflen);
#endif
- if (cmd->request_bufflen == 0)
+ if (cmd->request_bufflen)
{
- /*
- * In case the higher level SCSI code ever tries to send a zero
- * length command, ensure the SCB indicates no data. The driver
- * will interpret a zero length command as a Bus Device Reset.
- */
- scb->SG_segment_count = 0;
- memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
- memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
- scb->data_count = 0;
+ hscb->SG_segment_count = 1;
+ scb->sg_count = 1;
+ scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
+ scb->sg_list[0].length = cmd->request_bufflen;
+ hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
+ hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
+ hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
}
else
{
- scb->SG_segment_count = 1;
- scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
- scb->sg_list[0].length = cmd->request_bufflen;
- addr = VIRT_TO_BUS(&scb->sg_list[0]);
- memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
- scb->data_count = scb->sg_list[0].length;
- addr = VIRT_TO_BUS(cmd->request_buffer);
- memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer));
+ hscb->SG_segment_count = 0;
+ scb->sg_count = 0;
+ hscb->SG_list_pointer = 0;
+ hscb->data_pointer = 0;
+ hscb->data_count = SCB_LIST_NULL << 24;
}
}
}
@@ -5108,7 +6384,6 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
long processor_flags;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb;
- u_char curscb, intstat;
p = (struct aic7xxx_host *) cmd->host->hostdata;
if (p->host != cmd->host)
@@ -5140,34 +6415,21 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
cmd->lun & 0x07);
#endif
- /*
- * This is a critical section, since we don't want the interrupt
- * routine mucking with the host data or the card. For this reason
- * it is nice to know that this function can only be called in one
- * of two ways from scsi.c First, as part of a routine queue command,
- * in which case, the irq for our card is disabled before this
- * function is called. This doesn't help us if there is more than
- * one card using more than one IRQ in our system, therefore, we
- * should disable all interrupts on these grounds alone. Second,
- * this can be called as part of the scsi_done routine, in which case
- * we are in the aic7xxx_isr routine already and interrupts are
- * disabled, therefore we should saveflags first, then disable the
- * interrupts, do our work, then restore the CPU flags. If it weren't
- * for the possibility of more than one card using more than one IRQ
- * in our system, we wouldn't have to touch the interrupt flags at all.
- */
- save_flags(processor_flags);
- cli();
-
+ if (p->device_status[TARGET_INDEX(cmd)].active_cmds
+ > cmd->device->queue_depth)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
+ p->host_no, cmd->target, cmd->channel);
+ }
scb = aic7xxx_allocate_scb(p);
if (scb == NULL)
{
- panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
+ panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
}
else
{
scb->cmd = cmd;
- aic7xxx_position(cmd) = scb->tag;
+ aic7xxx_position(cmd) = scb->hscb->tag;
#if 0
debug_scb(scb);
#endif;
@@ -5179,14 +6441,14 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
aic7xxx_buildscb(p, cmd, scb);
#if 0
- if (scb != (p->scb_array[scb->position]))
+ if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
{
printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
"address.\n");
}
printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
- scb->position, (unsigned int) scb->cmd,
- scb->state, (unsigned int) p->free_scb);
+ scb->hscb->tag, (unsigned int) scb->cmd,
+ scb->flags, (unsigned int) p->free_scb);
#endif
/*
@@ -5201,70 +6463,28 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
cmd->host_scribble = NULL;
memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
- if (scb->position != SCB_LIST_NULL)
- {
- /* We've got a valid slot, yeah! */
- if (p->flags & IN_ISR)
- {
- scbq_insert_tail(&p->assigned_scbs, scb);
- scb->state |= SCB_ASSIGNEDQ;
- }
- else
- {
- /*
- * Pause the sequencer so we can play with its registers -
- * wait for it to acknowledge the pause.
- *
- * XXX - should the interrupts be left on while doing this?
- */
- PAUSE_SEQUENCER(p);
- intstat = inb(INTSTAT + p->base);
-
- /*
- * Save the SCB pointer and put our own pointer in - this
- * selects one of the four banks of SCB registers. Load
- * the SCB, then write its pointer into the queue in FIFO
- * and restore the saved SCB pointer.
- */
- curscb = inb(SCBPTR + p->base);
- outb(scb->position, SCBPTR + p->base);
- aic7xxx_putscb(p, scb);
- outb(curscb, SCBPTR + p->base);
- outb(scb->position, QINFIFO + p->base);
- scb->state |= SCB_ACTIVE;
+ scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
- /*
- * Guard against unpausing the sequencer if there is an interrupt
- * waiting to happen.
- */
- if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
- {
- UNPAUSE_SEQUENCER(p);
- }
- }
- }
- else
+ save_flags(processor_flags);
+ cli();
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
{
- scb->state |= SCB_WAITINGQ;
- scbq_insert_tail(&p->waiting_scbs, scb);
- if (!(p->flags & IN_ISR))
- {
- aic7xxx_run_waiting_queues(p);
- }
+ aic7xxx_run_waiting_queues(p);
}
+ restore_flags(processor_flags);
#if 0
printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
- (long) cmd, (long) scb->cmd, scb->position);
+ (long) cmd, (long) scb->cmd, scb->hscb->tag);
#endif;
- restore_flags(processor_flags);
}
return (0);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_abort_reset
+ * aic7xxx_bus_device_reset
*
* Description:
* Abort or reset the current SCSI command(s). If the scb has not
@@ -5276,204 +6496,257 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
static int
aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
- struct aic7xxx_scb *scb;
+ struct aic7xxx_scb *scb;
+ struct aic7xxx_hwscb *hscb;
unsigned char bus_state;
- int base, result = -1;
+ int result = -1;
char channel;
- scb = (p->scb_array[aic7xxx_position(cmd)]);
- base = p->base;
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ hscb = scb->hscb;
- channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
- if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
+ /*
+ * Ensure that the card doesn't do anything behind our back.
+ * Also make sure that we didn't just miss an interrupt that
+ * could affect this abort/reset.
+ */
+ pause_sequencer(p);
+ while (inb(p->base + INTSTAT) & INT_PEND);
{
+ aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
+ pause_sequencer(p);
+ }
+ if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
+ {
+ result = SCSI_RESET_NOT_RUNNING;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ return(result);
+ }
+
- if (scb->state & SCB_IN_PROGRESS)
+ printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
+ p->host_no, TC_OF_SCB(scb), scb->flags);
+ bus_state = inb(p->base + LASTPHASE);
+
+ switch (bus_state)
+ {
+ case P_DATAOUT:
+ printk("Data-Out phase, ");
+ break;
+ case P_DATAIN:
+ printk("Data-In phase, ");
+ break;
+ case P_COMMAND:
+ printk("Command phase, ");
+ break;
+ case P_MESGOUT:
+ printk("Message-Out phase, ");
+ break;
+ case P_STATUS:
+ printk("Status phase, ");
+ break;
+ case P_MESGIN:
+ printk("Message-In phase, ");
+ break;
+ default:
+ /*
+ * We're not in a valid phase, so assume we're idle.
+ */
+ printk("while idle, LASTPHASE = 0x%x, ", bus_state);
+ break;
+ }
+ printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+ inb(p->base + SCSISIGI),
+ inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+ inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+
+ channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+ /*
+ * Determine our course of action.
+ */
+ if (scb->flags & SCB_ABORT)
+ {
+ /*
+ * Been down this road before; do a full bus reset.
+ */
+ scb->flags |= SCB_RECOVERY_SCB;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ result = -1;
+ }
+#if 0
+ else if (hscb->control & TAG_ENB)
{
/*
- * Ensure that the card doesn't do anything
- * behind our back.
+ * We could be starving this command; try sending and ordered tag
+ * command to the target we come from.
*/
- PAUSE_SEQUENCER(p);
+ scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
+ p->orderedtag = p->orderedtag | 0xFF;
+ result = SCSI_RESET_PENDING;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
+ p->host_no);
+ }
+#endif
+ else
+ {
+ unsigned char active_scb_index, saved_scbptr;
+ struct aic7xxx_scb *active_scb;
- printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
- bus_state = inb(LASTPHASE + p->base);
+ /*
+ * Send an Abort Message:
+ * The target that is holding up the bus may not be the same as
+ * the one that triggered this timeout (different commands have
+ * different timeout lengths). Our strategy here is to queue an
+ * abort message to the timed out target if it is disconnected.
+ * Otherwise, if we have an active target we stuff the message buffer
+ * with an abort message and assert ATN in the hopes that the target
+ * will let go of the bus and go to the mesgout phase. If this
+ * fails, we'll get another timeout a few seconds later which will
+ * attempt a bus reset.
+ */
+ saved_scbptr = inb(p->base + SCBPTR);
+ active_scb_index = inb(p->base + SCB_TAG);
+ active_scb = p->scb_data->scb_array[active_scb_index];
- switch (bus_state)
+ if (bus_state != P_BUSFREE)
+ {
+ if (active_scb_index >= p->scb_data->numscbs)
{
- case P_DATAOUT:
- printk("Data-Out phase, ");
- break;
- case P_DATAIN:
- printk("Data-In phase, ");
- break;
- case P_COMMAND:
- printk("Command phase, ");
- break;
- case P_MESGOUT:
- printk("Message-Out phase, ");
- break;
- case P_STATUS:
- printk("Status phase, ");
- break;
- case P_MESGIN:
- printk("Message-In phase, ");
- break;
- default:
- printk("while idle, LASTPHASE = 0x%x, ", bus_state);
- /*
- * We're not in a valid phase, so assume we're idle.
- */
- bus_state = 0;
- break;
+ /*
+ * Perform a bus reset.
+ *
+ * XXX - We want to queue an abort for the timedout SCB
+ * instead.
+ */
+ result = -1;
+ printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+ "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
}
- printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
-
- /*
- * First, determine if we want to do a bus reset or simply a bus device
- * reset. If this is the first time that a transaction has timed out
- * and the SCB is not paged out, just schedule a bus device reset.
- * Otherwise, we reset the bus and abort all pending I/Os on that bus.
- */
- if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT)))
+ else
{
-#if 0
- if (scb->control & TAG_ENB)
- {
+ /* Send the abort message to the active SCB. */
+ outb(1, p->base + MSG_LEN);
+ if (active_scb->hscb->control & TAG_ENB)
+ {
+ outb(MSG_ABORT_TAG, p->base + MSG_OUT);
+ }
+ else
+ {
+ outb(MSG_ABORT, p->base + MSG_OUT);
+ }
+ outb(bus_state | ATNO, p->base + SCSISIGO);
+ printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
+ p->host_no);
+ active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
+ if (active_scb != scb)
+ {
/*
- * We could be starving this command; try sending and ordered tag
- * command to the target we come from.
+ * XXX - We would like to increment the timeout on scb, but
+ * access to that routine is denied because it is hidden
+ * in scsi.c. If we were able to do this, it would give
+ * scb a new lease on life.
*/
- scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG;
- p->orderedtag = p->orderedtag | 0xFF;
result = SCSI_RESET_PENDING;
- UNPAUSE_SEQUENCER(p);
- printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n");
- }
-#endif
- unsigned char active_scb, control;
- struct aic7xxx_scb *active_scbp;
+ aic7xxx_error(active_scb->cmd) = DID_RESET;
+ }
+ else
+ {
+ aic7xxx_error(scb->cmd) = DID_RESET;
+ result = SCSI_RESET_PENDING;
+ }
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ }
+ }
+ else
+ {
+ unsigned char hscb_index, linked_next;
+ int disconnected;
- /*
- * Send a Bus Device Reset Message:
- * The target we select to send the message to may be entirely
- * different than the target pointed to by the scb that timed
- * out. If the command is in the QINFIFO or the waiting for
- * selection list, its not tying up the bus and isn't responsible
- * for the delay so we pick off the active command which should
- * be the SCB selected by SCBPTR. If its disconnected or active,
- * we device reset the target scbp points to. Although it may
- * be that this target is not responsible for the delay, it may
- * may also be that we're timing out on a command that just takes
- * too much time, so we try the bus device reset there first.
- */
- active_scb = inb(SCBPTR + base);
- active_scbp = (p->scb_array[inb(SCB_TAG + base)]);
- control = inb(SCB_CONTROL + base);
+ disconnected = FALSE;
+ hscb_index = aic7xxx_find_scb(p, scb);
+ if (hscb_index == SCB_LIST_NULL)
+ {
+ disconnected = TRUE;
+ linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+ }
+ else
+ {
+ outb(hscb_index, p->base + SCBPTR);
+ if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+ {
+ disconnected = TRUE;
+ }
+ linked_next = inb(p->base + SCB_LINKED_NEXT);
+ }
+ if (disconnected)
+ {
+ /*
+ * Simply set the ABORT_SCB control bit and preserve the
+ * linked next pointer.
+ */
+ scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+ scb->hscb->data_count &= ~0xFF000000;
+ scb->hscb->data_count |= linked_next << 24;
+ if ((p->flags & PAGE_ENABLED) == 0)
+ {
+ scb->hscb->control &= ~DISCONNECTED;
+ }
+ scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+ if (hscb_index != SCB_LIST_NULL)
+ {
+ unsigned char scb_control;
- /*
- * Test to see if scbp is disconnected
- */
- outb(scb->position, SCBPTR + base);
- if (inb(SCB_CONTROL + base) & DISCONNECTED)
- {
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (abort_scb) scb %d is disconnected; "
- "bus device reset message queued.\n", scb->position);
-#endif
- if (p->flags & PAGE_ENABLED)
- {
- /* Pull this SCB out of the disconnected list. */
- u_char prev = inb(SCB_PREV + base);
- u_char next = inb(SCB_NEXT + base);
- if (prev == SCB_LIST_NULL)
- {
- /* Head of list */
- outb(next, DISCONNECTED_SCBH + base);
- }
- else
- {
- outb(prev, SCBPTR + base);
- outb(next, SCB_NEXT + base);
- if (next != SCB_LIST_NULL)
- {
- outb(next, SCBPTR + base);
- outb(prev, SCB_PREV + base);
- }
- outb(scb->position, SCBPTR + base);
- }
- }
- scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
- scb->control = scb->control & DISCENB;
- scb->SCSI_cmd_length = 0;
- scb->SG_segment_count = 0;
- memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
- memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
- scb->data_count = 0;
- aic7xxx_putscb(p, scb);
- aic7xxx_add_waiting_scb(base, scb);
- outb(active_scb, SCBPTR + base);
- result = SCSI_RESET_PENDING;
- UNPAUSE_SEQUENCER(p);
- }
- else
- {
- /*
- * Is the active SCB really active?
- */
- if ((active_scbp->state & SCB_ACTIVE) && bus_state)
- {
- /*
- * Load the message buffer and assert attention.
- */
- active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
- outb(1, MSG_LEN + base);
- outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
- outb(bus_state | ATNO, SCSISIGO + base);
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (abort_scb) asserted ATN - "
- "bus device reset in message buffer.\n");
-#endif
- if (active_scbp != scb)
- {
- /*
- * XXX - We would like to increment the timeout on scb, but
- * access to that routine is denied because it is hidden
- * in scsi.c. If we were able to do this, it would give
- * scb a new lease on life.
- */
- ;
- }
- aic7xxx_error(scb->cmd) = DID_RESET;
- /*
- * Restore the active SCB and unpause the sequencer.
- */
- outb(active_scb, SCBPTR + base);
- if (active_scbp != scb)
- {
- /*
- * The mid-level SCSI code requested us to reset a command
- * different from the one that we actually reset. Return
- * a "not running" indication and hope that the SCSI code
- * will Do the Right Thing (tm).
- */
- result = SCSI_RESET_NOT_RUNNING;
- }
- else
- {
- result = SCSI_RESET_PENDING;
- }
- UNPAUSE_SEQUENCER(p);
- }
- }
+ scb_control = inb(p->base + SCB_CONTROL);
+ outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+ }
+ /*
+ * Actually requeue this SCB in case we can select the
+ * device before it reconnects. If the transaction we
+ * want to abort is not tagged, unbusy it first so that
+ * we don't get held back from sending the command.
+ */
+ if ((scb->hscb->control & TAG_ENB) == 0)
+ {
+ unsigned char target;
+ int lun;
+
+ target = scb->cmd->target;
+ lun = scb->cmd->lun;
+ aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
+ 0, /* requeue */ TRUE);
+ }
+ printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
+ p->host_no, TC_OF_SCB(scb));
+ scbq_insert_head(&p->waiting_scbs, scb);
+ scb->flags |= SCB_WAITINGQ;
+ outb(saved_scbptr, p->base + SCBPTR);
+ if ((p->flags & IN_ISR) == 0)
+ {
+ /*
+ * Processing the waiting queue may unpause us.
+ */
+ aic7xxx_run_waiting_queues(p);
+ /*
+ * If we are using AAP, aic7xxx_run_waiting_queues() will not
+ * unpause us, so ensure we are unpaused.
+ */
+ unpause_sequencer(p, /*unpause_always*/ FALSE);
+ }
+ else
+ {
+ unpause_sequencer(p, /*unpause_always*/ TRUE);
+ }
+ result = SCSI_RESET_PENDING;
+ }
+ else
+ {
+ scb->flags |= SCB_RECOVERY_SCB;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ result = -1;
}
}
}
- /* Make sure the sequencer is unpaused upon return. */
- if (result == -1)
- {
- UNPAUSE_SEQUENCER(p);
- }
return (result);
}
@@ -5491,16 +6764,48 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
struct aic7xxx_scb *scb = NULL;
struct aic7xxx_host *p;
int base, result;
+ unsigned long processor_flags;
p = (struct aic7xxx_host *) cmd->host->hostdata;
- scb = (p->scb_array[aic7xxx_position(cmd)]);
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
base = p->base;
+ save_flags(processor_flags);
+ cli();
+
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
- scb->position, TCL_OF_SCB(scb));
+ if (scb != NULL)
+ {
+ printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ }
+ else
+ {
+ printk("aic7xxx: Abort called with no SCB for cmd.\n");
+ }
#endif
+ if (p->flags & IN_TIMEOUT)
+ {
+ /*
+ * We've already started a recovery operation.
+ */
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+ {
+ restore_flags(processor_flags);
+ return (SCSI_ABORT_PENDING);
+ }
+ else
+ {
+ /*
+ * This is the second time we've tried to abort the recovery
+ * SCB. We want the mid-level SCSI code to call the reset
+ * function to reset the SCSI bus.
+ */
+ restore_flags(processor_flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ }
if (cmd->serial_number != cmd->serial_number_at_timeout)
{
result = SCSI_ABORT_NOT_RUNNING;
@@ -5509,14 +6814,34 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
{
result = SCSI_ABORT_NOT_RUNNING;
}
- else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
+ else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
{
result = SCSI_ABORT_NOT_RUNNING;
}
else
{
- result = SCSI_ABORT_SNOOZE;
+ /*
+ * XXX - Check use of IN_TIMEOUT to see if we're Doing the
+ * Right Thing with it.
+ */
+ p->flags |= IN_TIMEOUT;
+ result = aic7xxx_bus_device_reset(p, scb->cmd);
+ switch (result)
+ {
+ case SCSI_RESET_NOT_RUNNING:
+ p->flags &= ~IN_TIMEOUT;
+ result = SCSI_ABORT_NOT_RUNNING;
+ break;
+ case SCSI_RESET_PENDING:
+ result = SCSI_ABORT_PENDING;
+ break;
+ default:
+ p->flags &= ~IN_TIMEOUT;
+ result = SCSI_ABORT_SNOOZE;
+ break;
+ }
}
+ restore_flags(processor_flags);
return (result);
}
@@ -5536,18 +6861,27 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
{
struct aic7xxx_scb *scb = NULL;
struct aic7xxx_host *p;
- int base, found, tindex, min_target, max_target, result = -1;
+ int base, found, tindex, min_target, max_target;
+ int result = -1;
char channel = 'A';
unsigned long processor_flags;
p = (struct aic7xxx_host *) cmd->host->hostdata;
- scb = (p->scb_array[aic7xxx_position(cmd)]);
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
base = p->base;
channel = cmd->channel ? 'B': 'A';
tindex = (cmd->channel << 4) | cmd->target;
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#ifdef 0 /* AIC7XXX_DEBUG_ABORT */
+ if (scb != NULL)
+ {
+ printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ }
+ else
+ {
+ printk("aic7xxx: Reset called with no SCB for cmd.\n");
+ }
#endif
/*
@@ -5562,34 +6896,45 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
if (scb->cmd != cmd)
scb = NULL;
- if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
- && (scb != NULL))
+ if (p->flags & IN_TIMEOUT)
{
/*
- * Attempt a bus device reset if commands have completed successfully
- * since the last bus device reset, or it has been less than 100ms
- * since the last reset.
+ * We've already started a recovery operation.
*/
- if ((p->flags & DEVICE_SUCCESS) ||
- ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0)
{
- if (cmd->serial_number != cmd->serial_number_at_timeout)
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- else
+ restore_flags(processor_flags);
+ return (SCSI_RESET_PENDING);
+ }
+ }
+ else
+ {
+ if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+ && (scb != NULL))
+ {
+ /*
+ * Attempt a bus device reset if commands have completed successfully
+ * since the last bus device reset, or it has been less than 100ms
+ * since the last reset.
+ */
+ if ((p->flags & DEVICE_SUCCESS) ||
+ ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
{
- if (scb == NULL)
+ if (cmd->serial_number != cmd->serial_number_at_timeout)
+ {
+ result = SCSI_RESET_NOT_RUNNING;
+ }
+ else if (scb == NULL)
{
result = SCSI_RESET_NOT_RUNNING;
}
else if (flags & SCSI_RESET_ASYNCHRONOUS)
{
- if (scb->state & SCB_ABORTED)
+ if (scb->flags & SCB_ABORTED)
{
result = SCSI_RESET_PENDING;
}
- else if (!(scb->state & SCB_IN_PROGRESS))
+ else if (!(scb->flags & SCB_ACTIVE))
{
result = SCSI_RESET_NOT_RUNNING;
}
@@ -5600,20 +6945,23 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
if ((flags & SCSI_RESET_SYNCHRONOUS) &&
(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
{
- scb->state |= SCB_ABORTED;
+ scb->flags |= SCB_ABORTED;
result = SCSI_RESET_PENDING;
}
else
{
+ p->flags |= IN_TIMEOUT;
result = aic7xxx_bus_device_reset(p, cmd);
if (result == 0)
+ {
+ p->flags &= ~IN_TIMEOUT;
result = SCSI_RESET_PENDING;
+ }
}
- }
+ }
}
}
}
-
if (result == -1)
{
/*
@@ -5626,11 +6974,11 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
{
result = SCSI_RESET_NOT_RUNNING;
}
- else if (!(scb->state & SCB_IN_PROGRESS))
+ else if (!(scb->flags & SCB_ACTIVE))
{
result = SCSI_RESET_NOT_RUNNING;
}
- else if ((scb->state & SCB_ABORTED) &&
+ else if ((scb->flags & SCB_ABORTED) &&
(!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
{
result = SCSI_RESET_PENDING;
@@ -5642,8 +6990,9 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
/*
* The reset channel function assumes that the sequencer is paused.
*/
- PAUSE_SEQUENCER(p);
+ pause_sequencer(p);
found = aic7xxx_reset_channel(p, channel, TRUE);
+ p->flags = p->flags & ~IN_TIMEOUT;
/*
* If this is a synchronous reset and there is no SCB for this
@@ -5689,8 +7038,10 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
}
result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+ p->flags &= ~IN_TIMEOUT;
}
}
+ aic7xxx_run_waiting_queues(p);
restore_flags(processor_flags);
return (result);
}