patch-1.3.4 linux/drivers/scsi/aha274x.c
Next file: linux/drivers/scsi/aha274x.h
Previous file: linux/drivers/scsi/README.aic7xxx
Back to the patch index
Back to the overall index
- Lines: 1491
- Date:
Thu Jan 1 02:00:00 1970
- Orig file:
v1.3.3/linux/drivers/scsi/aha274x.c
- Orig date:
Mon Jan 16 07:17:37 1995
diff -u --recursive --new-file v1.3.3/linux/drivers/scsi/aha274x.c linux/drivers/scsi/aha274x.c
@@ -1,1490 +0,0 @@
-/*
- * @(#)aha274x.c 1.29 94/10/29 jda
- *
- * Adaptec 274x device driver for Linux.
- * Copyright (c) 1994 The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Sources include the Adaptec 1740 driver (aha1740.c), the
- * Ultrastor 24F driver (ultrastor.c), various Linux kernel
- * source, the Adaptec EISA config file (!adp7771.cfg), the
- * Adaptec AHA-2740A Series User's Guide, the Linux Kernel
- * Hacker's Guide, Writing a SCSI Device Driver for Linux,
- * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA
- * overlay file (adp7770.ovl), the Adaptec AHA-2740 Series
- * Technical Reference Manual, the Adaptec AIC-7770 Data
- * Book, the ANSI SCSI specification, the ANSI SCSI-2
- * specification (draft 10c), ...
- *
- * On a twin-bus adapter card, channel B is ignored. Rationale:
- * it would greatly complicate the sequencer and host driver code,
- * and both busses are multiplexed on to the EISA bus anyway. So
- * I don't really see any technical advantage to supporting both.
- *
- * As well, multiple adapter card using the same IRQ level are
- * not supported. It doesn't make sense to configure the cards
- * this way from a performance standpoint. Not to mention that
- * the kernel would have to support two devices per registered IRQ.
- */
-
-#include <stdarg.h>
-#include <asm/io.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-
-#include "../block/blk.h"
-#include "sd.h"
-#include "scsi.h"
-#include "hosts.h"
-#include "aha274x.h"
-
-/*
- * There should be a specific return value for this in scsi.h, but
- * it seems that most drivers ignore it.
- */
-#define DID_UNDERFLOW DID_ERROR
-
-/* EISA stuff */
-
-#define MINEISA 1
-#define MAXEISA 15
-#define SLOTBASE(x) ((x) << 12)
-
-#define MAXIRQ 15
-
-/* AIC-7770 offset definitions */
-
-#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */
-#define O_MAXREG(x) ((x) + 0xcbf)
-
-#define O_SCSISEQ(x) ((x) + 0xc00) /* scsi sequence control */
-#define O_SCSISIGI(x) ((x) + 0xc03) /* scsi control signal read */
-#define O_SCSISIGO(x) ((x) + 0xc03) /* scsi control signal write */
-#define O_SCSIID(x) ((x) + 0xc05) /* scsi id */
-#define O_SSTAT0(x) ((x) + 0xc0b) /* scsi status register 0 */
-#define O_CLRSINT1(x) ((x) + 0xc0c) /* clear scsi interrupt 1 */
-#define O_SSTAT1(x) ((x) + 0xc0c) /* scsi status register 1 */
-#define O_SELID(x) ((x) + 0xc19) /* [re]selection id */
-#define O_SBLKCTL(x) ((x) + 0xc1f) /* scsi block control */
-#define O_SEQCTL(x) ((x) + 0xc60) /* sequencer control */
-#define O_SEQRAM(x) ((x) + 0xc61) /* sequencer ram data */
-#define O_SEQADDR(x) ((x) + 0xc62) /* sequencer address (W) */
-#define O_BIDx(x) ((x) + 0xc80) /* board id */
-#define O_BCTL(x) ((x) + 0xc84) /* board control */
-#define O_HCNTRL(x) ((x) + 0xc87) /* host control */
-#define O_SCBPTR(x) ((x) + 0xc90) /* scb pointer */
-#define O_INTSTAT(x) ((x) + 0xc91) /* interrupt status */
-#define O_ERROR(x) ((x) + 0xc92) /* hard error */
-#define O_CLRINT(x) ((x) + 0xc92) /* clear interrupt status */
-#define O_SCBCNT(x) ((x) + 0xc9a) /* scb auto increment */
-#define O_QINFIFO(x) ((x) + 0xc9b) /* queue in fifo */
-#define O_QINCNT(x) ((x) + 0xc9c) /* queue in count */
-#define O_QOUTFIFO(x) ((x) + 0xc9d) /* queue out fifo */
-#define O_QOUTCNT(x) ((x) + 0xc9e) /* queue out count */
-#define O_SCBARRAY(x) ((x) + 0xca0) /* scb array start */
-
-/* host adapter offset definitions */
-
-#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */
-#define HA_MSG_FLAGS(x) ((x) + 0xc35) /* outgoing message flag */
-#define HA_MSG_LEN(x) ((x) + 0xc36) /* outgoing message length */
-#define HA_MSG_START(x) ((x) + 0xc37) /* outgoing message body */
-#define HA_ARG_1(x) ((x) + 0xc4c) /* sdtr <-> rate parameters */
-#define HA_ARG_2(x) ((x) + 0xc4d)
-#define HA_RETURN_1(x) ((x) + 0xc4c)
-#define HA_RETURN_2(x) ((x) + 0xc4d)
-#define HA_SIGSTATE(x) ((x) + 0xc4e) /* value in SCSISIGO */
-#define HA_NEEDSDTR(x) ((x) + 0xc4f) /* synchronous negotiation? */
-
-#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */
-#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */
-#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */
-
-/* debugging code */
-
-#define AHA274X_DEBUG
-
-/*
- * If a parity error occurs during a data transfer phase, run the
- * command to completion - it's easier that way - making a note
- * of the error condition in this location. This then will modify
- * a DID_OK status into a DID_PARITY one for the higher-level SCSI
- * code.
- */
-#define aha274x_parity(cmd) ((cmd)->SCp.Status)
-
-/*
- * Since the sequencer code DMAs the scatter-gather structures
- * directly from memory, we use this macro to assert that the
- * kernel structure hasn't changed.
- */
-#define SG_STRUCT_CHECK(sg) \
- ((char *)&(sg).address - (char *)&(sg) != 0 || \
- (char *)&(sg).length - (char *)&(sg) != 8 || \
- sizeof((sg).address) != 4 || \
- sizeof((sg).length) != 4 || \
- sizeof(sg) != 12)
-
-/*
- * "Static" structures. Note that these are NOT initialized
- * to zero inside the kernel - we have to initialize them all
- * explicitly.
- *
- * We support a maximum of one adapter card per IRQ level (see the
- * rationale for this above). On an interrupt, use the IRQ as an
- * index into aha274x_boards[] to locate the card information.
- */
-static struct Scsi_Host *aha274x_boards[MAXIRQ + 1];
-
-struct aha274x_host {
- int base; /* card base address */
- int startup; /* intr type check */
- volatile int unpause; /* value for HCNTRL */
- volatile Scsi_Cmnd *SCB_array[AHA274X_MAXSCB]; /* active commands */
-};
-
-struct aha274x_scb {
- unsigned char control;
- unsigned char target_channel_lun; /* 4/1/3 bits */
- unsigned char SG_segment_count;
- unsigned char SG_list_pointer[4];
- unsigned char SCSI_cmd_pointer[4];
- unsigned char SCSI_cmd_length;
- unsigned char RESERVED[2]; /* must be zero */
- unsigned char target_status;
- unsigned char residual_data_count[3];
- unsigned char residual_SG_segment_count;
- unsigned char data_pointer[4];
- unsigned char data_count[3];
-#if 0
- /*
- * No real point in transferring this to the
- * SCB registers.
- */
- unsigned char RESERVED[6];
-#endif
-};
-
-/*
- * NB. This table MUST be ordered shortest period first.
- */
-static struct {
- short period;
- short rate;
- char *english;
-} aha274x_synctab[] = {
- {100, 0, "10.0"},
- {125, 1, "8.0"},
- {150, 2, "6.67"},
- {175, 3, "5.7"},
- {200, 4, "5.0"},
- {225, 5, "4.4"},
- {250, 6, "4.0"},
- {275, 7, "3.6"}
-};
-
-static int aha274x_synctab_max =
- sizeof(aha274x_synctab) / sizeof(aha274x_synctab[0]);
-
-enum aha_type {
- T_NONE,
- T_274X,
- T_284X,
- T_MAX
-};
-
-#ifdef AHA274X_DEBUG
-
- extern int vsprintf(char *, const char *, va_list);
-
- static
- void debug(const char *fmt, ...)
- {
- va_list ap;
- char buf[256];
-
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- printk(buf);
- va_end(ap);
- }
-
- static
- void debug_config(enum aha_type type, int base)
- {
- int ioport2, ioport3, ioport4;
-
- static char *BRT[T_MAX][16] = {
- { }, /* T_NONE */
- {
- "2", "???", "???", "12", /* T_274X */
- "???", "???", "???", "28",
- "???", "???", "???", "44",
- "???", "???", "???", "60"
- },
- {
- "2", "4", "8", "12", /* T_284X */
- "16", "20", "24", "28",
- "32", "36", "40", "44",
- "48", "52", "56", "60"
- }
- };
- static int DFT[4] = {
- 0, 50, 75, 100
- };
- static int SST[4] = {
- 256, 128, 64, 32
- };
-
- ioport2 = inb(HA_HOSTCONF(base));
- ioport3 = inb(HA_SCSICONF(base));
- ioport4 = inb(HA_INTDEF(base));
-
- if (type == T_284X)
- printk("AHA284X AT SLOT %d:\n", base >> 12);
- else
- printk("AHA274X AT EISA SLOT %d:\n", base >> 12);
-
- printk(" irq %d\n"
- " bus release time %s bclks\n"
- " data fifo threshold %d%%\n",
- ioport4 & 0xf,
- BRT[type][(ioport2 >> 2) & 0xf],
- DFT[(ioport2 >> 6) & 0x3]);
-
- printk(" SCSI CHANNEL A:\n"
- " scsi id %d\n"
- " scsi bus parity check %sabled\n"
- " scsi selection timeout %d ms\n"
- " scsi bus reset at power-on %sabled\n",
- ioport3 & 0x7,
- (ioport3 & 0x20) ? "en" : "dis",
- SST[(ioport3 >> 3) & 0x3],
- (ioport3 & 0x40) ? "en" : "dis");
-
- if (type == T_274X) {
- printk(" scsi bus termination %sabled\n",
- (ioport3 & 0x80) ? "en" : "dis");
- }
- }
-
- static
- void debug_rate(int base, int rate)
- {
- int target = inb(O_SCSIID(base)) >> 4;
-
- if (rate) {
- printk("aha274x: target %d now synchronous at %sMb/s\n",
- target,
- aha274x_synctab[(rate >> 4) & 0x7].english);
- } else {
- printk("aha274x: target %d using asynchronous mode\n",
- target);
- }
- }
-
-#else
-
-# define debug(fmt, args...)
-# define debug_config(x)
-# define debug_rate(x,y)
-
-#endif AHA274X_DEBUG
-
-/*
- * XXX - these options apply unilaterally to _all_ 274x/284x
- * cards in the system. This should be fixed, but then,
- * does anyone really have more than one in a machine?
- */
-static int aha274x_extended = 0; /* extended translation on? */
-
-void aha274x_setup(char *s, int *dummy)
-{
- int i;
- char *p;
-
- static struct {
- char *name;
- int *flag;
- } options[] = {
- {"extended", &aha274x_extended},
- {NULL, NULL }
- };
-
- for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
- for (i = 0; options[i].name; i++)
- if (!strcmp(options[i].name, p))
- *(options[i].flag) = !0;
- }
-}
-
-static
-void aha274x_getscb(int base, struct aha274x_scb *scb)
-{
- /*
- * This is almost identical to aha274x_putscb().
- */
- outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "insb"
- : /* no output */
- :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
- :"di", "cx", "dx");
-
- outb(0, O_SCBCNT(base));
-}
-
-/*
- * 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.
- */
-static
-unsigned aha274x_length(Scsi_Cmnd *cmd, int sg_last)
-{
- int i, segments;
- unsigned length;
- struct scatterlist *sg;
-
- segments = cmd->use_sg - sg_last;
- sg = (struct scatterlist *)cmd->buffer;
-
- if (cmd->use_sg) {
- for (i = length = 0;
- i < cmd->use_sg && i < segments;
- i++)
- {
- length += sg[i].length;
- }
- } else
- length = cmd->request_bufflen;
-
- return(length);
-}
-
-static
-void aha274x_sg_check(Scsi_Cmnd *cmd)
-{
- int i;
- struct scatterlist *sg = (struct scatterlist *)cmd->buffer;
-
- if (cmd->use_sg) {
- for (i = 0; i < cmd->use_sg; i++)
- if ((unsigned)sg[i].length > 0xffff)
- panic("aha274x_sg_check: s/g segment > 64k\n");
- }
-}
-
-static
-void aha274x_to_scsirate(unsigned char *rate,
- unsigned char transfer,
- unsigned char offset)
-{
- int i;
-
- transfer *= 4;
-
- for (i = 0; i < aha274x_synctab_max-1; i++) {
-
- if (transfer == aha274x_synctab[i].period) {
- *rate = (aha274x_synctab[i].rate << 4) | (offset & 0xf);
- return;
- }
-
- if (transfer > aha274x_synctab[i].period &&
- transfer < aha274x_synctab[i+1].period)
- {
- *rate = (aha274x_synctab[i+1].rate << 4) |
- (offset & 0xf);
- return;
- }
- }
- *rate = 0;
-}
-
-/*
- * 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) \
- do { \
- outb(0xe, O_HCNTRL(p->base)); /* IRQMS|PAUSE|INTEN */ \
- \
- while ((inb(O_HCNTRL(p->base)) & 0x4) == 0) \
- ; \
- } while (0)
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
- outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */
-
-/*
- * See comments in aha274x_loadram() wrt this.
- */
-#define RESTART_SEQUENCER(p) \
- do { \
- do { \
- outb(0x2, O_SEQCTL(p->base)); \
- } while (inw(O_SEQADDR(p->base)) != 0); \
- \
- UNPAUSE_SEQUENCER(p); \
- } while (0)
-
-/*
- * Since we declared this using SA_INTERRUPT, interrupts should
- * be disabled all through this function unless we say otherwise.
- */
-static
-void aha274x_isr(int irq, struct pt_regs * regs)
-{
- int base, intstat;
- struct aha274x_host *p;
-
- p = (struct aha274x_host *)aha274x_boards[irq]->hostdata;
- base = p->base;
-
- /*
- * Check the startup flag - if no commands have been queued,
- * we probably have the interrupt type set wrong. Reverse
- * the stored value and the active one in the host control
- * register.
- */
- if (p->startup) {
- p->unpause ^= 0x8;
- outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base));
- return;
- }
-
- /*
- * Handle all the interrupt sources - especially for SCSI
- * interrupts, we won't get a second chance at them.
- */
- intstat = inb(O_INTSTAT(base));
-
- if (intstat & 0x8) { /* BRKADRINT */
-
- panic("aha274x_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
- inb(O_ERROR(base)), inw(O_SEQADDR(base)));
- }
-
- if (intstat & 0x4) { /* SCSIINT */
-
- int scbptr = inb(O_SCBPTR(base));
- int status = inb(O_SSTAT1(base));
- Scsi_Cmnd *cmd;
-
- cmd = (Scsi_Cmnd *)p->SCB_array[scbptr];
- if (!cmd) {
- printk("aha274x_isr: no command for scb (scsiint)\n");
- /*
- * Turn off the interrupt and set status
- * to zero, so that it falls through the
- * reset of the SCSIINT code.
- */
- outb(status, O_CLRSINT1(base));
- UNPAUSE_SEQUENCER(p);
- outb(0x4, O_CLRINT(base)); /* undocumented */
- status = 0;
- }
- p->SCB_array[scbptr] = NULL;
-
- /*
- * Only the SCSI Status 1 register has information
- * about exceptional conditions that we'd have a
- * SCSIINT about; anything in SSTAT0 will be handled
- * by the sequencer. Note that there can be multiple
- * bits set.
- */
- if (status & 0x80) { /* SELTO */
- /*
- * Hardware selection timer has expired. Turn
- * off SCSI selection sequence.
- */
- outb(0, O_SCSISEQ(base));
- cmd->result = DID_TIME_OUT << 16;
-
- /*
- * If there's an active message, it belongs to the
- * command that is getting punted - remove it.
- */
- outb(0, HA_MSG_FLAGS(base));
-
- /*
- * Shut off the offending interrupt sources, reset
- * the sequencer address to zero and unpause it,
- * then call the high-level SCSI completion routine.
- *
- * WARNING! This is a magic sequence! After many
- * hours of guesswork, turning off the SCSI interrupts
- * in CLRSINT? does NOT clear the SCSIINT bit in
- * INTSTAT. By writing to the (undocumented, unused
- * according to the AIC-7770 manual) third bit of
- * CLRINT, you can clear INTSTAT. But, if you do it
- * while the sequencer is paused, you get a BRKADRINT
- * with an Illegal Host Address status, so the
- * sequencer has to be restarted first.
- */
- outb(0x80, O_CLRSINT1(base)); /* CLRSELTIMO */
- RESTART_SEQUENCER(p);
-
- outb(0x4, O_CLRINT(base)); /* undocumented */
- cmd->scsi_done(cmd);
- }
-
- if (status & 0x4) { /* SCSIPERR */
- /*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
- */
- printk("aha274x: parity error on target %d, lun %d\n",
- cmd->target,
- cmd->lun);
- aha274x_parity(cmd) = DID_PARITY;
-
- /*
- * Clear interrupt and resume as above.
- */
- outb(0x4, O_CLRSINT1(base)); /* CLRSCSIPERR */
- UNPAUSE_SEQUENCER(p);
-
- outb(0x4, O_CLRINT(base)); /* undocumented */
- }
-
- if ((status & (0x8|0x4)) == 0 && status) {
- /*
- * We don't know what's going on. Turn off the
- * interrupt source and try to continue.
- */
- printk("aha274x_isr: sstat1 = 0x%x\n", status);
- outb(status, O_CLRSINT1(base));
- UNPAUSE_SEQUENCER(p);
- outb(0x4, O_CLRINT(base)); /* undocumented */
- }
- }
-
- if (intstat & 0x2) { /* CMDCMPLT */
-
- int complete, old_scbptr;
- struct aha274x_scb scb;
- unsigned actual;
- Scsi_Cmnd *cmd;
-
- /*
- * The sequencer will continue running when it
- * issues this interrupt. There may be >1 commands
- * finished, so loop until we've processed them all.
- */
- do {
- complete = inb(O_QOUTFIFO(base));
-
- cmd = (Scsi_Cmnd *)p->SCB_array[complete];
- if (!cmd) {
- printk("aha274x warning: "
- "no command for scb (cmdcmplt)\n");
- continue;
- }
- p->SCB_array[complete] = NULL;
-
- PAUSE_SEQUENCER(p);
-
- /*
- * After pausing the sequencer (and waiting
- * for it to stop), save its SCB pointer, then
- * write in our completed one and read the SCB
- * registers. Afterwards, restore the saved
- * pointer, unpause the sequencer and call the
- * higher-level completion function - unpause
- * first since we have no idea how long done()
- * will take.
- */
- old_scbptr = inb(O_SCBPTR(base));
- outb(complete, O_SCBPTR(base));
-
- aha274x_getscb(base, &scb);
- outb(old_scbptr, O_SCBPTR(base));
-
- UNPAUSE_SEQUENCER(p);
-
- cmd->result = scb.target_status |
- (aha274x_parity(cmd) << 16);
-
- /*
- * Did we 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 = aha274x_length(cmd,
- scb.residual_SG_segment_count);
-
- actual -= ((scb.residual_data_count[2] << 16) |
- (scb.residual_data_count[1] << 8) |
- (scb.residual_data_count[0]));
-
- if (actual < cmd->underflow) {
- printk("aha274x: target %d underflow - "
- "wanted (at least) %u, got %u\n",
- cmd->target, cmd->underflow, actual);
-
- cmd->result = scb.target_status |
- (DID_UNDERFLOW << 16);
- }
-
- cmd->scsi_done(cmd);
-
- /*
- * Clear interrupt status before checking
- * the output queue again. This eliminates
- * a race condition whereby a command could
- * complete between the queue poll and the
- * interrupt clearing, so notification of the
- * command being complete never made it back
- * up to the kernel.
- */
- outb(0x2, O_CLRINT(base)); /* CLRCMDINT */
-
- } while (inb(O_QOUTCNT(base)));
- }
-
- if (intstat & 0x1) { /* SEQINT */
-
- unsigned char transfer, offset, rate;
-
- /*
- * Although the sequencer is paused immediately on
- * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
- * condition will have unpaused the sequencer before
- * this point.
- */
- PAUSE_SEQUENCER(p);
-
- switch (intstat & 0xf0) {
- case 0x00:
- panic("aha274x_isr: unknown scsi bus phase\n");
- case 0x10:
- debug("aha274x_isr warning: "
- "issuing message reject, 1st byte 0x%x\n",
- inb(HA_REJBYTE(base)));
- break;
- case 0x20:
- panic("aha274x_isr: reconnecting target %d "
- "didn't issue IDENTIFY message\n",
- (inb(O_SELID(base)) >> 4) & 0xf);
- case 0x30:
- debug("aha274x_isr: sequencer couldn't find match "
- "for reconnecting target %d - issuing ABORT\n",
- (inb(O_SELID(base)) >> 4) & 0xf);
- break;
- case 0x40:
- transfer = inb(HA_ARG_1(base));
- offset = inb(HA_ARG_2(base));
- aha274x_to_scsirate(&rate, transfer, offset);
- outb(rate, HA_RETURN_1(base));
- debug_rate(base, rate);
- break;
- default:
- debug("aha274x_isr: seqint, "
- "intstat = 0x%x, scsisigi = 0x%x\n",
- intstat, inb(O_SCSISIGI(base)));
- break;
- }
-
- outb(0x1, O_CLRINT(base)); /* CLRSEQINT */
- UNPAUSE_SEQUENCER(p);
- }
-}
-
-/*
- * Probing for EISA boards: it looks like the first two bytes
- * are a manufacturer code - three characters, five bits each:
- *
- * BYTE 0 BYTE 1 BYTE 2 BYTE 3
- * ?1111122 22233333 PPPPPPPP RRRRRRRR
- *
- * The characters are baselined off ASCII '@', so add that value
- * to each to get the real ASCII code for it. The next two bytes
- * appear to be a product and revision number, probably vendor-
- * specific. This is what is being searched for at each port,
- * and what should probably correspond to the ID= field in the
- * ECU's .cfg file for the card - if your card is not detected,
- * make sure your signature is listed in the array.
- *
- * The fourth byte's lowest bit seems to be an enabled/disabled
- * flag (rest of the bits are reserved?).
- */
-
-static
-enum aha_type aha274x_probe(int slot, int s_base)
-{
- int i;
- unsigned char buf[4];
-
- static struct {
- int n;
- unsigned char signature[sizeof(buf)];
- enum aha_type type;
- } S[] = {
- {4, { 0x04, 0x90, 0x77, 0x71 }, T_274X}, /* host adapter 274x */
- {4, { 0x04, 0x90, 0x77, 0x70 }, T_274X}, /* motherboard 274x */
- {4, { 0x04, 0x90, 0x77, 0x56 }, T_284X}, /* 284x, BIOS enabled */
- };
-
- for (i = 0; i < sizeof(buf); i++) {
- /*
- * The VL-bus cards need to be primed by
- * writing before a signature check.
- */
- outb(0x80 + i, s_base);
- buf[i] = inb(s_base + i);
- }
-
- for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) {
- if (!memcmp(buf, S[i].signature, S[i].n)) {
- /*
- * Signature match on enabled card?
- */
- if (inb(s_base + 4) & 1)
- return(S[i].type);
- printk("aha274x disabled at slot %d, ignored\n", slot);
- }
- }
- return(T_NONE);
-}
-
-/*
- * Return ' ' for plain 274x, 'T' for twin-channel, 'W' for
- * wide channel, '?' for anything else.
- */
-
-static
-char aha274x_type(int base)
-{
- /*
- * The AIC-7770 can be wired so that, on chip reset,
- * the SCSI Block Control register indicates how many
- * busses the chip is configured for.
- */
- switch (inb(O_SBLKCTL(base))) {
- case 0:
- return(' ');
- case 2:
- return('W');
- case 8:
- return('T');
- default:
- printk("aha274x has unknown bus configuration\n");
- return('?');
- }
-}
-
-static
-void aha274x_loadram(int base)
-{
- 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 "aha274x_seq.h"
- };
-
- /*
- * 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(0x83, O_SEQCTL(base)); /* PERRORDIS|SEQRESET|LOADRAM */
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base))
- :"si", "cx", "dx");
-
- /*
- * 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?)
- */
- outb(0, O_SEQCTL(base));
- do {
- /*
- * Actually, reset it until
- * the address shows up as
- * zero just to be safe..
- */
- outb(0x2, O_SEQCTL(base)); /* SEQRESET */
-
- } while (inw(O_SEQADDR(base)) != 0);
-}
-
-static
-int aha274x_register(Scsi_Host_Template *template,
- enum aha_type type,
- int base)
-{
- int i, irq, scsi_id;
- struct Scsi_Host *host;
- struct aha274x_host *p;
-
- /*
- * Give the AIC-7770 a reset - reading the 274x's registers
- * returns zeroes unless you do. This forces a pause of the
- * Sequencer.
- */
- outb(1, O_HCNTRL(base)); /* CHIPRST */
-
- /*
- * 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?
- */
- irq = inb(HA_INTDEF(base)) & 0xf;
- if (irq < 9 || irq > 15) {
- printk("aha274x uses unsupported IRQ level, ignoring\n");
- return(0);
- }
-
- /*
- * Lock out other contenders for our i/o space.
- */
- request_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base), "aha27x");
-
- /*
- * Any card-type-specific adjustments before we register
- * the scsi host(s).
- */
-
- scsi_id = inb(HA_SCSICONF(base)) & 0x7;
-
- switch (aha274x_type(base)) {
- case 'T':
- printk("aha274x warning: ignoring channel B of 274x-twin\n");
- break;
- case ' ':
- break;
- default:
- printk("aha274x is an unsupported type, ignoring\n");
- return(0);
- }
-
- /*
- * Before registry, make sure that the offsets of the
- * struct scatterlist are what the sequencer will expect,
- * otherwise disable scatter-gather altogether until someone
- * can fix it. This is important since the sequencer will
- * DMA elements of the SG array in while executing commands.
- */
- if (template->sg_tablesize != SG_NONE) {
- struct scatterlist sg;
-
- if (SG_STRUCT_CHECK(sg)) {
- printk("aha274x warning: kernel scatter-gather "
- "structures changed, disabling it\n");
- template->sg_tablesize = SG_NONE;
- }
- }
-
- /*
- * Register each "host" and fill in the returned Scsi_Host
- * structure as best we can. Some of the parameters aren't
- * really relevant for EISA, 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 aha274x_host));
- host->this_id = scsi_id;
- host->irq = irq;
-
- aha274x_boards[irq] = host;
-
- p = (struct aha274x_host *)host->hostdata;
- for (i = 0; i < AHA274X_MAXSCB; i++)
- p->SCB_array[i] = NULL;
- p->base = base;
-
- /*
- * The interrupt trigger is different depending
- * on whether the card is EISA or VL-bus - sometimes.
- * The startup variable will be cleared once the first
- * command is queued, and is checked in the isr to
- * try and detect when the interrupt type is set
- * incorrectly, triggering an interrupt immediately.
- */
- p->unpause = (type != T_274X ? 0x2 : 0xa);
- p->startup = !0;
-
- /*
- * Register IRQ with the kernel _after_ the host information
- * is set up, in case we take an interrupt right away, due to
- * the interrupt type being set wrong.
- */
- if (request_irq(irq, aha274x_isr, SA_INTERRUPT, "AHA274x/284x")) {
- printk("aha274x couldn't register irq %d, ignoring\n", irq);
- return(0);
- }
-
- /*
- * A reminder until this can be detected automatically.
- */
- printk("aha274x: extended translation %sabled\n",
- aha274x_extended ? "en" : "dis");
-
- /*
- * 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(type, base);
-
- /*
- * Load the sequencer program, then re-enable the board -
- * resetting the AIC-7770 disables it, leaving the lights
- * on with nobody home.
- */
- aha274x_loadram(base);
- outb(1, O_BCTL(base)); /* ENABLE */
-
- /*
- * Set the host adapter registers to indicate that synchronous
- * negotiation should be attempted the first time the targets
- * are communicated with. Also initialize the active message
- * flag to indicate that there is no message.
- */
- outb(0xff, HA_NEEDSDTR(base));
- outb(0, HA_MSG_FLAGS(base));
-
- /*
- * 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(1);
-}
-
-int aha274x_detect(Scsi_Host_Template *template)
-{
- enum aha_type type;
- int found = 0, slot, base;
-
- for (slot = MINEISA; slot <= MAXEISA; slot++) {
-
- base = SLOTBASE(slot);
-
- if (check_region(O_MINREG(base),
- O_MAXREG(base)-O_MINREG(base)))
- {
- /*
- * Some other driver has staked a
- * claim to this i/o region already.
- */
- continue;
- }
-
- type = aha274x_probe(slot, O_BIDx(base));
-
- if (type != T_NONE) {
- /*
- * We "find" a 274x if we locate the card
- * signature and we can set it up and register
- * it with the kernel without incident.
- */
- found += aha274x_register(template, type, base);
- }
- }
- template->name = (char *)aha274x_info(NULL);
- return(found);
-}
-
-const char *aha274x_info(struct Scsi_Host * shost)
-{
- return("Adaptec AHA274x/284x (EISA/VL-bus -> Fast SCSI) "
- AHA274X_SEQ_VERSION "/"
- AHA274X_H_VERSION "/"
- "1.29");
-}
-
-int aha274x_command(Scsi_Cmnd *cmd)
-{
- /*
- * This is a relic of non-interrupt-driven SCSI
- * drivers. With the can_queue variable set, this
- * should never be called.
- */
- panic("aha274x_command was called\n");
-}
-
-static
-void aha274x_buildscb(struct aha274x_host *p,
- Scsi_Cmnd *cmd,
- struct aha274x_scb *scb)
-{
- void *addr;
- unsigned length;
-
- memset(scb, 0, sizeof(*scb));
-
- /*
- * NB. channel selection (bit 3) is always zero.
- */
- scb->target_channel_lun = ((cmd->target << 4) & 0xf0) |
- (cmd->lun & 0x7);
-
- /*
- * The interpretation of request_buffer and request_bufflen
- * changes depending on whether or not use_sg is zero; a
- * non-zero use_sg indicates the number of elements in the
- * scatter-gather array.
- *
- * The AIC-7770 can't support transfers of any sort larger
- * than 2^24 (three-byte count) without backflips. For what
- * the kernel is doing, this shouldn't occur. I hope.
- */
- length = aha274x_length(cmd, 0);
-
- /*
- * The sequencer code cannot yet handle scatter-gather segments
- * larger than 64k (two-byte length). The 1.1.x kernels, however,
- * have a four-byte length field in the struct scatterlist, so
- * make sure we don't exceed 64k on these kernels for now.
- */
- aha274x_sg_check(cmd);
-
- if (length > 0xffffff) {
- panic("aha274x_buildscb: can't transfer > 2^24 - 1 bytes\n");
- }
-
- /*
- * XXX - this relies on the host data being stored in a
- * little-endian format.
- */
- addr = cmd->cmnd;
- scb->SCSI_cmd_length = cmd->cmd_len;
- memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
-
- if (cmd->use_sg) {
-#if 0
- debug("aha274x_buildscb: SG used, %d segments, length %u\n",
- cmd->use_sg,
- length);
-#endif
- scb->SG_segment_count = cmd->use_sg;
- memcpy(scb->SG_list_pointer,
- &cmd->request_buffer,
- sizeof(scb->SG_list_pointer));
- } else {
- scb->SG_segment_count = 0;
- memcpy(scb->data_pointer,
- &cmd->request_buffer,
- sizeof(scb->data_pointer));
- memcpy(scb->data_count,
- &cmd->request_bufflen,
- sizeof(scb->data_count));
- }
-}
-
-static
-void aha274x_putscb(int base, struct aha274x_scb *scb)
-{
- /*
- * 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.
- */
- outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
- :"si", "cx", "dx");
-
- outb(0, O_SCBCNT(base));
-}
-
-int aha274x_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
-{
- long flags;
- int empty, old_scbptr;
- struct aha274x_host *p;
- struct aha274x_scb scb;
-
-#if 0
- debug("aha274x_queue: cmd 0x%x (size %u), target %d, lun %d\n",
- cmd->cmnd[0],
- cmd->cmd_len,
- cmd->target,
- cmd->lun);
-#endif
-
- p = (struct aha274x_host *)cmd->host->hostdata;
-
- /*
- * Construct the SCB beforehand, so the sequencer is
- * paused a minimal amount of time.
- */
- aha274x_buildscb(p, cmd, &scb);
-
- /*
- * Clear the startup flag - we can now legitimately
- * expect interrupts.
- */
- p->startup = 0;
-
- /*
- * This is a critical section, since we don't want the
- * interrupt routine mucking with the host data or the
- * card. Since the kernel documentation is vague on
- * whether or not we are in a cli/sti pair already, save
- * the flags to be on the safe side.
- */
- save_flags(flags);
- cli();
-
- /*
- * Find a free slot in the SCB array to load this command
- * into. Since can_queue is set to AHA274X_MAXSCB, we
- * should always find one.
- */
- for (empty = 0; empty < AHA274X_MAXSCB; empty++)
- if (!p->SCB_array[empty])
- break;
- if (empty == AHA274X_MAXSCB)
- panic("aha274x_queue: couldn't find a free scb\n");
-
- /*
- * 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);
-
- /*
- * 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.
- */
- old_scbptr = inb(O_SCBPTR(p->base));
- outb(empty, O_SCBPTR(p->base));
-
- aha274x_putscb(p->base, &scb);
-
- outb(empty, O_QINFIFO(p->base));
- outb(old_scbptr, O_SCBPTR(p->base));
-
- /*
- * Make sure the Scsi_Cmnd pointer is saved, the struct it
- * points to is set up properly, and the parity error flag
- * is reset, then unpause the sequencer and watch the fun
- * begin.
- */
- cmd->scsi_done = fn;
- p->SCB_array[empty] = cmd;
- aha274x_parity(cmd) = DID_OK;
-
- UNPAUSE_SEQUENCER(p);
-
- restore_flags(flags);
- return(0);
-}
-
-/* return values from aha274x_kill */
-
-enum k_state {
- k_ok, /* scb found and message sent */
- k_busy, /* message already present */
- k_absent, /* couldn't locate scb */
- k_disconnect, /* scb found, but disconnected */
-};
-
-/*
- * This must be called with interrupts disabled - it's going to
- * be messing around with the host data, and an interrupt being
- * fielded in the middle could get ugly.
- *
- * Since so much of the abort and reset code is shared, this
- * function performs more magic than it really should. If the
- * command completes ok, then it will call scsi_done with the
- * result code passed in. The unpause parameter controls whether
- * or not the sequencer gets unpaused - the reset function, for
- * instance, may want to do something more aggressive.
- *
- * Note that the command is checked for in our SCB_array first
- * before the sequencer is paused, so if k_absent is returned,
- * then the sequencer is NOT paused.
- */
-
-static
-enum k_state aha274x_kill(Scsi_Cmnd *cmd, unsigned char message,
- unsigned int result, int unpause)
-{
- struct aha274x_host *p;
- int i, scb, found, queued;
- unsigned char scbsave[AHA274X_MAXSCB];
-
- p = (struct aha274x_host *)cmd->host->hostdata;
-
- /*
- * If we can't find the command, assume it just completed
- * and shrug it away.
- */
- for (scb = 0; scb < AHA274X_MAXSCB; scb++)
- if (p->SCB_array[scb] == cmd)
- break;
-
- if (scb == AHA274X_MAXSCB)
- return(k_absent);
-
- PAUSE_SEQUENCER(p);
-
- /*
- * This is the best case, really. Check to see if the
- * command is still in the sequencer's input queue. If
- * so, simply remove it. Reload the queue afterward.
- */
- queued = inb(O_QINCNT(p->base));
-
- for (i = found = 0; i < queued; i++) {
- scbsave[i] = inb(O_QINFIFO(p->base));
-
- if (scbsave[i] == scb) {
- found = 1;
- i -= 1;
- }
- }
-
- queued -= found;
- for (i = 0; i < queued; i++)
- outb(scbsave[i], O_QINFIFO(p->base));
-
- if (found)
- goto complete;
-
- /*
- * Check the current SCB bank. If it's not the one belonging
- * to the command we want to kill, assume that the command
- * is disconnected. It's rather a pain to force a reconnect
- * and send a message to the target, so we abdicate responsibility
- * in this case.
- */
- if (inb(O_SCBPTR(p->base)) != scb) {
- if (unpause)
- UNPAUSE_SEQUENCER(p);
- return(k_disconnect);
- }
-
- /*
- * Presumably at this point our target command is active. Check
- * to see if there's a message already in effect. If not, place
- * our message in and assert ATN so the target goes into MESSAGE
- * OUT phase.
- */
- if (inb(HA_MSG_FLAGS(p->base)) & 0x80) {
- if (unpause)
- UNPAUSE_SEQUENCER(p);
- return(k_busy);
- }
-
- outb(0x80, HA_MSG_FLAGS(p->base)); /* active message */
- outb(1, HA_MSG_LEN(p->base)); /* length = 1 */
- outb(message, HA_MSG_START(p->base)); /* message body */
-
- /*
- * Assert ATN. Use the value of SCSISIGO saved by the
- * sequencer code so we don't alter its contents radically
- * in the middle of something critical.
- */
- outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base));
-
- /*
- * The command has been killed. Do the bookkeeping, unpause
- * the sequencer, and notify the higher-level SCSI code.
- */
-complete:
- p->SCB_array[scb] = NULL;
- if (unpause)
- UNPAUSE_SEQUENCER(p);
-
- cmd->result = result << 16;
- cmd->scsi_done(cmd);
- return(k_ok);
-}
-
-int aha274x_abort(Scsi_Cmnd *cmd)
-{
- int rv;
- long flags;
-
- save_flags(flags);
- cli();
-
- switch (aha274x_kill(cmd, ABORT, DID_ABORT, !0)) {
- case k_ok: rv = SCSI_ABORT_SUCCESS; break;
- case k_busy: rv = SCSI_ABORT_BUSY; break;
- case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break;
- case k_disconnect: rv = SCSI_ABORT_SNOOZE; break;
- default:
- panic("aha274x_do_abort: internal error\n");
- }
-
- restore_flags(flags);
- return(rv);
-}
-
-/*
- * Resetting the bus always succeeds - is has to, otherwise the
- * kernel will panic! Try a surgical technique - sending a BUS
- * DEVICE RESET message - on the offending target before pulling
- * the SCSI bus reset line.
- */
-
-int aha274x_reset(Scsi_Cmnd *cmd)
-{
- int i;
- long flags;
- Scsi_Cmnd *reset;
- struct aha274x_host *p;
-
- p = (struct aha274x_host *)cmd->host->hostdata;
- save_flags(flags);
- cli();
-
- switch (aha274x_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) {
-
- case k_ok:
- /*
- * The RESET message was sent to the target
- * with no problems. Flag that target as
- * needing a SDTR negotiation on the next
- * connection and restart the sequencer.
- */
- outb((1 << cmd->target), HA_NEEDSDTR(p->base));
- UNPAUSE_SEQUENCER(p);
- break;
-
- case k_absent:
- /*
- * The sequencer will not be paused if aha274x_kill()
- * couldn't find the command.
- */
- PAUSE_SEQUENCER(p);
- /* falls through */
-
- case k_busy:
- case k_disconnect:
- /*
- * Do a hard reset of the SCSI bus. According to the
- * SCSI-2 draft specification, reset has to be asserted
- * for at least 25us. I'm invoking the kernel delay
- * function for 30us since I'm not totally trusting of
- * the busy loop timing.
- *
- * XXX - I'm not convinced this works. I tried resetting
- * the bus before, trying to get the devices on the
- * bus to revert to asynchronous transfer, and it
- * never seemed to work.
- */
- debug("aha274x: attempting to reset scsi bus and card\n");
-
- outb(1, O_SCSISEQ(p->base)); /* SCSIRSTO */
- udelay(30);
- outb(0, O_SCSISEQ(p->base)); /* !SCSIRSTO */
-
- outb(0xff, HA_NEEDSDTR(p->base));
- UNPAUSE_SEQUENCER(p);
-
- /*
- * Locate the command and return a "reset" status
- * for it. This is not completely correct and will
- * probably return to haunt me later.
- */
- for (i = 0; i < AHA274X_MAXSCB; i++) {
- if (cmd == p->SCB_array[i]) {
- reset = (Scsi_Cmnd *)p->SCB_array[i];
- p->SCB_array[i] = NULL;
- reset->result = DID_RESET << 16;
- reset->scsi_done(reset);
- break;
- }
- }
- break;
-
- default:
- panic("aha274x_reset: internal error\n");
- }
-
- restore_flags(flags);
- return(SCSI_RESET_SUCCESS);
-}
-
-int aha274x_biosparam(Disk *disk, int devno, int geom[])
-{
- int heads, sectors, cylinders;
-
- /*
- * XXX - if I could portably find the card's configuration
- * information, then this could be autodetected instead
- * of left to a boot-time switch.
- */
- heads = 64;
- sectors = 32;
- cylinders = disk->capacity / (heads * sectors);
-
- if (aha274x_extended && cylinders > 1024) {
- heads = 255;
- sectors = 63;
- cylinders = disk->capacity / (255 * 63);
- }
-
- geom[0] = heads;
- geom[1] = sectors;
- geom[2] = cylinders;
-
- return(0);
-}
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this