* (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.
* PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
* -------------------- ----- ----------------------
* CURRENT PATCH LEVEL: 1 00098
* -------------------- ----- ----------------------
* 16 Feb 93 Julian Elischer ADDED for SCSI system
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
* Revision 1.1.1.1 1993/06/12 14:57:59 rgrimes
* Initial import, 0.1 + pk 0.2.4-B1
* Revision 1.6 1992/08/24 21:01:58 jason
* many changes and bugfixes for osf1
* Revision 1.5 1992/07/31 01:22:03 julian
* support improved scsi.h layout
* Revision 1.4 1992/07/25 03:11:26 julian
* check each request fro sane flags.
* Revision 1.3 1992/07/24 00:52:45 julian
* improved timeout handling.
* added support for two arguments to the sd_done (or equiv) call so that
* they can pre-queue several arguments.
* slightly clean up error handling
* Revision 1.2 1992/07/17 22:03:54 julian
* upgraded the timeout code.
* added support for UIO-based i/o (as used for pmem operations)
* Revision 1.1 1992/05/27 00:51:12 balsup
* a FEW lines in this driver come from a MACH adaptec-disk driver
* so the copyright below is included:
* Copyright 1990 by Open Software Foundation,
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation, and that the name of OSF or Open Software
* Foundation not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
* IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
* NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifdef MACH /* EITHER CMU OR OSF */
#include <i386at/scsiconf.h>
#ifdef OSF /* OSF ONLY */
#include <i386/handler.h>
#include <i386/dispatcher.h>
#include <i386/AT386/atbus.h>
#include <i386at/atbus.h>
#endif MACH /* end of MACH specific */
#ifdef __386BSD__ /* 386BSD specific */
#define isa_dev isa_device
#define dev_addr id_iobase
#include <i386/isa/isa_device.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#define Debugger() panic("should call debugger here (adaptec.c)")
extern int delaycount
; /* from clock setup code */
/************************** board definitions *******************************/
#define AHA_BASE aha_base[unit]
#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 */
* 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 */
#define AHA_MBX_SIZE 16 /* mail box size */
unsigned char ccb_addr
[3];
unsigned char ccb_addr
[3];
#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 */
#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 */
extern struct aha_mbx aha_mbx
[];
/* 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) */
unsigned char data_in
:1; /* must be 0 */
unsigned char data_out
:1; /* must be 0 */
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 target_stat
;
unsigned char reserved
[2];
struct scsi_generic scsi_cmd
;
struct scsi_sense_data scsi_sense
;
unsigned char seg_len
[3];
unsigned char seg_addr
[3];
struct scsi_xfer
*xfer
; /* the scsi_xfer for this cmd */
struct aha_mbx_out
*mbx
; /* pointer to mail box */
long int delta
; /* difference from previous*/
struct aha_ccb
*later
,*sooner
;
struct aha_ccb
*aha_soonest
= (struct aha_ccb
*)0;
struct aha_ccb
*aha_latest
= (struct aha_ccb
*)0;
long int aha_furtherest
= 0; /* longest time in the timeout queue */
#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 scattter gather*/
#define AHA_RESET_CCB 0x81 /* SCSI Bus reset */
* 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 */
/*********************************** end of board definitions***************/
#define PHYSTOKV(x) phystokv(x)
#define KVTOPHYS(x) kvtophys(x)
#define PHYSTOKV(x) (x | 0xFE000000)
#define KVTOPHYS(x) vtophys(x)
#define AHA_DMA_PAGES AHA_NSEG
#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
u_char aha_scratch_buf
[256];
caddr_t aha_base
[NAHA
]; /* base port for each board */
short aha_base
[NAHA
]; /* base port for each board */
struct aha_mbx aha_mbx
[NAHA
];
struct aha_ccb
*aha_ccb_free
[NAHA
];
struct aha_ccb aha_ccb
[NAHA
][AHA_MBX_SIZE
];
struct scsi_xfer aha_scsi_xfer
[NAHA
];
struct isa_dev
*ahainfo
[NAHA
];
struct aha_ccb
*aha_get_ccb();
int aha_initialized
[NAHA
];
int ahaprobe(), ahaattach(), ahaintr();
struct isa_driver ahadriver
= { ahaprobe
, 0, ahaattach
, "aha", 0, 0, 0};
int (*ahaintrs
[])() = {ahaintr
, 0};
struct isa_driver ahadriver
= { ahaprobe
, ahaattach
, "aha",};
#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);
long int aha_adapter_info();
struct scsi_switch aha_switch
=
#define AHA_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */
#define AHA_RESET_TIMEOUT 1000000 /* time to wait for reset */
#define AHA_SCSI_TIMEOUT_FUDGE 20 /* divided by for mSecs */
/***********************************************************************\
* 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 ... *
* 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 *
\***********************************************************************/
aha_cmd(unit
,icnt
, ocnt
, wait
,retval
, opcode
, args
)
/*******************************************************\
* multiply the wait argument by a big constant *
\*******************************************************/
wait
= AHA_CMD_TIMEOUT_FUDGE
* delaycount
;
wait
*= AHA_CMD_TIMEOUT_FUDGE
* delaycount
;
/*******************************************************\
* 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
= AHA_CMD_TIMEOUT_FUDGE
* delaycount
; /* 1 sec?*/
sts
= inb(AHA_CTRL_STAT_PORT
);
printf("aha_cmd: aha1542 host not idle(0x%x)\n",sts
);
/*******************************************************\
* Now that it is idle, if we expect output, preflush the*
\*******************************************************/
while((inb(AHA_CTRL_STAT_PORT
)) & AHA_DF
)
/*******************************************************\
* Output the command and the number of arguments given *
* for each byte, first check the port is empty. *
\*******************************************************/
icnt
++; /* include the command */
sts
= inb(AHA_CTRL_STAT_PORT
);
sts
= inb(AHA_CTRL_STAT_PORT
);
printf("aha_cmd: aha1542 cmd/data port full\n");
outb(AHA_CTRL_STAT_PORT
, AHA_SRST
);
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 *
\*******************************************************/
sts
= inb(AHA_CTRL_STAT_PORT
);
sts
= inb(AHA_CTRL_STAT_PORT
);
printf("aha_cmd: aha1542 cmd/data port empty %d\n",ocnt
);
oc
= inb(AHA_CMD_DATA_PORT
);
/*******************************************************\
* Wait for the board to report a finised instruction *
\*******************************************************/
i
=AHA_CMD_TIMEOUT_FUDGE
* delaycount
; /* 1 sec? */
sts
= inb(AHA_INTR_PORT
);
printf("aha_cmd: aha1542 host not finished(0x%x)\n",sts
);
outb(AHA_CTRL_STAT_PORT
, AHA_IRST
);
/*******************************************************\
* 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_dev structure from *
\*******************************************************/
static ihandler_t aha_handler
[NAHA
];
static ihandler_id_t
*aha_handler_id
[NAHA
];
register ihandler_t
*chp
= &aha_handler
[unit
];;
#endif /* defined(OSF) */
/***********************************************\
/***********************************************\
* find unit and check we have that many defined *
\***********************************************/
aha_base
[unit
] = dev
->dev_addr
;
printf("aha: unit number (%d) too high\n",unit
);
/***********************************************\
* Try initialise a unit at this location *
* sets up dma and bus speed, loads aha_int[unit]*
\***********************************************/
/***********************************************\
* If it's there, put in it's interrupt vectors *
\***********************************************/
iunit
[aha_int
[unit
]] =unit
;
ivect
[aha_int
[unit
]] = ahaintr
;
intpri
[aha_int
[unit
]] = dev
->dev_spl
;
dev
->id_irq
= (1 << aha_int
[unit
]);
dev
->id_drq
= aha_dma
[unit
];
#else /* !defined(OSF) */
chp
->ih_level
= dev
->dev_pic
;
chp
->ih_handler
= dev
->dev_intr
[0];
chp
->ih_resolver
= i386_resolver
;
chp
->ih_stats
.intr_type
= INTR_DEVICE
;
chp
->ih_stats
.intr_cnt
= 0;
chp
->ih_hparam
[0].intparam
= unit
;
if ((aha_handler_id
[unit
] = handler_add(chp
)) != NULL
)
handler_enable(aha_handler_id
[unit
]);
panic("Unable to add aha interrupt handler");
#endif /* !defined(OSF) */
printf("port=%x spl=%d\n",
dev
->dev_addr
, dev
->dev_spl
);
/***********************************************\
* Attach all the sub-devices we can find *
\***********************************************/
int unit
= dev
->dev_unit
;
printf("**probing for scsi devices**\n");
/***********************************************\
* ask the adapter what subunits are present *
\***********************************************/
scsi_attachdevs( unit
, aha_scsi_dev
[unit
], &aha_switch
);
#endif /* defined(OSF) */
if(!unit
) /* only one for all boards */
/***********************************************\
* Return some information to the caller about *
* the adapter and it's capabilities *
\***********************************************/
long int aha_adapter_info(unit
)
return(2); /* 2 outstanding requests at a time per device */
/***********************************************\
* Catch an interrupt from the adaptor *
\***********************************************/
if(scsi_debug
& PRINTROUTINES
)
/***********************************************\
* First acknowlege the interrupt, Then if it's *
* not telling about a completed operation *
\***********************************************/
stat
= inb(AHA_INTR_PORT
);
outb(AHA_CTRL_STAT_PORT
, AHA_IRST
);
if(scsi_debug
& TRACEINTERRUPTS
)
if(scsi_debug
& TRACEINTERRUPTS
)
#endif /* defined(OSF) */
/***********************************************\
* If it IS then process the competed operation *
\***********************************************/
for (i
= 0; i
< AHA_MBX_SIZE
; i
++)
if (aha_mbx
[unit
].mbi
[i
].stat
!= AHA_MBI_FREE
)
ccb
= (struct aha_ccb
*)PHYSTOKV(
(_3btol(aha_mbx
[unit
].mbi
[i
].ccb_addr
)));
if((stat
= aha_mbx
[unit
].mbi
[i
].stat
) != AHA_MBI_OK
)
ccb
->host_stat
= AHA_ABORTED
;
ccb
= (struct aha_ccb
*)0;
printf("unknown ccb for abort ");
/* no such ccb known for abort */
panic("Impossible mbxi status");
cp
= (u_char
*)(&(ccb
->scsi_cmd
));
printf("op=%x %x %x %x %x %x\n",
printf("stat %x for mbi[%d]\n"
, aha_mbx
[unit
].mbi
[i
].stat
, i
);
printf("addr = 0x%x\n", ccb
);
aha_mbx
[unit
].mbi
[i
].stat
= AHA_MBI_FREE
;
/***********************************************\
* A ccb (and hence a mbx-out is put onto the *
\***********************************************/
aha_free_ccb(unit
,ccb
, flags
)
if(scsi_debug
& PRINTROUTINES
)
printf("ccb%d(0x%x)> ",unit
,flags
);
if (!(flags
& SCSI_NOMASK
))
ccb
->next
= aha_ccb_free
[unit
];
aha_ccb_free
[unit
] = ccb
;
if(ccb
->sooner
|| ccb
->later
)
printf("yikes, still in timeout queue\n");
/***********************************************\
* If there were none, wake abybody waiting for *
* one to come free, starting with queued entries*
\***********************************************/
wakeup(&aha_ccb_free
[unit
]);
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
* Get a free ccb (and hence mbox-out entry) *
\***********************************************/
if(scsi_debug
& PRINTROUTINES
)
printf("<ccb%d(0x%x) ",unit
,flags
);
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
* If we can and have to, sleep waiting for one *
\***********************************************/
while ((!(rc
= aha_ccb_free
[unit
])) && (!(flags
& SCSI_NOSLEEP
)))
sleep(&aha_ccb_free
[unit
], PRIBIO
);
aha_ccb_free
[unit
] = aha_ccb_free
[unit
]->next
;
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
* 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 *
\***********************************************/
struct scsi_sense_data
*s1
,*s2
;
struct scsi_xfer
*xs
= ccb
->xfer
;
if(scsi_debug
& PRINTROUTINES
)
/***********************************************\
* Otherwise, put the results of the operation *
* into the xfer and call whoever started it *
\***********************************************/
printf("exiting but not in use! ");
if ( ( ccb
->host_stat
!= AHA_OK
|| ccb
->target_stat
!= SCSI_OK
)
&& (!(xs
->flags
& SCSI_ERR_OK
)))
s1
= (struct scsi_sense_data
*)(((char *)(&ccb
->scsi_cmd
))
case AHA_SEL_TIMEOUT
: /* No response */
default: /* Other scsi protocol messes */
xs
->error
= XS_DRIVER_STUFFUP
;
printf("target_stat%x\n",
xs
->error
= XS_DRIVER_STUFFUP
;
else /* All went correctly OR errors expected */
aha_free_ccb(unit
,ccb
, xs
->flags
);
(*(xs
->when_done
))(xs
->done_arg
,xs
->done_arg2
);
/***********************************************\
* Start the board, ready for normal operation *
\***********************************************/
/***********************************************\
* 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
=0; i
< AHA_RESET_TIMEOUT
; i
++)
sts
= inb(AHA_CTRL_STAT_PORT
) ;
if ( sts
== (AHA_IDLE
| AHA_INIT
))
if (i
>= AHA_RESET_TIMEOUT
)
printf("aha_init: No answer from adaptec board\n");
/***********************************************\
* Assume we have a board at this stage *
* setup dma channel from jumpers and save int *
\***********************************************/
#define PRNT(x) printf(x)
aha_cmd(unit
,0, sizeof(conf
), 0 ,&conf
, AHA_CONF_GET
);
printf("illegal dma jumper setting\n");
printf("illegal int jumper setting\n");
/* who are we on the scsi bus */
aha_scsi_dev
[unit
] = conf
.scsi_dev
;
/***********************************************\
* Initialize memory transfer speed *
\***********************************************/
if(!(aha_set_bus_speed(unit
)))
/***********************************************\
\***********************************************/
lto3b(KVTOPHYS(&aha_mbx
[unit
]), ad
);
aha_cmd(unit
,4, 0, 0, 0, AHA_MBX_INIT
,
/***********************************************\
* link the ccb's with the mbox-out entries and *
\***********************************************/
for (i
=0; i
< AHA_MBX_SIZE
; i
++) {
aha_ccb
[unit
][i
].next
= aha_ccb_free
[unit
];
aha_ccb_free
[unit
] = &aha_ccb
[unit
][i
];
aha_ccb_free
[unit
]->flags
= CCB_FREE
;
aha_ccb_free
[unit
]->mbx
= &aha_mbx
[unit
].mbo
[i
];
lto3b(KVTOPHYS(aha_ccb_free
[unit
]), aha_mbx
[unit
].mbo
[i
].ccb_addr
);
/***********************************************\
* Note that we are going and return (to probe) *
\***********************************************/
bp
->b_flags
|= B_NPAGES
; /* can support scat/gather */
#endif /* !defined(OSF) */
/* 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
);
/***********************************************\
* start a scsi operation given the command and *
* the data address. Also needs the unit, target *
\***********************************************/
struct scsi_sense_data
*s1
,*s2
;
struct aha_scat_gath
*sg
;
int seg
; /* scatter gather seg being worked on */
int bytes_this_seg
,bytes_this_page
,datalen
,flags
;
if(scsi_debug
& PRINTROUTINES
)
/***********************************************\
* 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 *
\***********************************************/
printf("Already done! check device retry code ");
if(xs
->bp
) flags
|= (SCSI_NOSLEEP
); /* just to be sure */
if (!(ccb
= aha_get_ccb(unit
,flags
)))
xs
->error
= XS_DRIVER_STUFFUP
;
if (ccb
->mbx
->cmd
!= AHA_MBO_FREE
)
printf("MBO not free\n");
/***********************************************\
* Put all the arguments for the xfer in the ccb *
\***********************************************/
ccb
->opcode
= AHA_RESET_CCB
;
/* can't use S/G if zero length */
ccb
->opcode
= (xs
->datalen
?
ccb
->scsi_cmd_length
= xs
->cmdlen
;
ccb
->req_sense_length
= sizeof(ccb
->scsi_sense
);
if((xs
->datalen
) && (!(flags
& SCSI_RESET
)))
{ /* can use S/G only if not zero length */
lto3b(KVTOPHYS(ccb
->scat_gath
), ccb
->data_addr
);
if(flags
& SCSI_DATA_UIO
)
iovp
= ((struct uio
*)xs
->data
)->uio_iov
;
datalen
= ((struct uio
*)xs
->data
)->uio_iovcnt
;
while ((datalen
) && (seg
< AHA_NSEG
))
lto3b(iovp
->iov_base
,&(sg
->seg_addr
));
lto3b(iovp
->iov_len
,&(sg
->seg_len
));
if(scsi_debug
& SHOWSCATGATH
)
/***********************************************\
* Set up the scatter gather block *
\***********************************************/
if(scsi_debug
& SHOWSCATGATH
)
printf("%d @0x%x:- ",xs
->datalen
,xs
->data
);
thisphys
= KVTOPHYS(thiskv
);
while ((datalen
) && (seg
< AHA_NSEG
))
/* put in the base address */
lto3b(thisphys
,&(sg
->seg_addr
));
if(scsi_debug
& SHOWSCATGATH
)
/* do it at least once */
while ((datalen
) && (thisphys
== nextphys
))
/***************************************\
* This page is contiguous (physically) *
* with the the last, just extend the *
\***************************************/
/** how far to the end of the page ***/
nextphys
= (thisphys
& (~(PAGESIZ
- 1)))
bytes_this_page
= nextphys
- thisphys
;
bytes_this_page
= min(bytes_this_page
bytes_this_seg
+= bytes_this_page
;
datalen
-= bytes_this_page
;
/**** get more ready for the next page ****/
thiskv
= (thiskv
& (~(PAGESIZ
- 1)))
thisphys
= KVTOPHYS(thiskv
);
/***************************************\
* next page isn't contiguous, finish the seg*
\***************************************/
if(scsi_debug
& SHOWSCATGATH
)
printf("(0x%x)",bytes_this_seg
);
lto3b(bytes_this_seg
,&(sg
->seg_len
));
lto3b(seg
* sizeof(struct aha_scat_gath
),ccb
->data_length
);
if(scsi_debug
& SHOWSCATGATH
)
{ /* there's still data, must have run out of segs! */
printf("aha_scsi_cmd%d: more than %d DMA segs\n",
xs
->error
= XS_DRIVER_STUFFUP
;
aha_free_ccb(unit
,ccb
,flags
);
{ /* No data xfer, use non S/G values */
lto3b(0, ccb
->data_addr
);
lto3b(0,ccb
->data_length
);
lto3b(0, ccb
->link_addr
);
/***********************************************\
* Put the scsi command in the ccb and start it *
\***********************************************/
if(!(flags
& SCSI_RESET
))
bcopy(xs
->cmd
, &ccb
->scsi_cmd
, ccb
->scsi_cmd_length
);
if(scsi_debug
& SHOWCOMMANDS
)
u_char
*b
= (u_char
*)&ccb
->scsi_cmd
;
if(!(flags
& SCSI_RESET
))
while(i
< ccb
->scsi_cmd_length
)
printf("aha%d:%d:%d-RESET- "
if (!(flags
& SCSI_NOMASK
))
s
= splbio(); /* stop instant timeouts */
aha_add_timeout(ccb
,xs
->timeout
);
/***********************************************\
* Usually return SUCCESSFULLY QUEUED *
\***********************************************/
if(scsi_debug
& TRACEINTERRUPTS
)
return(SUCCESSFULLY_QUEUED
);
if(scsi_debug
& TRACEINTERRUPTS
)
printf("cmd_sent, waiting ");
/***********************************************\
* If we can't use interrupts, poll on completion*
\***********************************************/
int count
= delaycount
* xs
->timeout
/ AHA_SCSI_TIMEOUT_FUDGE
;
while ( (!done
) && i
<AHA_MBX_SIZE
)
if ((aha_mbx
[unit
].mbi
[i
].stat
!= AHA_MBI_FREE
)
&& (PHYSTOKV(_3btol(aha_mbx
[unit
].mbi
[i
].ccb_addr
)
aha_mbx
[unit
].mbi
[i
].stat
= AHA_MBI_FREE
;
if (!(xs
->flags
& SCSI_SILENT
))
count
= delaycount
* 2000 / AHA_SCSI_TIMEOUT_FUDGE
;
while ( (!done
) && i
<AHA_MBX_SIZE
)
if ((aha_mbx
[unit
].mbi
[i
].stat
!= AHA_MBI_FREE
)
&& (PHYSTOKV(_3btol(aha_mbx
[unit
].mbi
[i
].ccb_addr
)
aha_mbx
[unit
].mbi
[i
].stat
= AHA_MBI_FREE
;
printf("abort failed in wait\n");
ccb
->mbx
->cmd
= AHA_MBO_FREE
;
aha_free_ccb(unit
,ccb
,flags
);
xs
->error
= XS_DRIVER_STUFFUP
;
if(xs
->error
) return(HAD_ERROR
);
/***************************************************************\
* try each speed in turn, when we find one that works, use *
* the NEXT one for a safety margin, unless that doesn't exist *
* or doesn't work. returns the nSEC value of the time used *
* or 0 if it could get a working speed ( or the NEXT speed *
\***************************************************************/
int aha_set_bus_speed(unit
)
speed
= 0; /* start at the fastest */
speed
= 1; /* 100 ns can crash some ISA busses (!?!) */
retval
= aha_bus_speed_check(unit
,speed
);
printf("no working bus speed!!!\n");
else /* Go one slower to be safe */
{ /* unless eisa at 100 ns.. trust it */
/* XXX printf("%d nSEC ok, use ",retval); */
retval2
= aha_bus_speed_check(unit
,speed
);
if(retval2
== HAD_ERROR
) /* retval is slowest already */
/* XXX printf("marginal "); */
/* XXX printf("%d nSEC ",retval2); */
/* XXX printf(".. slower failed, abort.\n",retval); */
/***************************************************************\
* 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!@";
int aha_bus_speed_check(unit
,speed
)
int numspeeds
= sizeof(aha_bus_speeds
)/sizeof(struct bus_speed
);
/*******************************************************\
* Check we have such an entry *
\*******************************************************/
if(speed
>= numspeeds
) return(HAD_ERROR
); /* illegal 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 *
\*******************************************************/
strcpy(aha_scratch_buf
,aha_test_string
);
lto3b(KVTOPHYS(aha_scratch_buf
),ad
);
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 *
\*******************************************************/
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
))
{ /* copy failed.. assume too fast */
{ /* copy succeded assume speed ok */
return(aha_bus_speeds
[speed
].nsecs
);
* +----------+ +----------+ +----------+
* aha_soonest--->| later |--->| later|--->| later|-->0
* | [Delta] | | [Delta] | | [Delta] |
* 0<---|sooner |<---|sooner |<---|sooner |<---aha_latest
* +----------+ +----------+ +----------+
* aha_furtherest = sum(Delta[1..n])
aha_add_timeout(ccb
,time
)
if(prev
= aha_latest
) /* yes, an assign */
timeprev
= aha_furtherest
;
while(prev
&& (timeprev
> time
))
ccb
->delta
= time
- timeprev
;
if( ccb
->later
= prev
->later
) /* yes an assign */
ccb
->later
->sooner
= ccb
;
ccb
->later
->delta
-= ccb
->delta
;
if( ccb
->later
= aha_soonest
) /* yes, an assign*/
ccb
->later
->sooner
= ccb
;
ccb
->later
->delta
-= time
;
ccb
->sooner
= (struct aha_ccb
*)0;
ccb
->sooner
->later
= ccb
->later
;
aha_soonest
= ccb
->later
;
ccb
->later
->sooner
= ccb
->sooner
;
ccb
->later
->delta
+= ccb
->delta
;
aha_latest
= ccb
->sooner
;
aha_furtherest
-= ccb
->delta
;
ccb
->sooner
= ccb
->later
= (struct aha_ccb
*)0;
#define ONETICK 500 /* milliseconds */
#define SLEEPTIME ((hz * 1000) / ONETICK)
while( ccb
= aha_soonest
)
if(ccb
->delta
<= ONETICK
)
/***********************************************\
* It has timed out, we need to do some work *
\***********************************************/
unit
= ccb
->xfer
->adapter
;
printf("aha%d: device %d timed out ",unit
/***************************************\
* Unlink it from the queue *
\***************************************/
/***************************************\
* If The ccb's mbx is not free, then *
* the board has gone south *
\***************************************/
if(ccb
->mbx
->cmd
!= AHA_MBO_FREE
)
printf("aha%d not taking commands!\n"
/***************************************\
* If it has been through before, then *
* a previous abort has failed, don't *
\***************************************/
if(ccb
->flags
== CCB_ABORTED
) /* abort timed out */
ccb
->xfer
->retries
= 0; /* I MEAN IT ! */
ccb
->host_stat
= AHA_ABORTED
;
else /* abort the operation that has timed out */
/* 2 secs for the abort */
aha_add_timeout(ccb
,2000 + ONETICK
);
ccb
->flags
= CCB_ABORTED
;
/***********************************************\
* It has not timed out, adjust and leave *
\***********************************************/
aha_furtherest
-= ONETICK
;
timeout(aha_timeout
,arg
,SLEEPTIME
);