freebsd-src/sys/i386/isa/aha1542.c
Peter Dufault 5dde8b2750 Support the 1542CP by recognizing the new ID. Try to make future
1542 revs work by assuming the next few sequential ID codes are
new Adaptec boards and enabling them after printing a warning.

Conditionalize the informational boot messages with "if (bootverbose)".
1995-10-01 15:09:51 +00:00

1866 lines
44 KiB
C

/*
* (Mostly) Written by Julian Elischer (julian@tfs.com)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
*
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
*
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
*
* $Id: aha1542.c,v 1.47 1995/09/19 18:55:04 bde Exp $
*/
/*
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
*/
#include <sys/types.h>
#ifdef KERNEL /* don't laugh.. look for main() */
#include "aha.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <machine/clock.h>
#include <i386/isa/isa_device.h>
#include <machine/clock.h>
#include <machine/cpu.h> /* XXX for bootverbose: a funny place */
#endif /* KERNEL */
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <sys/devconf.h>
#ifdef KERNEL
#include <sys/kernel.h>
#else /*KERNEL */
#define NAHA 1
#endif /*KERNEL */
/************************** board definitions *******************************/
/*
* I/O Port Interface
*/
#define AHA_BASE aha->aha_base
#define AHA_CTRL_STAT_PORT (AHA_BASE + 0x0) /* control & status */
#define AHA_CMD_DATA_PORT (AHA_BASE + 0x1) /* cmds and datas */
#define AHA_INTR_PORT (AHA_BASE + 0x2) /* Intr. stat */
/*
* AHA_CTRL_STAT bits (write)
*/
#define AHA_HRST 0x80 /* Hardware reset */
#define AHA_SRST 0x40 /* Software reset */
#define AHA_IRST 0x20 /* Interrupt reset */
#define AHA_SCRST 0x10 /* SCSI bus reset */
/*
* AHA_CTRL_STAT bits (read)
*/
#define AHA_STST 0x80 /* Self test in Progress */
#define AHA_DIAGF 0x40 /* Diagnostic Failure */
#define AHA_INIT 0x20 /* Mbx Init required */
#define AHA_IDLE 0x10 /* Host Adapter Idle */
#define AHA_CDF 0x08 /* cmd/data out port full */
#define AHA_DF 0x04 /* Data in port full */
#define AHA_INVDCMD 0x01 /* Invalid command */
/*
* AHA_CMD_DATA bits (write)
*/
#define AHA_NOP 0x00 /* No operation */
#define AHA_MBX_INIT 0x01 /* Mbx initialization */
#define AHA_START_SCSI 0x02 /* start scsi command */
#define AHA_START_BIOS 0x03 /* start bios command */
#define AHA_INQUIRE 0x04 /* Adapter Inquiry */
#define AHA_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
#define AHA_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
#define AHA_BUS_ON_TIME_SET 0x07 /* set bus-on time */
#define AHA_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
#define AHA_SPEED_SET 0x09 /* set transfer speed */
#define AHA_DEV_GET 0x0a /* return installed devices */
#define AHA_CONF_GET 0x0b /* return configuration data */
#define AHA_TARGET_EN 0x0c /* enable target mode */
#define AHA_SETUP_GET 0x0d /* return setup data */
#define AHA_WRITE_CH2 0x1a /* write channel 2 buffer */
#define AHA_READ_CH2 0x1b /* read channel 2 buffer */
#define AHA_WRITE_FIFO 0x1c /* write fifo buffer */
#define AHA_READ_FIFO 0x1d /* read fifo buffer */
#define AHA_ECHO 0x1e /* Echo command data */
#define AHA_EXT_BIOS 0x28 /* return extended bios info */
#define AHA_MBX_ENABLE 0x29 /* enable mail box interface */
struct aha_cmd_buf {
u_char byte[16];
};
/*
* AHA_INTR_PORT bits (read)
*/
#define AHA_ANY_INTR 0x80 /* Any interrupt */
#define AHA_SCRD 0x08 /* SCSI reset detected */
#define AHA_HACC 0x04 /* Command complete */
#define AHA_MBOA 0x02 /* MBX out empty */
#define AHA_MBIF 0x01 /* MBX in full */
/*
* Mail box defs
*/
#define AHA_MBX_SIZE 16 /* mail box size */
struct aha_mbx {
struct aha_mbx_out {
unsigned char cmd;
unsigned char ccb_addr[3];
} mbo[AHA_MBX_SIZE];
struct aha_mbx_in {
unsigned char stat;
unsigned char ccb_addr[3];
} mbi[AHA_MBX_SIZE];
};
/*
* mbo.cmd values
*/
#define AHA_MBO_FREE 0x0 /* MBO entry is free */
#define AHA_MBO_START 0x1 /* MBO activate entry */
#define AHA_MBO_ABORT 0x2 /* MBO abort entry */
/*
* mbi.stat values
*/
#define AHA_MBI_FREE 0x0 /* MBI entry is free */
#define AHA_MBI_OK 0x1 /* completed without error */
#define AHA_MBI_ABORT 0x2 /* aborted ccb */
#define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
#define AHA_MBI_ERROR 0x4 /* Completed with error */
#define AHA_MBI_TGT_NO_CCB 0x10 /* Target received, no CCB ready */
/* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */
#define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */
/* allow 64 K i/o (min) */
struct aha_ccb {
unsigned char opcode;
unsigned char lun:3;
unsigned char data_in:1; /* must be 0 */
unsigned char data_out:1; /* must be 0 */
unsigned char target:3;
unsigned char scsi_cmd_length;
unsigned char req_sense_length;
unsigned char data_length[3];
unsigned char data_addr[3];
unsigned char link_addr[3];
unsigned char link_id;
unsigned char host_stat;
unsigned char target_stat;
unsigned char reserved[2];
struct scsi_generic scsi_cmd;
struct scsi_sense_data scsi_sense;
struct aha_scat_gath {
unsigned char seg_len[3];
unsigned char seg_addr[3];
} scat_gath[AHA_NSEG];
struct aha_ccb *next;
struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
struct aha_mbx_out *mbx; /* pointer to mail box */
int flags;
#define CCB_FREE 0
#define CCB_ACTIVE 1
#define CCB_ABORTED 2
};
/*
* opcode fields
*/
#define AHA_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
#define AHA_TARGET_CCB 0x01 /* SCSI Target CCB */
#define AHA_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scatter gather */
#define AHA_RESET_CCB 0x81 /* SCSI Bus reset */
#define AHA_INIT_RESID_CCB 0x03 /* SCSI Initiator CCB */
#define AHA_INIT_SG_RESID_CCB 0x04 /* SCSI initiator with scatter gather */
/*
* aha_ccb.host_stat values
*/
#define AHA_OK 0x00 /* cmd ok */
#define AHA_LINK_OK 0x0a /* Link cmd ok */
#define AHA_LINK_IT 0x0b /* Link cmd ok + int */
#define AHA_SEL_TIMEOUT 0x11 /* Selection time out */
#define AHA_OVER_UNDER 0x12 /* Data over/under run */
#define AHA_BUS_FREE 0x13 /* Bus dropped at unexpected time */
#define AHA_INV_BUS 0x14 /* Invalid bus phase/sequence */
#define AHA_BAD_MBO 0x15 /* Incorrect MBO cmd */
#define AHA_BAD_CCB 0x16 /* Incorrect ccb opcode */
#define AHA_BAD_LINK 0x17 /* Not same values of LUN for links */
#define AHA_INV_TARGET 0x18 /* Invalid target direction */
#define AHA_CCB_DUP 0x19 /* Duplicate CCB received */
#define AHA_INV_CCB 0x1a /* Invalid CCB or segment list */
#define AHA_ABORTED 42
struct aha_setup {
u_char sync_neg:1;
u_char parity:1;
u_char:6;
u_char speed;
u_char bus_on;
u_char bus_off;
u_char num_mbx;
u_char mbx[3];
struct {
u_char offset:4;
u_char period:3;
u_char valid:1;
} sync[8];
u_char disc_sts;
};
struct aha_config {
u_char chan;
u_char intr;
u_char scsi_dev:3;
u_char:5;
};
struct aha_inquire
{
u_char boardid; /* type of board */
/* 0x20 (' ') = BusLogic 545, but it gets
the command wrong, only returns
one byte */
/* 0x31 ('1') = AHA-1540 */
/* 0x41 ('A') = AHA-1540A/1542A/1542B */
/* 0x42 ('B') = AHA-1640 */
/* 0x43 ('C') = AHA-1542C */
/* 0x44 ('D') = AHA-1542CF */
/* 0x45 ('E') = AHA-1542CF, BIOS v2.01 */
/* 0x46 ('F') = AHA-1542CP, "Plug'nPlay" */
u_char spec_opts; /* special options ID */
/* 0x41 = Board is standard model */
u_char revision_1; /* firmware revision [0-9A-Z] */
u_char revision_2; /* firmware revision [0-9A-Z] */
};
struct aha_extbios
{
u_char flags; /* Bit 3 == 1 extended bios enabled */
u_char mailboxlock; /* mail box lock code to unlock it */
};
#define INT9 0x01
#define INT10 0x02
#define INT11 0x04
#define INT12 0x08
#define INT14 0x20
#define INT15 0x40
#define CHAN0 0x01
#define CHAN5 0x20
#define CHAN6 0x40
#define CHAN7 0x80
/*********************************** end of board definitions***************/
#define PHYSTOKV(x) (((long int)(x)) ^ aha->kv_phys_xor)
#define KVTOPHYS(x) vtophys(x)
#define AHA_DMA_PAGES AHA_NSEG
#define PAGESIZ 4096
#ifdef AHADEBUG
int aha_debug = 1;
#endif /*AHADEBUG */
struct aha_data {
short aha_base; /* base port for each board */
/*
* xor this with a physaddr to get a kv addr and visa versa
* for items in THIS STRUCT only.
* Used to get the CCD's physical and kv addresses from each
* other.
*/
long int kv_phys_xor;
struct aha_mbx aha_mbx; /* all the mailboxes */
struct aha_ccb *aha_ccb_free; /* the next free ccb */
struct aha_ccb aha_ccb[AHA_MBX_SIZE]; /* all the CCBs */
int aha_int; /* irq level */
int aha_dma; /* DMA req channel */
int aha_scsi_dev; /* scsi bus address */
int flags;
/* We use different op codes for different revs of the board
* if we think residual codes will work.
*/
short init_opcode; /* Command to use for initiator */
short sg_opcode; /* Command to use for scatter/gather */
struct scsi_link sc_link; /* prototype for subdevs */
} *ahadata[NAHA];
struct aha_ccb *aha_get_ccb();
int ahaprobe();
void aha_done();
int ahaattach();
inthand2_t ahaintr;
int32 aha_scsi_cmd();
timeout_t aha_timeout;
void ahaminphys();
u_int32 aha_adapter_info();
#ifdef KERNEL
struct scsi_adapter aha_switch =
{
aha_scsi_cmd,
ahaminphys,
0,
0,
aha_adapter_info,
"aha",
{ 0, 0 }
};
/* the below structure is so we have a default dev struct for out link struct */
struct scsi_device aha_dev =
{
NULL, /* Use default error handler */
NULL, /* have a queue, served by this */
NULL, /* have no async handler */
NULL, /* Use default 'done' routine */
"aha",
0,
{ 0, 0 }
};
struct isa_driver ahadriver =
{
ahaprobe,
ahaattach,
"aha"
};
static struct kern_devconf kdc_aha[NAHA] = { {
0, 0, 0, /* filled in by dev_attach */
"aha", 0, { MDDT_ISA, 0, "bio" },
isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
&kdc_isa0, /* parent */
0, /* parentdata */
DC_UNCONFIGURED, /* always start out here */
"Adaptec 154x-series SCSI host adapter",
DC_CLS_MISC /* SCSI host adapters aren't special */
} };
static inline void
aha_registerdev(struct isa_device *id)
{
if(id->id_unit)
kdc_aha[id->id_unit] = kdc_aha[0];
kdc_aha[id->id_unit].kdc_unit = id->id_unit;
kdc_aha[id->id_unit].kdc_parentdata = id;
dev_attach(&kdc_aha[id->id_unit]);
}
#endif /* KERNEL */
static int ahaunit = 0;
#define aha_abortmbx(mbx) \
(mbx)->cmd = AHA_MBO_ABORT; \
outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
#define aha_startmbx(mbx) \
(mbx)->cmd = AHA_MBO_START; \
outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
#define AHA_RESET_TIMEOUT 2000 /* time to wait for reset (mSec) */
int aha_poll __P((int, struct scsi_xfer *, struct aha_ccb *));
int aha_init __P((int));
#ifndef KERNEL
main()
{
printf("size of aha_data is %d\n", sizeof(struct aha_data));
printf("size of aha_ccb is %d\n", sizeof(struct aha_ccb));
printf("size of aha_mbx is %d\n", sizeof(struct aha_mbx));
}
#else /*KERNEL */
/*
* aha_cmd(unit,icnt, ocnt,wait, retval, opcode, args)
* Activate Adapter command
* icnt: number of args (outbound bytes written after opcode)
* ocnt: number of expected returned bytes
* wait: number of seconds to wait for response
* retval: buffer where to place returned bytes
* opcode: opcode AHA_NOP, AHA_MBX_INIT, AHA_START_SCSI ...
* args: parameters
*
* Performs an adapter command through the ports. Not to be confused
* with a scsi command, which is read in via the dma. One of the adapter
* commands tells it to read in a scsi command but that one is done
* separately. This is only called during set-up.
*/
int
aha_cmd(unit, icnt, ocnt, wait, retval, opcode, args)
int unit;
int icnt;
int ocnt;
int wait;
u_char *retval;
unsigned opcode;
u_char args;
{
struct aha_data *aha = ahadata[unit];
unsigned *ic = &opcode;
u_char oc;
register i;
int sts;
/*
* multiply the wait argument by a big constant
* zero defaults to 1 sec..
* all wait loops are in 50uSec cycles
*/
if (wait)
wait *= 20000;
else
wait = 20000;
/*
* Wait for the adapter to go idle, unless it's one of
* the commands which don't need this
*/
if (opcode != AHA_MBX_INIT && opcode != AHA_START_SCSI) {
i = 20000; /*do this for upto about a second */
while (--i) {
sts = inb(AHA_CTRL_STAT_PORT);
if (sts & AHA_IDLE) {
break;
}
DELAY(50);
}
if (!i) {
printf("aha%d: aha_cmd, host not idle(0x%x)\n",
unit, sts);
return (ENXIO);
}
}
/*
* Now that it is idle, if we expect output, preflush the
* queue feeding to us.
*/
if (ocnt) {
while ((inb(AHA_CTRL_STAT_PORT)) & AHA_DF)
inb(AHA_CMD_DATA_PORT);
}
/*
* Output the command and the number of arguments given
* for each byte, first check the port is empty.
*/
icnt++;
/* include the command */
while (icnt--) {
sts = inb(AHA_CTRL_STAT_PORT);
for (i = wait; i; i--) {
sts = inb(AHA_CTRL_STAT_PORT);
if (!(sts & AHA_CDF))
break;
DELAY(50);
}
if (i == 0) {
printf("aha%d: aha_cmd, cmd/data port full\n", unit);
outb(AHA_CTRL_STAT_PORT, AHA_SRST);
return (ENXIO);
}
outb(AHA_CMD_DATA_PORT, (u_char) (*ic++));
}
/*
* If we expect input, loop that many times, each time,
* looking for the data register to have valid data
*/
while (ocnt--) {
sts = inb(AHA_CTRL_STAT_PORT);
for (i = wait; i; i--) {
sts = inb(AHA_CTRL_STAT_PORT);
if (sts & AHA_DF)
break;
DELAY(50);
}
if (i == 0) {
printf("aha%d: aha_cmd, cmd/data port empty %d\n",
unit, ocnt);
return (ENXIO);
}
oc = inb(AHA_CMD_DATA_PORT);
if (retval)
*retval++ = oc;
}
/*
* Wait for the board to report a finised instruction
*/
i = 20000;
while (--i) {
sts = inb(AHA_INTR_PORT);
if (sts & AHA_HACC) {
break;
}
DELAY(50);
}
if (i == 0) {
printf("aha%d: aha_cmd, host not finished(0x%x)\n", unit, sts);
return (ENXIO);
}
outb(AHA_CTRL_STAT_PORT, AHA_IRST);
return 0;
}
/*
* Check if the device can be found at the port given
* and if so, set it up ready for further work
* as an argument, takes the isa_device structure from
* autoconf.c
*/
int
ahaprobe(dev)
struct isa_device *dev;
{
int unit = ahaunit;
struct aha_data *aha;
/*
* find unit and check we have that many defined
*/
if (unit >= NAHA) {
printf("aha%d: unit number too high\n", unit);
return 0;
}
dev->id_unit = unit;
/*
* a quick safety check so we can be sleazy later
*/
if (sizeof(struct aha_data) > PAGESIZ) {
printf("aha struct > pagesize\n");
return 0;
}
/*
* Allocate a storage area for us
*/
if (ahadata[unit]) {
printf("aha%d: memory already allocated\n", unit);
return 0;
}
aha = malloc(sizeof(struct aha_data), M_TEMP, M_NOWAIT);
if (!aha) {
printf("aha%d: cannot malloc!\n", unit);
return 0;
}
bzero(aha, sizeof(struct aha_data));
ahadata[unit] = aha;
aha->aha_base = dev->id_iobase;
#ifndef DEV_LKM
aha_registerdev(dev);
#endif
/*
* Try initialise a unit at this location
* sets up dma and bus speed, loads aha->aha_int
*/
if (aha_init(unit) != 0) {
ahadata[unit] = NULL;
free(aha, M_TEMP);
return 0;
}
/*
* Calculate the xor product of the aha struct's
* physical and virtual address. This allows us
* to change addresses within the structure
* from physical to virtual easily, as long as
* the structure is less than 1 page in size.
* This is used to recognise CCBs which are in
* this struct and which are refered to by the
* hardware using physical addresses.
* (assumes malloc returns a chunk that doesn't
* span pages)
* eventually use the hash table in aha1742.c
*/
aha->kv_phys_xor = (long int) aha ^ (KVTOPHYS(aha));
/*
* If it's there, put in it's interrupt vectors
*/
dev->id_irq = (1 << aha->aha_int);
dev->id_drq = aha->aha_dma;
ahaunit++;
return 0x4;
}
/*
* Attach all the sub-devices we can find
*/
int
ahaattach(dev)
struct isa_device *dev;
{
int unit = dev->id_unit;
struct aha_data *aha = ahadata[unit];
struct scsibus_data *scbus;
/*
* fill in the prototype scsi_link.
*/
aha->sc_link.adapter_unit = unit;
aha->sc_link.adapter_targ = aha->aha_scsi_dev;
aha->sc_link.adapter = &aha_switch;
aha->sc_link.device = &aha_dev;
aha->sc_link.flags = aha->flags;;
/*
* Prepare the scsibus_data area for the upperlevel
* scsi code.
*/
scbus = scsi_alloc_bus();
if(!scbus)
return 0;
scbus->adapter_link = &aha->sc_link;
/*
* ask the adapter what subunits are present
*/
kdc_aha[unit].kdc_state = DC_BUSY; /* host adapters are always busy */
scsi_attachdevs(scbus);
return 1;
}
/*
* Return some information to the caller about the adapter and its
* capabilities.
*/
u_int32
aha_adapter_info(unit)
int unit;
{
return (2); /* 2 outstanding requests at a time per device */
}
/*
* Catch an interrupt from the adaptor
*/
void
ahaintr(unit)
int unit;
{
unsigned char stat;
register i;
struct aha_data *aha = ahadata[unit];
#ifdef AHADEBUG
printf("ahaintr ");
#endif /*AHADEBUG */
/*
* First acknowledge the interrupt, Then if it's not telling about
* a completed operation just return.
*/
stat = inb(AHA_INTR_PORT);
outb(AHA_CTRL_STAT_PORT, AHA_IRST);
if (!(stat & AHA_MBIF))
return;
#ifdef AHADEBUG
printf("mbxin ");
#endif /*AHADEBUG */
/*
* If it IS then process the completed operation
*/
for (i = 0; i < AHA_MBX_SIZE; i++) {
struct aha_mbx_in *mbi = aha->aha_mbx.mbi + i;
if (mbi->stat != AHA_MBI_FREE) {
struct aha_ccb *ccb =
(struct aha_ccb *)PHYSTOKV(scsi_3btou(mbi->ccb_addr));
stat = mbi->stat;
switch (stat) {
case AHA_MBI_OK:
break;
case AHA_MBI_ABORT:
#ifdef AHADEBUG
if (aha_debug)
printf("abort");
#endif /*AHADEBUG */
ccb->host_stat = AHA_ABORTED;
break;
case AHA_MBI_TGT_NO_CCB:
/* We enabled target mode and received a SEND
* or RECEIVE command from the initiator, but
* we don't have any CCB registered to handle the command.
* At this point it would be nice to wakeup a
* process sleeping on this event via an ioctl,
* returning whether it is a SEND or RECEIVE and the
* required length.
* However, I want to look at the CAM documentation before
* I start extending the API at all.
*/
#ifdef NOISE_WHEN_TGT_NO_CDB
printf("Target received, but no CCB ready.\n");
printf("Initiator & lun: %02x\n", mbi->ccb_addr[0]);
printf("Max data length: %06x\n",
(mbi->ccb_addr[1] << 16) | (mbi->ccb_addr[2] << 8)
+ 255);
#endif
#ifdef AHADEBUG
if (aha_debug)
printf("target-no-ccb");
#endif /*AHADEBUG */
ccb = 0;
break;
case AHA_MBI_UNKNOWN:
ccb = 0;
#ifdef AHADEBUG
if (aha_debug)
printf("unknown ccb for abort ");
#endif /*AHADEBUG */
/* may have missed it */
/* no such ccb known for abort */
case AHA_MBI_ERROR:
/* XXX ccb is still set up? Driver fails without it? */
break;
default:
panic("Impossible mbxi status");
}
#ifdef AHADEBUG
if (aha_debug && ccb && stat != AHA_MBI_OK) {
u_char *cp;
cp = (u_char *) (&(ccb->scsi_cmd));
printf("op=%x %x %x %x %x %x\n",
cp[0], cp[1], cp[2],
cp[3], cp[4], cp[5]);
printf("stat %x for mbi[%d]\n"
,mbi->stat, i);
printf("addr = 0x%x\n", ccb);
}
#endif /*AHADEBUG */
if (ccb) {
untimeout(aha_timeout, (caddr_t)ccb);
aha_done(unit, ccb);
}
mbi->stat = AHA_MBI_FREE;
}
}
}
/*
* A ccb (and hence a mbx-out) is put onto the
* free list.
*/
void
aha_free_ccb(unit, ccb, flags)
int unit;
struct aha_ccb *ccb;
int flags;
{
struct aha_data *aha = ahadata[unit];
unsigned int opri = 0;
if (!(flags & SCSI_NOMASK))
opri = splbio();
ccb->next = aha->aha_ccb_free;
aha->aha_ccb_free = ccb;
ccb->flags = CCB_FREE;
/*
* If there were none, wake anybody waiting for
* one to come free, starting with queued entries
*/
if (!ccb->next) {
wakeup((caddr_t)&aha->aha_ccb_free);
}
if (!(flags & SCSI_NOMASK))
splx(opri);
}
/*
* Get a free ccb (and hence mbox-out entry)
*/
struct aha_ccb *
aha_get_ccb(unit, flags)
int unit;
int flags;
{
struct aha_data *aha = ahadata[unit];
unsigned opri = 0;
struct aha_ccb *rc;
if (!(flags & SCSI_NOMASK))
opri = splbio();
/*
* If we can and have to, sleep waiting for one
* to come free
*/
while ((!(rc = aha->aha_ccb_free)) && (!(flags & SCSI_NOSLEEP))) {
tsleep((caddr_t)&aha->aha_ccb_free, PRIBIO, "ahaccb", 0);
}
if (rc) {
aha->aha_ccb_free = aha->aha_ccb_free->next;
rc->flags = CCB_ACTIVE;
}
if (!(flags & SCSI_NOMASK))
splx(opri);
return (rc);
}
static void
put_host_stat(int host_stat)
{
int i;
struct { int host_stat; char *text; } tab[] = {
{ AHA_OK, "Cmd ok" },
{ AHA_LINK_OK, "Link cmd ok" },
{ AHA_LINK_IT, "Link cmd ok + int" },
{ AHA_SEL_TIMEOUT, "Selection time out" },
{ AHA_OVER_UNDER, "Data over/under run" },
{ AHA_BUS_FREE, "Bus dropped at unexpected time" },
{ AHA_INV_BUS, "Invalid bus phase/sequence" },
{ AHA_BAD_MBO, "Incorrect MBO cmd" },
{ AHA_BAD_CCB, "Incorrect ccb opcode" },
{ AHA_BAD_LINK, "Not same values of LUN for links" },
{ AHA_INV_TARGET, "Invalid target direction" },
{ AHA_CCB_DUP, "Duplicate CCB received" },
{ AHA_INV_CCB, "Invalid CCB or segment list" },
{ AHA_ABORTED, "Software abort" },
};
for (i = 0; i < sizeof(tab) / sizeof(tab[0]); i++) {
if (tab[i].host_stat == host_stat) {
printf("%s\n", tab[i].text);
return;
}
}
printf("Unknown host_stat %02x\n", host_stat);
}
/*
* We have a ccb which has been processed by the
* adaptor, now we look to see how the operation
* went. Wake up the owner if waiting
*/
void
aha_done(unit, ccb)
int unit;
struct aha_ccb *ccb;
{
struct scsi_sense_data *s1, *s2;
struct scsi_xfer *xs = ccb->xfer;
SC_DEBUG(xs->sc_link, SDEV_DB2, ("aha_done\n"));
/*
* Otherwise, put the results of the operation
* into the xfer and call whoever started it
*/
if (!(xs->flags & INUSE)) {
printf("aha%d: exiting but not in use!\n", unit);
Debugger("aha1542");
}
xs->status = ccb->target_stat;
xs->resid = 0;
if (((ccb->host_stat != AHA_OK) || (ccb->target_stat != SCSI_OK))
&& ((xs->flags & SCSI_ERR_OK) == 0)) {
/*
* We have an error, that we cannot ignore.
*/
s1 = (struct scsi_sense_data *) (((char *) (&ccb->scsi_cmd))
+ ccb->scsi_cmd_length);
s2 = &(xs->sense);
if (ccb->host_stat) {
SC_DEBUG(xs->sc_link, SDEV_DB3, ("host err 0x%x\n",
ccb->host_stat));
switch (ccb->host_stat) {
case AHA_ABORTED:
case AHA_SEL_TIMEOUT: /* No response */
xs->error = XS_TIMEOUT;
break;
case AHA_OVER_UNDER: /* Over run / under run */
switch(ccb->opcode)
{
case AHA_TARGET_CCB:
xs->resid = xs->datalen - scsi_3btoi(ccb->data_length);
xs->flags |= SCSI_RESID_VALID;
if (xs->resid <= 0)
xs->error = XS_LENGTH;
break;
case AHA_INIT_RESID_CCB:
case AHA_INIT_SG_RESID_CCB:
xs->resid = scsi_3btoi(ccb->data_length);
xs->flags |= SCSI_RESID_VALID;
if (xs->resid <= 0)
xs->error = XS_LENGTH;
break;
default:
xs->error = XS_LENGTH;
}
break;
default: /* Other scsi protocol messes */
xs->error = XS_DRIVER_STUFFUP;
printf("aha%d: ", unit);
put_host_stat(ccb->host_stat);
}
} else {
SC_DEBUG(xs->sc_link, SDEV_DB3, ("target err 0x%x\n",
ccb->target_stat));
switch (ccb->target_stat) {
case 0x02:
/* structure copy!!!!! */
*s2 = *s1;
xs->error = XS_SENSE;
break;
case 0x08:
xs->error = XS_BUSY;
break;
default:
printf("aha%d:target_stat%x\n",
unit, ccb->target_stat);
xs->error = XS_DRIVER_STUFFUP;
}
}
}
xs->flags |= ITSDONE;
aha_free_ccb(unit, ccb, xs->flags);
scsi_done(xs);
}
/* Macro to determine that a rev is potentially a new valid one
* so that the driver doesn't keep breaking on new revs as it
* did for the CF and CP.
*/
#define PROBABLY_NEW_BOARD(REV) (REV > 0x43 && REV < 0x56)
static char *board_rev(int unit, int type)
{
switch(type)
{
case 0x20: return "Buslogic 545?";
case 0x31: return "AHA-1540";
case 0x41: return "AHA-154x[AB]";
case 0x42: return "AHA-1640";
case 0x43: return "AHA-1542C";
case 0x44: return "AHA-1542CF";
case 0x45: return "AHA-1542CF BIOS v2.01";
case 0x46: return "AHA-1542CP";
default:
if (PROBABLY_NEW_BOARD(type))
{
printf("aha%d: Assuming type %02x is a new board.\n",
unit, type);
return "New Adaptec rev?";
}
printf("aha%d: type %02x is an unknown board.\n", unit, type);
return "Unknown board";
}
}
/*
* Start the board, ready for normal operation
*/
int
aha_init(unit)
int unit;
{
struct aha_data *aha = ahadata[unit];
char *desc;
unsigned char ad[3];
volatile int i, sts;
struct aha_config conf;
struct aha_inquire inquire;
struct aha_extbios extbios;
/* Assume that residual codes don't work. If they
* do we enable that after we figure out what kind of
* board it is.
*/
aha->init_opcode = AHA_INITIATOR_CCB;
aha->sg_opcode = AHA_INIT_SCAT_GATH_CCB;
/*
* reset board, If it doesn't respond, assume
* that it's not there.. good for the probe
*/
outb(AHA_CTRL_STAT_PORT, AHA_HRST | AHA_SRST);
for (i = AHA_RESET_TIMEOUT; i; i--) {
sts = inb(AHA_CTRL_STAT_PORT);
if (sts == (AHA_IDLE | AHA_INIT)) {
break;
}
DELAY(1000); /* calibrated in msec */
}
#ifdef AHADEBUG
printf("aha_init: AHA_RESET_TIMEOUT went to %d\n", i);
#endif /* AHADEBUG */
if (i == 0) {
#ifdef AHADEBUG
if (aha_debug)
printf("aha_init: No answer from board\n");
#endif /*AHADEBUG */
return (ENXIO);
}
/*
* Assume we have a board at this stage, do an adapter inquire
* to find out what type of controller it is. If the AHA_INQUIRE
* command fails, blatter about it, nuke the boardid so the 1542C
* stuff gets skipped over, and reset the board again.
*/
if(aha_cmd(unit, 0, sizeof(inquire), 1 ,&inquire, AHA_INQUIRE)) {
/*
* Blah.. not a real adaptec board!!!
* Seems that the Buslogic 545S and the DTC3290 both get
* this wrong.
*/
printf ("aha%d: not a REAL adaptec board, may cause warnings\n",
unit);
inquire.boardid = 0;
outb(AHA_CTRL_STAT_PORT, AHA_HRST | AHA_SRST);
for (i = AHA_RESET_TIMEOUT; i; i--) {
sts = inb(AHA_CTRL_STAT_PORT);
if (sts == (AHA_IDLE | AHA_INIT)) {
break;
}
DELAY(1000); /* calibrated in msec */
}
#ifdef AHADEBUG
printf("aha_init2: AHA_RESET_TIMEOUT went to %d\n", i);
#endif /* AHADEBUG */
if (i == 0) {
#ifdef AHADEBUG
if (aha_debug)
printf("aha_init2: No answer from board\n");
#endif /*AHADEBUG */
return (ENXIO);
}
}
#ifdef AHADEBUG
printf("aha%d: inquire %x, %x, %x, %x\n",
unit,
inquire.boardid, inquire.spec_opts,
inquire.revision_1, inquire.revision_2);
#endif /* AHADEBUG */
aha->flags = SDEV_BOUNCE;
#define PRVERBOSE(x) if (bootverbose) printf x
/*
* If we are a new type of 1542 board (anything newer than a 1542C)
* then disable the extended bios so that the
* mailbox interface is unlocked.
* This is also true for the 1542B Version 3.20. First Adaptec
* board that supports >1Gb drives.
* No need to check the extended bios flags as some of the
* extensions that cause us problems are not flagged in that byte.
*/
desc = board_rev(unit, inquire.boardid);
PRVERBOSE( ("aha%d: Rev %02x (%s) V%c.%c",
unit, inquire.boardid, desc, inquire.revision_1,
inquire.revision_2) );
if (PROBABLY_NEW_BOARD(inquire.boardid) ||
(inquire.boardid == 0x41
&& inquire.revision_1 == 0x31 && inquire.revision_2 == 0x34)) {
aha_cmd(unit, 0, sizeof(extbios), 0, &extbios, AHA_EXT_BIOS);
#ifdef AHADEBUG
printf("aha%d: extended bios flags %x\n", unit, extbios.flags);
#endif /* AHADEBUG */
PRVERBOSE( (", enabling mailbox") );
aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE,
0, extbios.mailboxlock);
}
/* Which boards support residuals? Some early 1542A's apparently
* don't. The 1542B with V0.5 of the software does, so I've
* arbitrarily set that as the earliest rev.
*/
if (PROBABLY_NEW_BOARD(inquire.boardid) ||
(inquire.boardid == 0x41
&& (inquire.revision_1 > '0' || inquire.revision_2 >= '5'))) {
PRVERBOSE( (", enabling residuals") );
aha->init_opcode = AHA_INIT_RESID_CCB;
aha->sg_opcode = AHA_INIT_SG_RESID_CCB;
}
/* Which boards support target operations? The 1542C completely
* locks up the SCSI bus if you enable them. I'm only sure
* about the B, which was sold in the OEM market as a target
* board.
*/
if (inquire.boardid == 0x41) {
PRVERBOSE( (", target ops") );
aha->flags |= SDEV_TARGET_OPS;
}
PRVERBOSE( ("\n") );
/*
* setup dma channel from jumpers and save int
* level
*/
PRVERBOSE(("aha%d: reading board settings, ", unit));
if (inquire.boardid == 0x20) {
DELAY(1000); /* for Bustek 545 */
}
aha_cmd(unit, 0, sizeof(conf), 0, &conf, AHA_CONF_GET);
switch (conf.chan) {
case CHAN0:
outb(0x0b, 0x0c);
outb(0x0a, 0x00);
aha->aha_dma = 0;
break;
case CHAN5:
outb(0xd6, 0xc1);
outb(0xd4, 0x01);
aha->aha_dma = 5;
break;
case CHAN6:
outb(0xd6, 0xc2);
outb(0xd4, 0x02);
aha->aha_dma = 6;
break;
case CHAN7:
outb(0xd6, 0xc3);
outb(0xd4, 0x03);
aha->aha_dma = 7;
break;
default:
printf("aha%d: illegal dma jumper setting\n", unit);
return (EIO);
}
PRVERBOSE( ("dma=%d ", aha->aha_dma) );
switch (conf.intr) {
case INT9:
aha->aha_int = 9;
break;
case INT10:
aha->aha_int = 10;
break;
case INT11:
aha->aha_int = 11;
break;
case INT12:
aha->aha_int = 12;
break;
case INT14:
aha->aha_int = 14;
break;
case INT15:
aha->aha_int = 15;
break;
default:
printf("aha%d: illegal int jumper setting\n", unit);
return (EIO);
}
PRVERBOSE( ("int=%d ", aha->aha_int) );
/* who are we on the scsi bus? */
aha->aha_scsi_dev = conf.scsi_dev;
PRVERBOSE( ("id=%d ", aha->aha_scsi_dev) );
/*
* Change the bus on/off times to not clash with other dma users.
*/
aha_cmd(unit, 1, 0, 0, 0, AHA_BUS_ON_TIME_SET, 7);
aha_cmd(unit, 1, 0, 0, 0, AHA_BUS_OFF_TIME_SET, 4);
#ifdef TUNE_1542
/*
* Initialize memory transfer speed
* Not compiled in by default because it breaks some machines
*/
if (!(aha_set_bus_speed(unit))) {
return (EIO);
}
#else
PRVERBOSE( (" (bus speed defaulted)\n") );
#endif /*TUNE_1542*/
/*
* Initialize mail box
*/
scsi_uto3b(KVTOPHYS(&aha->aha_mbx), ad);
aha_cmd(unit, 4, 0, 0, 0, AHA_MBX_INIT,
AHA_MBX_SIZE,
ad[0],
ad[1],
ad[2]);
/*
* link the ccb's with the mbox-out entries and
* into a free-list
* this is a kludge but it works
*/
for (i = 0; i < AHA_MBX_SIZE; i++) {
aha->aha_ccb[i].next = aha->aha_ccb_free;
aha->aha_ccb_free = &aha->aha_ccb[i];
aha->aha_ccb_free->flags = CCB_FREE;
aha->aha_ccb_free->mbx = &aha->aha_mbx.mbo[i];
scsi_uto3b(KVTOPHYS(aha->aha_ccb_free), aha->aha_mbx.mbo[i].ccb_addr);
}
/*
* Note that we are going and return (to probe)
*/
return 0;
}
void
ahaminphys(bp)
struct buf *bp;
{
/* aha seems to explode with 17 segs (64k may require 17 segs) */
/* on old boards so use a max of 16 segs if you have problems here */
if (bp->b_bcount > ((AHA_NSEG - 1) * PAGESIZ)) {
bp->b_bcount = ((AHA_NSEG - 1) * PAGESIZ);
}
}
int aha_escape(xs, ccb)
struct scsi_xfer *xs;
struct aha_ccb *ccb;
{
int ret = 0;
int s;
if (xs->cmd)
{
switch(xs->cmd->opcode)
{
case SCSI_OP_RESET:
ccb->opcode = AHA_RESET_CCB;
ret = 0;
break;
case SCSI_OP_TARGET:
s= splbio();
aha_cmd(xs->sc_link->adapter_unit, 2, 0, 0, 0, AHA_TARGET_EN,
(int)xs->cmd->bytes[0], (int)1);
splx(s);
ret = COMPLETE;
break;
default:
ret = ESCAPE_NOT_SUPPORTED;
break;
}
}
else
{
ccb->opcode = AHA_RESET_CCB;
ret = 0;
}
return ret;
}
#define physdb(ARG) (void)(ARG)
/* physcontig: Scan forward from a KV and return length to the
* end of physically contiguous addresses. This belongs in
* i386/.../something_or_other.c
* XXX: Find the right thing in the kernel.
*/
static int physcontig(int kv, int len)
{
int len_was = len;
u_long kvl = (u_long)kv;
int phys_len;
u_long phys, prev_phys;
prev_phys = KVTOPHYS(kvl);
/* We go at least to the end of this page:
*/
phys_len = PAGESIZ - (prev_phys & (PAGESIZ - 1));
len -= phys_len;
kvl += phys_len;
prev_phys &= ~(PAGESIZ - 1);
while (len > 0)
{
phys = KVTOPHYS(kvl);
if (phys != prev_phys + PAGESIZ)
{
physdb(("phys %08x != prev_phys %08x + PAGESIZ\n",
phys, prev_phys));
break;
}
prev_phys = phys;
kvl += PAGESIZ;
len -= PAGESIZ;
}
phys_len = (len < 0) ? len_was : (len_was - len);
physdb(("physcontig(%08x, %d) = %d\n", kv, len_was, phys_len));
return phys_len;
}
/*
* start a scsi operation given the command and
* the data address. Also needs the unit, target
* and lu
*/
int32
aha_scsi_cmd(xs)
struct scsi_xfer *xs;
{
struct scsi_link *sc_link = xs->sc_link;
int unit = sc_link->adapter_unit;
struct aha_data *aha = ahadata[unit];
struct aha_ccb *ccb;
struct aha_scat_gath *sg;
int seg; /* scatter gather seg being worked on */
int thiskv;
int thisphys, nextphys;
int bytes_this_seg, bytes_this_page, datalen, flags;
int s;
SC_DEBUG(xs->sc_link, SDEV_DB2, ("aha_scsi_cmd\n"));
/*
* get a ccb (mbox-out) to use. If the transfer
* is from a buf (possibly from interrupt time)
* then we can't allow it to sleep
*/
flags = xs->flags;
if (!(ccb = aha_get_ccb(unit, flags))) {
xs->error = XS_DRIVER_STUFFUP;
return (TRY_AGAIN_LATER);
}
if (ccb->mbx->cmd != AHA_MBO_FREE)
printf("aha%d: MBO %02x and not %02x (free)\n",
unit, ccb->mbx->cmd, AHA_MBO_FREE);
/*
* Put all the arguments for the xfer in the ccb
*/
ccb->xfer = xs;
if (flags & SCSI_RESET) {
ccb->opcode = AHA_RESET_CCB;
} else {
/* can't use S/G if zero length */
ccb->opcode = (xs->datalen ?
ahadata[unit]->sg_opcode
: ahadata[unit]->init_opcode);
}
ccb->target = sc_link->target;
ccb->data_out = 0;
ccb->data_in = 0;
ccb->lun = sc_link->lun;
ccb->scsi_cmd_length = xs->cmdlen;
/* Some devices (e.g, Microtek ScanMaker II)
* fall on the ground if you ask for anything but
* an exact number of sense bytes (wiping out the
* sense data)
* XXX: This was lost at some point in scsi_ioctl.c.
*/
ccb->req_sense_length = (xs->req_sense_length)
? xs->req_sense_length
: sizeof(ccb->scsi_sense);
/* XXX: I propose we move the reset handling into the escape
* handling.
*/
if (flags & SCSI_RESET) {
flags |= SCSI_ESCAPE;
xs->cmd->opcode = SCSI_OP_RESET;
}
/* Set up the CCB. For an escape function, the escape hook may
* set it up for us.
*/
if (flags & SCSI_ESCAPE) {
int ret;
ret = aha_escape(xs, ccb);
if (ret)
return ret;
}
else if (flags & SCSI_TARGET)
{
ccb->opcode = AHA_TARGET_CCB;
/* These must be set up for target mode:
*/
if (flags & SCSI_DATA_IN)
ccb->data_in = 1;
if (flags & SCSI_DATA_OUT)
ccb->data_out = 1;
}
else
{
ccb->opcode = (xs->datalen? /* can't use S/G if zero length */
AHA_INIT_SCAT_GATH_CCB
:AHA_INITIATOR_CCB);
}
switch(ccb->opcode)
{
case AHA_TARGET_CCB:
if (xs->data)
scsi_uto3b(KVTOPHYS((int)xs->data), ccb->data_addr);
else
scsi_uto3b(0, ccb->data_addr);
/* For non scatter-gather I/O (and Target mode doesn't do
* scatter-gather) we need to truncate the transfer
* at the first non consecutive physical address.
*/
scsi_uto3b(physcontig((int)xs->data, xs->datalen), ccb->data_length);
break;
/* This should be folded in with TARGET_CCB once
* physcontig is debugged.
*/
case AHA_INITIATOR_CCB:
case AHA_INIT_RESID_CCB:
if (xs->data)
scsi_uto3b(KVTOPHYS((int)xs->data), ccb->data_addr);
else
scsi_uto3b(0, ccb->data_addr);
scsi_uto3b(xs->datalen, ccb->data_length);
break;
case AHA_RESET_CCB:
scsi_uto3b(0, ccb->data_addr);
scsi_uto3b(0, ccb->data_length);
break;
case AHA_INIT_SCAT_GATH_CCB:
case AHA_INIT_SG_RESID_CCB:
scsi_uto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr );
sg = ccb->scat_gath ;
seg = 0;
#ifdef TFS_ONLY
if (flags & SCSI_DATA_UIO) {
iovp = ((struct uio *) xs->data)->uio_iov;
datalen = ((struct uio *) xs->data)->uio_iovcnt;
while ((datalen) && (seg < AHA_NSEG)) {
scsi_uto3b(iovp->iov_base, sg->seg_addr);
scsi_uto3b(iovp->iov_len, sg->seg_len);
SC_DEBUGN(xs->sc_link, SDEV_DB4, ("UIO(0x%x@0x%x)"
,iovp->iov_len
,iovp->iov_base));
sg++;
iovp++;
seg++;
datalen--;
}
} else
#endif /*TFS_ONLY */
{
/*
* Set up the scatter gather block
*/
SC_DEBUG(xs->sc_link, SDEV_DB4,
("%ld @%p:- ", xs->datalen, xs->data));
datalen = xs->datalen;
thiskv = (int) xs->data;
thisphys = KVTOPHYS(thiskv);
while ((datalen) && (seg < AHA_NSEG)) {
bytes_this_seg = 0;
/* put in the base address */
scsi_uto3b(thisphys, sg->seg_addr);
SC_DEBUGN(xs->sc_link, SDEV_DB4,
("0x%x", thisphys));
/* do it at least once */
nextphys = thisphys;
while ((datalen) && (thisphys == nextphys)) {
/*
* This page is contiguous (physically)
* with the the last, just extend the
* length
*/
/* check it fits on the ISA bus */
if (thisphys > 0xFFFFFF)
{
printf("aha%d: DMA beyond"
" end Of ISA\n", unit);
xs->error = XS_DRIVER_STUFFUP;
aha_free_ccb(unit, ccb, flags);
return (HAD_ERROR);
}
/** how far to the end of the page ***/
nextphys = (thisphys & (~(PAGESIZ - 1)))
+ PAGESIZ;
bytes_this_page = nextphys - thisphys;
/**** or the data ****/
bytes_this_page = min(bytes_this_page
,datalen);
bytes_this_seg += bytes_this_page;
datalen -= bytes_this_page;
/**** get more ready for the next page ****/
thiskv = (thiskv & (~(PAGESIZ - 1)))
+ PAGESIZ;
if (datalen)
thisphys = KVTOPHYS(thiskv);
}
/*
* next page isn't contiguous, finish the seg
*/
SC_DEBUGN(xs->sc_link, SDEV_DB4,
("(0x%x)", bytes_this_seg));
scsi_uto3b(bytes_this_seg, sg->seg_len);
sg++;
seg++;
}
}
scsi_uto3b(seg * sizeof(struct aha_scat_gath), ccb->data_length);
SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
if (datalen) { /* there's still data, must have run out of segs! */
printf("aha%d: aha_scsi_cmd, more than %d DMA segs\n",
unit, AHA_NSEG);
xs->error = XS_DRIVER_STUFFUP;
aha_free_ccb(unit, ccb, flags);
return (HAD_ERROR);
}
break;
default:
printf("aha_scsi_cmd%d: Illegal CCB opcode.\n", unit);
xs->error = XS_DRIVER_STUFFUP;
aha_free_ccb(unit,ccb,flags);
return HAD_ERROR;
}
scsi_uto3b(0, ccb->link_addr);
/*
* Put the scsi command in the ccb and start it
*/
if (!(flags & SCSI_ESCAPE))
bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length);
if (!(flags & SCSI_NOMASK)) {
s = splbio(); /* stop instant timeouts */
timeout(aha_timeout, (caddr_t)ccb, (xs->timeout * hz) / 1000);
aha_startmbx(ccb->mbx);
/*
* Usually return SUCCESSFULLY QUEUED
*/
splx(s);
SC_DEBUG(xs->sc_link, SDEV_DB3, ("sent\n"));
return (SUCCESSFULLY_QUEUED);
}
aha_startmbx(ccb->mbx);
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd sent, waiting\n"));
/*
* If we can't use interrupts, poll on completion
*/
return (aha_poll(unit, xs, ccb)); /* only during boot */
}
/*
* Poll a particular unit, looking for a particular xs
*/
int
aha_poll(unit, xs, ccb)
int unit;
struct scsi_xfer *xs;
struct aha_ccb *ccb;
{
struct aha_data *aha = ahadata[unit];
int count = xs->timeout;
u_char stat;
/*timeouts are in msec, so we loop in 1000uSec cycles */
while (count) {
/*
* If we had interrupts enabled, would we
* have got an interrupt?
*/
stat = inb(AHA_INTR_PORT);
if (stat & AHA_ANY_INTR) {
ahaintr(unit);
}
if (xs->flags & ITSDONE) {
break;
}
DELAY(1000); /* only happens in boot so ok */
count--;
}
if (count == 0) {
/*
* We timed out, so call the timeout handler
* manually, accout for the fact that the
* clock is not running yet by taking out the
* clock queue entry it makes
*/
aha_timeout((caddr_t)ccb);
/*
* because we are polling,
* take out the timeout entry aha_timeout made
*/
untimeout(aha_timeout, (caddr_t)ccb);
count = 2000;
while (count) {
/*
* Once again, wait for the int bit
*/
stat = inb(AHA_INTR_PORT);
if (stat & AHA_ANY_INTR) {
ahaintr(unit);
}
if (xs->flags & ITSDONE) {
break;
}
DELAY(1000); /* only happens in boot so ok */
count--;
}
if (count == 0) {
/*
* We timed out again.. this is bad
* Notice that this time there is no
* clock queue entry to remove
*/
aha_timeout((caddr_t)ccb);
}
}
if (xs->error)
return (HAD_ERROR);
return (COMPLETE);
}
#ifdef TUNE_1542
/*
* Try all the speeds from slowest to fastest.. if it finds a
* speed that fails, back off one notch from the last working
* speed (unless there is no other notch).
* Returns the nSEC value of the time used
* or 0 if it could get a working speed (or the NEXT speed
* failed)
*/
static struct bus_speed
{
char arg;
int nsecs;
}aha_bus_speeds[] =
{
{0x88,100},
{0x99,150},
{0xaa,200},
{0xbb,250},
{0xcc,300},
{0xdd,350},
{0xee,400},
{0xff,450}
};
int
aha_set_bus_speed(unit)
int unit;
{
int speed;
int lastworking;
int retval,retval2;
struct aha_data *aha = ahadata[unit];
lastworking = -1;
speed = 7;
while (1) {
retval = aha_bus_speed_check(unit,speed);
if(retval != 0) {
lastworking = speed;
}
if((retval == 0) || (speed == 0)) {
if(lastworking == -1) {
printf("No working bus speed for aha154X\n");
return 0;
}
printf("%d nSEC ok, using "
,aha_bus_speeds[lastworking].nsecs);
if(lastworking == 7) { /* is slowest already */
printf("marginal ");
} else {
lastworking++;
}
retval2 = aha_bus_speed_check(unit,lastworking);
if(retval2 == 0) {
printf("test retry failed.. aborting.\n");
return 0;
}
printf("%d nSEC\n",retval2);
return retval2 ;
}
speed--;
}
}
/*
* Set the DMA speed to the Nth speed and try an xfer. If it
* fails return 0, if it succeeds return the nSec value selected
* If there is no such speed return HAD_ERROR.
*/
static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@";
u_char aha_scratch_buf[256];
int
aha_bus_speed_check(unit, speed)
int unit, speed;
{
int numspeeds = sizeof(aha_bus_speeds) / sizeof(struct bus_speed);
int loopcount;
u_char ad[3];
struct aha_data *aha = ahadata[unit];
/*
* Check we have such an entry
*/
if (speed >= numspeeds)
return (HAD_ERROR); /* illegal speed */
/*
* Set the dma-speed
*/
aha_cmd(unit, 1, 0, 0, 0, AHA_SPEED_SET, aha_bus_speeds[speed].arg);
/*
* put the test data into the buffer and calculate
* it's address. Read it onto the board
*/
scsi_uto3b(KVTOPHYS(aha_scratch_buf), ad);
for(loopcount = 2000;loopcount;loopcount--)
{
strcpy(aha_scratch_buf, aha_test_string);
aha_cmd(unit, 3, 0, 0, 0, AHA_WRITE_FIFO, ad[0], ad[1], ad[2]);
/*
* clear the buffer then copy the contents back from the
* board.
*/
bzero(aha_scratch_buf, 54); /* 54 bytes transfered by test */
aha_cmd(unit, 3, 0, 0, 0, AHA_READ_FIFO, ad[0], ad[1], ad[2]);
/*
* Compare the original data and the final data and
* return the correct value depending upon the result
*/
if (strcmp(aha_test_string, aha_scratch_buf))
return 0; /* failed test */
}
/* copy succeded assume speed ok */
return (aha_bus_speeds[speed].nsecs);
}
#endif /*TUNE_1542*/
void
aha_timeout(void *arg1)
{
struct aha_ccb * ccb = (struct aha_ccb *)arg1;
int unit;
int s = splbio();
struct aha_data *aha;
unit = ccb->xfer->sc_link->adapter_unit;
aha = ahadata[unit];
sc_print_addr(ccb->xfer->sc_link);
printf("timed out ");
/*
* If The ccb's mbx is not free, then
* the board has gone south
*/
if (ccb->mbx->cmd != AHA_MBO_FREE) {
printf("\nadapter not taking commands.. frozen?!\n");
Debugger("aha1542");
}
/*
* If it has been through before, then
* a previous abort has failed, don't
* try abort again
*/
if (ccb->flags == CCB_ABORTED) {
/* abort timed out */
printf(" AGAIN\n");
ccb->xfer->retries = 0; /* I MEAN IT ! */
ccb->host_stat = AHA_ABORTED;
aha_done(unit, ccb);
} else {
/* abort the operation that has timed out */
printf("\n");
aha_abortmbx(ccb->mbx);
/* 4 secs for the abort */
timeout(aha_timeout, (caddr_t)ccb, 4 * hz);
ccb->flags = CCB_ABORTED;
} splx(s);
}
#endif /* KERNEL */