+#define EXPERIMENTAL_FLAGS 0
+
/*+M*************************************************************************
* Adaptec AIC7xxx device driver for Linux.
*
* Parts of this driver are based on the FreeBSD driver by Justin
* T. Gibbs.
*
- * 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
- * aic7xxx=no_reset
- * aic7xxx=ultra
- * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level
- * aic7xxx=verbose
+ * Form: aic7xxx=extended,no_reset
*
- * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
*
- * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
+ * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
*-M*************************************************************************/
#ifdef MODULE
#include "scsi.h"
#include "hosts.h"
#include "aic7xxx.h"
-
-#ifndef u_int8_t
-typedef unsigned char u_int8_t;
-#endif
-#include "aic7xxx/bsd_q.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() */
*/
#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
-struct proc_dir_entry proc_scsi_aic7xxx = {
+static struct proc_dir_entry proc_scsi_aic7xxx = {
PROC_SCSI_AIC7XXX, 7, "aic7xxx",
- S_IFDIR | S_IRUGO | S_IXUGO, 2,
- 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-#define AIC7XXX_C_VERSION "$Revision: 4.1 $"
+#define AIC7XXX_C_VERSION "$Revision: 4.0 $"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) ((a < b) ? a : b)
#define ALL_TARGETS -1
-#define ALL_CHANNELS '\0'
-#define ALL_LUNS -1
#ifndef TRUE
# define TRUE 1
#endif
* 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. It is
- * not an option anymore.
+ * 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 Tagged queueing - this driver is capable of tagged queueing
* but I am unsure as to how well the higher level driver implements
* 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
- * 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 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 SCB paging support - SCB paging is enabled by defining
* AIC7XXX_PAGE_ENABLE. Support for this was taken from the
* 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, 01/26/96
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
*/
+/* Uncomment this for testing twin bus support. */
+#define AIC7XXX_TWIN_SUPPORT
+
/* Uncomment this for tagged queueing. */
-#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
-#define AIC7XXX_TAGGED_QUEUEING
-#endif
+/* #define AIC7XXX_TAGGED_QUEUEING */
/*
* You can try raising me if tagged queueing is enabled, or lowering
* me if you only have 4 SCBs.
*/
-#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
-#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
-#endif
+/* #define AIC7XXX_CMDS_PER_LUN 8 */
/* 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
/*
- * Control collection of SCSI transfer statistics for the /proc filesystem.
+ * Uncomment the following define for 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.
*/
-#ifdef CONFIG_AIC7XXX_PROC_STATS
-#define AIC7XXX_PROC_STATS
-#endif
+/* #define AIC7XXX_PROC_STATS */
/*
- * Enable SCB paging.
+ * Uncomment the following to enable SCB paging.
*/
-#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
-#define AIC7XXX_PAGE_ENABLE
-#endif
+/* #define AIC7XXX_PAGE_ENABLE */
/*
- * 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.
+ * Uncomment the following to enable sharing of the external bank
+ * of 255 SCBs for the 3985.
*/
-#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
-#define AIC7XXX_USE_EXT_SCBRAM
-#endif
+#define AIC7XXX_SHARE_SCBS
/*
* For debugging the abort/reset code.
*/
#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
-{
- unsigned 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_enable
- * 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.
- *
- * 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, 128 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, 128, 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
*/
AIC_7882, /* PCI aic7882 on 3940 Ultra */
AIC_7883, /* PCI aic7883 on 3985 Ultra */
AIC_7884 /* PCI aic7884 on 294x Ultra Differential */
-} aha_chip_type;
+} aha_type;
typedef enum {
AIC_777x, /* AIC-7770 based */
- AIC_785x, /* AIC-7850 based (3 SCBs)*/
- AIC_786x, /* AIC-7860 based (7850 ultra) */
+ AIC_785x, /* AIC-7850 based */
AIC_787x, /* AIC-7870 based */
- AIC_788x /* AIC-7880 based (ultra) */
-} aha_chip_class_type;
+ AIC_788x /* AIC-7880 based */
+} aha_chip_type;
typedef enum {
AIC_SINGLE, /* Single Channel */
* Don't forget to change this when changing the types!
*/
static const char *board_names[] = {
- "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 */
+ "<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 */
};
/*
*/
#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)
#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
*/
* 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;
/*
*
/*
* Host Adapter Control Bits
*/
-#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+/* UNUSED 0x0001 */
#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 */
+#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */
#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 boot */
+#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */
/* UNUSED 0xFF80 */
unsigned short adapter_control; /* word 17 */
unsigned short checksum; /* word 31 */
};
-#define SELBUS_MASK 0x0a
-#define SELNARROW 0x00
-#define SELBUSB 0x08
-#define SINGLE_BUS 0x00
-#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)
+#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);
/*
* If an error occurs during a data transfer phase, run the command
*/
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
*/
#define MAX_SG 256
-/*
- * 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
-
-/*
- * We keep a pool of kernel memory for hardware scatter/gather
- * arrays. One page (PAGE_SIZE) of kernel memory is allocated
- * at a time, and SG arrays are taken from this pool. An SG
- * array is 2048 bytes (256 * 8) long. A page of memory is either
- * 4096 (i386) or 8192 (alpha) bytes from which SG arrays can be
- * taken without fragmenting memory.
- *
- * The number of free SG arrays left in the SG pool is kept in
- * aic7xxx_sg_arrays_free. The pool of SG arrays is kept in
- * aic7xxx_next_sg_array; after each each allocation of an SG array,
- * this variable is updated to point to the next available SG array
- * in the pool. When aic7xxx_sg_arrays_free is zero, a new page is
- * allocated for the pool. See aic7xxx_allocate_sglist() for
- * further details.
- *
- * The SG memory pool is global to the driver, and is not per
- * instance of the driver.
- */
-static struct hw_scatterlist *aic7xxx_next_sg_array = NULL;
-static int aic7xxx_sg_arrays_free = 0;
-
-struct aic7xxx_hwscb {
+struct aic7xxx_scb {
/* ------------ 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 int SG_list_pointer;
+/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed));
/* 8*/ unsigned char residual_SG_segment_count;
-/* 9*/ unsigned char residual_data_count[3];
-/*12*/ unsigned int data_pointer;
-/*16*/ unsigned int data_count;
-/*20*/ unsigned int SCSI_cmd_pointer;
+/* 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));
/*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
#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
* via PIO to initialize a transaction.
*/
-/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
+/*26*/ u_char next; /* Used to thread SCBs awaiting selection
* or disconnected down in the sequencer.
*/
-/*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.
- */
+ /*-----------------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 */
};
/*
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)
*/
- 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;
+ 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
/*
* Define a structure used for each host adapter, only one per IRQ.
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 */
- 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
+ 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 flags;
unsigned int isr_count; /* Interrupt count */
unsigned short needsdtr_copy; /* default config */
unsigned short wdtr_pending;
unsigned short orderedtag;
unsigned short discenable; /* Targets allowed to disconnect */
- aha_chip_type chip_type; /* card type */
- aha_chip_class_type chip_class;
+ aha_type type; /* card type */
+ aha_chip_type chip_type; /* chip base type */
aha_bus_type bus_type; /* normal/twin/wide bus */
- unsigned char chan_num; /* for 39xx, channel number */
+ char * mbase; /* I/O memory address */
+ unsigned char chan_num; /* for 3940/3985, channel number */
unsigned char unpause; /* unpause value for HCNTRL */
unsigned char pause; /* pause value for HCNTRL */
unsigned char qcntmask;
- unsigned char qfullcount;
- unsigned char curqincnt;
+ struct seeprom_config seeprom;
struct Scsi_Host *next; /* allow for multiple IRQs */
- unsigned char activescbs; /* active scbs */
+ 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)
+ */
scb_queue_type waiting_scbs; /*
- * SCBs waiting for space in
- * the QINFIFO.
+ * SCBs waiting to be paged and
+ * started.
*/
- scb_data_type *scb_data;
+ 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;
struct aic7xxx_cmd_queue {
Scsi_Cmnd *head;
#define BUS_DEVICE_RESET_PENDING 0x02
int flags;
int commands_sent;
- int active_cmds;
} device_status[16];
#ifdef AIC7XXX_PROC_STATS
/*
#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/4 to the proper value to
- * stick in the SCSIRATE reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns to the proper value to
+ * stick in the scsiscfr reg to use that transfer rate.
*/
static struct {
short period;
short rate;
const char *english;
} aic7xxx_syncrates[] = {
- { 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" }
+ { 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" }
};
static int num_aic7xxx_syncrates =
#ifdef CONFIG_PCI
static int number_of_3940s = 0;
static int number_of_3985s = 0;
-#endif /* CONFIG_PCI */
+#ifdef AIC7XXX_SHARE_SCBS
+static scb_usage_type *shared_3985_scbs = NULL;
+#endif
+#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)
{
- 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);
+ 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);
}
#endif
#else
+# define debug_config(x)
# define debug_scb(x)
#endif AIC7XXX_DEBUG
-#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 CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+#define TCL_OF_SCB(x) (((x)->target_channel_lun >> 4) & 0xf), \
+ (((x)->target_channel_lun >> 3) & 0x01), \
+ ((x)->target_channel_lun & 0x07)
-#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
+#define TARGET_INDEX(x) ((x)->target | ((x)->channel << 3))
/*
* XXX - these options apply unilaterally to _all_ 274x/284x/294x
- * cards in the system. This should be fixed.
+ * cards in the system. This should be fixed, but then,
+ * does anyone really have more than one in a machine?
*/
static unsigned int aic7xxx_extended = 0; /* extended translation on? */
static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */
* 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:
{ "no_reset", &aic7xxx_no_reset },
{ "irq_trigger", &aic7xxx_irq_trigger },
{ "ultra", &aic7xxx_enable_ultra },
- { "verbose", &aic7xxx_verbose },
{ NULL, NULL }
};
/*+F*************************************************************************
* Function:
- * pause_sequencer
+ * aic7xxx_loadseq
*
* Description:
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
+ * Load the sequencer code into the controller memory.
*-F*************************************************************************/
-static inline void
-pause_sequencer(struct aic7xxx_host *p)
+static void
+aic7xxx_loadseq(int base)
{
- outb(p->pause, p->base + HCNTRL);
- while ((inb(p->base + HCNTRL) & PAUSE) == 0);
- {
- ;
- }
-}
+ 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"
+ };
-/*+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);
- }
+ /*
+ * 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);
+
+ outsb(SEQRAM + base, seqprog, sizeof(seqprog));
+
+ /*
+ * 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));
}
/*+F*************************************************************************
* Function:
- * restart_sequencer
+ * aic7xxx_delay
*
* Description:
- * Restart the sequencer program from address zero. This assumes
- * that the sequencer is already paused.
+ * Delay for specified amount of time.
*-F*************************************************************************/
-static inline void
-restart_sequencer(struct aic7xxx_host *p)
+static void
+aic7xxx_delay(int seconds)
{
- /* Set the sequencer address to 0. */
- outb(0, p->base + SEQADDR0);
- outb(0, p->base + SEQADDR1);
+ unsigned long i;
- /*
- * 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);
+ i = jiffies + (seconds * HZ); /* compute time to stop */
- unpause_sequencer(p, /*unpause_always*/ TRUE);
+ while (jiffies < i)
+ {
+ ; /* Do nothing! */
+ }
}
-
/*+F*************************************************************************
* Function:
- * aic7xxx_next_patch
+ * rcs_version
*
* Description:
- * Find the next patch to download.
+ * Return a string containing just the RCS version number from either
+ * an Id or Revision RCS clause.
*-F*************************************************************************/
-static struct patch *
-aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+static const char *
+rcs_version(const char *version_info)
{
- while (cur_patch != NULL)
+ static char buf[10];
+ char *bp, *ep;
+
+ bp = NULL;
+ strcpy(buf, "????");
+ if (!strncmp(version_info, "$Id: ", 5))
{
- if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
- || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
- || (instrptr >= cur_patch->end))
+ if ((bp = strchr(version_info, ' ')) != NULL)
{
- /*
- * 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)
+ bp++;
+ if ((bp = strchr(bp, ' ')) != NULL)
{
- /* Out of patches. */
- cur_patch = NULL;
+ bp++;
}
}
- else
+ }
+ else
+ {
+ if (!strncmp(version_info, "$Revision: ", 11))
{
- /* Found an OK patch. */
- break;
+ if ((bp = strchr(version_info, ' ')) != NULL)
+ {
+ bp++;
+ }
+ }
+ }
+
+ if (bp != NULL)
+ {
+ if ((ep = strchr(bp, ' ')) != NULL)
+ {
+ register int len = ep - bp;
+
+ strncpy(buf, bp, len);
+ buf[len] = '\0';
}
}
- return (cur_patch);
-}
+ return buf;
+}
/*+F*************************************************************************
* Function:
- * aic7xxx_download_instr
+ * aic7xxx_info
*
* Description:
- * Find the next patch to download.
+ * Return a string describing the driver.
*-F*************************************************************************/
-static void
-aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
{
- 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(struct aic7xxx_host *p)
-{
- int options;
- struct patch *cur_patch;
- int i;
- int downloaded;
-
- 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;
-
- cur_patch = patches;
- outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
- outb(0, p->base + SEQADDR0);
- outb(0, p->base + SEQADDR1);
-
- 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*************************************************************************
- * Function:
- * aic7xxx_delay
- *
- * Description:
- * 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)
-{
- int i;
-
- /*
- * Call udelay() for 1 millisecond inside a loop for
- * the requested amount of seconds.
- */
- for (i=0; i < seconds*1000; i++)
- {
- udelay(1000); /* Delay for 1 millisecond. */
- }
-}
-
-/*+F*************************************************************************
- * Function:
- * rcs_version
- *
- * Description:
- * Return a string containing just the RCS version number from either
- * an Id or Revision RCS clause.
- *-F*************************************************************************/
-const char *
-rcs_version(const char *version_info)
-{
- static char buf[10];
- char *bp, *ep;
-
- bp = NULL;
- strcpy(buf, "????");
- if (!strncmp(version_info, "$Id: ", 5))
- {
- if ((bp = strchr(version_info, ' ')) != NULL)
- {
- bp++;
- if ((bp = strchr(bp, ' ')) != NULL)
- {
- bp++;
- }
- }
- }
- else
- {
- if (!strncmp(version_info, "$Revision: ", 11))
- {
- if ((bp = strchr(version_info, ' ')) != NULL)
- {
- bp++;
- }
- }
- }
-
- if (bp != NULL)
- {
- if ((ep = strchr(bp, ' ')) != NULL)
- {
- register int len = ep - bp;
-
- strncpy(buf, bp, len);
- buf[len] = '\0';
- }
- }
-
- return buf;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_info
- *
- * Description:
- * Return a string describing the driver.
- *-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *notused)
-{
- static char buffer[128];
+ static char buffer[128];
strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
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;
}
* aic7xxx_length
*
* Description:
- * 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.
+ * 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.
*-F*************************************************************************/
static unsigned
aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
if (cmd->use_sg)
{
- for (i = length = 0; i < segments; i++)
+ for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
{
length += sg[i].length;
}
*-F*************************************************************************/
static void
aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
- unsigned char *period, unsigned char *offset, int target, char channel)
+ short period, unsigned char offset, int target, char channel)
{
- int i = num_aic7xxx_syncrates;
+ int i;
unsigned long ultra_enb_addr;
unsigned char ultra_enb, sxfrctl0;
* If the offset is 0, then the device is requesting asynchronous
* transfers.
*/
- if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
+ if (offset != 0)
{
for (i = 0; i < num_aic7xxx_syncrates; i++)
{
- if (*period <= aic7xxx_syncrates[i].period)
+ if ((aic7xxx_syncrates[i].period - period) >= 0)
{
/*
* Watch out for Ultra speeds when ultra is not enabled and
*/
continue;
}
- *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
- *period = aic7xxx_syncrates[i].period;
+ *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
- if (aic7xxx_verbose)
+ /*
+ * Ensure Ultra mode is set properly for this target.
+ */
+ ultra_enb_addr = ULTRA_ENB;
+ if ((channel == 'B') || (target > 7))
{
- printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
- "offset %d.\n", p->host_no, target, channel,
- aic7xxx_syncrates[i].english, *offset);
+ ultra_enb_addr++;
}
- break;
+ ultra_enb = inb(p->base + ultra_enb_addr);
+ sxfrctl0 = inb(p->base + SXFRCTL0);
+ if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
+ {
+ ultra_enb |= 0x01 << (target & 0x07);
+ sxfrctl0 |= ULTRAEN;
+ }
+ 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;
}
}
}
- 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);
- }
- }
+ /*
+ * 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);
/*
- * Ensure Ultra mode is set properly for this target.
+ * 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.
*/
- ultra_enb_addr = ULTRA_ENB;
- if ((channel == 'B') || (target > 7))
+ if (p->type == AIC_284x)
{
- 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))
- {
- ultra_enb |= 0x01 << (target & 0x07);
- sxfrctl0 |= FAST20;
+ outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
}
else
{
- ultra_enb &= ~(0x01 << (target & 0x07));
- sxfrctl0 &= ~FAST20;
+ outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
}
- outb(ultra_enb, p->base + ultra_enb_addr);
- outb(sxfrctl0, p->base + SXFRCTL0);
+
+ 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);
}
/*+F*************************************************************************
queue->tail = NULL;
}
-/*+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
* 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,
- int lun, unsigned char tag)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
{
- 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;
+ int targ = (scb->target_channel_lun >> 4) & 0x0F;
+ char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
#ifdef AIC7XXX_DEBUG_ABORT
- 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);
+ printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
+ target, channel, targ, chan);
#endif
- 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)
+ if (target == ALL_TARGETS)
{
- outb(prev, p->base + SCBPTR);
- outb(next, p->base + SCB_NEXT);
+ return (chan == channel);
}
else
{
- outb(next, p->base + DISCONNECTED_SCBH);
- }
-
- if (next != SCB_LIST_NULL)
- {
- outb(next, p->base + SCBPTR);
- outb(prev, p->base + SCB_PREV);
+ return ((chan == channel) && (targ == target));
}
- return next;
}
/*+F*************************************************************************
* aic7xxx_busy_target
*
* Description:
- * Set the specified target busy.
+ * Set the specified target active.
*-F*************************************************************************/
static void
-aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
- char channel, unsigned char scbid)
-{
- 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)
+aic7xxx_busy_target(unsigned char target, char channel, int base)
{
- 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;
+ unsigned char active;
+ unsigned long active_port = ACTIVE_A + base;
- 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)
+ if ((target > 0x07) || (channel == 'B'))
{
- outb(SCB_LIST_NULL, p->base + scb_offset);
+ /*
+ * targets on the Second channel or above id 7 store info in byte two
+ * of ACTIVE
+ */
+ active_port++;
}
- outb(active_scb, p->base + SCBPTR);
- return (busy_scbid);
+ active = inb(active_port);
+ active |= (0x01 << (target & 0x07));
+ outb(active, active_port);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_find_scb
+ * aic7xxx_unbusy_target
*
* Description:
- * 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.
+ * Set the specified target inactive.
*-F*************************************************************************/
-static unsigned char
-aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+static void
+aic7xxx_unbusy_target(unsigned char target, char channel, int base)
{
- unsigned char saved_scbptr;
- unsigned char curindex;
+ unsigned char active;
+ unsigned long active_port = ACTIVE_A + base;
- saved_scbptr = inb(p->base + SCBPTR);
- curindex = 0;
- for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
+ if ((target > 0x07) || (channel == 'B'))
{
- outb(curindex, p->base + SCBPTR);
- if (inb(p->base + SCB_TAG) == scb->hscb->tag)
- {
- break;
- }
- }
- outb(saved_scbptr, p->base + SCBPTR);
- if (curindex >= p->scb_data->maxhscbs)
- {
- curindex = SCB_LIST_NULL;
+ /*
+ * targets on the Second channel or above id 7 store info in byte two
+ * of ACTIVE
+ */
+ active_port++;
}
-
- return (curindex);
+ active = inb(active_port);
+ active &= ~(0x01 << (target & 0x07));
+ outb(active, active_port);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_allocate_sglist
+ * aic7xxx_allocate_scb
*
* Description:
- * Allocate a hardware SG array.
+ * 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.
*-F*************************************************************************/
-static struct hw_scatterlist *
-aic7xxx_allocate_sglist(void)
+static struct aic7xxx_scb *
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
{
- int alloc_size;
- struct hw_scatterlist *sg = NULL;
+ struct aic7xxx_scb *scbp = NULL;
+ int maxscbs;
- if (aic7xxx_next_sg_array == NULL)
+ scbp = p->scb_link->free_scbs.head;
+ if (scbp != NULL)
{
- alloc_size = sizeof (struct hw_scatterlist) * MAX_SG;
- aic7xxx_sg_arrays_free = PAGE_SIZE / alloc_size;
- alloc_size = alloc_size * aic7xxx_sg_arrays_free;
- if (alloc_size == 0)
- {
- panic("aic7xxx: SG list doesn't fit in a page.\n");
- }
-#ifdef DMA_KMALLOC
- aic7xxx_next_sg_array = (struct hw_scatterlist *)
- kmalloc(alloc_size, GFP_ATOMIC | GFP_DMA);
-#else
- aic7xxx_next_sg_array = (struct hw_scatterlist *)
- kmalloc(alloc_size, GFP_ATOMIC);
-#endif
+ scbq_remove_head(&p->scb_link->free_scbs);
}
- if (aic7xxx_next_sg_array != NULL)
+ else
{
- sg = aic7xxx_next_sg_array;
- aic7xxx_sg_arrays_free = aic7xxx_sg_arrays_free - 1;
- if (aic7xxx_sg_arrays_free == 0)
+ /*
+ * This should always be NULL if paging is not enabled.
+ */
+ scbp = p->page_scbs.head;
+ if (scbp != NULL)
{
- aic7xxx_next_sg_array = NULL;
+ scbq_remove_head(&p->page_scbs);
}
else
{
/*
- * Point to the next available SG array in the pool.
+ * Set limit the SCB allocation to the maximum number of
+ * hardware SCBs if paging is not enabled; otherwise use
+ * the maximum (255).
*/
- aic7xxx_next_sg_array = &(aic7xxx_next_sg_array[MAX_SG]);
- }
- }
- return (sg);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_allocate_scb
- *
- * Description:
- * 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;
- struct aic7xxx_hwscb *hscbp = NULL;
-#ifdef AGRESSIVE
- long processor_flags;
-
- save_flags(processor_flags);
- cli();
-#endif
-
- scbp = p->scb_data->free_scbs.head;
- if (scbp != NULL)
- {
- scbq_remove_head(&p->scb_data->free_scbs);
- }
- else
- {
- if (p->scb_data->numscbs < p->scb_data->maxscbs)
- {
- int scb_index = p->scb_data->numscbs;
- int scb_size = sizeof(struct aic7xxx_scb);
-
- scbp = kmalloc(scb_size, GFP_ATOMIC);
- if (scbp != NULL)
+ if (p->flags & PAGE_ENABLED)
+ maxscbs = p->maxscbs;
+ else
+ maxscbs = p->maxhscbs;
+ if (p->scb_link->numscbs < maxscbs)
{
- memset(scbp, 0, sizeof(struct aic7xxx_scb));
- hscbp = &p->scb_data->hscbs[scb_index];
- scbp->hscb = hscbp;
- scbp->sg_list = aic7xxx_allocate_sglist();
- if (scbp->sg_list == NULL)
- {
- kfree(scbp);
- }
- else
+ int scb_index = p->scb_link->numscbs;
+ int scb_size = sizeof(struct aic7xxx_scb);
+
+ p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
+ scbp = (p->scb_array[scb_index]);
+ if (scbp != NULL)
{
- 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;
+ 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++;
}
}
}
}
-#ifdef AIC7XXX_DEBUG
if (scbp != NULL)
{
- p->activescbs++;
- }
-#endif
-
-#ifdef AGRESSIVE
- restore_flags(processor_flags);
+#ifdef AIC7XXX_DEBUG
+ p->scb_link->activescbs++;
#endif
+ }
return (scbp);
}
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;
* aic7xxx_free_scb
*
* Description:
- * Free the scb and insert into the free scb list.
+ * Free the scb and update the page, waiting, free scb lists.
*-F*************************************************************************/
static void
aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- struct aic7xxx_hwscb *hscb;
- long flags;
-
- hscb = scb->hscb;
- save_flags(flags);
- cli();
+ struct aic7xxx_scb *wscb;
- scb->flags = SCB_FREE;
+ scb->state = SCB_FREE;
scb->cmd = NULL;
- hscb->control = 0;
- hscb->target_status = 0;
+ scb->control = 0;
+ scb->state = 0;
- scbq_insert_head(&p->scb_data->free_scbs, scb);
+ 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);
+ }
#ifdef AIC7XXX_DEBUG
- p->activescbs--; /* For debugging purposes. */
+ p->scb_link->activescbs--; /* For debugging purposes. */
#endif
-
- restore_flags(flags);
+ }
}
/*+F*************************************************************************
{
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_run_done_queue
+ * aic7xxx_done_aborted_scbs
*
* Description:
- * 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.
+ * Calls the scsi_done() for the Scsi_Cmnd of each scb in the
+ * aborted list, and adds each scb to the free list.
*-F*************************************************************************/
static void
-aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
+aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
{
+ Scsi_Cmnd *cmd;
struct aic7xxx_scb *scb;
int i;
- for (i = 0; i < p->scb_data->numscbs; i++)
+ for (i = 0; i < p->scb_link->numscbs; i++)
{
- scb = p->scb_data->scb_array[i];
- if (scb->flags & SCB_QUEUED_FOR_DONE)
+ scb = (p->scb_array[i]);
+ if (scb->state & SCB_QUEUED_FOR_DONE)
{
#ifdef AIC7XXX_DEBUG_ABORT
- printk("(scsi%d:%d:%d) Aborting scb %d\n",
- p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+ printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
+ scb->position, TCL_OF_SCB(scb));
#endif
- aic7xxx_done(p, scb);
+ /*
+ * 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 */
}
}
- if (complete)
- {
- aic7xxx_done_cmds_complete(p);
- }
+}
+
+/*+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);
}
/*+F*************************************************************************
*-F*************************************************************************/
static unsigned char
aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
- unsigned char scbpos, unsigned char prev)
+ 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(p->base + SCBPTR);
- outb(scbpos, p->base + SCBPTR);
- next = inb(p->base + SCB_NEXT);
+ curscb = inb(SCBPTR + base);
+ outb(scb->position, SCBPTR + base);
+ next = inb(SCB_NEXT + base);
/*
* Clear the necessary fields
*/
- outb(0, p->base + SCB_CONTROL);
-
- aic7xxx_add_curscb_to_free_list(p);
+ outb(0, SCB_CONTROL + base);
+ outb(SCB_LIST_NULL, SCB_NEXT + base);
+ aic7xxx_unbusy_target(target, channel, base);
/*
* Update the waiting list
/*
* First in the list
*/
- outb(next, p->base + WAITING_SCBH);
+ outb(next, WAITING_SCBH + base);
}
else
{
/*
* Select the scb that pointed to us and update its next pointer.
*/
- outb(prev, p->base + SCBPTR);
- outb(next, p->base + SCB_NEXT);
+ outb(prev, SCBPTR + base);
+ outb(next, SCB_NEXT + base);
}
/*
* Point us back at the original scb position and inform the SCSI
* system that the command has been aborted.
*/
- outb(curscb, p->base + SCBPTR);
- scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
- scb->flags &= ~SCB_ACTIVE;
+ outb(curscb, SCBPTR + base);
+ scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
scb->cmd->result = (DID_RESET << 16);
return (next);
}
-/*+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
* all active and queued scbs for that target/channel.
*-F*************************************************************************/
static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
- int lun, unsigned char tag)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
{
- struct aic7xxx_scb *scbp;
+ int base = p->base;
+ struct aic7xxx_scb *scb;
unsigned char active_scb;
int i = 0;
- int found;
+ int found = 0;
/*
* Restore this when we're done
*/
- active_scb = inb(p->base + SCBPTR);
+ active_scb = inb(SCBPTR + base);
#ifdef AIC7XXX_DEBUG_ABORT
- printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
- p->host_no, target, CHAN_TO_INT(channel), active_scb);
+ printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
+ target, channel, active_scb);
#endif
-
/*
- * Deal with the busy target and linked next issues.
+ * Search the QINFIFO.
*/
{
- int min_target, max_target;
- unsigned char busy_scbid;
+ int saved_queue[AIC7XXX_MAXSCB];
+ int queued = inb(QINCNT + base) & p->qcntmask;
- /* Make all targets 'relative' to bus A. */
- if (target == ALL_TARGETS)
+ for (i = 0; i < (queued - found); i++)
{
- switch (channel)
+ 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))
{
- 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;
+ /*
+ * 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++;
}
}
- else
- {
- min_target = target + channel == 'B' ? 8 : 0;
- max_target = min_target;
- }
-
- for (i = min_target; i <= max_target; i++)
+ /*
+ * Now put the saved scbs back.
+ */
+ for (queued = 0; queued < i; queued++)
{
- 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;
- }
- }
+ outb(saved_queue[queued], QINFIFO + base);
}
}
- 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, scb_index;
+ unsigned char next, prev;
- next = inb(p->base + WAITING_SCBH); /* Start at head of list. */
+ next = inb(WAITING_SCBH + base); /* Start at head of list. */
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: 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))
+ outb(next, SCBPTR + base);
+ scb = (p->scb_array[inb(SCB_TAG + base)]);
+ /*
+ * Select the SCB.
+ */
+ if (aic7xxx_match_scb(scb, target, channel))
{
- 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;
- }
- }
+ next = aic7xxx_abort_waiting_scb(p, scb, prev);
found++;
}
else
{
prev = next;
- 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);
+ next = inb(SCB_NEXT + base);
}
}
}
/*
* Go through the entire SCB array now and look for commands for
- * for this target that are stillactive. These are other (most likely
+ * for this target that are active. These are other (most likely
* tagged) commands that were disconnected when the reset occurred.
*/
- for (i = 0; i < p->scb_data->numscbs; i++)
+ for (i = 0; i < p->scb_link->numscbs; i++)
{
- scbp = p->scb_data->scb_array[i];
- if (((scbp->flags & SCB_ACTIVE) != 0) &&
- aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ scb = (p->scb_array[i]);
+ if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
{
- 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)
+ /*
+ * Ensure the target is "free"
+ */
+ aic7xxx_unbusy_target(target, channel, base);
+ if (! (scb->state & SCB_PAGED_OUT))
{
- scbq_remove(&p->waiting_scbs, scbp);
- scbp->flags &= ~SCB_WAITINGQ;
+ outb(scb->position, SCBPTR + base);
+ outb(0, SCB_CONTROL + base);
}
+ scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scb->cmd->result = (DID_RESET << 16);
+ found++;
}
}
- outb(active_scb, p->base + SCBPTR);
+ outb(active_scb, SCBPTR + base);
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
* Reset the current SCSI bus.
*-F*************************************************************************/
static void
-aic7xxx_reset_current_bus(struct aic7xxx_host *p)
+aic7xxx_reset_current_bus(int 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);
-
+ outb(SCSIRSTO, SCSISEQ + base);
udelay(1000);
+ outb(0, SCSISEQ + base);
}
/*+F*************************************************************************
static int
aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
{
- unsigned long offset, offset_max;
- int found;
+ int base = p->base;
unsigned char sblkctl;
char cur_channel;
+ unsigned long offset, offset_max;
+ int found;
- 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, ALL_LUNS, SCB_LIST_NULL);
+ found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
if (channel == 'B')
{
p->needsdtr |= (p->needsdtr_copy & 0xFF00);
p->sdtr_pending &= 0x00FF;
- offset = TARG_SCRATCH + 8;
- offset_max = TARG_SCRATCH + 16;
+ outb(0, ACTIVE_B + base);
+ offset = TARG_SCRATCH + base + 8;
+ offset_max = TARG_SCRATCH + base + 16;
}
else
{
p->needwdtr = p->needwdtr_copy;
p->sdtr_pending = 0x0;
p->wdtr_pending = 0x0;
- offset = TARG_SCRATCH;
- offset_max = TARG_SCRATCH + 16;
+ outb(0, ACTIVE_A + base);
+ outb(0, ACTIVE_B + base);
+ offset = TARG_SCRATCH + base;
+ offset_max = TARG_SCRATCH + base + 16;
}
else
{
- /* Channel A */
p->needsdtr |= (p->needsdtr_copy & 0x00FF);
p->sdtr_pending &= 0xFF00;
- offset = TARG_SCRATCH;
- offset_max = TARG_SCRATCH + 8;
+ outb(0, ACTIVE_A + base);
+ offset = TARG_SCRATCH + base;
+ offset_max = TARG_SCRATCH + base + 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(p->base + offset);
+ targ_scratch = inb(offset);
targ_scratch &= SXFR;
- outb(targ_scratch, p->base + offset);
+ outb(targ_scratch, offset);
offset++;
}
/*
* Reset the bus and unpause/restart the controller
*/
- sblkctl = inb(p->base + SBLKCTL);
+
+ /*
+ * Case 1: Command for another bus is active
+ */
+ sblkctl = inb(SBLKCTL + base);
cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
if (cur_channel != channel)
{
- /*
- * Case 1: Command for another bus is active
- */
#ifdef AIC7XXX_DEBUG_ABORT
- printk("scsi%d: Stealthily resetting channel %c\n",
- p->host_no, channel);
+ printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
+ 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, p->base + SBLKCTL);
- outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+ outb(sblkctl ^ SELBUSB, SBLKCTL + base);
if (initiate_reset)
{
- 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));
+ aic7xxx_reset_current_bus(base);
}
- outb(0, p->base + SCSISEQ);
- aic7xxx_clear_intstat(p);
- outb(sblkctl, p->base + SBLKCTL);
- unpause_sequencer(p, /* unpause_always */ FALSE);
+ outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
+ outb(CLRSCSIINT, CLRINT + base);
+ outb(sblkctl, SBLKCTL + base);
+
+ UNPAUSE_SEQUENCER(p);
}
+ /*
+ * 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("scsi%d: Resetting current channel %c\n",
- p->host_no, channel);
+ printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
+ channel);
#endif
- outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
if (initiate_reset)
{
- 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
+ aic7xxx_reset_current_bus(base);
}
- outb(0, p->base + SCSISEQ);
- aic7xxx_clear_intstat(p);
- restart_sequencer(p);
+ outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
+ outb(CLRSCSIINT, CLRINT + base);
+ RESTART_SEQUENCER(p);
#ifdef AIC7XXX_DEBUG_ABORT
- printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
+ printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
#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_run_done_queue(p, /*complete*/ TRUE);
- return (found);
+ 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;
}
/*+F*************************************************************************
* aic7xxx_run_waiting_queues
*
* Description:
- * Scan the awaiting_scbs queue downloading and starting as many
- * scbs as we can.
+ * 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.
+ *
*-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->waiting_scbs.head == NULL)
+ if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
return;
- pause_sequencer(p);
+ save_flags(flags);
+ cli();
+
+ PAUSE_SEQUENCER(p);
+ cur_scb = inb(SCBPTR + base);
+ intstat = inb(INTSTAT + base);
+
/*
* First handle SCBs that are waiting but have been assigned a slot.
*/
- scb = p->waiting_scbs.head;
+ scb = p->assigned_scbs.head;
while (scb != NULL)
{
- if (p->curqincnt >= p->qfullcount)
+ 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)
+ {
+ 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)
{
- p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
- if (p->curqincnt >= p->qfullcount)
- {
+ /* Attempt to page this SCB in */
+ if (disc_scb == SCB_LIST_NULL)
break;
- }
- }
- /*
- * We have some space.
- */
- scbq_remove_head(&(p->waiting_scbs));
- scb->flags &= ~SCB_WAITINGQ;
+ /*
+ * 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)
+ {
+ u_char out_scbi;
+ struct aic7xxx_scb *out_scbp;
- outb(scb->hscb->tag, p->base + QINFIFO);
+ scbq_remove_head(&(p->waiting_scbs));
- if ((p->flags & PAGE_ENABLED) != 0)
+ /*
+ * 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;
+ }
+ else
+ scb = NULL;
+ }
+
+ if (count)
{
- /*
- * We only care about this statistic when paging
- * since it's impossible to overflow the qinfifo
- * in the non-paging case.
+ /*
+ * Update the head of the disconnected list.
*/
- p->curqincnt++;
+ outb(disc_scb, DISCONNECTED_SCBH + base);
+ if (disc_scb != SCB_LIST_NULL)
+ {
+ outb(disc_scb, SCBPTR + base);
+ outb(SCB_LIST_NULL, SCB_PREV + base);
+ }
}
- scb = p->waiting_scbs.head;
}
+ /* Restore old position */
+ outb(cur_scb, SCBPTR + base);
- unpause_sequencer(p, FALSE);
-}
+ /*
+ * Guard against unpausing the sequencer if there is an interrupt
+ * waiting to happen.
+ */
+ if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
+ {
+ UNPAUSE_SEQUENCER(p);
+ }
-/*+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);
+ restore_flags(flags);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_construct_wdtr
+ * aic7xxx_isr
*
* Description:
- * 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
+ * SCSI controller interrupt handler.
*
- * Description:
- * Calculate the residual data not yet transferred.
+ * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ * be disabled all through this function unless we say otherwise.
*-F*************************************************************************/
static void
-aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
{
- struct aic7xxx_hwscb *hscb;
+ 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 */
Scsi_Cmnd *cmd;
- int actual;
- cmd = scb->cmd;
- hscb = scb->hscb;
+ p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
/*
- * Don't destroy valid residual information with
- * residual coming from a check sense operation.
+ * Search for the host with a pending interrupt. If we can't find
+ * one, then we've encountered a spurious interrupt.
*/
- if (((scb->hscb->control & DISCONNECTED) == 0) &&
- (scb->flags & SCB_SENSE) == 0)
+ while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
{
- /*
- * 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)
+ if (p->next == NULL)
+ {
+ p = NULL;
+ }
+ else
{
- 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;
+ p = (struct aic7xxx_host *) p->next->hostdata;
}
}
+ if (p == NULL)
+ return;
+
/*
- * Clean out the residual information in the SCB for the
- * next consumer.
+ * Keep track of interrupts for /proc/scsi
*/
- hscb->residual_data_count[2] = 0;
- hscb->residual_data_count[1] = 0;
- hscb->residual_data_count[0] = 0;
- hscb->residual_SG_segment_count = 0;
-}
-
-/*+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;
+ p->isr_count++;
- if (channel == 'B')
+ if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
{
- scratch_offset += 8;
+ /*
+ * 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;
}
- targ_mask = (0x01 << scratch_offset);
+
+ base = p->base;
/*
- * Go back to async/narrow transfers and renegotiate.
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
*/
- 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);
-}
+ intstat = inb(INTSTAT + base);
-/*+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;
+ /*
+ * Indicate that we're in the interrupt handler.
+ */
+ p->flags |= IN_ISR;
- if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
- {
- 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)
+ if (intstat & BRKADRINT)
{
- channel = 'B';
- scratch_offset += 8;
+ 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_mask = (0x01 << scratch_offset);
- switch (intstat & SEQINT_MASK)
+ if (intstat & SEQINT)
{
- 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;
+ /*
+ * 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);
- busy_scbid = aic7xxx_index_busy_target(p, target, channel,
- /*unbusy*/ FALSE);
- arg1 = inb(p->base + ARG_1);
+ 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);
- if (arg1 == SCB_LIST_NULL)
- {
- /* untagged request */
- scb_index = busy_scbid;
- }
- else
+ switch (intstat & SEQINT_MASK)
+ {
+ case NO_MATCH:
+ if (p->flags & PAGE_ENABLED)
{
- scb_index = arg1;
- }
+ /* SCB Page-in request */
+ struct aic7xxx_scb *outscb;
+ u_char arg_1 = inb(ARG_1 + base);
+ int use_disconnected = FALSE;
- if (scb_index < p->scb_data->numscbs)
- {
- scb = p->scb_data->scb_array[scb_index];
- if (scb->hscb->control & ABORT_SCB)
+ /*
+ * 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)
{
- /*
- * 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));
+ /* Non-tagged command */
+ int index = scsi_id;
+ if (channel == 'B')
+ {
+ index |= SELBUSB;
+ }
+ scb = p->pagedout_ntscbs[index];
+ }
+ else
+ scb = (p->scb_array[arg_1]);
+
+ if (!(scb->state & SCB_PAGED_OUT))
+ {
+ 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);
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;
-
- 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;
-
- 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:
+ /*
+ * 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)
{
- 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)
+ int scb_index;
+
+ outb(CLRCMDINT, CLRINT + base);
+ scb_index = inb(QOUTFIFO + base);
+ if (!(inb(QOUTCNT + base) & p->qcntmask))
{
- outb(SEND_REJ, p->base + RETURN_1);
- break;
+ intstat &= ~CMDCMPLT;
+ }
+ outscb = (p->scb_array[scb_index]);
+ if (!(outscb->state & SCB_ACTIVE))
+ {
+ printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
+ "during NO_MATCH interrupt\n", scb_index, p->host_no);
+ use_disconnected = TRUE;
}
-
- 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)
{
- /* We started it. */
- if (saved_offset == offset)
+ 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))
{
- /*
- * Don't send an SDTR back to the target.
- */
- outb(0, p->base + RETURN_1);
+ /*
+ * Got sense information.
+ */
+ outscb->cmd->flags &= ASKED_FOR_SENSE;
}
- else
+ p->device_status[TARGET_INDEX(outscb->cmd)].flags
+ |= DEVICE_SUCCESS;
+ aic7xxx_done(p, outscb);
+ }
+ }
+ else
+ {
+ use_disconnected = TRUE;
+ }
+ if (use_disconnected)
+ {
+ u_char tag;
+ u_char next;
+ u_char disc_scb = inb(DISCONNECTED_SCBH + base);
+ if (disc_scb != SCB_LIST_NULL)
+ {
+ 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)
+ {
+ /* 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)
{
- /* We went too low - force async. */
- outb(SEND_REJ, p->base + RETURN_1);
+ /*
+ * 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]);
}
+ 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
{
- /*
- * 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);
+ 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);
}
- /*
- * Clear the flags.
- */
- p->needsdtr &= ~target_mask;
- break;
}
+ }
+ 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 MSG_EXT_WDTR:
- {
- unsigned char scratch, bus_width;
-
- if (message_length != MSG_EXT_WDTR_LEN)
- {
- outb(SEND_REJ, p->base + RETURN_1);
- break;
- }
-
- bus_width = inb(p->base + MSGIN_EXT_BYTES);
- scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ case BAD_PHASE:
+ panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
+ break;
- if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
- {
- /*
- * Don't send an WDTR back to the target, since we asked first.
- */
- outb(0, p->base + RETURN_1);
- switch (bus_width)
- {
- 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;
- }
- }
- else
- {
- /*
- * 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);
- }
- p->needwdtr &= ~target_mask;
- outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
- outb(scratch, p->base + SCSIRATE);
- break;
- } /* case MSG_EXT_WDTR */
+ 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;
- default:
- /*
- * Unknown extended message - reject it.
- */
- outb(SEND_REJ, p->base + RETURN_1);
- break;
- } /* switch (message_code) */
- } /* case EXTENDED_MSG */
- break;
+ 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;
- case REJECT_MSG:
- {
+ case SDTR_MSG:
/*
- * 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.
+ * 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.
*/
- 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)
+ transfer = (inb(ARG_1 + base) << 2);
+ offset = inb(ACCUM + base);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
+ /*
+ * The maximum offset for a wide device is 0x08; for a
+ * 8-bit bus device the maximum offset is 0x0F.
+ */
+ if (scratch & WIDEXFER)
{
- /*
- * 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);
+ max_offset = 0x08;
}
else
{
- if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
+ 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))
{
/*
- * note asynch xfers and clear flag
+ * Device requested asynchronous transfers or we're
+ * forcing asynchronous transfers for the first time.
*/
- 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);
+ outb(0, RETURN_1 + base);
+ }
+ else
+ {
+ /*
+ * The first time in forcing asynchronous transfers
+ * failed, so we try sending a reject message.
+ */
+ outb(SEND_REJ, RETURN_1 + base);
}
- /*
- * Otherwise, we ignore it.
- */
}
- outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
- outb(targ_scratch, p->base + SCSIRATE);
- }
- break;
+ 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 BAD_STATUS:
+ case WDTR_MSG:
{
- 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;
+ 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);
- /*
- * 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))
+ if (p->wdtr_pending & target_mask)
{
- 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);
+ /*
+ * 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
{
- cmd = scb->cmd;
- hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
- aic7xxx_status(cmd) = hscb->target_status;
-
- cmd->result |= hscb->target_status;
-
- 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;
-
- 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]);
-
- /*
- * XXX - We should allow disconnection, but can't as it
- * might allow overlapped tagged commands.
- */
- /* 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 the target is busy since this will be an
- * an untagged request.
- */
- 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;
-
- case QUEUE_FULL:
-#ifdef NOT_YET
- if (scb->hscb->control & TAG_ENB)
- {
- 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;
- }
-#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;
+ /*
+ * 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;
- 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 */
+ 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);
+ }
+ p->needwdtr &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
+ break;
}
- break;
- case AWAITING_MSG:
+ case REJECT_MSG:
{
- unsigned char scb_index;
- unsigned char message_offset;
-
- scb_index = inb(p->base + SCB_TAG);
- scb = p->scb_data->scb_array[scb_index];
-
/*
- * 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.
+ * 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.
*/
- message_offset = inb(p->base + MSG_LEN);
- if (scb->flags & SCB_DEVICE_RESET)
+
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
+
+ if (p->wdtr_pending & target_mask)
{
- 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));
+ /*
+ * 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 (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)
+ else
{
- aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
- }
- else if (scb->flags & SCB_MSGOUT_SDTR)
- {
- unsigned char target_scratch;
- unsigned short ultra_enable;
- int i, sxfr;
-
- /*
- * Pull the user defined setting from scratch RAM.
- */
- 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
- {
- panic("aic7xxx: AWAITING_MSG for an SCB that does "
- "not have a waiting message.");
+ 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;
}
- break;
- case DATA_OVERRUN:
- {
- 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;
+ 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.
+ */
-/* #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;
+ 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))
+ {
+ 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;
+ scb->target_status = inb(SCB_TARGET_STATUS + base);
+ aic7xxx_status(cmd) = scb->target_status;
- default: /* unknown */
- printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
- p->host_no, intstat, inb(p->base + SCSISIGI));
- break;
- }
+ cmd->result |= scb->target_status;
- /*
- * Clear the sequencer interrupt and unpause the sequencer.
- */
- outb(CLRSEQINT, p->base + CLRINT);
- unpause_sequencer(p, /* unpause always */ TRUE);
-}
+ switch (status_byte(scb->target_status))
+ {
+ case GOOD:
+ printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
+ 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;
+ case CHECK_CONDITION:
+ if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
+ {
+ unsigned char tcl;
+ unsigned int req_buf; /* must be 32 bits */
- scb_index = inb(p->base + SCB_TAG);
- status = inb(p->base + SSTAT1);
+ tcl = scb->target_channel_lun;
- 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';
+ /*
+ * Send a sense command to the requesting target.
+ */
+ 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);
+ /*
+ * Ensure that the target is "BUSY" so we don't get overlapping
+ * commands if we happen to be doing tagged I/O.
+ */
+ aic7xxx_busy_target(scsi_id, channel, base);
- 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, /* Initiate Reset */ FALSE);
- scb = NULL;
- }
- else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
- {
- /*
- * 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;
+ aic7xxx_add_waiting_scb(base, scb);
+ outb(SEND_SENSE, RETURN_1 + base);
+ } /* first time sense, no errors */
+ else
+ {
+ cmd->flags &= ~ASKED_FOR_SENSE;
+ if (aic7xxx_error(cmd) == 0)
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ }
+ 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 */
+ } /* end else of */
+ break;
- outb(0, p->base + SCSISEQ);
- if (lastphase == P_MESGOUT)
- {
- unsigned char sindex;
- unsigned char message;
+ 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;
- sindex = inb(p->base + SINDEX);
- message = inb(p->base + sindex - 1);
+ 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;
- 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;
- }
- }
- if (printerror != 0)
- {
- if (scb != NULL)
- {
- unsigned char tag;
+ case AWAITING_MSG:
+ 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
+ {
+ /*
+ * 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;
- if ((scb->hscb->control & TAG_ENB) != 0)
+ 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)
{
- tag = scb->hscb->tag;
+ int found;
+
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ 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;
}
else
{
- tag = SCB_LIST_NULL;
+ panic("scsi%d: Immediate complete for unknown operation.\n",
+ p->host_no);
}
- aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
- }
- else
- {
- 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;
-
- scbptr = inb(p->base + WAITING_SCBH);
- outb(scbptr, p->base + SCBPTR);
- scb_index = inb(p->base + SCB_TAG);
+ break;
- scb = NULL;
- if (scb_index < p->scb_data->numscbs)
- {
- scb = p->scb_data->scb_array[scb_index];
- if ((scb->flags & SCB_ACTIVE) == 0)
+ case DATA_OVERRUN:
{
- scb = NULL;
+ 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;
}
- }
- if (scb == NULL)
- {
- 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.
- */
- 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);
-
- /*
- * Shift the waiting for selection queue forward
- */
- nextscb = inb(p->base + SCB_NEXT);
- outb(nextscb, p->base + WAITING_SCBH);
-
- /*
- * Put this SCB back on the free list.
- */
- 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);
- cmd = scb->cmd;
- switch (lastphase)
- {
- case P_DATAOUT:
- phase = "Data-Out";
- break;
- case P_DATAIN:
- phase = "Data-In";
- mesg_out = MSG_INITIATOR_DET_ERR;
+#if AIC7XXX_NOT_YET
+ /* XXX Fill these in later */
+ case MESG_BUFFER_BUSY:
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";
+ case MSGIN_PHASEMIS:
break;
+#endif
+
+ default: /* unknown */
+ printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+ p->host_no, intstat, inb(SCSISIGI + base));
+ break;
}
/*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
+ * Clear the sequencer interrupt and unpause the sequencer.
*/
- printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
- p->host_no, TC_OF_SCB(scb), phase);
+ outb(CLRSEQINT, CLRINT + base);
+ UNPAUSE_SEQUENCER(p);
+ }
- /*
- * 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)
+ if (intstat & SCSIINT)
+ {
+ int status = inb(SSTAT1 + base);
+ scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
+ channel = 'A';
+ if (inb(SBLKCTL + base) & SELBUSB)
{
- outb(mesg_out, p->base + MSG_OUT);
- outb(1, p->base + MSG_LEN);
- scb = NULL;
+ channel = 'B';
}
- else
+
+ 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);
/*
- * Should we allow the target to make this decision for us?
+ * Go through and abort all commands for the channel, but do not
+ * reset the channel again.
*/
- 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)
+ aic7xxx_reset_channel(p, channel, FALSE);
+ run_aborted_queue = TRUE;
+ }
+ else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
- p = NULL;
+ 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;
}
- else
+ else if (status & SCSIPERR)
{
- p = (struct aic7xxx_host *) p->next->hostdata;
+ char *phase;
+ unsigned char mesg_out = MSG_NOP;
+ unsigned char lastphase = inb(LASTPHASE + base);
+
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ 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;
+ }
+
+ /*
+ * 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);
+
+ /*
+ * 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
+ {
+ /*
+ * Should we allow the target to make this decision for us?
+ */
+ cmd->result = DID_RETRY_COMMAND << 16;
+ }
+ aic7xxx_done(p, scb);
}
- }
+ else if (status & SELTO)
+ {
+ unsigned char waiting;
- if (p == NULL)
- return;
+ cmd = scb->cmd;
- /*
- * Handle all the interrupt sources - especially for SCSI
- * interrupts, we won't get a second chance at them.
- */
- intstat = inb(p->base + INTSTAT);
+ 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);
+ /*
+ * Stop the selection.
+ */
+ outb(0, SCSISEQ + base);
+ outb(0, SCB_CONTROL + base);
+ outb(CLRSELTIMEO, CLRSINT1 + base);
+ outb(CLRSCSIINT, CLRINT + base);
- /*
- * Keep track of interrupts for /proc/scsi
- */
- p->isr_count++;
+ /*
+ * Shift the waiting for selection queue forward
+ */
+ waiting = inb(WAITING_SCBH + base);
+ outb(waiting, SCBPTR + base);
+ waiting = inb(SCB_NEXT + base);
+ outb(waiting, WAITING_SCBH + base);
- 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)
+ RESTART_SEQUENCER(p);
+ aic7xxx_done(p, scb);
+ }
+ else if (!(status & BUSFREE))
{
- /* Try clearing all interrupts. */
- outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
+ /*
+ * 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, CLRSINT1 + base);
+ UNPAUSE_SEQUENCER(p);
+ outb(CLRSCSIINT, CLRINT + base);
}
- return;
- }
-
- 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 (run_aborted_queue)
+ aic7xxx_done_aborted_scbs(p);
if (intstat & CMDCMPLT)
{
- struct aic7xxx_scb *scb = NULL;
- Scsi_Cmnd *cmd;
- unsigned char qoutcnt;
- unsigned char scb_index;
- int i, interrupts_cleared = 0;
+ int complete;
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
- qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+ do {
+ complete = inb(QOUTFIFO + base);
-#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++)
+ scb = (p->scb_array[complete]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
- 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);
+ 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))
+ {
+ /*
+ * Got sense information.
+ */
+ cmd->flags &= ASKED_FOR_SENSE;
}
+ 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
* so notification of the command being complete never made it
* back up to the kernel.
*/
- outb(CLRCMDINT, p->base + CLRINT);
- interrupts_cleared++;
- qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
- }
-
- 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);
+ outb(CLRCMDINT, CLRINT + base);
+ aic7xxx_done(p, scb);
- 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)
+#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))
{
- 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);
- }
-
- if (intstat & SCSIINT)
- {
- aic7xxx_handle_scsiint(p, intstat);
- }
-
- 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)));
+ struct aic7xxx_xferstats *sp;
+ long *ptr;
+ int x;
-#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;
+ sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+ sp->xfers++;
- tindex = device->id | (device->channel << 3);
- if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == -1)
+ if (cmd->request.cmd == WRITE)
{
- tag_enabled = FALSE;
- device->queue_depth = 2; /* Tagged queueing is disabled. */
+ sp->w_total++;
+ sp->w_total512 += (actual >> 9);
+ ptr = sp->w_bins;
}
- else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
+ else
{
- device->queue_depth = default_depth;
+ sp->r_total++;
+ sp->r_total512 += (actual >> 9);
+ ptr = sp->r_bins;
}
- else
+ for (x = 9; x <= 17; x++)
{
- device->queue_depth =
- aic7xxx_tag_info[p->instance].tag_commands[tindex];
+ if (actual < (1 << x))
+ {
+ ptr[x - 9]++;
+ break;
+ }
}
- }
-#endif
- if ((device->tagged_queue == 0) && tag_enabled)
- {
- if (aic7xxx_verbose)
+ if (x > 17)
{
- printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
- "queue depth %d.\n", p->host_no,
- device->id, device->channel, device->queue_depth);
+ ptr[x - 9]++;
}
- device->tagged_queue = 1;
- device->current_tag = SCB_LIST_NULL;
}
- }
+#endif /* AIC7XXX_PROC_STATS */
+
+ } while (inb(QOUTCNT + base) & p->qcntmask);
}
-#endif
+ aic7xxx_done_cmds_complete(p);
+ p->flags &= ~IN_ISR;
+ aic7xxx_run_waiting_queues(p);
}
/*+F*************************************************************************
* 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_Device *device = scsi_devs;
+ int tq_depth = 2;
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)
{
- aic7xxx_device_queue_depth(p, device);
+ 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
}
}
}
* The fourth byte's lowest bit seems to be an enabled/disabled
* flag (rest of the bits are reserved?).
*-F*************************************************************************/
-static aha_chip_type
+static aha_type
aic7xxx_probe(int slot, int base, aha_status_type *bios)
{
int i;
static struct {
int n;
unsigned char signature[sizeof(buf)];
- aha_chip_type type;
+ aha_type type;
int bios_disabled;
} AIC7xxx[] = {
{ 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
return (AIC7xxx[i].type);
}
- printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
- "disabled at slot %d, ignored.\n", slot);
+ printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
}
}
* 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_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
+read_2840_seeprom(int base, struct seeprom_config *sc)
{
int i = 0, k = 0;
unsigned char temp;
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \
+ while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \
{ \
; /* Do nothing */ \
} \
- (void) inb(p->base + SEECTL_2840);
+ (void) inb(SEECTL_2840 + base);
/*
* Read the first 32 registers of the seeprom. For the 2840,
/*
* Send chip select for one clock cycle.
*/
- outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ outb(CK_2840 | CS_2840, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
/*
* Now we're ready to send the read command followed by the
for (i = 0; i < seeprom_read.len; i++)
{
temp = CS_2840 | seeprom_read.bits[i];
- outb(temp, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
temp = temp ^ CK_2840;
- outb(temp, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
}
/*
* Send the 6 bit address (MSB first, LSB last).
temp = k;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = CS_2840 | temp;
- outb(temp, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
temp = temp ^ CK_2840;
- outb(temp, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
}
/*
for (i = 0; i <= 16; i++)
{
temp = CS_2840;
- outb(temp, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
temp = temp ^ CK_2840;
- seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
- outb(temp, p->base + SEECTL_2840);
- CLOCK_PULSE(p);
+ seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
}
/*
* The serial EEPROM has a checksum in the last word. Keep a
/*
* Reset the chip select for the next command cycle.
*/
- 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);
+ 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);
}
#if 0
#undef CLOCK_PULSE
}
-/*+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
* first). The clock cycling from low to high initiates the next data
* bit to be sent from the chip.
*
- * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
+ * The 7870 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,
* 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(struct aic7xxx_host *p, int offset, unsigned short *scarray,
- unsigned int len, seeprom_chip_type chip)
+read_seeprom(int base, int offset, struct seeprom_config *sc,
+ 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];
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(p->base + SEECTL) & SEERDY) == 0) \
+ while ((inb(SEECTL + base) & SEERDY) == 0) \
{ \
; /* Do nothing */ \
}
/*
- * Request access of the memory port.
+ * 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.
*/
- if (acquire_seeprom(p) == 0)
+ 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)
{
+ outb(0, SEECTL + base);
return (0);
}
/*
- * 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.
+ * 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.
*/
- for (k = 0; k < len; k++)
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
{
/*
* Send chip select for one clock cycle.
*/
- outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
- CLOCK_PULSE(p);
+ outb(SEEMS | SEECK | SEECS, SEECTL + base);
+ CLOCK_PULSE(base);
/*
* Now we're ready to send the read command followed by the
for (i = 0; i < seeprom_read.len; i++)
{
temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
- outb(temp, p->base + SEECTL);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
temp = temp ^ SEECK;
- outb(temp, p->base + SEECTL);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
}
/*
- * Send the 6 or 8 bit address (MSB first, LSB last).
+ * Send the 6 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, p->base + SEECTL);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
temp = temp ^ SEECK;
- outb(temp, p->base + SEECTL);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
}
/*
for (i = 0; i <= 16; i++)
{
temp = SEEMS | SEECS;
- outb(temp, p->base + SEECTL);
- CLOCK_PULSE(p);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
temp = temp ^ SEECK;
- scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
- outb(temp, p->base + SEECTL);
- CLOCK_PULSE(p);
+ seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
}
/*
- * 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.
+ * 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.
*/
- if (k < (len - 1))
+ if (k < (sizeof(*sc) / 2) - 1)
{
- checksum = checksum + scarray[k];
+ checksum = checksum + seeprom[k];
}
/*
* Reset the chip select for the next command cycle.
*/
- 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);
+ outb(SEEMS, SEECTL + base);
+ CLOCK_PULSE(base);
+ outb(SEEMS | SEECK, SEECTL + base);
+ CLOCK_PULSE(base);
+ outb(SEEMS, SEECTL + base);
+ CLOCK_PULSE(base);
}
/*
* Release access to the memory port and the serial EEPROM.
*/
- release_seeprom(p);
+ outb(0, SEECTL + base);
#if 0
- printk("Computed checksum 0x%x, checksum read 0x%x\n",
- checksum, scarray[len - 1]);
+ printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
printk("Serial EEPROM:");
- for (k = 0; k < len; k++)
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
{
if (((k % 8) == 0) && (k != 0))
{
printk("\n ");
}
- printk(" 0x%x", scarray[k]);
+ printk(" 0x%x", seeprom[k]);
}
printk("\n");
#endif
- if (checksum != scarray[len - 1])
+ if (checksum != sc->checksum)
{
return (0);
}
/*+F*************************************************************************
* Function:
- * write_brdctl
+ * detect_maxscb
*
* Description:
- * Writes a value to the BRDCTL register.
+ * Detects the maximum number of SCBs for the controller and returns
+ * the count and a mask in config (config->maxscbs, config->qcntmask).
*-F*************************************************************************/
-static inline void
-write_brdctl(struct aic7xxx_host *p, unsigned char value)
+static void
+detect_maxscb(struct aic7xxx_host_config *config)
{
- 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);
-}
+ 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");
-/*+F*************************************************************************
- * Function:
- * read_brdctl
- *
- * Description:
- * Reads the BRDCTL register.
- *-F*************************************************************************/
-static inline unsigned char
-read_brdctl(struct aic7xxx_host *p)
-{
- outb(BRDRW | BRDCS, p->base + BRDCTL);
- return (inb(p->base + BRDCTL));
}
/*+F*************************************************************************
* Function:
- * configure_termination
+ * aic7xxx_register
*
* Description:
- * Configures the termination settings on PCI adapters that have
- * SEEPROMs available.
+ * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
*-F*************************************************************************/
-static void
-configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
- unsigned short adapter_control, unsigned char max_targ)
+static int
+aic7xxx_register(Scsi_Host_Template *template,
+ struct aic7xxx_host_config *config)
{
- 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))
- {
- 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.");
+ 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;
- /* Configure auto termination. */
- outb(SEECS | SEEMS, p->base + SEECTL);
+ base = config->base;
+
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ request_region(base, MAXREG - MINREG, "aic7xxx");
+
+ switch (config->type)
+ {
+ 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;
/*
- * 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.
+ * 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.
*/
- write_brdctl(p, 0);
+ 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);
/*
- * 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).
+ * 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.
*/
- brdctl_int = read_brdctl(p);
- internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
- if (max_targ > 8)
+ config->irq = inb(INTDEF + base) & 0x0F;
+ if ((config->type == AIC_7771) &&
+ (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
+ {
+ config->bios = AIC_DISABLED;
+ config->flags |= USE_DEFAULTS;
+ }
+ else
{
- internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
+ host_conf = inb(HOSTCONF + base);
+ config->bus_speed = host_conf & DFTHRSH;
+ config->busrtime = (host_conf << 2) & BOFF;
}
/*
- * Set the rom bank to 1 and determine
- * the other signals.
+ * Setup the FIFO threshold and the bus off time
*/
- write_brdctl(p, BRDDAT5);
+ outb(config->bus_speed & DFTHRSH, BUSSPD + base);
+ outb(config->busrtime, BUSTIME + base);
/*
- * 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.
+ * A reminder until this can be detected automatically.
*/
- brdctl_ext = read_brdctl(p);
- external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
- eprom_present = brdctl_ext & BRDDAT7;
- if (aic7xxx_verbose)
+ 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)
{
- if (max_targ > 8)
+ 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))
{
- 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");
+ /*
+ * 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;
}
else
{
- printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
- internal50_present ? "YES" : "NO",
- external_present ? "YES" : "NO");
+ 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: 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;
+
/*
- * Now set the termination based on what we found. BRDDAT6
- * controls wide termination enable.
+ * Setup the FIFO threshold and the bus off time
*/
- high_on = FALSE;
- low_on = FALSE;
- if ((max_targ > 8) &&
- ((external_present == 0) || (internal68_present == 0)))
- {
- high_on = TRUE;
- }
+ 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;
- if ((internal50_present + internal68_present + external_present) <= 1)
+ 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.
+ */
+ 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;
+
+ printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
+ if ((config->type == AIC_7873) || (config->type == AIC_7883))
{
- low_on = TRUE;
+ have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
+ &sc, c56_66);
}
-
- if (internal50_present && internal68_present && external_present)
+ else
{
- printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
- " Only two connectors on the adapter may be "
- "used at a time!\n");
+ have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
+ &sc, c46);
}
-
- if (high_on == TRUE)
- write_brdctl(p, BRDDAT6);
- else
- write_brdctl(p, 0);
-
- if (low_on == TRUE)
- *sxfrctl1 |= STPWEN;
-
- if (aic7xxx_verbose)
+ if (!have_seeprom)
{
- if (max_targ > 8)
+ for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
{
- printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
- low_on ? "ON" : "OFF",
- high_on ? "ON" : "OFF");
+ 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");
}
else
{
- printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
+ 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;
}
- }
- 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);
+ scsi_conf = ENSPCHK | RESET_SCSI;
}
else
{
- 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");
+ 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;
+ }
+ }
}
- }
- release_seeprom(p);
- }
-}
-/*+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;
-
- /*
- * 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);
+ outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
+ config->bus_speed = DFTHRSH_100;
+ outb(config->bus_speed, DSPCISTATUS + base);
- 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;
-
- 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);
+ /*
+ * In case we are a wide card...
+ */
+ outb(config->scsi_id, SCSICONF + base + 1);
- /* Ensure we clear the first (0) SCBs control byte. */
- outb(0, p->base + SCBPTR);
- outb(0, p->base + SCB_CONTROL);
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+ break;
- p->scb_data->maxhscbs = i;
+ default:
+ panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
}
- 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;
- }
+ detect_maxscb(config);
- /*
- * Set the Queue Full Count. Some cards have more queue space than
- * SCBs.
- */
- switch (p->chip_class)
+ if (config->chip_type == AIC_777x)
{
- 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;
+ if (config->pause & IRQMS)
+ {
+ printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
+ }
+ else
+ {
+ printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
+ }
}
-}
-
-/*+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(p->base + SBLKCTL);
- if (p->flags & PAGE_ENABLED)
+ sblkctl = inb(SBLKCTL + base);
+ if (config->flags & PAGE_ENABLED)
flags = PAGESCBS;
switch (sblkctl & SELBUS_MASK)
{
case SELNARROW: /* narrow/normal bus */
- 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);
+ config->scsi_id = inb(SCSICONF + base) & 0x07;
+ config->bus_type = AIC_SINGLE;
+ outb(flags | SINGLE_BUS, FLAGS + base);
break;
case SELWIDE: /* Wide bus */
- 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);
+ 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);
break;
case SELBUSB: /* Twin bus */
- 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);
+ 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
break;
default:
printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
- "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
- outb(0, p->base + SEQ_FLAGS);
+ "mail deang@teleport.com\n", inb(SBLKCTL + base));
+ outb(0, FLAGS + base);
return (0);
}
/*
- * Detect SCB parameters and initialize the SCB array.
+ * 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).
+ */
+ outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
+
+ /*
+ * 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.
*/
- 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);
+ 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);
- host->can_queue = p->scb_data->maxscbs;
+ /*
+ * 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->cmd_per_lun = 2;
host->select_queue_depths = aic7xxx_select_queue_depth;
- host->this_id = p->scsi_id;
- host->io_port = p->base;
+ host->this_id = config->scsi_id;
+ host->io_port = config->base;
host->n_io_port = 0xFF;
- host->base = (unsigned char *) p->mbase;
- host->irq = p->irq;
- if (p->bus_type == AIC_WIDE)
+ host->base = (unsigned char *)config->mbase;
+ host->irq = config->irq;
+ if (config->bus_type == AIC_WIDE)
{
host->max_id = 16;
}
- if (p->bus_type == AIC_TWIN)
+ if (config->bus_type == AIC_TWIN)
{
host->max_channel = 1;
}
+ p = (struct aic7xxx_host *) host->hostdata;
+
p->host = host;
- p->host_no = host->host_no;
+ p->host_no = (int)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_data->free_scbs);
+ scbq_init(&p->scb_link->free_scbs);
+ scbq_init(&p->page_scbs);
scbq_init(&p->waiting_scbs);
+ scbq_init(&p->assigned_scbs);
+
+ p->unpause = config->unpause;
+ p->pause = config->pause;
- for (i = 0; i <= NUMBER(p->device_status); i++)
+ for (i = 0; i <= 15; 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[p->irq] == NULL)
+ if (aic7xxx_boards[config->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
* kernel, an interrupt is triggered immediately. Therefore, we
* must ensure the board data is correctly set before the request.
*/
- aic7xxx_boards[p->irq] = host;
+ aic7xxx_boards[config->irq] = host;
/*
- * Register IRQ with the kernel. Only allow sharing IRQs with
- * PCI devices.
+ * Register IRQ with the kernel.
*/
- 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)
+ if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+ "aic7xxx", NULL))
{
printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
- p->irq);
- aic7xxx_boards[p->irq] = NULL;
+ config->irq);
+ aic7xxx_boards[config->irq] = NULL;
return (0);
}
}
* 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[p->irq];
- aic7xxx_boards[p->irq] = host;
+ 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);
}
+ printk("done.\n");
+
/*
* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
*/
if (p->bus_type == AIC_TWIN)
{
/*
- * The controller is gated to channel B after a chip reset; set
- * bus B values first.
+ * Select Channel B.
*/
- 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);
+ 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
if (p->flags & ULTRA_ENABLED)
{
- outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
+ outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
}
else
{
- outb(DFON | SPIOEN, p->base + SXFRCTL0);
- }
-
- 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);
+ outb(DFON | SPIOEN, SXFRCTL0 + base);
}
- /* Select channel A */
- outb(SELNARROW, p->base + SBLKCTL);
+ /*
+ * Select Channel A
+ */
+ outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
}
-
- 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);
+ 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
if (p->flags & ULTRA_ENABLED)
{
- outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
+ outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
}
else
{
- 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);
+ outb(DFON | SPIOEN, SXFRCTL0 + base);
}
/*
/*
* Grab the disconnection disable table and invert it for our needs
*/
- if (p->flags & USE_DEFAULTS)
+ if (have_seeprom)
{
- printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
- "device parameters.\n");
- p->discenable = 0xFFFF;
+ p->discenable = 0x0;
}
else
{
- p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
- inb(p->base + DISC_DSB));
+ 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));
+ }
}
for (i = 0; i < max_targets; i++)
{
- if (p->flags & USE_DEFAULTS)
+ if (config->flags & USE_DEFAULTS)
{
- target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */
+ target_settings = 0; /* 10 MHz */
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
{
- target_settings = inb(p->base + TARG_SCRATCH + i);
- if (target_settings & 0x0F)
+ if (have_seeprom)
{
- p->needsdtr_copy |= (0x01 << i);
- /*
- * Default to asynchronous transfers (0 offset)
- */
- target_settings &= 0xF0;
+ 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);
+ }
}
- if (target_settings & 0x80)
+ else
{
- p->needwdtr_copy |= (0x01 << i);
- /*
- * Clear the wide flag. When wide negotiation is successful,
- * we'll enable it.
- */
- target_settings &= 0x7F;
+ 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;
+ }
}
if (p->flags & ULTRA_ENABLED)
{
case 0x20:
ultraenable |= (0x01 << i);
break;
- case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */
+ case 0x40:
target_settings &= ~(0x70);
break;
default:
- break;
- }
- }
- }
- outb(target_settings, p->base + TARG_SCRATCH + i);
- }
-
- /*
- * If we are not wide, forget WDTR. This makes the driver
- * work on some cards that don't leave these fields cleared
- * when BIOS is not installed.
- */
- if (p->bus_type != AIC_WIDE)
- {
- p->needwdtr_copy = 0;
- }
- p->needsdtr = p->needsdtr_copy;
- p->needwdtr = p->needwdtr_copy;
- p->orderedtag = 0;
- outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
- outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
-
- /*
- * Set the number of available hardware SCBs.
- */
- outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
-
- /*
- * 2s compliment of maximum tag value.
- */
- i = p->scb_data->maxscbs;
- outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
-
- /*
- * 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.
- */
- 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);
- }
-
- /*
- * 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, p->base + WAITING_SCBH);
- outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
-
- /*
- * Message out buffer starts empty
- */
- outb(0, p->base + MSG_LEN);
-
- /*
- * 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.
- */
- host = scsi_register(sht, sizeof(struct aic7xxx_host));
-
- if (host != NULL)
- {
- 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
- {
- /*
- * We are not sharing SCB data; allocate one.
- */
- 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);
-}
-
-/*+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;
-
- /*
- * 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);
- }
-
- /*
- * 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;
+ }
}
- 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;
+ }
+ outb(target_settings, (TARG_SCRATCH + base + i));
}
- if (!have_seeprom)
+ /*
+ * If we are not wide, forget WDTR. This makes the driver
+ * work on some cards that don't leave these fields cleared
+ * when BIOS is not installed.
+ */
+ if (p->bus_type != AIC_WIDE)
{
- if (aic7xxx_verbose)
- {
- printk("\naic7xxx: No SEEPROM available; using defaults.\n");
- }
- p->flags |= USE_DEFAULTS;
+ p->needwdtr_copy = 0;
}
- 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;
+ 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);
- 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.
- */
- max_targets = 16;
- }
- else
- {
- /* PCI adapter seeproms */
- if (sc->bios_control & CFEXTEND)
- p->flags |= EXTENDED_TRANSLATION;
+ /*
+ * Set the number of available SCBs.
+ */
+ outb(config->maxhscbs, SCBCOUNT + base);
- if (sc->adapter_control & CFSTERM)
- *sxfrctl1 |= STPWEN;
+ /*
+ * 2s compliment of maximum tag value.
+ */
+ i = p->maxscbs;
+ outb(-i & 0xFF, COMP_SCBCOUNT + base);
- /* Limit to 16 targets just in case. */
- max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
- }
+ /*
+ * Set the QCNT (queue count) mask to deal with broken aic7850s that
+ * sporatically get garbage in the upper bits of their QCNT registers.
+ */
+ outb(config->qcntmask, QCNTMASK + base);
- 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);
+ /*
+ * Clear the active flags - no targets are busy.
+ */
+ outb(0, ACTIVE_A + base);
+ outb(0, ACTIVE_B + base);
- p->scsi_id = sc->brtime_id & CFSCSIID;
+ /*
+ * We don't have any waiting selections or disconnected SCBs.
+ */
+ outb(SCB_LIST_NULL, WAITING_SCBH + base);
+ outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
- scsi_conf = (p->scsi_id & 0x7);
- if (sc->adapter_control & CFSPARITY)
- scsi_conf |= ENSPCHK;
- if (sc->adapter_control & CFRESETB)
- scsi_conf |= RESET_SCSI;
+ /*
+ * Message out buffer starts empty
+ */
+ outb(0, MSG_LEN + base);
- if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ /*
+ * 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.
+ */
+ if (!aic7xxx_no_reset)
+ {
+ printk("aic7xxx: Resetting the SCSI bus...");
+ if (p->bus_type == AIC_TWIN)
{
/*
- * We allow the operator to override ultra enable through
- * the boot prompt.
+ * Select Channel B.
*/
- if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
- {
- /* Treat us as a non-ultra card */
- p->flags &= ~ULTRA_ENABLED;
- }
- }
+ outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
- /* 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);
+ outb(SCSIRSTO, SCSISEQ + base);
+ udelay(1000);
+ outb(0, SCSISEQ + base);
- if (p->chip_class != AIC_777x)
- {
- /*
- * Update the settings in sxfrctl1 to match the termination
- * settings.
+ /* Ensure we don't get a RSTI interrupt from this. */
+ outb(CLRSCSIRSTI, CLRSINT1 + base);
+ outb(CLRSCSIINT, CLRINT + base);
+
+ /*
+ * Select Channel A.
*/
- *sxfrctl1 = 0;
- configure_termination(p, sxfrctl1, sc->adapter_control,
- (unsigned char) sc->max_targets & CFMAXTARG);
+ outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
}
+
+ outb(SCSIRSTO, SCSISEQ + base);
+ udelay(1000);
+ outb(0, SCSISEQ + base);
+
+ /* Ensure we don't get a RSTI interrupt from this. */
+ outb(CLRSCSIRSTI, CLRSINT1 + base);
+ outb(CLRSCSIINT, CLRINT + base);
+
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
+
+ printk("done.\n");
}
- return (have_seeprom);
+
+ /*
+ * 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);
}
/*+F*************************************************************************
*
* 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;
- 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 found = 0, slot, base;
+ unsigned char irq = 0;
int i;
- struct aic7xxx_host *p;
+ struct aic7xxx_host_config config;
+
+ template->proc_dir = &proc_scsi_aic7xxx;
+ config.chan_num = 0;
/*
* Since we may allow sharing of IRQs, it is imperative
aic7xxx_boards[i] = NULL;
}
- template->proc_dir = &proc_scsi_aic7xxx;
- template->name = aic7xxx_info(NULL);
-
/*
* Initialize the spurious count to 0.
*/
continue;
}
- chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
- if (chip_type != AIC_NONE)
+ config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
+ if (config.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;
/*
- * Pause the card preserving the IRQ type. Allow the operator
- * to override the IRQ trigger.
+ * 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.
*/
- 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");
- }
+ 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);
- /*
- * 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.
*/
{
struct
{
- unsigned short vendor_id;
- unsigned short device_id;
- aha_chip_type chip_type;
- aha_chip_class_type chip_class;
+ unsigned short vendor_id;
+ unsigned short device_id;
+ aha_type card_type;
+ aha_chip_type chip_type;
} 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_786x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
+ {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_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},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
};
- int error, flags;
+ int error;
int done = 0;
unsigned int iobase, mbase;
unsigned short index = 0;
unsigned char pci_bus, pci_device_fn;
- unsigned char ultra_enb = 0;
- unsigned int devconfig, class_revid;
- scb_data_type *shared_scb_data = NULL;
+ unsigned int csize_lattime;
+ unsigned int class_revid;
+ unsigned int devconfig;
char rev_id[] = {'B', 'C', 'D'};
for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
}
else /* Found an Adaptec PCI device. */
{
- 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)
+ 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)
{
case AIC_7850:
case AIC_7855:
- flags |= USE_DEFAULTS;
+ case AIC_7860:
+ case AIC_7861:
+ config.bios = AIC_DISABLED;
+ config.flags |= USE_DEFAULTS;
+ config.bus_speed = DFTHRSH_100;
break;
case AIC_7872: /* 3940 */
case AIC_7882: /* 3940-Ultra */
- flags |= MULTI_CHANNEL;
- chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
+ config.chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
number_of_3940s++;
break;
case AIC_7873: /* 3985 */
case AIC_7883: /* 3985-Ultra */
- chan_num = number_of_3985s; /* Has 3 controllers */
- flags |= MULTI_CHANNEL;
+ config.chan_num = number_of_3985s; /* Has 3 controllers */
number_of_3985s++;
if (number_of_3985s == 3)
{
number_of_3985s = 0;
- shared_scb_data = NULL;
}
break;
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 (LSB) of PCI_BASE_ADDRESS_0 is always set, so
+ * The first bit 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;
-
/*
- * Remember how the card was setup in case there is no seeprom.
+ * Read the PCI burst size and latency timer.
*/
- 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;
-
- aic7xxx_chip_reset(p);
-
-#ifdef AIC7XXX_USE_EXT_SCBRAM
- if (devconfig & RAMPSM)
- {
- 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,
+ CSIZE_LATTIME, &csize_lattime);
+ printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
+ "PCLKS\n", (int) (csize_lattime & CACHESIZE),
+ (csize_lattime >> 8) & 0x000000ff);
- if ((p->flags & USE_DEFAULTS) == 0)
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CLASS_PROGIF_REVID, &class_revid);
+ if ((class_revid & DEVREVID) < 3)
{
- load_seeprom(p, &sxfrctl1);
+ printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
+ rev_id[class_revid & DEVREVID]);
}
- /*
- * 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)
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, &devconfig);
+ if (error)
{
- 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;
- }
+ panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
+ error);
}
- /*
- * 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");
+ printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
/*
* I don't think we need to bother with allowing
*/
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)
- p->flags |= EXTENDED_TRANSLATION;
-
- /*
- * 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
+ config.flags |= EXTENDED_TRANSLATION;
+#ifdef AIC7XXX_SHARE_SCBs
+ if (devconfig & RAMPSM)
+#else
+ if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
+ (config.type != AIC_7883))
+#endif
{
- found = found + 1;
-
-#ifdef AIC7XXX_USE_EXT_SCBRAM
/*
- * Set the shared SCB data once we've successfully probed a
- * 398x adapter.
+ * 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.
*
- * Note that we can only do this if the use of external
- * SCB RAM is enabled.
+ * 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.
+ *
+ * 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.
*/
- if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
- {
- if (shared_scb_data == NULL)
- {
- shared_scb_data = p->scb_data;
- }
- }
-#endif
+ 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);
}
+ 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);
}
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.
*/
- if (p->discenable & mask)
- {
- hscb->control |= DISCENB;
#ifdef AIC7XXX_TAGGED_QUEUEING
- if (cmd->device->tagged_queue)
+ if (cmd->device->tagged_queue)
+ {
+ 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)
{
- 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;
- }
+ scb->control |= 0x02;
+ p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+ }
+#if 0
+ if (p->orderedtag & mask)
+ {
+ scb->control |= 0x02;
+ p->orderedtag = p->orderedtag & ~mask;
}
-#endif /* Tagged queueing */
+#endif
+ }
+#endif
+ if (p->discenable & mask)
+ {
+ scb->control |= DISCENB;
}
-
if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
{
p->wdtr_pending |= mask;
- hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_WDTR;
+ scb->control |= NEEDWDTR;
#if 0
- printk("scsi%d: Sending WDTR request to target %d.\n",
- p->host_no, cmd->target);
+ printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
#endif
}
else
if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
{
p->sdtr_pending |= mask;
- hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_SDTR;
+ scb->control |= NEEDSDTR;
#if 0
- printk("scsi%d: Sending SDTR request to target %d.\n",
- p->host_no, cmd->target);
+ printk("aic7xxx: Sending SDTR request to target %d.\n", 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
- hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+ scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
/*
* XXX - this relies on the host data being stored in a
* little-endian format.
*/
- hscb->SCSI_cmd_length = cmd->cmd_len;
- hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
+ addr = VIRT_TO_BUS(cmd->cmnd);
+ scb->SCSI_cmd_length = cmd->cmd_len;
+ memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
if (cmd->use_sg)
{
scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
scb->sg_list[i].length = (unsigned int) sg[i].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);
+ 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;
#if 0
printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
- cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
+ cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
#endif
}
else
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)
+ if (cmd->request_bufflen == 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);
+ /*
+ * 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;
}
else
{
- 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;
+ 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));
}
}
}
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)
cmd->lun & 0x07);
#endif
- 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);
- }
+ /*
+ * 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();
+
scb = aic7xxx_allocate_scb(p);
if (scb == NULL)
{
- panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
+ panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
}
else
{
scb->cmd = cmd;
- aic7xxx_position(cmd) = scb->hscb->tag;
+ aic7xxx_position(cmd) = scb->tag;
#if 0
debug_scb(scb);
#endif;
aic7xxx_buildscb(p, cmd, scb);
#if 0
- if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
+ if (scb != (p->scb_array[scb->position]))
{
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->hscb->tag, (unsigned int) scb->cmd,
- scb->flags, (unsigned int) p->free_scb);
+ scb->position, (unsigned int) scb->cmd,
+ scb->state, (unsigned int) p->free_scb);
#endif
/*
cmd->host_scribble = NULL;
memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
- scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
+ 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;
- save_flags(processor_flags);
- cli();
- scbq_insert_tail(&p->waiting_scbs, scb);
- if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
+ /*
+ * Guard against unpausing the sequencer if there is an interrupt
+ * waiting to happen.
+ */
+ if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
+ {
+ UNPAUSE_SEQUENCER(p);
+ }
+ }
+ }
+ else
{
- aic7xxx_run_waiting_queues(p);
+ scb->state |= SCB_WAITINGQ;
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ if (!(p->flags & IN_ISR))
+ {
+ 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->hscb->tag);
+ (long) cmd, (long) scb->cmd, scb->position);
#endif;
+ restore_flags(processor_flags);
}
return (0);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_bus_device_reset
+ * aic7xxx_abort_reset
*
* Description:
* Abort or reset the current SCSI command(s). If the scb has not
static int
aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
- struct aic7xxx_scb *scb;
- struct aic7xxx_hwscb *hscb;
+ struct aic7xxx_scb *scb;
unsigned char bus_state;
- int result = -1;
+ int base, result = -1;
char channel;
- scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
- hscb = scb->hscb;
-
- /*
- * 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);
- }
-
-
- 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);
+ scb = (p->scb_array[aic7xxx_position(cmd)]);
+ base = p->base;
- switch (bus_state)
+ channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
+ if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
{
- 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)
+ if (scb->state & SCB_IN_PROGRESS)
{
/*
- * We could be starving this command; try sending and ordered tag
- * command to the target we come from.
+ * Ensure that the card doesn't do anything
+ * behind our back.
*/
- 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;
+ PAUSE_SEQUENCER(p);
- /*
- * 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];
+ printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
+ bus_state = inb(LASTPHASE + p->base);
- if (bus_state != P_BUSFREE)
- {
- if (active_scb_index >= p->scb_data->numscbs)
- {
- /*
- * 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);
- }
- else
+ switch (bus_state)
{
- /* 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)
- {
+ 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);
/*
- * 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.
+ * We're not in a valid phase, so assume we're idle.
*/
- result = SCSI_RESET_PENDING;
- aic7xxx_error(active_scb->cmd) = DID_RESET;
- }
- else
- {
- aic7xxx_error(scb->cmd) = DID_RESET;
- result = SCSI_RESET_PENDING;
- }
- unpause_sequencer(p, /* unpause_always */ TRUE);
+ bus_state = 0;
+ break;
}
- }
- else
- {
- unsigned char hscb_index, linked_next;
- int disconnected;
+ printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
- 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)
+ /*
+ * 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)))
{
- /*
- * 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;
-
- 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 0
+ if (scb->control & TAG_ENB)
+ {
/*
- * If we are using AAP, aic7xxx_run_waiting_queues() will not
- * unpause us, so ensure we are unpaused.
+ * We could be starving this command; try sending and ordered tag
+ * command to the target we come from.
*/
- 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;
+ 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;
+
+ /*
+ * 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);
+
+ /*
+ * 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);
+ }
+ }
}
}
}
+ /* Make sure the sequencer is unpaused upon return. */
+ if (result == -1)
+ {
+ UNPAUSE_SEQUENCER(p);
+ }
return (result);
}
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_data->scb_array[aic7xxx_position(cmd)]);
+ scb = (p->scb_array[aic7xxx_position(cmd)]);
base = p->base;
- save_flags(processor_flags);
- cli();
-
#ifdef AIC7XXX_DEBUG_ABORT
- 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");
- }
+ printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
+ scb->position, TCL_OF_SCB(scb));
#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;
{
result = SCSI_ABORT_NOT_RUNNING;
}
- else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
+ else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
{
result = SCSI_ABORT_NOT_RUNNING;
}
else
{
- /*
- * 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;
- }
+ result = SCSI_ABORT_SNOOZE;
}
- restore_flags(processor_flags);
return (result);
}
{
struct aic7xxx_scb *scb = NULL;
struct aic7xxx_host *p;
- int base, found, tindex, min_target, max_target;
- int result = -1;
+ int base, found, tindex, min_target, max_target, result = -1;
char channel = 'A';
unsigned long processor_flags;
p = (struct aic7xxx_host *) cmd->host->hostdata;
- scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ scb = (p->scb_array[aic7xxx_position(cmd)]);
base = p->base;
channel = cmd->channel ? 'B': 'A';
tindex = (cmd->channel << 4) | cmd->target;
-#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");
- }
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
#endif
/*
if (scb->cmd != cmd)
scb = NULL;
- if (p->flags & IN_TIMEOUT)
+ if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+ && (scb != NULL))
{
/*
- * We've already started a recovery operation.
+ * 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 ((scb->flags & SCB_RECOVERY_SCB) == 0)
- {
- restore_flags(processor_flags);
- return (SCSI_RESET_PENDING);
- }
- }
- else
- {
- if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
- && (scb != NULL))
+ if ((p->flags & DEVICE_SUCCESS) ||
+ ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
{
- /*
- * 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 (cmd->serial_number != cmd->serial_number_at_timeout)
{
- 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 (scb == NULL)
{
result = SCSI_RESET_NOT_RUNNING;
}
else if (flags & SCSI_RESET_ASYNCHRONOUS)
{
- if (scb->flags & SCB_ABORTED)
+ if (scb->state & SCB_ABORTED)
{
result = SCSI_RESET_PENDING;
}
- else if (!(scb->flags & SCB_ACTIVE))
+ else if (!(scb->state & SCB_IN_PROGRESS))
{
result = SCSI_RESET_NOT_RUNNING;
}
if ((flags & SCSI_RESET_SYNCHRONOUS) &&
(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
{
- scb->flags |= SCB_ABORTED;
+ scb->state |= 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)
{
/*
{
result = SCSI_RESET_NOT_RUNNING;
}
- else if (!(scb->flags & SCB_ACTIVE))
+ else if (!(scb->state & SCB_IN_PROGRESS))
{
result = SCSI_RESET_NOT_RUNNING;
}
- else if ((scb->flags & SCB_ABORTED) &&
+ else if ((scb->state & SCB_ABORTED) &&
(!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
{
result = SCSI_RESET_PENDING;
/*
* 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
}
result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
- p->flags &= ~IN_TIMEOUT;
}
}
- aic7xxx_run_waiting_queues(p);
restore_flags(processor_flags);
return (result);
}