1994-11-17 20:22:31 +00:00
|
|
|
/*
|
1995-01-13 02:24:31 +00:00
|
|
|
* Generic driver for the aic7xxx based adaptec SCSI controllers
|
1996-01-29 03:17:39 +00:00
|
|
|
* Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
|
1994-12-31 19:31:56 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
1995-01-13 02:24:31 +00:00
|
|
|
* Product specific probe and attach routines can be found in:
|
1996-01-03 06:32:12 +00:00
|
|
|
* i386/eisa/aic7770.c 27/284X and aic7770 motherboard controllers
|
|
|
|
* pci/aic7870.c 3940, 2940, aic7870 and aic7850 controllers
|
1995-01-13 02:24:31 +00:00
|
|
|
*
|
1995-05-30 08:16:23 +00:00
|
|
|
* Portions of this driver are based on the FreeBSD 1742 Driver:
|
1994-11-17 20:22:31 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* commenced: Sun Sep 27 18:14:01 PDT 1992
|
|
|
|
*
|
1996-01-29 03:17:39 +00:00
|
|
|
* $Id: aic7xxx.c,v 1.54 1996/01/23 21:47:50 se Exp $
|
1994-11-17 20:22:31 +00:00
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* TODO:
|
|
|
|
* Implement Target Mode
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/proc.h>
|
1995-12-06 23:52:35 +00:00
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
#include <scsi/scsi_all.h>
|
|
|
|
#include <scsi/scsiconf.h>
|
1995-12-06 23:52:35 +00:00
|
|
|
|
1995-03-07 08:59:28 +00:00
|
|
|
#include <machine/clock.h>
|
1995-12-06 23:52:35 +00:00
|
|
|
|
|
|
|
#include <vm/vm.h>
|
1995-12-07 12:48:31 +00:00
|
|
|
#include <vm/vm_param.h>
|
|
|
|
#include <vm/pmap.h>
|
1995-12-06 23:52:35 +00:00
|
|
|
|
1995-01-13 02:27:08 +00:00
|
|
|
#include <i386/scsi/aic7xxx.h>
|
1995-12-06 23:52:35 +00:00
|
|
|
|
1995-11-05 04:50:55 +00:00
|
|
|
#include <dev/aic7xxx/aic7xxx_reg.h>
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
#define PAGESIZ 4096
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-02-22 01:43:25 +00:00
|
|
|
#define MAX_TAGS 4;
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#define KVTOPHYS(x) vtophys(x)
|
|
|
|
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
#define MIN(a,b) ((a < b) ? a : b)
|
1995-07-31 08:25:36 +00:00
|
|
|
#define ALL_TARGETS -1
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
|
1995-11-06 05:21:13 +00:00
|
|
|
u_long ahc_unit = 0;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
static int ahc_debug = AHC_SHOWABORTS|AHC_SHOWMISC;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
/**** bit definitions for SCSIDEF ****/
|
1995-01-13 02:24:31 +00:00
|
|
|
#define HSCSIID 0x07 /* our SCSI ID */
|
|
|
|
#define HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-07-31 08:25:36 +00:00
|
|
|
typedef enum {
|
|
|
|
list_head,
|
|
|
|
list_second,
|
|
|
|
}insert_t;
|
|
|
|
|
1995-12-15 23:49:42 +00:00
|
|
|
static u_int32 ahc_adapter_info __P((int unit));
|
|
|
|
static void ahcminphys __P((struct buf *bp));
|
|
|
|
static int32 ahc_scsi_cmd __P((struct scsi_xfer *xs));
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static struct scsi_adapter ahc_switch =
|
1995-05-30 08:16:23 +00:00
|
|
|
{
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_scsi_cmd,
|
|
|
|
ahcminphys,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
ahc_adapter_info,
|
|
|
|
"ahc",
|
|
|
|
{ 0, 0 }
|
1995-05-30 08:16:23 +00:00
|
|
|
};
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/* the below structure is so we have a default dev struct for our link struct */
|
1995-10-31 18:41:49 +00:00
|
|
|
static struct scsi_device ahc_dev =
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
|
|
|
NULL, /* Use default error handler */
|
|
|
|
NULL, /* have a queue, served by this */
|
|
|
|
NULL, /* have no async handler */
|
|
|
|
NULL, /* Use default 'done' routine */
|
|
|
|
"ahc",
|
1995-05-30 08:16:23 +00:00
|
|
|
0,
|
1994-11-17 20:22:31 +00:00
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since the sequencer can disable pausing in a critical section, we
|
1995-05-30 08:16:23 +00:00
|
|
|
* must loop until it actually stops.
|
1994-12-31 19:31:56 +00:00
|
|
|
* XXX Should add a timeout in here??
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
#define PAUSE_SEQUENCER(ahc) \
|
|
|
|
outb(HCNTRL + ahc->baseport, ahc->pause); \
|
|
|
|
\
|
|
|
|
while ((inb(HCNTRL + ahc->baseport) & PAUSE) == 0) \
|
|
|
|
;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
#define UNPAUSE_SEQUENCER(ahc) \
|
|
|
|
outb( HCNTRL + ahc->baseport, ahc->unpause )
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Restart the sequencer program from address zero
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
#define RESTART_SEQUENCER(ahc) \
|
|
|
|
do { \
|
|
|
|
outb( SEQCTL + ahc->baseport, SEQRESET|FASTMODE ); \
|
|
|
|
} while (inb(SEQADDR0 + ahc->baseport) != 0 && \
|
|
|
|
inb(SEQADDR1 + ahc->baseport != 0)); \
|
|
|
|
\
|
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
|
|
|
|
static u_char ahc_abort_wscb __P((struct ahc_data *ahc, struct scb *scbp,
|
|
|
|
u_char prev, u_long iobase,
|
|
|
|
u_char timedout_scb, u_int32 xs_error));
|
1995-12-15 23:49:42 +00:00
|
|
|
static void ahc_add_waiting_scb __P((u_long iobase, struct scb *scb,
|
|
|
|
insert_t where));
|
1996-01-03 06:32:12 +00:00
|
|
|
static void ahc_done __P((struct ahc_data *ahc, struct scb *scbp));
|
|
|
|
static void ahc_free_scb __P((struct ahc_data *ahc, struct scb *scb,
|
|
|
|
int flags));
|
1996-01-29 03:17:39 +00:00
|
|
|
static inline void ahc_getscb __P((struct ahc_data *ahc, struct scb *scb,
|
|
|
|
int iosize));
|
1995-12-15 23:49:42 +00:00
|
|
|
static struct scb *
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_get_scb __P((struct ahc_data *ahc, int flags));
|
1995-12-15 23:49:42 +00:00
|
|
|
static void ahc_loadseq __P((u_long iobase));
|
|
|
|
static int ahc_match_scb __P((struct scb *scb, int target, char channel));
|
1996-01-29 03:17:39 +00:00
|
|
|
static int ahc_poll __P((struct ahc_data *ahc, int wait));
|
1995-12-15 23:49:42 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
static void ahc_print_active_scb __P((struct ahc_data *ahc));
|
|
|
|
static void ahc_print_scb __P((struct scb *scb));
|
|
|
|
#endif
|
1996-01-03 06:32:12 +00:00
|
|
|
static int ahc_reset_channel __P((struct ahc_data *ahc, char channel,
|
|
|
|
u_char timedout_scb, u_int32 xs_error));
|
|
|
|
static int ahc_reset_device __P((struct ahc_data *ahc, int target,
|
|
|
|
char channel, u_char timedout_scb,
|
|
|
|
u_int32 xs_error));
|
1995-12-15 23:49:42 +00:00
|
|
|
static void ahc_reset_current_bus __P((u_long iobase));
|
1996-01-03 06:32:12 +00:00
|
|
|
static void ahc_scb_timeout __P((struct ahc_data *ahc, struct scb *scb));
|
|
|
|
static void ahc_scsirate __P((struct ahc_data* ahc, u_char *scsirate,
|
|
|
|
int period, int offset, int target));
|
1996-01-29 03:17:39 +00:00
|
|
|
static inline void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb,
|
|
|
|
int iosize));
|
1995-12-15 23:49:42 +00:00
|
|
|
static timeout_t
|
|
|
|
ahc_timeout;
|
1996-01-03 06:32:12 +00:00
|
|
|
static void ahc_busy_target __P((int target, char channel,
|
|
|
|
u_long iobase));
|
1995-12-15 23:49:42 +00:00
|
|
|
static void ahc_unbusy_target __P((int target, char channel,
|
|
|
|
u_long iobase));
|
|
|
|
|
1995-02-22 01:43:25 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_print_scb(scb)
|
|
|
|
struct scb *scb;
|
|
|
|
{
|
1995-11-06 05:21:13 +00:00
|
|
|
printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n"
|
1994-11-17 20:22:31 +00:00
|
|
|
,scb
|
|
|
|
,scb->control
|
|
|
|
,scb->target_channel_lun
|
|
|
|
,scb->cmdlen
|
1995-05-30 08:16:23 +00:00
|
|
|
,scb->cmdpointer );
|
1996-01-03 06:32:12 +00:00
|
|
|
printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n"
|
|
|
|
,scb->datalen
|
1994-11-17 20:22:31 +00:00
|
|
|
,scb->data
|
|
|
|
,scb->SG_segment_count
|
|
|
|
,scb->SG_list_pointer);
|
1995-11-06 05:21:13 +00:00
|
|
|
printf(" sg_addr:%lx sg_len:%ld\n"
|
1994-11-17 20:22:31 +00:00
|
|
|
,scb->ahc_dma[0].addr
|
|
|
|
,scb->ahc_dma[0].len);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1995-05-30 08:16:23 +00:00
|
|
|
ahc_print_active_scb(ahc)
|
1994-11-17 20:22:31 +00:00
|
|
|
struct ahc_data *ahc;
|
|
|
|
{
|
|
|
|
int cur_scb_offset;
|
1995-01-13 02:24:31 +00:00
|
|
|
u_long iobase = ahc->baseport;
|
1996-01-03 06:32:12 +00:00
|
|
|
PAUSE_SEQUENCER(ahc);
|
|
|
|
cur_scb_offset = inb(SCBPTR + iobase);
|
1994-11-17 20:22:31 +00:00
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
ahc_print_scb(ahc->scbarray[cur_scb_offset]);
|
|
|
|
}
|
|
|
|
|
1995-02-22 01:43:25 +00:00
|
|
|
#endif
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
#define PARERR 0x08
|
|
|
|
#define ILLOPCODE 0x04
|
|
|
|
#define ILLSADDR 0x02
|
|
|
|
#define ILLHADDR 0x01
|
|
|
|
|
|
|
|
static struct {
|
1995-05-30 08:16:23 +00:00
|
|
|
u_char errno;
|
1994-11-17 20:22:31 +00:00
|
|
|
char *errmesg;
|
|
|
|
} hard_error[] = {
|
1995-03-07 08:59:28 +00:00
|
|
|
{ ILLHADDR, "Illegal Host Access" },
|
|
|
|
{ ILLSADDR, "Illegal Sequencer Address referrenced" },
|
|
|
|
{ ILLOPCODE, "Illegal Opcode in sequencer program" },
|
|
|
|
{ PARERR, "Sequencer Ram Parity Error" }
|
1995-05-30 08:16:23 +00:00
|
|
|
};
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Valid SCSIRATE values. (p. 3-17)
|
|
|
|
* Provides a mapping of tranfer periods in ns to the proper value to
|
|
|
|
* stick in the scsiscfr reg to use that transfer rate.
|
|
|
|
*/
|
|
|
|
static struct {
|
|
|
|
short sxfr;
|
1995-10-26 23:57:18 +00:00
|
|
|
/* Rates in Ultra mode have bit 8 of sxfr set */
|
|
|
|
#define ULTRA_SXFR 0x100
|
1994-11-17 20:22:31 +00:00
|
|
|
short period; /* in ns */
|
|
|
|
char *rate;
|
|
|
|
} ahc_syncrates[] = {
|
1995-10-26 23:57:18 +00:00
|
|
|
{ 0x100, 50, "20.0" },
|
|
|
|
{ 0x110, 62, "16.0" },
|
|
|
|
{ 0x120, 75, "13.4" },
|
|
|
|
{ 0x140, 100, "10.0" },
|
|
|
|
{ 0x000, 100, "10.0" },
|
|
|
|
{ 0x010, 125, "8.0" },
|
|
|
|
{ 0x020, 150, "6.67" },
|
|
|
|
{ 0x030, 175, "5.7" },
|
|
|
|
{ 0x040, 200, "5.0" },
|
|
|
|
{ 0x050, 225, "4.4" },
|
|
|
|
{ 0x060, 250, "4.0" },
|
|
|
|
{ 0x070, 275, "3.6" }
|
1994-11-17 20:22:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int ahc_num_syncrates =
|
|
|
|
sizeof(ahc_syncrates) / sizeof(ahc_syncrates[0]);
|
|
|
|
|
|
|
|
/*
|
1995-11-05 04:50:55 +00:00
|
|
|
* Allocate a controller structures for a new device and initialize it.
|
|
|
|
* ahc_reset should be called before now since we assume that the card
|
|
|
|
* is paused.
|
|
|
|
*
|
1994-11-17 20:22:31 +00:00
|
|
|
*/
|
1995-11-05 04:50:55 +00:00
|
|
|
struct ahc_data *
|
|
|
|
ahc_alloc(unit, iobase, type, flags)
|
1995-01-13 02:24:31 +00:00
|
|
|
int unit;
|
|
|
|
u_long iobase;
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_type type;
|
1995-09-05 23:52:03 +00:00
|
|
|
ahc_flag flags;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
/*
|
|
|
|
* find unit and check we have that many defined
|
|
|
|
*/
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
struct ahc_data *ahc;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
/*
|
|
|
|
* Allocate a storage area for us
|
|
|
|
*/
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc = malloc(sizeof(struct ahc_data), M_TEMP, M_NOWAIT);
|
|
|
|
if (!ahc) {
|
|
|
|
printf("ahc%d: cannot malloc!\n", unit);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bzero(ahc, sizeof(struct ahc_data));
|
1995-11-05 04:50:55 +00:00
|
|
|
ahc->unit = unit;
|
1995-01-13 02:24:31 +00:00
|
|
|
ahc->baseport = iobase;
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc->type = type;
|
1995-09-05 23:52:03 +00:00
|
|
|
ahc->flags = flags;
|
1995-11-05 04:50:55 +00:00
|
|
|
ahc->unpause = (inb(HCNTRL + iobase) & IRQMS) | INTEN;
|
|
|
|
ahc->pause = ahc->unpause | PAUSE;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
return (ahc);
|
1995-11-05 04:50:55 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-11-05 04:50:55 +00:00
|
|
|
void
|
|
|
|
ahc_free(ahc)
|
1996-01-03 06:32:12 +00:00
|
|
|
struct ahc_data *ahc;
|
1995-11-05 04:50:55 +00:00
|
|
|
{
|
|
|
|
free(ahc, M_DEVBUF);
|
|
|
|
return;
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
1995-11-05 04:50:55 +00:00
|
|
|
void
|
|
|
|
ahc_reset(iobase)
|
1996-01-03 06:32:12 +00:00
|
|
|
u_long iobase;
|
1995-11-05 04:50:55 +00:00
|
|
|
{
|
|
|
|
u_char hcntrl;
|
|
|
|
int wait;
|
|
|
|
|
|
|
|
/* Retain the IRQ type accross the chip reset */
|
|
|
|
hcntrl = (inb(HCNTRL + iobase) & IRQMS) | INTEN;
|
|
|
|
outb(HCNTRL + iobase, CHIPRST | PAUSE);
|
|
|
|
/*
|
|
|
|
* Ensure that the reset has finished
|
|
|
|
*/
|
|
|
|
wait = 1000;
|
|
|
|
while (wait--) {
|
|
|
|
DELAY(1000);
|
|
|
|
if(!(inb(HCNTRL + iobase) & CHIPRST))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(wait == 0) {
|
1995-11-06 05:21:13 +00:00
|
|
|
printf("ahc at 0x%lx: WARNING - Failed chip reset! "
|
1995-11-05 04:50:55 +00:00
|
|
|
"Trying to initialize anyway.\n", iobase);
|
|
|
|
}
|
|
|
|
outb(HCNTRL + iobase, hcntrl | PAUSE);
|
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up the valid period to SCSIRATE conversion in our table.
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_scsirate(ahc, scsirate, period, offset, target )
|
|
|
|
struct ahc_data *ahc;
|
1994-11-17 20:22:31 +00:00
|
|
|
u_char *scsirate;
|
1996-01-03 06:32:12 +00:00
|
|
|
short period;
|
|
|
|
u_char offset;
|
|
|
|
int target;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
int i;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
for (i = 0; i < ahc_num_syncrates; i++) {
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
if ((ahc_syncrates[i].period - period) >= 0) {
|
1995-10-26 23:57:18 +00:00
|
|
|
/*
|
|
|
|
* Watch out for Ultra speeds when ultra is not
|
|
|
|
* enabled and vice-versa.
|
|
|
|
*/
|
|
|
|
if (ahc->type & AHC_ULTRA) {
|
|
|
|
if (!(ahc_syncrates[i].sxfr & ULTRA_SXFR)) {
|
|
|
|
printf("ahc%d: target %d requests "
|
1995-11-05 04:50:55 +00:00
|
|
|
"%sMHz transfers, but adapter "
|
1995-10-26 23:57:18 +00:00
|
|
|
"in Ultra mode can only sync at "
|
1996-01-03 06:32:12 +00:00
|
|
|
"10MHz or above\n", ahc->unit,
|
1995-10-26 23:57:18 +00:00
|
|
|
target, ahc_syncrates[i].rate);
|
|
|
|
break; /* Use Async */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (ahc_syncrates[i].sxfr & ULTRA_SXFR) {
|
|
|
|
/*
|
|
|
|
* This should only happen if the
|
|
|
|
* drive is the first to negotiate
|
|
|
|
* and chooses a high rate. We'll
|
|
|
|
* just move down the table util
|
|
|
|
* we hit a non ultra speed.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
*scsirate = (ahc_syncrates[i].sxfr) | (offset & 0x0f);
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose) {
|
1995-11-05 04:50:55 +00:00
|
|
|
printf("ahc%d: target %d synchronous at %sMHz,"
|
1996-01-03 06:32:12 +00:00
|
|
|
" offset = 0x%x\n", ahc->unit, target,
|
1995-09-05 23:52:03 +00:00
|
|
|
ahc_syncrates[i].rate, offset );
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
1995-04-15 21:37:32 +00:00
|
|
|
/* Default to asyncronous transfers. Also reject this SDTR request. */
|
|
|
|
*scsirate = 0;
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose) {
|
|
|
|
printf("ahc%d: target %d using asyncronous transfers\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, target );
|
1995-09-05 23:52:03 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach all the sub-devices we can find
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1994-11-17 20:22:31 +00:00
|
|
|
int
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_attach(ahc)
|
|
|
|
struct ahc_data *ahc;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1995-08-23 23:03:17 +00:00
|
|
|
struct scsibus_data *scbus;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fill in the prototype scsi_link.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->sc_link.adapter_unit = ahc->unit;
|
1995-08-23 23:03:17 +00:00
|
|
|
ahc->sc_link.adapter_targ = ahc->our_id;
|
1996-01-07 19:24:36 +00:00
|
|
|
ahc->sc_link.adapter_softc = ahc;
|
1995-08-23 23:03:17 +00:00
|
|
|
ahc->sc_link.adapter = &ahc_switch;
|
1995-03-17 23:58:27 +00:00
|
|
|
ahc->sc_link.opennings = 2;
|
1995-08-23 23:03:17 +00:00
|
|
|
ahc->sc_link.device = &ahc_dev;
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc->sc_link.flags = DEBUGLEVEL;
|
1994-12-31 19:31:56 +00:00
|
|
|
ahc->sc_link.fordriver = 0;
|
1994-11-18 20:34:30 +00:00
|
|
|
|
1995-08-23 23:03:17 +00:00
|
|
|
/*
|
|
|
|
* Prepare the scsibus_data area for the upperlevel
|
|
|
|
* scsi code.
|
|
|
|
*/
|
|
|
|
scbus = scsi_alloc_bus();
|
|
|
|
if(!scbus)
|
|
|
|
return 0;
|
|
|
|
scbus->adapter_link = &ahc->sc_link;
|
|
|
|
if(ahc->type & AHC_WIDE)
|
|
|
|
scbus->maxtarg = 15;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ask the adapter what subunits are present
|
|
|
|
*/
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose)
|
1996-01-03 06:32:12 +00:00
|
|
|
printf("ahc%d: Probing channel A\n", ahc->unit);
|
1995-08-23 23:03:17 +00:00
|
|
|
scsi_attachdevs(scbus);
|
|
|
|
scbus = NULL; /* Upper-level SCSI code owns this now */
|
1995-02-22 01:43:25 +00:00
|
|
|
if(ahc->type & AHC_TWIN) {
|
1994-12-31 19:31:56 +00:00
|
|
|
/* Configure the second scsi bus */
|
|
|
|
ahc->sc_link_b = ahc->sc_link;
|
1995-08-23 23:03:17 +00:00
|
|
|
ahc->sc_link_b.adapter_targ = ahc->our_id_b;
|
|
|
|
ahc->sc_link_b.adapter_bus = 1;
|
1995-07-17 23:35:16 +00:00
|
|
|
ahc->sc_link_b.fordriver = (void *)SELBUSB;
|
1995-08-23 23:03:17 +00:00
|
|
|
scbus = scsi_alloc_bus();
|
|
|
|
if(!scbus)
|
|
|
|
return 0;
|
|
|
|
scbus->adapter_link = &ahc->sc_link_b;
|
|
|
|
if(ahc->type & AHC_WIDE)
|
|
|
|
scbus->maxtarg = 15;
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose)
|
1996-01-03 06:32:12 +00:00
|
|
|
printf("ahc%d: Probing Channel B\n", ahc->unit);
|
1995-08-23 23:03:17 +00:00
|
|
|
scsi_attachdevs(scbus);
|
|
|
|
scbus = NULL; /* Upper-level SCSI code owns this now */
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1995-08-23 23:03:17 +00:00
|
|
|
return 1;
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
/* Send an SCB down to the card via PIO maintaining the SCB pointer */
|
|
|
|
static inline void
|
|
|
|
ahc_send_scb(ahc, scb, iosize)
|
|
|
|
struct ahc_data *ahc;
|
|
|
|
struct scb *scb;
|
|
|
|
int iosize; /* In bytes */
|
1995-05-30 08:16:23 +00:00
|
|
|
{
|
1995-01-13 02:24:31 +00:00
|
|
|
u_long iobase = ahc->baseport;
|
1996-01-29 03:17:39 +00:00
|
|
|
u_char cur_scb;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
cur_scb = inb(SCBPTR + iobase);
|
|
|
|
outb(SCBPTR + iobase, scb->position);
|
|
|
|
outb(SCBCNT + iobase, SCBAUTO);
|
|
|
|
if( ahc->type == AHC_284 )
|
|
|
|
/* Can only do 8bit PIO */
|
|
|
|
outsb(SCBARRAY+iobase, scb, iosize);
|
|
|
|
else
|
|
|
|
outsl(SCBARRAY+iobase, scb, (iosize + 3) / 4);
|
|
|
|
outb(SCBCNT + iobase, 0);
|
|
|
|
outb(SCBPTR + iobase, cur_scb);
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
static inline void
|
|
|
|
ahc_getscb(ahc, scb, iosize)
|
|
|
|
struct ahc_data *ahc;
|
|
|
|
struct scb *scb;
|
|
|
|
int iosize;
|
1995-05-30 08:16:23 +00:00
|
|
|
{
|
1996-01-29 03:17:39 +00:00
|
|
|
u_long iobase = ahc->baseport;
|
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(SCBCNT + iobase, 0x80); /* SCBAUTO */
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
/* Can only do 8bit PIO for reads */
|
|
|
|
insb(SCBARRAY+iobase, scb, iosize);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(SCBCNT + iobase, 0);
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
1995-07-31 08:25:36 +00:00
|
|
|
/*
|
|
|
|
* Add this SCB to the "waiting for selection" list.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void ahc_add_waiting_scb (iobase, scb, where)
|
|
|
|
u_long iobase;
|
|
|
|
struct scb *scb;
|
|
|
|
insert_t where;
|
|
|
|
{
|
|
|
|
u_char head, tail;
|
|
|
|
u_char curscb;
|
|
|
|
|
|
|
|
curscb = inb(SCBPTR + iobase);
|
|
|
|
head = inb(WAITING_SCBH + iobase);
|
|
|
|
if(head == SCB_LIST_NULL) {
|
|
|
|
/* List was empty */
|
|
|
|
head = scb->position;
|
|
|
|
tail = SCB_LIST_NULL;
|
|
|
|
}
|
|
|
|
else if (where == list_head) {
|
|
|
|
outb(SCBPTR+iobase, scb->position);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(SCB_NEXT_WAITING+iobase, head);
|
1995-07-31 08:25:36 +00:00
|
|
|
head = scb->position;
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
else /*where == list_second*/ {
|
1995-07-31 08:25:36 +00:00
|
|
|
u_char third_scb;
|
|
|
|
outb(SCBPTR+iobase, head);
|
1996-01-03 06:32:12 +00:00
|
|
|
third_scb = inb(SCB_NEXT_WAITING+iobase);
|
|
|
|
outb(SCB_NEXT_WAITING+iobase,scb->position);
|
1995-07-31 08:25:36 +00:00
|
|
|
outb(SCBPTR+iobase, scb->position);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(SCB_NEXT_WAITING+iobase,third_scb);
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
|
|
|
outb(WAITING_SCBH + iobase, head);
|
|
|
|
outb(SCBPTR + iobase, curscb);
|
|
|
|
}
|
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1994-11-17 20:22:31 +00:00
|
|
|
* Catch an interrupt from the adaptor
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1996-01-23 21:48:28 +00:00
|
|
|
void
|
|
|
|
ahc_intr(arg)
|
1995-11-05 04:50:55 +00:00
|
|
|
void *arg;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
|
|
|
int intstat;
|
|
|
|
u_char status;
|
1995-11-05 04:50:55 +00:00
|
|
|
u_long iobase;
|
1994-11-17 20:22:31 +00:00
|
|
|
struct scb *scb = NULL;
|
1995-05-30 08:16:23 +00:00
|
|
|
struct scsi_xfer *xs = NULL;
|
1995-11-05 04:50:55 +00:00
|
|
|
struct ahc_data *ahc = (struct ahc_data *)arg;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-11-05 04:50:55 +00:00
|
|
|
iobase = ahc->baseport;
|
1995-01-13 02:24:31 +00:00
|
|
|
intstat = inb(INTSTAT + iobase);
|
1995-03-31 13:54:41 +00:00
|
|
|
/*
|
|
|
|
* Is this interrupt for me? or for
|
|
|
|
* someone who is sharing my interrupt
|
|
|
|
*/
|
|
|
|
if (!(intstat & INT_PEND))
|
1996-01-23 21:48:28 +00:00
|
|
|
return;
|
1995-11-05 04:50:55 +00:00
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
if (intstat & BRKADRINT) {
|
1994-11-17 20:22:31 +00:00
|
|
|
/* We upset the sequencer :-( */
|
|
|
|
|
|
|
|
/* Lookup the error message */
|
1995-01-13 02:24:31 +00:00
|
|
|
int i, error = inb(ERROR + iobase);
|
1994-11-17 20:22:31 +00:00
|
|
|
int num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
|
|
|
|
for(i = 0; error != 1 && i < num_errors; i++)
|
|
|
|
error >>= 1;
|
1995-03-31 13:54:41 +00:00
|
|
|
panic("ahc%d: brkadrint, %s at seqaddr = 0x%x\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, hard_error[i].errmesg,
|
1995-05-30 08:16:23 +00:00
|
|
|
(inb(SEQADDR1 + iobase) << 8) |
|
1995-01-13 02:24:31 +00:00
|
|
|
inb(SEQADDR0 + iobase));
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
1995-06-11 19:33:05 +00:00
|
|
|
if (intstat & SEQINT) {
|
|
|
|
u_short targ_mask;
|
|
|
|
u_char target = (inb(SCSIID + iobase) >> 4) & 0x0f;
|
|
|
|
u_char scratch_offset = target;
|
|
|
|
char channel =
|
|
|
|
inb(SBLKCTL + iobase) & SELBUSB ? 'B': 'A';
|
|
|
|
|
|
|
|
if (channel == 'B')
|
|
|
|
scratch_offset += 8;
|
|
|
|
targ_mask = (0x01 << scratch_offset);
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
switch (intstat & SEQINT_MASK) {
|
|
|
|
case BAD_PHASE:
|
1995-06-11 19:33:05 +00:00
|
|
|
panic("ahc%d:%c:%d: unknown scsi bus phase. "
|
|
|
|
"Attempting to continue\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, channel, target);
|
1995-06-11 19:33:05 +00:00
|
|
|
break;
|
|
|
|
case SEND_REJECT:
|
1995-07-04 21:14:45 +00:00
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
u_char rejbyte = inb(REJBYTE + iobase);
|
1995-07-04 21:14:45 +00:00
|
|
|
if(( rejbyte & 0xf0) == 0x20) {
|
|
|
|
/* Tagged Message */
|
1995-09-05 23:52:03 +00:00
|
|
|
printf("\nahc%d:%c:%d: Tagged message "
|
1996-01-03 06:32:12 +00:00
|
|
|
"received without identify. "
|
|
|
|
"Disabling tagged commands "
|
|
|
|
"for this target.\n",
|
|
|
|
ahc->unit, channel, target);
|
1995-07-04 21:14:45 +00:00
|
|
|
ahc->tagenable &= ~targ_mask;
|
|
|
|
}
|
1995-09-05 23:52:03 +00:00
|
|
|
else
|
1996-01-03 06:32:12 +00:00
|
|
|
printf("ahc%d:%c:%d: Warning - "
|
|
|
|
"unknown message recieved from "
|
|
|
|
"target (0x%x). Rejecting\n",
|
|
|
|
ahc->unit, channel, target,
|
|
|
|
rejbyte);
|
1995-07-04 21:14:45 +00:00
|
|
|
break;
|
|
|
|
}
|
1995-06-11 19:33:05 +00:00
|
|
|
case NO_IDENT:
|
|
|
|
panic("ahc%d:%c:%d: Target did not send an IDENTIFY "
|
|
|
|
"message. SAVED_TCL == 0x%x\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, channel, target,
|
1995-02-22 01:43:25 +00:00
|
|
|
inb(SAVED_TCL + iobase));
|
1994-11-17 20:22:31 +00:00
|
|
|
break;
|
|
|
|
case NO_MATCH:
|
|
|
|
{
|
1995-07-04 21:14:45 +00:00
|
|
|
printf("ahc%d:%c:%d: no active SCB for "
|
|
|
|
"reconnecting target - "
|
1996-01-03 06:32:12 +00:00
|
|
|
"issuing ABORT\n", ahc->unit, channel,
|
1995-06-11 19:33:05 +00:00
|
|
|
target);
|
1995-02-22 01:43:25 +00:00
|
|
|
printf("SAVED_TCL == 0x%x\n",
|
|
|
|
inb(SAVED_TCL + iobase));
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_unbusy_target(target, channel, iobase);
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SCBARRAY + iobase, 0);
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(CLRSINT1 + iobase, CLRSELTIMEO);
|
1994-11-17 20:22:31 +00:00
|
|
|
RESTART_SEQUENCER(ahc);
|
|
|
|
break;
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
case SDTR_MSG:
|
1995-01-13 02:24:31 +00:00
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
short period;
|
|
|
|
u_char offset, rate;
|
1995-06-11 19:33:05 +00:00
|
|
|
u_char targ_scratch;
|
|
|
|
u_char maxoffset;
|
|
|
|
/*
|
|
|
|
* Help the sequencer to translate the
|
|
|
|
* negotiated transfer rate. Transfer is
|
|
|
|
* 1/4 the period in ns as is returned by
|
|
|
|
* the sync negotiation message. So, we must
|
1995-01-13 02:24:31 +00:00
|
|
|
* multiply by four
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
period = inb(ARG_1 + iobase) << 2;
|
1995-01-13 02:24:31 +00:00
|
|
|
offset = inb(ACCUM + iobase);
|
1996-01-03 06:32:12 +00:00
|
|
|
targ_scratch = inb(TARG_SCRATCH + iobase
|
1995-06-11 19:33:05 +00:00
|
|
|
+ scratch_offset);
|
|
|
|
if(targ_scratch & WIDEXFER)
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
maxoffset = 0x08;
|
|
|
|
else
|
|
|
|
maxoffset = 0x0f;
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_scsirate(ahc, &rate, period,
|
|
|
|
MIN(offset,maxoffset),
|
|
|
|
target);
|
1995-01-13 02:24:31 +00:00
|
|
|
/* Preserve the WideXfer flag */
|
1995-06-11 19:33:05 +00:00
|
|
|
targ_scratch = rate | (targ_scratch & WIDEXFER);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(TARG_SCRATCH + iobase + scratch_offset,
|
1995-06-11 19:33:05 +00:00
|
|
|
targ_scratch);
|
|
|
|
outb(SCSIRATE + iobase, targ_scratch);
|
|
|
|
if( (targ_scratch & 0x0f) == 0 )
|
1995-04-15 21:37:32 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The requested rate was so low
|
|
|
|
* that asyncronous transfers are
|
1995-05-30 08:16:23 +00:00
|
|
|
* faster (not to mention the
|
1995-04-15 21:37:32 +00:00
|
|
|
* controller won't support them),
|
|
|
|
* so we issue a message reject to
|
|
|
|
* ensure we go to asyncronous
|
|
|
|
* transfers.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase, SEND_REJ);
|
1995-04-15 21:37:32 +00:00
|
|
|
}
|
1995-01-13 02:24:31 +00:00
|
|
|
/* See if we initiated Sync Negotiation */
|
1995-06-11 19:33:05 +00:00
|
|
|
else if(ahc->sdtrpending & targ_mask)
|
1995-01-13 02:24:31 +00:00
|
|
|
{
|
1995-03-17 23:58:27 +00:00
|
|
|
/*
|
|
|
|
* Don't send an SDTR back to
|
|
|
|
* the target
|
1995-01-13 02:24:31 +00:00
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase, 0);
|
1995-01-13 02:24:31 +00:00
|
|
|
}
|
|
|
|
else{
|
|
|
|
/*
|
|
|
|
* Send our own SDTR in reply
|
|
|
|
*/
|
1995-03-17 23:58:27 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWMISC)
|
|
|
|
printf("Sending SDTR!!\n");
|
|
|
|
#endif
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase, SEND_SDTR);
|
1995-01-13 02:24:31 +00:00
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-03-17 23:58:27 +00:00
|
|
|
* Negate the flags
|
|
|
|
*/
|
1995-06-11 19:33:05 +00:00
|
|
|
ahc->needsdtr &= ~targ_mask;
|
|
|
|
ahc->sdtrpending &= ~targ_mask;
|
1995-01-13 02:24:31 +00:00
|
|
|
break;
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
case WDTR_MSG:
|
1995-01-13 02:24:31 +00:00
|
|
|
{
|
1995-06-11 19:33:05 +00:00
|
|
|
u_char scratch, bus_width;
|
1995-01-13 02:24:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
bus_width = inb(ARG_1 + iobase);
|
1995-01-13 02:24:31 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
scratch = inb(TARG_SCRATCH + iobase
|
1995-06-11 19:33:05 +00:00
|
|
|
+ scratch_offset);
|
1995-01-13 02:24:31 +00:00
|
|
|
|
1995-06-11 19:33:05 +00:00
|
|
|
if(ahc->wdtrpending & targ_mask)
|
1995-01-13 02:24:31 +00:00
|
|
|
{
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
|
|
|
* Don't send a WDTR back to the
|
1995-01-13 02:24:31 +00:00
|
|
|
* target, since we asked first.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase, 0);
|
1995-01-13 02:24:31 +00:00
|
|
|
switch(bus_width)
|
|
|
|
{
|
|
|
|
case BUS_8_BIT:
|
1995-09-05 23:52:03 +00:00
|
|
|
scratch &= 0x7f;
|
|
|
|
break;
|
1995-01-13 02:24:31 +00:00
|
|
|
case BUS_16_BIT:
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose)
|
1995-01-13 02:24:31 +00:00
|
|
|
printf("ahc%d: target "
|
|
|
|
"%d using 16Bit "
|
|
|
|
"transfers\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit,
|
|
|
|
target);
|
1995-09-05 23:52:03 +00:00
|
|
|
scratch |= 0x80;
|
|
|
|
break;
|
1996-01-03 06:32:12 +00:00
|
|
|
case BUS_32_BIT:
|
|
|
|
/*
|
|
|
|
* How can we do 32bit
|
|
|
|
* transfers on a 16bit
|
|
|
|
* bus?
|
|
|
|
*/
|
|
|
|
outb(RETURN_1 + iobase,
|
|
|
|
SEND_REJ);
|
|
|
|
printf("ahc%d: target "
|
|
|
|
"%d requested 32Bit "
|
|
|
|
"transfers. "
|
|
|
|
"Rejecting...\n",
|
|
|
|
ahc->unit, target);
|
|
|
|
break;
|
1995-09-05 23:52:03 +00:00
|
|
|
default:
|
|
|
|
break;
|
1995-01-13 02:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Send our own WDTR in reply
|
|
|
|
*/
|
|
|
|
switch(bus_width)
|
|
|
|
{
|
|
|
|
case BUS_8_BIT:
|
|
|
|
scratch &= 0x7f;
|
|
|
|
break;
|
|
|
|
case BUS_32_BIT:
|
|
|
|
/* Negotiate 16_BITS */
|
|
|
|
bus_width = BUS_16_BIT;
|
|
|
|
case BUS_16_BIT:
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose)
|
1995-01-13 02:24:31 +00:00
|
|
|
printf("ahc%d: target "
|
|
|
|
"%d using 16Bit "
|
|
|
|
"transfers\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit,
|
|
|
|
target);
|
1995-09-05 23:52:03 +00:00
|
|
|
scratch |= 0x80;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
1995-01-13 02:24:31 +00:00
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase,
|
1995-01-13 02:24:31 +00:00
|
|
|
bus_width | SEND_WDTR);
|
|
|
|
}
|
1995-06-11 19:33:05 +00:00
|
|
|
ahc->needwdtr &= ~targ_mask;
|
|
|
|
ahc->wdtrpending &= ~targ_mask;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(TARG_SCRATCH + iobase + scratch_offset,
|
1995-06-11 19:33:05 +00:00
|
|
|
scratch);
|
|
|
|
outb(SCSIRATE + iobase, scratch);
|
1995-01-13 02:24:31 +00:00
|
|
|
break;
|
|
|
|
}
|
1996-01-05 16:13:44 +00:00
|
|
|
case REJECT_MSG:
|
1995-01-16 16:33:47 +00:00
|
|
|
{
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-01-16 16:33:47 +00:00
|
|
|
* What we care about here is if we had an
|
|
|
|
* outstanding SDTR or WDTR message for this
|
|
|
|
* target. If we did, this is a signal that
|
|
|
|
* the target is refusing negotiation.
|
|
|
|
*/
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1995-01-16 16:33:47 +00:00
|
|
|
u_char targ_scratch;
|
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
targ_scratch = inb(TARG_SCRATCH + iobase
|
1995-06-11 19:33:05 +00:00
|
|
|
+ scratch_offset);
|
1995-01-16 16:33:47 +00:00
|
|
|
|
1995-06-11 19:33:05 +00:00
|
|
|
if(ahc->wdtrpending & targ_mask){
|
1995-01-16 16:33:47 +00:00
|
|
|
/* note 8bit xfers and clear flag */
|
|
|
|
targ_scratch &= 0x7f;
|
1995-06-11 19:33:05 +00:00
|
|
|
ahc->needwdtr &= ~targ_mask;
|
|
|
|
ahc->wdtrpending &= ~targ_mask;
|
1995-07-31 08:25:36 +00:00
|
|
|
printf("ahc%d:%c:%d: refuses "
|
1995-01-16 16:33:47 +00:00
|
|
|
"WIDE negotiation. Using "
|
|
|
|
"8bit transfers\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, channel, target);
|
1995-01-16 16:33:47 +00:00
|
|
|
}
|
1995-06-11 19:33:05 +00:00
|
|
|
else if(ahc->sdtrpending & targ_mask){
|
1995-01-27 17:37:05 +00:00
|
|
|
/* note asynch xfers and clear flag */
|
|
|
|
targ_scratch &= 0xf0;
|
1995-06-11 19:33:05 +00:00
|
|
|
ahc->needsdtr &= ~targ_mask;
|
|
|
|
ahc->sdtrpending &= ~targ_mask;
|
1995-07-31 08:25:36 +00:00
|
|
|
printf("ahc%d:%c:%d: refuses "
|
1995-01-27 17:37:05 +00:00
|
|
|
"syncronous negotiation. Using "
|
|
|
|
"asyncronous transfers\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, channel, target);
|
1995-01-27 17:37:05 +00:00
|
|
|
}
|
1995-03-17 23:58:27 +00:00
|
|
|
else {
|
1995-01-27 17:37:05 +00:00
|
|
|
/*
|
|
|
|
* Otherwise, we ignore it.
|
|
|
|
*/
|
1995-03-17 23:58:27 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWMISC)
|
1995-06-11 19:33:05 +00:00
|
|
|
printf("ahc%d:%c:%d: Message
|
|
|
|
reject -- ignored\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, channel,
|
|
|
|
target);
|
1995-03-17 23:58:27 +00:00
|
|
|
#endif
|
1995-01-27 17:37:05 +00:00
|
|
|
break;
|
1995-03-17 23:58:27 +00:00
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(TARG_SCRATCH + iobase + scratch_offset,
|
1995-01-16 16:33:47 +00:00
|
|
|
targ_scratch);
|
1995-01-27 17:37:05 +00:00
|
|
|
outb(SCSIRATE + iobase, targ_scratch);
|
1995-01-16 16:33:47 +00:00
|
|
|
break;
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
case BAD_STATUS:
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1995-03-07 08:59:28 +00:00
|
|
|
int scb_index;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/* The sequencer will notify us when a command
|
|
|
|
* has an error that would be of interest to
|
|
|
|
* the kernel. This allows us to leave the sequencer
|
|
|
|
* running in the common case of command completes
|
|
|
|
* without error.
|
|
|
|
*/
|
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
scb_index = inb(SCBPTR + iobase);
|
1994-11-17 20:22:31 +00:00
|
|
|
scb = ahc->scbarray[scb_index];
|
1995-05-01 09:49:45 +00:00
|
|
|
|
|
|
|
/*
|
1995-05-30 08:16:23 +00:00
|
|
|
* Set the default return value to 0 (don't
|
1996-01-03 06:32:12 +00:00
|
|
|
* send sense). The sense code will change
|
1995-05-30 08:16:23 +00:00
|
|
|
* this if needed and this reduces code
|
1995-05-01 09:49:45 +00:00
|
|
|
* duplication.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase, 0);
|
1994-11-17 20:22:31 +00:00
|
|
|
if (!scb || !(scb->flags & SCB_ACTIVE)) {
|
1996-01-23 21:48:28 +00:00
|
|
|
printf("ahc%d:%c:%d: ahc_intr - referenced scb "
|
1995-06-11 19:33:05 +00:00
|
|
|
"not valid during seqint 0x%x scb(%d)\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, channel, target, intstat,
|
|
|
|
scb_index);
|
1994-11-17 20:22:31 +00:00
|
|
|
goto clear;
|
1995-05-01 09:49:45 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-05-01 09:49:45 +00:00
|
|
|
xs = scb->xs;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
ahc_getscb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-02-22 01:43:25 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-07-31 08:25:36 +00:00
|
|
|
if((ahc_debug & AHC_SHOWSCBS)
|
|
|
|
&& xs->sc_link->target == DEBUGTARG)
|
1994-12-31 19:31:56 +00:00
|
|
|
ahc_print_scb(scb);
|
1994-11-17 20:22:31 +00:00
|
|
|
#endif
|
1995-05-01 09:49:45 +00:00
|
|
|
xs->status = scb->target_status;
|
|
|
|
switch(scb->target_status){
|
1994-12-31 19:31:56 +00:00
|
|
|
case SCSI_OK:
|
|
|
|
printf("ahc%d: Interrupted for staus of"
|
1996-01-03 06:32:12 +00:00
|
|
|
" 0???\n", ahc->unit);
|
1994-12-31 19:31:56 +00:00
|
|
|
break;
|
|
|
|
case SCSI_CHECK:
|
1995-02-22 01:43:25 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-07-31 08:25:36 +00:00
|
|
|
if(ahc_debug & AHC_SHOWSENSE)
|
|
|
|
{
|
|
|
|
sc_print_addr(xs->sc_link);
|
|
|
|
printf("requests Check Status\n");
|
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
#endif
|
1995-01-13 02:24:31 +00:00
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
if((xs->error == XS_NOERROR) &&
|
1994-12-31 19:31:56 +00:00
|
|
|
!(scb->flags & SCB_SENSE)) {
|
1995-07-04 21:14:45 +00:00
|
|
|
u_char control = scb->control;
|
|
|
|
u_short active;
|
1994-11-17 20:22:31 +00:00
|
|
|
struct ahc_dma_seg *sg = scb->ahc_dma;
|
|
|
|
struct scsi_sense *sc = &(scb->sense_cmd);
|
1994-12-31 19:31:56 +00:00
|
|
|
u_char tcl = scb->target_channel_lun;
|
1995-02-22 01:43:25 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-07-31 08:25:36 +00:00
|
|
|
if(ahc_debug & AHC_SHOWSENSE)
|
|
|
|
{
|
|
|
|
sc_print_addr(xs->sc_link);
|
|
|
|
printf("Sending Sense\n");
|
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
#endif
|
1996-01-03 06:32:12 +00:00
|
|
|
bzero(scb, SCB_PIO_TRANSFER_SIZE);
|
|
|
|
scb->control |= control & DISCENB;
|
1994-11-17 20:22:31 +00:00
|
|
|
scb->flags |= SCB_SENSE;
|
|
|
|
sc->op_code = REQUEST_SENSE;
|
|
|
|
sc->byte2 = xs->sc_link->lun << 5;
|
|
|
|
sc->length = sizeof(struct scsi_sense_data);
|
1994-12-31 19:31:56 +00:00
|
|
|
sc->control = 0;
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
sg->addr = KVTOPHYS(&xs->sense);
|
|
|
|
sg->len = sizeof(struct scsi_sense_data);
|
1995-04-23 22:04:58 +00:00
|
|
|
|
1994-12-31 19:31:56 +00:00
|
|
|
scb->target_channel_lun = tcl;
|
|
|
|
scb->SG_segment_count = 1;
|
|
|
|
scb->SG_list_pointer = KVTOPHYS(sg);
|
|
|
|
scb->cmdpointer = KVTOPHYS(sc);
|
|
|
|
scb->cmdlen = sizeof(*sc);
|
|
|
|
|
1995-07-04 21:14:45 +00:00
|
|
|
scb->data = sg->addr;
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->datalen = sg->len;
|
1996-01-29 03:17:39 +00:00
|
|
|
|
|
|
|
ahc_send_scb(ahc, scb,
|
|
|
|
SCB_PIO_TRANSFER_SIZE);
|
|
|
|
outb(SCB_NEXT_WAITING+iobase,
|
|
|
|
SCB_LIST_NULL);
|
1995-07-04 21:14:45 +00:00
|
|
|
/*
|
|
|
|
* Ensure that the target is "BUSY"
|
|
|
|
* so we don't get overlapping
|
|
|
|
* commands if we happen to be doing
|
|
|
|
* tagged I/O.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_busy_target(target,channel,iobase);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make us the next command to run
|
|
|
|
*/
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_add_waiting_scb(iobase, scb,
|
|
|
|
list_head);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(RETURN_1 + iobase, SEND_SENSE);
|
1995-01-16 16:33:47 +00:00
|
|
|
break;
|
1994-12-31 19:31:56 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Clear the SCB_SENSE Flag and have
|
|
|
|
* the sequencer do a normal command
|
|
|
|
* complete with either a "DRIVER_STUFFUP"
|
|
|
|
* error or whatever other error condition
|
|
|
|
* we already had.
|
|
|
|
*/
|
|
|
|
scb->flags &= ~SCB_SENSE;
|
|
|
|
if(xs->error == XS_NOERROR)
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
break;
|
|
|
|
case SCSI_BUSY:
|
|
|
|
xs->error = XS_BUSY;
|
1995-06-11 19:33:05 +00:00
|
|
|
sc_print_addr(xs->sc_link);
|
|
|
|
printf("Target Busy\n");
|
1995-03-31 13:54:41 +00:00
|
|
|
break;
|
1995-02-22 01:43:25 +00:00
|
|
|
case SCSI_QUEUE_FULL:
|
|
|
|
/*
|
1995-03-31 13:54:41 +00:00
|
|
|
* The upper level SCSI code will eventually
|
|
|
|
* handle this properly.
|
1995-02-22 01:43:25 +00:00
|
|
|
*/
|
1995-06-11 19:33:05 +00:00
|
|
|
sc_print_addr(xs->sc_link);
|
|
|
|
printf("Queue Full\n");
|
1995-02-22 01:43:25 +00:00
|
|
|
xs->error = XS_BUSY;
|
|
|
|
break;
|
1994-12-31 19:31:56 +00:00
|
|
|
default:
|
1995-06-11 19:33:05 +00:00
|
|
|
sc_print_addr(xs->sc_link);
|
1994-12-31 19:31:56 +00:00
|
|
|
printf("unexpected targ_status: %x\n",
|
|
|
|
scb->target_status);
|
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
1995-03-31 13:54:41 +00:00
|
|
|
case RESIDUAL:
|
|
|
|
{
|
|
|
|
int scb_index;
|
|
|
|
scb_index = inb(SCBPTR + iobase);
|
|
|
|
scb = ahc->scbarray[scb_index];
|
1995-06-11 19:33:05 +00:00
|
|
|
xs = scb->xs;
|
1995-03-31 13:54:41 +00:00
|
|
|
/*
|
|
|
|
* Don't clobber valid resid info with
|
|
|
|
* a resid coming from a check sense
|
|
|
|
* operation.
|
|
|
|
*/
|
1995-06-11 19:33:05 +00:00
|
|
|
if(!(scb->flags & SCB_SENSE)) {
|
1996-01-03 06:32:12 +00:00
|
|
|
int resid_sgs;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remainder of the SG where the transfer
|
|
|
|
* stopped.
|
|
|
|
*/
|
|
|
|
scb->xs->resid =
|
|
|
|
(inb(iobase+SCB_RESID_DCNT2)<<16) |
|
|
|
|
(inb(iobase+SCB_RESID_DCNT1)<<8) |
|
|
|
|
inb(iobase+SCB_RESID_DCNT0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add up the contents of all residual
|
|
|
|
* SG segments that are after the SG where
|
|
|
|
* the transfer stopped.
|
|
|
|
*/
|
|
|
|
resid_sgs = inb(SCB_RESID_SGCNT + iobase) - 1;
|
|
|
|
while(resid_sgs > 0) {
|
|
|
|
int sg;
|
|
|
|
|
|
|
|
sg = scb->SG_segment_count - resid_sgs;
|
|
|
|
scb->xs->resid += scb->ahc_dma[sg].len;
|
|
|
|
resid_sgs--;
|
|
|
|
}
|
|
|
|
|
1995-06-11 19:33:05 +00:00
|
|
|
xs->flags |= SCSI_RESID_VALID;
|
1995-04-23 22:04:58 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-07-31 08:25:36 +00:00
|
|
|
if(ahc_debug & AHC_SHOWMISC) {
|
|
|
|
sc_print_addr(xs->sc_link);
|
1995-11-06 05:21:13 +00:00
|
|
|
printf("Handled Residual of %ld bytes\n"
|
1996-01-03 06:32:12 +00:00
|
|
|
,scb->xs->resid);
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
1995-04-23 22:04:58 +00:00
|
|
|
#endif
|
1995-06-11 19:33:05 +00:00
|
|
|
}
|
1995-03-31 13:54:41 +00:00
|
|
|
break;
|
|
|
|
}
|
1995-04-01 19:53:04 +00:00
|
|
|
case ABORT_TAG:
|
|
|
|
{
|
|
|
|
int scb_index;
|
|
|
|
scb_index = inb(SCBPTR + iobase);
|
|
|
|
scb = ahc->scbarray[scb_index];
|
1995-06-11 19:33:05 +00:00
|
|
|
xs = scb->xs;
|
1995-04-01 19:53:04 +00:00
|
|
|
/*
|
1995-05-30 08:16:23 +00:00
|
|
|
* We didn't recieve a valid tag back from
|
1995-04-01 19:53:04 +00:00
|
|
|
* the target on a reconnect.
|
|
|
|
*/
|
1995-06-11 19:33:05 +00:00
|
|
|
sc_print_addr(xs->sc_link);
|
|
|
|
printf("invalid tag recieved -- sending ABORT_TAG\n");
|
1995-04-01 19:53:04 +00:00
|
|
|
scb->xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
untimeout(ahc_timeout, (caddr_t)scb);
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done(ahc, scb);
|
1995-04-01 19:53:04 +00:00
|
|
|
break;
|
|
|
|
}
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
case AWAITING_MSG:
|
|
|
|
{
|
|
|
|
int scb_index;
|
|
|
|
scb_index = inb(SCBPTR + iobase);
|
|
|
|
scb = ahc->scbarray[scb_index];
|
|
|
|
/*
|
|
|
|
* This SCB had a zero length command, informing
|
|
|
|
* the sequencer that we wanted to send a special
|
|
|
|
* message to this target. We only do this for
|
|
|
|
* BUS_DEVICE_RESET messages currently.
|
|
|
|
*/
|
|
|
|
if(scb->flags & SCB_DEVICE_RESET)
|
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(MSG0 + iobase,
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
MSG_BUS_DEVICE_RESET);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(MSG_LEN + iobase, 1);
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
}
|
|
|
|
else
|
1996-01-29 03:17:39 +00:00
|
|
|
panic("ahc_intr: AWAITING_MSG for an SCB that "
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
"does not have a waiting message");
|
|
|
|
break;
|
|
|
|
}
|
1995-07-31 08:25:36 +00:00
|
|
|
case IMMEDDONE:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Take care of device reset messages
|
|
|
|
*/
|
|
|
|
u_char scbindex = inb(SCBPTR + iobase);
|
|
|
|
scb = ahc->scbarray[scbindex];
|
|
|
|
if(scb->flags & SCB_DEVICE_RESET) {
|
|
|
|
u_char targ_scratch;
|
|
|
|
int found;
|
|
|
|
/*
|
|
|
|
* Go back to async/narrow transfers and
|
|
|
|
* renegotiate.
|
|
|
|
*/
|
|
|
|
ahc_unbusy_target(target, channel, iobase);
|
|
|
|
ahc->needsdtr |= ahc->needsdtr_orig & targ_mask;
|
|
|
|
ahc->needwdtr |= ahc->needwdtr_orig & targ_mask;
|
|
|
|
ahc->sdtrpending &= ~targ_mask;
|
|
|
|
ahc->wdtrpending &= ~targ_mask;
|
1996-01-03 06:32:12 +00:00
|
|
|
targ_scratch = inb(TARG_SCRATCH + iobase
|
1995-07-31 08:25:36 +00:00
|
|
|
+ scratch_offset);
|
|
|
|
targ_scratch &= SXFR;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(TARG_SCRATCH + iobase + scratch_offset,
|
1995-07-31 08:25:36 +00:00
|
|
|
targ_scratch);
|
1996-01-03 06:32:12 +00:00
|
|
|
found = ahc_reset_device(ahc, target,
|
1995-07-31 08:25:36 +00:00
|
|
|
channel, SCB_LIST_NULL,
|
|
|
|
XS_NOERROR);
|
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWABORTS) {
|
|
|
|
sc_print_addr(scb->xs->sc_link);
|
|
|
|
printf("Bus Device Reset delivered. "
|
|
|
|
"%d SCBs aborted\n", found);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
1996-01-23 21:48:28 +00:00
|
|
|
panic("ahc_intr: Immediate complete for "
|
1995-07-31 08:25:36 +00:00
|
|
|
"unknown operation.");
|
|
|
|
break;
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
#if NOT_YET
|
|
|
|
/* XXX Fill these in later */
|
|
|
|
case MESG_BUFFER_BUSY:
|
|
|
|
break;
|
|
|
|
case MSGIN_PHASEMIS:
|
|
|
|
break;
|
|
|
|
#endif
|
1996-01-29 03:17:39 +00:00
|
|
|
case PARITY_ERROR:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Determine the bus phase and
|
|
|
|
* queue an appropriate message
|
|
|
|
*/
|
|
|
|
char *phase;
|
|
|
|
u_char mesg_out = MSG_NOP;
|
|
|
|
u_char lastphase = inb(LASTPHASE + iobase);
|
|
|
|
int scb_index = inb(SCBPTR + iobase);
|
|
|
|
|
|
|
|
scb = ahc->scbarray[scb_index];
|
|
|
|
|
|
|
|
sc_print_addr(scb->xs->sc_link);
|
|
|
|
|
|
|
|
switch(lastphase) {
|
|
|
|
case P_DATAOUT:
|
|
|
|
phase = "Data-Out";
|
|
|
|
break;
|
|
|
|
case P_DATAIN:
|
|
|
|
phase = "Data-In";
|
|
|
|
mesg_out = MSG_INITIATOR_DET_ERROR;
|
|
|
|
break;
|
|
|
|
case P_COMMAND:
|
|
|
|
phase = "Command";
|
|
|
|
break;
|
|
|
|
case P_MESGOUT:
|
|
|
|
phase = "Message-Out";
|
|
|
|
break;
|
|
|
|
case P_STATUS:
|
|
|
|
phase = "Status";
|
|
|
|
mesg_out = MSG_INITIATOR_DET_ERROR;
|
|
|
|
break;
|
|
|
|
case P_MESGIN:
|
|
|
|
phase = "Message-In";
|
|
|
|
mesg_out = MSG_MSG_PARITY_ERROR;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
phase = "unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
printf("parity error during %s phase.\n", phase);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We've set the hardware to assert ATN if we
|
|
|
|
* get a parity error on "in" phases, so all we
|
|
|
|
* need to do is stuff the message buffer with
|
|
|
|
* the appropriate message. In phases have set
|
|
|
|
* mesg_out to something other than MSG_NOP.
|
|
|
|
*/
|
|
|
|
if(mesg_out != MSG_NOP) {
|
|
|
|
outb(MSG0 + iobase, mesg_out);
|
|
|
|
outb(MSG_LEN + iobase, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/*
|
|
|
|
* Should we allow the target to make
|
|
|
|
* this decision for us?
|
|
|
|
*/
|
|
|
|
scb->xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
break;
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
default:
|
1994-12-31 19:31:56 +00:00
|
|
|
printf("ahc: seqint, "
|
|
|
|
"intstat == 0x%x, scsisigi = 0x%x\n",
|
1995-01-13 02:24:31 +00:00
|
|
|
intstat, inb(SCSISIGI + iobase));
|
1994-11-17 20:22:31 +00:00
|
|
|
break;
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
|
|
|
clear:
|
|
|
|
/*
|
1995-03-17 23:58:27 +00:00
|
|
|
* Clear the upper byte that holds SEQINT status
|
|
|
|
* codes and clear the SEQINT bit.
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1995-03-17 23:58:27 +00:00
|
|
|
outb(CLRINT + iobase, CLRSEQINT);
|
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-03-17 23:58:27 +00:00
|
|
|
* The sequencer is paused immediately on
|
|
|
|
* a SEQINT, so we should restart it when
|
1995-05-30 08:16:23 +00:00
|
|
|
* we leave this section.
|
|
|
|
*/
|
1995-03-17 23:58:27 +00:00
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
if (intstat & SCSIINT) {
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
int scb_index = inb(SCBPTR + iobase);
|
|
|
|
status = inb(SSTAT1 + iobase);
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
scb = ahc->scbarray[scb_index];
|
1994-12-31 19:31:56 +00:00
|
|
|
if (!scb || !(scb->flags & SCB_ACTIVE)) {
|
1996-01-23 21:48:28 +00:00
|
|
|
printf("ahc%d: ahc_intr - referenced scb not "
|
1994-11-17 20:22:31 +00:00
|
|
|
"valid during scsiint 0x%x scb(%d)\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, status, scb_index);
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(CLRSINT1 + iobase, status);
|
1994-11-17 20:22:31 +00:00
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
1995-03-31 13:54:41 +00:00
|
|
|
outb(CLRINT + iobase, CLRSCSIINT);
|
1994-11-17 20:22:31 +00:00
|
|
|
scb = NULL;
|
|
|
|
goto cmdcomplete;
|
|
|
|
}
|
|
|
|
xs = scb->xs;
|
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
if (status & SELTO) {
|
1995-04-27 17:47:17 +00:00
|
|
|
u_char waiting;
|
1995-01-13 02:24:31 +00:00
|
|
|
u_char flags;
|
1995-04-27 17:47:17 +00:00
|
|
|
outb(SCSISEQ + iobase, ENRSELI);
|
1994-11-17 20:22:31 +00:00
|
|
|
xs->error = XS_TIMEOUT;
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1994-11-17 20:22:31 +00:00
|
|
|
* Clear any pending messages for the timed out
|
|
|
|
* target, and mark the target as free
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
flags = inb(FLAGS + iobase);
|
|
|
|
outb(MSG_LEN + iobase, 0);
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_unbusy_target(xs->sc_link->target,
|
|
|
|
((long)xs->sc_link->fordriver & SELBUSB)
|
|
|
|
? 'B' : 'A',
|
|
|
|
iobase);
|
1994-12-31 19:31:56 +00:00
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SCBARRAY + iobase, 0);
|
1995-01-16 16:33:47 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(CLRSINT1 + iobase, CLRSELTIMEO);
|
1995-01-16 16:33:47 +00:00
|
|
|
|
1995-03-31 13:54:41 +00:00
|
|
|
outb(CLRINT + iobase, CLRSCSIINT);
|
1995-04-27 17:47:17 +00:00
|
|
|
|
|
|
|
/* Shift the waiting for selection queue forward */
|
|
|
|
waiting = inb(WAITING_SCBH + iobase);
|
|
|
|
outb(SCBPTR + iobase, waiting);
|
1996-01-03 06:32:12 +00:00
|
|
|
waiting = inb(SCB_NEXT_WAITING + iobase);
|
1995-04-27 17:47:17 +00:00
|
|
|
outb(WAITING_SCBH + iobase, waiting);
|
|
|
|
|
|
|
|
RESTART_SEQUENCER(ahc);
|
1995-06-11 19:33:05 +00:00
|
|
|
}
|
|
|
|
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
else if (!(status & BUSFREE)) {
|
1995-06-11 19:33:05 +00:00
|
|
|
sc_print_addr(xs->sc_link);
|
|
|
|
printf("Unknown SCSIINT. Status = 0x%x\n", status);
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(CLRSINT1 + iobase, status);
|
1994-11-17 20:22:31 +00:00
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
1995-03-31 13:54:41 +00:00
|
|
|
outb(CLRINT + iobase, CLRSCSIINT);
|
1994-11-17 20:22:31 +00:00
|
|
|
scb = NULL;
|
|
|
|
}
|
|
|
|
if(scb != NULL) {
|
|
|
|
/* We want to process the command */
|
|
|
|
untimeout(ahc_timeout, (caddr_t)scb);
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done(ahc, scb);
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
cmdcomplete:
|
|
|
|
if (intstat & CMDCMPLT) {
|
1995-03-07 08:59:28 +00:00
|
|
|
int scb_index;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
|
|
|
do {
|
1995-01-13 02:24:31 +00:00
|
|
|
scb_index = inb(QOUTFIFO + iobase);
|
1994-11-17 20:22:31 +00:00
|
|
|
scb = ahc->scbarray[scb_index];
|
|
|
|
if (!scb || !(scb->flags & SCB_ACTIVE)) {
|
|
|
|
printf("ahc%d: WARNING "
|
1994-12-31 19:31:56 +00:00
|
|
|
"no command for scb %d (cmdcmplt)\n"
|
|
|
|
"QOUTCNT == %d\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, scb_index,
|
|
|
|
inb(QOUTCNT + iobase));
|
1995-05-30 08:16:23 +00:00
|
|
|
outb(CLRINT + iobase, CLRCMDINT);
|
1994-11-17 20:22:31 +00:00
|
|
|
continue;
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
outb(CLRINT + iobase, CLRCMDINT);
|
1994-11-17 20:22:31 +00:00
|
|
|
untimeout(ahc_timeout, (caddr_t)scb);
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done(ahc, scb);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
} while (inb(QOUTCNT + iobase));
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1995-11-04 14:43:30 +00:00
|
|
|
}
|
1995-07-04 21:14:45 +00:00
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* We have a scb which has been processed by the
|
|
|
|
* adaptor, now we look to see how the operation
|
|
|
|
* went.
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done(ahc, scb)
|
|
|
|
struct ahc_data *ahc;
|
1994-11-17 20:22:31 +00:00
|
|
|
struct scb *scb;
|
|
|
|
{
|
|
|
|
struct scsi_xfer *xs = scb->xs;
|
|
|
|
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahc_done\n"));
|
|
|
|
/*
|
|
|
|
* Put the results of the operation
|
|
|
|
* into the xfer and call whoever started it
|
|
|
|
*/
|
1994-12-31 19:31:56 +00:00
|
|
|
if(scb->flags & SCB_SENSE)
|
|
|
|
xs->error = XS_SENSE;
|
1995-05-30 08:16:23 +00:00
|
|
|
if ((xs->flags & SCSI_ERR_OK) && !(xs->error == XS_SENSE)) {
|
1994-11-17 20:22:31 +00:00
|
|
|
/* All went correctly OR errors expected */
|
1995-02-22 01:43:25 +00:00
|
|
|
xs->error = XS_NOERROR;
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
xs->flags |= ITSDONE;
|
1995-04-09 06:39:01 +00:00
|
|
|
#ifdef AHC_TAGENABLE
|
1995-05-30 08:16:23 +00:00
|
|
|
if(xs->cmd->opcode == 0x12 && xs->error == XS_NOERROR)
|
1995-02-22 01:43:25 +00:00
|
|
|
{
|
|
|
|
struct scsi_inquiry_data *inq_data;
|
|
|
|
u_short mask = 0x01 << (xs->sc_link->target |
|
|
|
|
(scb->target_channel_lun & 0x08));
|
|
|
|
/*
|
|
|
|
* Sneak a look at the results of the SCSI Inquiry
|
|
|
|
* command and see if we can do Tagged queing. This
|
|
|
|
* should really be done by the higher level drivers.
|
|
|
|
*/
|
|
|
|
inq_data = (struct scsi_inquiry_data *)xs->data;
|
1996-01-03 06:32:12 +00:00
|
|
|
if((inq_data->flags & SID_CmdQue) && !(ahc->tagenable & mask))
|
1995-02-22 01:43:25 +00:00
|
|
|
{
|
|
|
|
printf("ahc%d: target %d Tagged Queuing Device\n",
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->unit, xs->sc_link->target);
|
1995-02-22 01:43:25 +00:00
|
|
|
ahc->tagenable |= mask;
|
1995-03-17 23:58:27 +00:00
|
|
|
#ifdef QUEUE_FULL_SUPPORTED
|
1995-11-07 07:01:05 +00:00
|
|
|
xs->sc_link->opennings += 2;
|
1995-03-17 23:58:27 +00:00
|
|
|
#endif
|
1995-02-22 01:43:25 +00:00
|
|
|
}
|
|
|
|
}
|
1995-04-09 06:39:01 +00:00
|
|
|
#endif
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_free_scb(ahc, scb, xs->flags);
|
1995-05-30 08:16:23 +00:00
|
|
|
scsi_done(xs);
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start the board, ready for normal operation
|
|
|
|
*/
|
1995-11-05 04:50:55 +00:00
|
|
|
int
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_init(ahc)
|
|
|
|
struct ahc_data *ahc;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1995-01-13 02:24:31 +00:00
|
|
|
u_long iobase = ahc->baseport;
|
1996-01-03 06:32:12 +00:00
|
|
|
u_char scsi_conf, sblkctl, i;
|
|
|
|
int max_targ = 15;
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
1995-11-05 04:50:55 +00:00
|
|
|
* Assume we have a board at this stage and it has been reset.
|
1994-11-17 20:22:31 +00:00
|
|
|
*/
|
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
/* Determine channel configuration and who we are on the scsi bus. */
|
1995-07-04 21:14:45 +00:00
|
|
|
switch ( (sblkctl = inb(SBLKCTL + iobase) & 0x0a) ) {
|
1994-11-17 20:22:31 +00:00
|
|
|
case 0:
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->our_id = (inb(SCSICONF + iobase) & HSCSIID);
|
1995-09-05 23:52:03 +00:00
|
|
|
if(ahc->type == AHC_394)
|
|
|
|
printf("Channel %c, SCSI Id=%d, ",
|
|
|
|
ahc->flags & AHC_CHNLB ? 'B' : 'A',
|
|
|
|
ahc->our_id);
|
|
|
|
else
|
|
|
|
printf("Single Channel, SCSI Id=%d, ", ahc->our_id);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(FLAGS + iobase, SINGLE_BUS);
|
1994-11-17 20:22:31 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->our_id = (inb(SCSICONF + 1 + iobase) & HWSCSIID);
|
1995-09-05 23:52:03 +00:00
|
|
|
if(ahc->type == AHC_394)
|
|
|
|
printf("Wide Channel %c, SCSI Id=%d, ",
|
|
|
|
ahc->flags & AHC_CHNLB ? 'B' : 'A',
|
|
|
|
ahc->our_id);
|
|
|
|
else
|
|
|
|
printf("Wide Channel, SCSI Id=%d, ", ahc->our_id);
|
1995-02-22 01:43:25 +00:00
|
|
|
ahc->type |= AHC_WIDE;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(FLAGS + iobase, WIDE_BUS);
|
1994-11-17 20:22:31 +00:00
|
|
|
break;
|
|
|
|
case 8:
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->our_id = (inb(SCSICONF + iobase) & HSCSIID);
|
|
|
|
ahc->our_id_b = (inb(SCSICONF + 1 + iobase) & HSCSIID);
|
1995-01-13 02:24:31 +00:00
|
|
|
printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, ",
|
|
|
|
ahc->our_id, ahc->our_id_b);
|
1995-02-22 01:43:25 +00:00
|
|
|
ahc->type |= AHC_TWIN;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(FLAGS + iobase, TWIN_BUS);
|
1994-11-17 20:22:31 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf(" Unsupported adapter type. Ignoring\n");
|
|
|
|
return(-1);
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
printf("%d SCBs\n", ahc->maxscbs);
|
1995-04-27 17:47:17 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWMISC) {
|
|
|
|
struct scb test;
|
|
|
|
printf("ahc%d: hardware scb %ld bytes; kernel scb; "
|
|
|
|
"ahc_dma %d bytes\n",
|
|
|
|
ahc->unit, (u_long)&(test.next) - (u_long)(&test),
|
|
|
|
sizeof(test),
|
|
|
|
sizeof(struct ahc_dma_seg));
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
#endif /* AHC_DEBUG */
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-10-26 23:57:18 +00:00
|
|
|
/* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
|
1996-01-03 06:32:12 +00:00
|
|
|
if(ahc->type & AHC_TWIN)
|
1995-01-22 00:48:39 +00:00
|
|
|
{
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-01-22 00:48:39 +00:00
|
|
|
* The device is gated to channel B after a chip reset,
|
|
|
|
* so set those values first
|
|
|
|
*/
|
|
|
|
outb(SCSIID + iobase, ahc->our_id_b);
|
1996-01-03 06:32:12 +00:00
|
|
|
scsi_conf = inb(SCSICONF + 1 + iobase) & (ENSPCHK|STIMESEL);
|
1995-01-22 00:48:39 +00:00
|
|
|
outb(SXFRCTL1 + iobase, scsi_conf|ENSTIMER|ACTNEGEN|STPWEN);
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SIMODE1 + iobase, ENSELTIMO);
|
1995-10-26 23:57:18 +00:00
|
|
|
if(ahc->type & AHC_ULTRA)
|
1995-10-29 05:57:48 +00:00
|
|
|
outb(SXFRCTL0 + iobase, DFON|SPIOEN|ULTRAEN);
|
|
|
|
else
|
|
|
|
outb(SXFRCTL0 + iobase, DFON|SPIOEN);
|
1995-07-31 08:25:36 +00:00
|
|
|
|
|
|
|
/* Reset the bus */
|
|
|
|
outb(SCSISEQ + iobase, SCSIRSTO);
|
1995-09-05 23:52:03 +00:00
|
|
|
DELAY(1000);
|
1995-07-31 08:25:36 +00:00
|
|
|
outb(SCSISEQ + iobase, 0);
|
|
|
|
|
1995-01-22 00:48:39 +00:00
|
|
|
/* Select Channel A */
|
|
|
|
outb(SBLKCTL + iobase, 0);
|
|
|
|
}
|
|
|
|
outb(SCSIID + iobase, ahc->our_id);
|
1996-01-03 06:32:12 +00:00
|
|
|
scsi_conf = inb(SCSICONF + iobase) & (ENSPCHK|STIMESEL);
|
1995-01-22 00:48:39 +00:00
|
|
|
outb(SXFRCTL1 + iobase, scsi_conf|ENSTIMER|ACTNEGEN|STPWEN);
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SIMODE1 + iobase, ENSELTIMO);
|
1995-10-26 23:57:18 +00:00
|
|
|
if(ahc->type & AHC_ULTRA)
|
1995-10-29 05:57:48 +00:00
|
|
|
outb(SXFRCTL0 + iobase, DFON|SPIOEN|ULTRAEN);
|
|
|
|
else
|
|
|
|
outb(SXFRCTL0 + iobase, DFON|SPIOEN);
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-07-31 08:25:36 +00:00
|
|
|
/* Reset the bus */
|
|
|
|
outb(SCSISEQ + iobase, SCSIRSTO);
|
1995-09-05 23:52:03 +00:00
|
|
|
DELAY(1000);
|
1995-07-31 08:25:36 +00:00
|
|
|
outb(SCSISEQ + iobase, 0);
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
1994-12-31 19:31:56 +00:00
|
|
|
* Look at the information that board initialization or
|
|
|
|
* the board bios has left us. In the lower four bits of each
|
|
|
|
* target's scratch space any value other than 0 indicates
|
1995-05-30 08:16:23 +00:00
|
|
|
* that we should initiate syncronous transfers. If it's zero,
|
|
|
|
* the user or the BIOS has decided to disable syncronous
|
1996-01-03 06:32:12 +00:00
|
|
|
* negotiation to that target so we don't activate the needsdtr
|
1994-12-31 19:31:56 +00:00
|
|
|
* flag.
|
|
|
|
*/
|
1995-01-16 16:33:47 +00:00
|
|
|
ahc->needsdtr_orig = 0;
|
|
|
|
ahc->needwdtr_orig = 0;
|
1995-07-04 21:14:45 +00:00
|
|
|
|
|
|
|
/* Grab the disconnection disable table and invert it for our needs */
|
1996-01-03 06:32:12 +00:00
|
|
|
if(ahc->flags & AHC_USEDEFAULTS) {
|
1995-07-04 21:14:45 +00:00
|
|
|
printf("ahc%d: Host Adapter Bios disabled. Using default SCSI "
|
1996-01-03 06:32:12 +00:00
|
|
|
"device parameters\n", ahc->unit);
|
1995-07-04 21:14:45 +00:00
|
|
|
ahc->discenable = 0xff;
|
|
|
|
}
|
|
|
|
else
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc->discenable = ~((inb(DISC_DSB + iobase + 1) << 8)
|
|
|
|
| inb(DISC_DSB + iobase));
|
1995-07-04 21:14:45 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
if(!(ahc->type & (AHC_WIDE|AHC_TWIN)))
|
1995-09-05 23:52:03 +00:00
|
|
|
max_targ = 7;
|
1995-03-31 13:54:41 +00:00
|
|
|
|
1995-09-05 23:52:03 +00:00
|
|
|
for(i = 0; i <= max_targ; i++){
|
1995-07-04 21:14:45 +00:00
|
|
|
u_char target_settings;
|
1996-01-03 06:32:12 +00:00
|
|
|
if (ahc->flags & AHC_USEDEFAULTS) {
|
1995-07-04 21:14:45 +00:00
|
|
|
target_settings = 0; /* 10MHz */
|
|
|
|
ahc->needsdtr_orig |= (0x01 << i);
|
1995-01-16 16:33:47 +00:00
|
|
|
ahc->needwdtr_orig |= (0x01 << i);
|
1995-07-04 21:14:45 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Take the settings leftover in scratch RAM. */
|
1996-01-03 06:32:12 +00:00
|
|
|
target_settings = inb(TARG_SCRATCH + i + iobase);
|
1995-07-04 21:14:45 +00:00
|
|
|
|
|
|
|
if(target_settings & 0x0f){
|
|
|
|
ahc->needsdtr_orig |= (0x01 << i);
|
|
|
|
/*Default to a asyncronous transfers(0 offset)*/
|
|
|
|
target_settings &= 0xf0;
|
|
|
|
}
|
|
|
|
if(target_settings & 0x80){
|
|
|
|
ahc->needwdtr_orig |= (0x01 << i);
|
|
|
|
/*
|
|
|
|
* We'll set the Wide flag when we
|
1996-01-03 06:32:12 +00:00
|
|
|
* are successful with Wide negotiation.
|
|
|
|
* Turn it off for now so we aren't
|
1995-07-04 21:14:45 +00:00
|
|
|
* confused.
|
|
|
|
*/
|
|
|
|
target_settings &= 0x7f;
|
|
|
|
}
|
1995-01-13 02:24:31 +00:00
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(TARG_SCRATCH+i+iobase,target_settings);
|
1994-12-31 19:31:56 +00:00
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-02-22 01:43:25 +00:00
|
|
|
* If we are not a WIDE device, forget WDTR. This
|
|
|
|
* makes the driver work on some cards that don't
|
|
|
|
* leave these fields cleared when the BIOS is not
|
|
|
|
* installed.
|
|
|
|
*/
|
|
|
|
if(!(ahc->type & AHC_WIDE))
|
|
|
|
ahc->needwdtr_orig = 0;
|
1995-03-07 08:59:28 +00:00
|
|
|
ahc->needsdtr = ahc->needsdtr_orig;
|
1995-01-16 16:33:47 +00:00
|
|
|
ahc->needwdtr = ahc->needwdtr_orig;
|
1995-02-03 17:15:12 +00:00
|
|
|
ahc->sdtrpending = 0;
|
|
|
|
ahc->wdtrpending = 0;
|
1995-02-22 01:43:25 +00:00
|
|
|
ahc->tagenable = 0;
|
1995-03-17 23:58:27 +00:00
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
/* How did we do? */
|
|
|
|
if(ahc_debug & AHC_SHOWMISC)
|
|
|
|
printf("NEEDSDTR == 0x%x\nNEEDWDTR == 0x%x\n"
|
|
|
|
"DISCENABLE == 0x%x\n", ahc->needsdtr,
|
|
|
|
ahc->needwdtr, ahc->discenable);
|
|
|
|
#endif
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-04-27 17:47:17 +00:00
|
|
|
* Clear the control byte for every SCB so that the sequencer
|
|
|
|
* doesn't get confused and think that one of them is valid
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1995-04-27 17:47:17 +00:00
|
|
|
for(i = 0; i < ahc->maxscbs; i++) {
|
|
|
|
outb(SCBPTR + iobase, i);
|
|
|
|
outb(SCBARRAY + iobase, 0);
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-12-31 19:31:56 +00:00
|
|
|
/*
|
1995-01-13 02:24:31 +00:00
|
|
|
* Set the number of availible SCBs
|
1994-11-17 20:22:31 +00:00
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(SCBCOUNT + iobase, ahc->maxscbs);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 2s compliment of SCBCOUNT
|
|
|
|
*/
|
|
|
|
i = ahc->maxscbs;
|
|
|
|
outb(COMP_SCBCOUNT + iobase, -i & 0xff);
|
1994-12-31 19:31:56 +00:00
|
|
|
|
|
|
|
/* We don't have any busy targets right now */
|
1996-01-03 06:32:12 +00:00
|
|
|
outb( ACTIVE_A + iobase, 0 );
|
|
|
|
outb( ACTIVE_B + iobase, 0 );
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-04-27 17:47:17 +00:00
|
|
|
/* We don't have any waiting selections */
|
|
|
|
outb( WAITING_SCBH + iobase, SCB_LIST_NULL );
|
|
|
|
outb( WAITING_SCBT + iobase, SCB_LIST_NULL );
|
1996-01-03 06:32:12 +00:00
|
|
|
|
|
|
|
/* Message out buffer starts empty */
|
|
|
|
outb(MSG_LEN + iobase, 0x00);
|
|
|
|
|
1995-03-07 08:59:28 +00:00
|
|
|
/*
|
1996-01-03 06:32:12 +00:00
|
|
|
* Load the Sequencer program and Enable the adapter
|
|
|
|
* in "fast" mode.
|
1995-03-07 08:59:28 +00:00
|
|
|
*/
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose)
|
1996-01-03 06:32:12 +00:00
|
|
|
printf("ahc%d: Downloading Sequencer Program...", ahc->unit);
|
|
|
|
|
1995-03-07 08:59:28 +00:00
|
|
|
ahc_loadseq(iobase);
|
1996-01-03 06:32:12 +00:00
|
|
|
|
1995-09-05 23:52:03 +00:00
|
|
|
if(bootverbose)
|
|
|
|
printf("Done\n");
|
1995-03-07 08:59:28 +00:00
|
|
|
|
|
|
|
outb(SEQCTL + iobase, FASTMODE);
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that we are going and return (to probe)
|
|
|
|
*/
|
1995-01-13 02:24:31 +00:00
|
|
|
ahc->flags = AHC_INIT;
|
1994-11-17 20:22:31 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1994-11-17 20:22:31 +00:00
|
|
|
ahcminphys(bp)
|
1995-05-30 08:16:23 +00:00
|
|
|
struct buf *bp;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-01-13 02:24:31 +00:00
|
|
|
* Even though the card can transfer up to 16megs per command
|
1994-11-17 20:22:31 +00:00
|
|
|
* we are limited by the number of segments in the dma segment
|
|
|
|
* list that we can hold. The worst case is that all pages are
|
|
|
|
* discontinuous physically, hense the "page per segment" limit
|
|
|
|
* enforced here.
|
|
|
|
*/
|
|
|
|
if (bp->b_bcount > ((AHC_NSEG - 1) * PAGESIZ)) {
|
|
|
|
bp->b_bcount = ((AHC_NSEG - 1) * PAGESIZ);
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* start a scsi operation given the command and
|
1995-05-30 08:16:23 +00:00
|
|
|
* the data address, target, and lun all of which
|
1994-11-17 20:22:31 +00:00
|
|
|
* are stored in the scsi_xfer struct
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static int32
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_scsi_cmd(xs)
|
|
|
|
struct scsi_xfer *xs;
|
|
|
|
{
|
1996-01-07 19:24:36 +00:00
|
|
|
struct scb *scb;
|
|
|
|
struct ahc_dma_seg *sg;
|
1994-11-17 20:22:31 +00:00
|
|
|
int seg; /* scatter gather seg being worked on */
|
1995-05-30 08:16:23 +00:00
|
|
|
int thiskv;
|
1994-11-17 20:22:31 +00:00
|
|
|
physaddr thisphys, nextphys;
|
|
|
|
int bytes_this_seg, bytes_this_page, datalen, flags;
|
1996-01-07 19:24:36 +00:00
|
|
|
struct ahc_data *ahc;
|
|
|
|
u_short mask;
|
1994-11-17 20:22:31 +00:00
|
|
|
int s;
|
1995-01-13 02:24:31 +00:00
|
|
|
|
1996-01-07 19:24:36 +00:00
|
|
|
ahc = (struct ahc_data *)xs->sc_link->adapter_softc;
|
|
|
|
mask = (0x01 << (xs->sc_link->target
|
|
|
|
| ((u_long)xs->sc_link->fordriver & 0x08)));
|
1994-11-17 20:22:31 +00:00
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahc_scsi_cmd\n"));
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1994-11-17 20:22:31 +00:00
|
|
|
* get an scb 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 (flags & ITSDONE) {
|
1996-01-07 19:24:36 +00:00
|
|
|
printf("ahc%d: Already done?", ahc->unit);
|
1995-05-30 08:16:23 +00:00
|
|
|
xs->flags &= ~ITSDONE;
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
if (!(flags & INUSE)) {
|
1996-01-07 19:24:36 +00:00
|
|
|
printf("ahc%d: Not in use?", ahc->unit);
|
1994-11-17 20:22:31 +00:00
|
|
|
xs->flags |= INUSE;
|
|
|
|
}
|
1996-01-03 06:32:12 +00:00
|
|
|
if (!(scb = ahc_get_scb(ahc, flags))) {
|
1994-11-17 20:22:31 +00:00
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
|
|
return (TRY_AGAIN_LATER);
|
|
|
|
}
|
1995-05-11 19:26:53 +00:00
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3, ("start scb(%p)\n", scb));
|
1994-11-17 20:22:31 +00:00
|
|
|
scb->xs = xs;
|
1995-05-30 08:16:23 +00:00
|
|
|
if (flags & SCSI_RESET)
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
scb->flags |= SCB_DEVICE_RESET|SCB_IMMED;
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* Put all the arguments for the xfer in the scb
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1995-02-22 01:43:25 +00:00
|
|
|
|
1995-03-31 13:54:41 +00:00
|
|
|
if(ahc->tagenable & mask)
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->control |= TAG_ENB;
|
1995-07-04 21:14:45 +00:00
|
|
|
if(ahc->discenable & mask)
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->control |= DISCENB;
|
1995-05-30 08:16:23 +00:00
|
|
|
if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask))
|
1995-02-03 17:15:12 +00:00
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->control |= NEEDWDTR;
|
1995-02-22 01:43:25 +00:00
|
|
|
ahc->wdtrpending |= mask;
|
1995-02-03 17:15:12 +00:00
|
|
|
}
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask))
|
1995-02-03 17:15:12 +00:00
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->control |= NEEDSDTR;
|
1995-02-22 01:43:25 +00:00
|
|
|
ahc->sdtrpending |= mask;
|
1995-02-03 17:15:12 +00:00
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
scb->target_channel_lun = ((xs->sc_link->target << 4) & 0xF0) |
|
1994-12-31 19:31:56 +00:00
|
|
|
((u_long)xs->sc_link->fordriver & 0x08) |
|
1995-03-07 08:59:28 +00:00
|
|
|
(xs->sc_link->lun & 0x07);
|
1994-11-17 20:22:31 +00:00
|
|
|
scb->cmdlen = xs->cmdlen;
|
|
|
|
scb->cmdpointer = KVTOPHYS(xs->cmd);
|
1995-03-31 13:54:41 +00:00
|
|
|
xs->resid = 0;
|
1995-06-11 19:33:05 +00:00
|
|
|
xs->status = 0;
|
1994-11-17 20:22:31 +00:00
|
|
|
if (xs->datalen) { /* should use S/G only if not zero length */
|
1995-05-30 08:16:23 +00:00
|
|
|
scb->SG_list_pointer = KVTOPHYS(scb->ahc_dma);
|
1994-11-17 20:22:31 +00:00
|
|
|
sg = scb->ahc_dma;
|
|
|
|
seg = 0;
|
1995-05-30 08:16:23 +00:00
|
|
|
{
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* Set up the scatter gather block
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1994-11-17 20:22:31 +00:00
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB4,
|
1995-05-11 19:26:53 +00:00
|
|
|
("%ld @%p:- ", xs->datalen, xs->data));
|
1994-11-17 20:22:31 +00:00
|
|
|
datalen = xs->datalen;
|
|
|
|
thiskv = (int) xs->data;
|
|
|
|
thisphys = KVTOPHYS(thiskv);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
while ((datalen) && (seg < AHC_NSEG)) {
|
|
|
|
bytes_this_seg = 0;
|
|
|
|
|
|
|
|
/* put in the base address */
|
|
|
|
sg->addr = thisphys;
|
|
|
|
|
1995-05-11 19:26:53 +00:00
|
|
|
SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%lx",
|
1995-05-30 08:16:23 +00:00
|
|
|
thisphys));
|
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/* do it at least once */
|
|
|
|
nextphys = thisphys;
|
|
|
|
while ((datalen) && (thisphys == nextphys)) {
|
|
|
|
/*
|
1995-05-30 08:16:23 +00:00
|
|
|
* This page is contiguous (physically)
|
|
|
|
* with the the last, just extend the
|
1994-11-17 20:22:31 +00:00
|
|
|
* length
|
|
|
|
*/
|
|
|
|
/* 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
|
1995-05-30 08:16:23 +00:00
|
|
|
,datalen);
|
1994-11-17 20:22:31 +00:00
|
|
|
bytes_this_seg += bytes_this_page;
|
|
|
|
datalen -= bytes_this_page;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/* 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,
|
1995-05-30 08:16:23 +00:00
|
|
|
("(0x%x)", bytes_this_seg));
|
1994-11-17 20:22:31 +00:00
|
|
|
sg->len = bytes_this_seg;
|
|
|
|
sg++;
|
|
|
|
seg++;
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
|
|
|
} /*end of iov/kv decision */
|
1994-11-17 20:22:31 +00:00
|
|
|
scb->SG_segment_count = seg;
|
1995-07-04 21:14:45 +00:00
|
|
|
|
|
|
|
/* Copy the first SG into the data pointer area */
|
|
|
|
scb->data = scb->ahc_dma->addr;
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->datalen = scb->ahc_dma->len;
|
1994-11-17 20:22:31 +00:00
|
|
|
SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
|
1995-07-04 21:14:45 +00:00
|
|
|
if (datalen) {
|
|
|
|
/* there's still data, must have run out of segs! */
|
1994-11-17 20:22:31 +00:00
|
|
|
printf("ahc_scsi_cmd%d: more than %d DMA segs\n",
|
1996-01-07 19:24:36 +00:00
|
|
|
ahc->unit, AHC_NSEG);
|
1994-11-17 20:22:31 +00:00
|
|
|
xs->error = XS_DRIVER_STUFFUP;
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_free_scb(ahc, scb, flags);
|
1994-11-17 20:22:31 +00:00
|
|
|
return (HAD_ERROR);
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
else {
|
|
|
|
/*
|
1995-05-30 08:16:23 +00:00
|
|
|
* No data xfer, use non S/G values
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
*/
|
|
|
|
scb->SG_segment_count = 0;
|
|
|
|
scb->SG_list_pointer = 0;
|
1995-07-04 21:14:45 +00:00
|
|
|
scb->data = 0;
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->datalen = 0;
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-02-22 01:43:25 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-07-31 08:25:36 +00:00
|
|
|
if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARG))
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_print_scb(scb);
|
|
|
|
#endif
|
1996-01-29 03:17:39 +00:00
|
|
|
if (!(flags & SCSI_NOMASK)) {
|
|
|
|
s = splbio();
|
|
|
|
PAUSE_SEQUENCER(ahc);
|
|
|
|
ahc_send_scb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
|
|
|
|
outb(QINFIFO + ahc->baseport, scb->position);
|
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
timeout(ahc_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000);
|
|
|
|
splx(s);
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
|
|
|
|
return (SUCCESSFULLY_QUEUED);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If we can't use interrupts, poll for completion
|
|
|
|
*/
|
|
|
|
s = splbio();
|
|
|
|
PAUSE_SEQUENCER(ahc);
|
|
|
|
ahc_send_scb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
|
|
|
|
outb(QINFIFO + ahc->baseport, scb->position);
|
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_wait\n"));
|
|
|
|
do {
|
|
|
|
if (ahc_poll(ahc, xs->timeout)) {
|
|
|
|
if (!(xs->flags & SCSI_SILENT))
|
|
|
|
printf("cmd fail\n");
|
|
|
|
ahc_scb_timeout(ahc, scb);
|
|
|
|
splx(s);
|
|
|
|
return (HAD_ERROR);
|
|
|
|
}
|
|
|
|
} while (!(xs->flags & ITSDONE)); /* a non command complete intr */
|
|
|
|
if (xs->error) {
|
|
|
|
splx(s);
|
|
|
|
return (HAD_ERROR);
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (COMPLETE);
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1994-11-17 20:22:31 +00:00
|
|
|
* Return some information to the caller about
|
1995-05-30 08:16:23 +00:00
|
|
|
* the adapter and it's capabilities.
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static u_int32
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_adapter_info(unit)
|
|
|
|
int unit;
|
|
|
|
{
|
1995-05-30 08:16:23 +00:00
|
|
|
return (2); /* 2 outstanding requests at a time per device */
|
|
|
|
}
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* A scb (and hence an scb entry on the board is put onto the
|
|
|
|
* free list.
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_free_scb(ahc, scb, flags)
|
|
|
|
struct ahc_data *ahc;
|
|
|
|
int flags;
|
1994-11-17 20:22:31 +00:00
|
|
|
struct scb *scb;
|
|
|
|
{
|
1995-04-23 22:04:58 +00:00
|
|
|
unsigned int opri;
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-04-23 22:04:58 +00:00
|
|
|
opri = splbio();
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1994-12-31 19:31:56 +00:00
|
|
|
scb->flags = SCB_FREE;
|
1994-11-17 20:22:31 +00:00
|
|
|
scb->next = ahc->free_scb;
|
|
|
|
ahc->free_scb = scb;
|
1995-04-23 22:04:58 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-05-30 08:16:23 +00:00
|
|
|
ahc->activescbs--;
|
1995-04-23 22:04:58 +00:00
|
|
|
#endif
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* If there were none, wake abybody waiting for
|
|
|
|
* one to come free, starting with queued entries
|
|
|
|
*/
|
|
|
|
if (!scb->next) {
|
|
|
|
wakeup((caddr_t)&ahc->free_scb);
|
|
|
|
}
|
1995-04-23 22:04:58 +00:00
|
|
|
splx(opri);
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* Get a free scb
|
|
|
|
* If there are none, see if we can allocate a
|
|
|
|
* new one. Otherwise either return an error or sleep
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static struct scb *
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_get_scb(ahc, flags)
|
|
|
|
struct ahc_data *ahc;
|
|
|
|
int flags;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1995-04-23 22:04:58 +00:00
|
|
|
unsigned opri;
|
1994-11-17 20:22:31 +00:00
|
|
|
struct scb *scbp;
|
|
|
|
|
1995-04-23 22:04:58 +00:00
|
|
|
opri = splbio();
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* If we can and have to, sleep waiting for one to come free
|
|
|
|
* but only if we can't allocate a new one.
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1994-11-17 20:22:31 +00:00
|
|
|
while (!(scbp = ahc->free_scb)) {
|
|
|
|
if (ahc->numscbs < ahc->maxscbs) {
|
|
|
|
scbp = (struct scb *) malloc(sizeof(struct scb),
|
|
|
|
M_TEMP, M_NOWAIT);
|
|
|
|
if (scbp) {
|
1995-01-16 16:33:47 +00:00
|
|
|
u_long iobase = ahc->baseport;
|
|
|
|
u_char curscb;
|
1995-04-27 17:47:17 +00:00
|
|
|
bzero(scbp, sizeof(struct scb));
|
1994-11-17 20:22:31 +00:00
|
|
|
scbp->position = ahc->numscbs;
|
1995-04-27 17:47:17 +00:00
|
|
|
ahc->numscbs++;
|
|
|
|
scbp->flags = SCB_ACTIVE;
|
1994-11-17 20:22:31 +00:00
|
|
|
/*
|
|
|
|
* Place in the scbarray
|
|
|
|
* Never is removed. Position
|
|
|
|
* in ahc->scbarray is the scbarray
|
|
|
|
* position on the board we will
|
|
|
|
* load it into.
|
|
|
|
*/
|
|
|
|
ahc->scbarray[scbp->position] = scbp;
|
|
|
|
} else {
|
1996-01-03 06:32:12 +00:00
|
|
|
printf("ahc%d: Can't malloc SCB\n", ahc->unit);
|
1995-04-23 22:04:58 +00:00
|
|
|
}
|
|
|
|
break;
|
1994-11-17 20:22:31 +00:00
|
|
|
} else {
|
|
|
|
if (!(flags & SCSI_NOSLEEP)) {
|
|
|
|
tsleep((caddr_t)&ahc->free_scb, PRIBIO,
|
|
|
|
"ahcscb", 0);
|
1995-04-23 22:04:58 +00:00
|
|
|
continue;
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
1995-04-23 22:04:58 +00:00
|
|
|
break;
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
1995-04-23 22:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (scbp) {
|
1994-11-17 20:22:31 +00:00
|
|
|
/* Get SCB from from free list */
|
|
|
|
ahc->free_scb = scbp->next;
|
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a
value above 15, the old code inadvertantly truncated it with an "& 0x0f".
If the periferal picked something really bad like 0x32, you'd end up with
an offset of 2 which would hang the drive since it didn't expect to ever
get something so low. We now do a MIN(maxoffset, given_offset).
2) In the case of Wide cards, we were turning on sync transfers after a
sucessfull wide negotiation. Now we leave the offset alone in the per
target scratch space (which implies asyncronous transfers since we initialize
it that way) until a syncronous negotation occurs.
3) We were advertizing a max offset of 15 instead of 8 for wide devices.
4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the
system because we would end up sending a null command to the sequencer. Now
we handle SCSI_RESET correctly by having the sequencer interrupt us when it
is about to fill the message buffer so that we can fill it in ourselves.
The sequencer will also "simulate" a command complete for these "message only"
SCBs so that the kernel driver can finish up properly. The cdplay utility
will send a "SCSI_REST" to the cdplayer if you use the reset command.
5) The code that handles SCSIINTs was broken in that if more than one type
of error was true at once, we'd do outbs without the card being paused.
The else clause after the busfree case was also an accident waiting to
happen. I've now turned this into an if, else if, else type of thing, since
in most cases when we handle one type of error, it should be okay to ignore
the rest (ie if we have a SELTO, who cares if there was a parity error on
the transaction?), but the section should really be rewritten after 2.0.5.
This fix was the least obtrusive way to patch the problem.
6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is
that I don't account for the case when an SCB that is tagged to do a particular
type of negotiation completes or SELTOs (selection timeout) without the
negotiation taking place, so the accounting of sdtrpending and wdtrpending
gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr,
it only performs wdtr (since wdtr must occur first and we spread out the
negotiation over two commands) so we always have sdtrpending set for that
target and we never do a real SDTR. I fill properly fix the accounting
after 2.0.5 goes out the door, but this works (as confirmed by Dan) on
wide targets.
Other stuff that is also included:
1) Don't do a bzero when recycling SCBs. The only thing that must explicitly
be set to zero is the scb control byte which is done in ahc_get_scb. We also
need to set the SG_list_pointer and SG_list_count to 0 for commands that do
not transfer data.
2) Mask the interrupt type printout for the aic7870 case. The bit we were
using to determine interrupt type is only valid for the aic7770.
Submitted by: Justin Gibbs
1995-05-17 07:06:02 +00:00
|
|
|
scbp->control = 0;
|
1994-11-17 20:22:31 +00:00
|
|
|
scbp->flags = SCB_ACTIVE;
|
1995-04-23 22:04:58 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
ahc->activescbs++;
|
1996-01-03 06:32:12 +00:00
|
|
|
if((ahc_debug & AHC_SHOWSCBCNT)
|
1995-07-31 08:25:36 +00:00
|
|
|
&& (ahc->activescbs == ahc->maxscbs))
|
1996-01-03 06:32:12 +00:00
|
|
|
printf("ahc%d: Max SCBs active\n", ahc->unit);
|
1995-04-23 22:04:58 +00:00
|
|
|
#endif
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
1995-04-23 22:04:58 +00:00
|
|
|
|
|
|
|
splx(opri);
|
1994-11-17 20:22:31 +00:00
|
|
|
|
|
|
|
return (scbp);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void ahc_loadseq(iobase)
|
1995-01-13 02:24:31 +00:00
|
|
|
u_long iobase;
|
1994-11-17 20:22:31 +00:00
|
|
|
{
|
1994-11-18 09:14:14 +00:00
|
|
|
static unsigned char seqprog[] = {
|
1995-01-13 02:24:31 +00:00
|
|
|
# include "aic7xxx_seq.h"
|
1994-11-18 09:14:14 +00:00
|
|
|
};
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(SEQCTL + iobase, PERRORDIS|SEQRESET|LOADRAM);
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
outsb(SEQRAM + iobase, seqprog, sizeof(seqprog));
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(SEQCTL + iobase, FASTMODE|SEQRESET);
|
1994-11-17 20:22:31 +00:00
|
|
|
do {
|
1995-01-13 02:24:31 +00:00
|
|
|
outb(SEQCTL + iobase, SEQRESET|FASTMODE);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1995-01-13 02:24:31 +00:00
|
|
|
} while (inb(SEQADDR0 + iobase) != 0 &&
|
1995-05-30 08:16:23 +00:00
|
|
|
inb(SEQADDR1 + iobase != 0));
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
1996-01-29 03:17:39 +00:00
|
|
|
/*
|
|
|
|
* Function to poll for command completion when
|
|
|
|
* interrupts are disabled (crash dumps)
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ahc_poll(ahc, wait)
|
|
|
|
struct ahc_data *ahc;
|
|
|
|
int wait; /* in msec */
|
|
|
|
{
|
|
|
|
u_long iobase = ahc->baseport;
|
|
|
|
u_long stport = INTSTAT + iobase;
|
|
|
|
|
|
|
|
while (--wait) {
|
|
|
|
DELAY(1000);
|
|
|
|
if (inb(stport) & INT_PEND)
|
|
|
|
break;
|
|
|
|
} if (wait == 0) {
|
|
|
|
printf("ahc%d: board not responding\n", ahc->unit);
|
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
ahc_intr((void *)ahc);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_scb_timeout(ahc, scb)
|
1994-12-31 19:31:56 +00:00
|
|
|
struct ahc_data *ahc;
|
|
|
|
struct scb *scb;
|
|
|
|
{
|
1995-01-13 02:24:31 +00:00
|
|
|
u_long iobase = ahc->baseport;
|
1994-12-31 19:31:56 +00:00
|
|
|
int found = 0;
|
1995-07-31 08:25:36 +00:00
|
|
|
char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
|
1994-12-31 19:31:56 +00:00
|
|
|
|
|
|
|
/*
|
1995-07-31 08:25:36 +00:00
|
|
|
* Ensure that the card doesn't do anything
|
|
|
|
* behind our back.
|
1994-12-31 19:31:56 +00:00
|
|
|
*/
|
1995-07-31 08:25:36 +00:00
|
|
|
PAUSE_SEQUENCER(ahc);
|
1994-12-31 19:31:56 +00:00
|
|
|
|
|
|
|
/*
|
1995-07-31 08:25:36 +00:00
|
|
|
* First, determine if we want to do a bus
|
|
|
|
* reset or simply a bus device reset.
|
|
|
|
* If this is the first time that a transaction
|
|
|
|
* has timed out, just schedule a bus device
|
|
|
|
* reset. Otherwise, we reset the bus and
|
|
|
|
* abort all pending I/Os on that bus.
|
1994-12-31 19:31:56 +00:00
|
|
|
*/
|
1995-07-31 08:25:36 +00:00
|
|
|
if(scb->flags & SCB_ABORTED)
|
|
|
|
{
|
1994-12-31 19:31:56 +00:00
|
|
|
/*
|
1995-07-31 08:25:36 +00:00
|
|
|
* Been down this road before.
|
|
|
|
* Do a full bus reset.
|
1994-12-31 19:31:56 +00:00
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
found = ahc_reset_channel(ahc, channel, scb->position,
|
1995-07-31 08:25:36 +00:00
|
|
|
XS_TIMEOUT);
|
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWABORTS)
|
|
|
|
printf("ahc%d: Issued Channel %c Bus Reset #1. "
|
1996-01-03 06:32:12 +00:00
|
|
|
"%d SCBs aborted\n", ahc->unit, channel, found);
|
1995-07-31 08:25:36 +00:00
|
|
|
#endif
|
1995-04-01 19:53:04 +00:00
|
|
|
}
|
1995-07-31 08:25:36 +00:00
|
|
|
else {
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-07-31 08:25:36 +00:00
|
|
|
* Send a Bus Device Reset Message:
|
|
|
|
* The target we select to send the message to may
|
|
|
|
* be entirely different than the target pointed to
|
|
|
|
* by the scb that timed out. If the command is
|
|
|
|
* in the QINFIFO or the waiting for selection list,
|
|
|
|
* its not tying up the bus and isn't responsible
|
|
|
|
* for the delay so we pick off the active command
|
|
|
|
* which should be the SCB selected by SCBPTR. If
|
|
|
|
* its disconnected or active, we device reset the
|
|
|
|
* target scbp points to. Although it may be that
|
|
|
|
* this target is not responsible for the delay, it
|
|
|
|
* may also be that we're timing out on a command that
|
|
|
|
* just takes too much time, so we try the bus device
|
|
|
|
* reset there first.
|
1994-12-31 19:31:56 +00:00
|
|
|
*/
|
1995-07-31 08:25:36 +00:00
|
|
|
u_char active_scb, control;
|
|
|
|
struct scb *active_scbp;
|
|
|
|
active_scb = inb(SCBPTR + iobase);
|
|
|
|
active_scbp = ahc->scbarray[active_scb];
|
|
|
|
control = inb(SCBARRAY + iobase);
|
|
|
|
|
|
|
|
/* Test to see if scbp is disconnected */
|
|
|
|
outb(SCBPTR + iobase, scb->position);
|
1996-01-03 06:32:12 +00:00
|
|
|
if(inb(SCB_CONTROL + iobase) & DISCONNECTED) {
|
1995-07-31 08:25:36 +00:00
|
|
|
scb->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
|
|
|
|
scb->SG_segment_count = 0;
|
|
|
|
scb->SG_list_pointer = 0;
|
|
|
|
scb->data = 0;
|
1996-01-03 06:32:12 +00:00
|
|
|
scb->datalen = 0;
|
1995-07-31 08:25:36 +00:00
|
|
|
outb(SCBCNT + iobase, 0x80);
|
1996-01-03 06:32:12 +00:00
|
|
|
outsb(SCBARRAY+iobase,scb,SCB_PIO_TRANSFER_SIZE);
|
1995-07-31 08:25:36 +00:00
|
|
|
outb(SCBCNT + iobase, 0);
|
|
|
|
ahc_add_waiting_scb(iobase, scb, list_second);
|
1995-10-26 23:57:18 +00:00
|
|
|
timeout(ahc_timeout, (caddr_t)scb, (2 * hz));
|
1995-07-31 08:25:36 +00:00
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWABORTS) {
|
|
|
|
sc_print_addr(scb->xs->sc_link);
|
|
|
|
printf("BUS DEVICE RESET message queued.\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
1994-12-31 19:31:56 +00:00
|
|
|
}
|
1995-07-31 08:25:36 +00:00
|
|
|
/* Is the active SCB really active? */
|
1996-01-29 03:17:39 +00:00
|
|
|
else if(active_scbp->flags & SCB_ACTIVE) {
|
|
|
|
/* XXX && (control & NEEDDMA) == NEEDDMA) { */
|
1996-01-03 06:32:12 +00:00
|
|
|
u_char msg_len = inb(MSG_LEN + iobase);
|
|
|
|
if(msg_len) {
|
1995-07-31 08:25:36 +00:00
|
|
|
/*
|
|
|
|
* If we're in a message phase, tacking on
|
|
|
|
* another message may confuse the target totally.
|
|
|
|
* The bus is probably wedged, so reset the
|
|
|
|
* channel.
|
|
|
|
*/
|
|
|
|
channel = (active_scbp->target_channel_lun & SELBUSB)
|
|
|
|
? 'B': 'A';
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_reset_channel(ahc, channel, scb->position,
|
1995-07-31 08:25:36 +00:00
|
|
|
XS_TIMEOUT);
|
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWABORTS)
|
|
|
|
printf("ahc%d: Issued Channel %c Bus Reset #2. "
|
1996-01-03 06:32:12 +00:00
|
|
|
"%d SCBs aborted\n", ahc->unit, channel,
|
1995-07-31 08:25:36 +00:00
|
|
|
found);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Load the message buffer and assert attention.
|
|
|
|
*/
|
|
|
|
active_scbp->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
|
|
|
|
if(active_scbp != scb)
|
|
|
|
untimeout(ahc_timeout, (caddr_t)active_scbp);
|
1995-10-26 23:57:18 +00:00
|
|
|
timeout(ahc_timeout, (caddr_t)active_scbp, (2 * hz));
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(MSG_LEN + iobase, 1);
|
|
|
|
outb(MSG0 + iobase, MSG_BUS_DEVICE_RESET);
|
1995-07-31 08:25:36 +00:00
|
|
|
if(active_scbp->target_channel_lun
|
|
|
|
!= scb->target_channel_lun) {
|
|
|
|
/* Give scb a new lease on life */
|
|
|
|
timeout(ahc_timeout, (caddr_t)scb,
|
|
|
|
(scb->xs->timeout * hz) / 1000);
|
|
|
|
}
|
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWABORTS) {
|
|
|
|
sc_print_addr(active_scbp->xs->sc_link);
|
|
|
|
printf("BUS DEVICE RESET message queued.\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
}
|
1994-12-31 19:31:56 +00:00
|
|
|
}
|
1995-07-31 08:25:36 +00:00
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* No active command to single out, so reset
|
|
|
|
* the bus for the timed out target.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_reset_channel(ahc, channel, scb->position,
|
1995-07-31 08:25:36 +00:00
|
|
|
XS_TIMEOUT);
|
|
|
|
#ifdef AHC_DEBUG
|
|
|
|
if(ahc_debug & AHC_SHOWABORTS)
|
|
|
|
printf("ahc%d: Issued Channel %c Bus Reset #3. "
|
1996-01-03 06:32:12 +00:00
|
|
|
"%d SCBs aborted\n", ahc->unit, channel,
|
1995-07-31 08:25:36 +00:00
|
|
|
found);
|
|
|
|
#endif
|
1994-12-31 19:31:56 +00:00
|
|
|
}
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
1994-12-31 19:31:56 +00:00
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1994-11-17 20:22:31 +00:00
|
|
|
ahc_timeout(void *arg1)
|
1995-05-30 08:16:23 +00:00
|
|
|
{
|
1996-01-03 06:32:12 +00:00
|
|
|
struct scb *scb = (struct scb *)arg1;
|
|
|
|
struct ahc_data *ahc;
|
|
|
|
int s;
|
|
|
|
|
1995-10-26 23:57:18 +00:00
|
|
|
s = splhigh();
|
|
|
|
|
|
|
|
if (!(scb->flags & SCB_ACTIVE)) {
|
|
|
|
/* Previous timeout took care of me already */
|
|
|
|
splx(s);
|
|
|
|
return;
|
|
|
|
}
|
1995-09-05 23:52:03 +00:00
|
|
|
|
1996-01-07 19:24:36 +00:00
|
|
|
ahc = (struct ahc_data *)scb->xs->sc_link->adapter_softc;
|
|
|
|
printf("ahc%d: target %d, lun %d (%s%d) timed out\n", ahc->unit
|
1995-09-05 23:52:03 +00:00
|
|
|
,scb->xs->sc_link->target
|
|
|
|
,scb->xs->sc_link->lun
|
|
|
|
,scb->xs->sc_link->device->name
|
|
|
|
,scb->xs->sc_link->dev_unit);
|
1995-04-15 21:37:32 +00:00
|
|
|
#ifdef SCSIDEBUG
|
1995-10-26 23:57:18 +00:00
|
|
|
show_scsi_cmd(scb->xs);
|
1995-03-17 23:58:27 +00:00
|
|
|
#endif
|
1995-04-15 21:37:32 +00:00
|
|
|
#ifdef AHC_DEBUG
|
1995-10-26 23:57:18 +00:00
|
|
|
if (ahc_debug & AHC_SHOWSCBS)
|
|
|
|
ahc_print_active_scb(ahc);
|
1995-02-22 01:43:25 +00:00
|
|
|
#endif /*AHC_DEBUG */
|
1994-11-17 20:22:31 +00:00
|
|
|
|
1995-10-26 23:57:18 +00:00
|
|
|
/*
|
|
|
|
* If it's immediate, don't try to abort it
|
|
|
|
*/
|
|
|
|
if (scb->flags & SCB_IMMED) {
|
|
|
|
scb->xs->retries = 0; /* I MEAN IT ! */
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done(ahc, scb);
|
1995-10-26 23:57:18 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* abort the operation that has timed out */
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_scb_timeout(ahc, scb);
|
1995-09-05 23:52:03 +00:00
|
|
|
}
|
1995-10-26 23:57:18 +00:00
|
|
|
splx(s);
|
1994-11-17 20:22:31 +00:00
|
|
|
}
|
|
|
|
|
1995-07-31 08:25:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The device at the given target/channel has been reset. Abort
|
|
|
|
* all active and queued scbs for that target/channel.
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static int
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
|
1995-07-31 08:25:36 +00:00
|
|
|
struct ahc_data *ahc;
|
|
|
|
int target;
|
|
|
|
char channel;
|
|
|
|
u_char timedout_scb;
|
|
|
|
u_int32 xs_error;
|
|
|
|
{
|
|
|
|
u_long iobase = ahc->baseport;
|
|
|
|
struct scb *scbp;
|
|
|
|
u_char active_scb;
|
|
|
|
int i = 0;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
/* restore this when we're done */
|
|
|
|
active_scb = inb(SCBPTR + iobase);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the QINFIFO.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int saved_queue[AHC_SCB_MAX];
|
|
|
|
int queued = inb(QINCNT + iobase);
|
|
|
|
|
|
|
|
for (i = 0; i < (queued - found); i++) {
|
|
|
|
saved_queue[i] = inb(QINFIFO + iobase);
|
|
|
|
scbp = ahc->scbarray[saved_queue[i]];
|
|
|
|
if (ahc_match_scb (scbp, target, channel)){
|
|
|
|
/*
|
|
|
|
* We found an scb that needs to be aborted.
|
|
|
|
*/
|
|
|
|
scbp->flags |= SCB_ABORTED;
|
|
|
|
scbp->xs->error |= xs_error;
|
|
|
|
if(scbp->position != timedout_scb)
|
|
|
|
untimeout(ahc_timeout, (caddr_t)scbp);
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done (ahc, scbp);
|
1995-07-31 08:25:36 +00:00
|
|
|
outb(SCBPTR + iobase, scbp->position);
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SCBARRAY + iobase, 0);
|
1995-07-31 08:25:36 +00:00
|
|
|
i--;
|
|
|
|
found++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Now put the saved scbs back. */
|
|
|
|
for (queued = 0; queued < i; queued++) {
|
|
|
|
outb (QINFIFO + iobase, saved_queue[queued]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search waiting for selection list.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
u_char next, prev;
|
|
|
|
|
|
|
|
next = inb(WAITING_SCBH + iobase); /* Start at head of list. */
|
|
|
|
prev = SCB_LIST_NULL;
|
|
|
|
|
|
|
|
while (next != SCB_LIST_NULL) {
|
|
|
|
scbp = ahc->scbarray[next];
|
|
|
|
/*
|
|
|
|
* Select the SCB.
|
|
|
|
*/
|
|
|
|
if (ahc_match_scb(scbp, target, channel)) {
|
1996-01-03 06:32:12 +00:00
|
|
|
next = ahc_abort_wscb(ahc, scbp, prev,
|
1995-07-31 08:25:36 +00:00
|
|
|
iobase, timedout_scb, xs_error);
|
|
|
|
found++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
outb(SCBPTR + iobase, scbp->position);
|
|
|
|
prev = next;
|
1996-01-03 06:32:12 +00:00
|
|
|
next = inb(SCB_NEXT_WAITING + iobase);
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Go through the entire SCB array now and look for
|
|
|
|
* commands for this target that are active. These
|
|
|
|
* are other (most likely tagged) commands that
|
|
|
|
* were disconnected when the reset occured.
|
|
|
|
*/
|
|
|
|
for(i = 0; i < ahc->numscbs; i++) {
|
|
|
|
scbp = ahc->scbarray[i];
|
|
|
|
if((scbp->flags & SCB_ACTIVE)
|
|
|
|
&& ahc_match_scb(scbp, target, channel)) {
|
|
|
|
/* Ensure the target is "free" */
|
|
|
|
ahc_unbusy_target(target, channel, iobase);
|
|
|
|
outb(SCBPTR + iobase, scbp->position);
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SCBARRAY + iobase, 0);
|
1995-07-31 08:25:36 +00:00
|
|
|
scbp->flags |= SCB_ABORTED;
|
|
|
|
scbp->xs->error |= xs_error;
|
|
|
|
if(scbp->position != timedout_scb)
|
|
|
|
untimeout(ahc_timeout, (caddr_t)scbp);
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done (ahc, scbp);
|
1995-07-31 08:25:36 +00:00
|
|
|
found++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outb(SCBPTR + iobase, active_scb);
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Manipulate the waiting for selection list and return the
|
|
|
|
* scb that follows the one that we remove.
|
|
|
|
*/
|
1995-10-31 18:41:49 +00:00
|
|
|
static u_char
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_abort_wscb (ahc, scbp, prev, iobase, timedout_scb, xs_error)
|
|
|
|
struct ahc_data *ahc;
|
1995-07-31 08:25:36 +00:00
|
|
|
struct scb *scbp;
|
|
|
|
u_char prev;
|
|
|
|
u_long iobase;
|
|
|
|
u_char timedout_scb;
|
|
|
|
u_int32 xs_error;
|
|
|
|
{
|
|
|
|
u_char curscbp, next;
|
|
|
|
int target = ((scbp->target_channel_lun >> 4) & 0x0f);
|
|
|
|
char channel = (scbp->target_channel_lun & SELBUSB) ? 'B' : 'A';
|
|
|
|
/*
|
|
|
|
* Select the SCB we want to abort and
|
|
|
|
* pull the next pointer out of it.
|
|
|
|
*/
|
|
|
|
curscbp = inb(SCBPTR + iobase);
|
|
|
|
outb(SCBPTR + iobase, scbp->position);
|
1996-01-03 06:32:12 +00:00
|
|
|
next = inb(SCB_NEXT_WAITING + iobase);
|
1995-07-31 08:25:36 +00:00
|
|
|
|
|
|
|
/* Clear the necessary fields */
|
1996-01-29 03:17:39 +00:00
|
|
|
outb(SCBARRAY + iobase, 0);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(SCB_NEXT_WAITING + iobase, SCB_LIST_NULL);
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_unbusy_target(target, channel, iobase);
|
|
|
|
|
|
|
|
/* update the waiting list */
|
|
|
|
if( prev == SCB_LIST_NULL )
|
|
|
|
/* First in the list */
|
|
|
|
outb(WAITING_SCBH + iobase, next);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Select the scb that pointed to us
|
|
|
|
* and update its next pointer.
|
|
|
|
*/
|
|
|
|
outb(SCBPTR + iobase, prev);
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(SCB_NEXT_WAITING + iobase, next);
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
|
|
|
/* Update the tale pointer */
|
|
|
|
if(inb(WAITING_SCBT + iobase) == scbp->position)
|
|
|
|
outb(WAITING_SCBT + iobase, prev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Point us back at the original scb position
|
|
|
|
* and inform the SCSI system that the command
|
|
|
|
* has been aborted.
|
|
|
|
*/
|
|
|
|
outb(SCBPTR + iobase, curscbp);
|
|
|
|
scbp->flags |= SCB_ABORTED;
|
|
|
|
scbp->xs->error |= xs_error;
|
|
|
|
if(scbp->position != timedout_scb)
|
|
|
|
untimeout(ahc_timeout, (caddr_t)scbp);
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_done (ahc, scbp);
|
1995-07-31 08:25:36 +00:00
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
1996-01-03 06:32:12 +00:00
|
|
|
static void
|
|
|
|
ahc_busy_target(target, channel, iobase)
|
|
|
|
u_char target;
|
|
|
|
char channel;
|
|
|
|
u_long iobase;
|
|
|
|
{
|
|
|
|
u_char active;
|
|
|
|
u_long active_port = ACTIVE_A + iobase;
|
|
|
|
if(target > 0x07 || channel == 'B') {
|
|
|
|
/*
|
|
|
|
* targets on the Second channel or
|
|
|
|
* above id 7 store info in byte two
|
|
|
|
* of HA_ACTIVE
|
|
|
|
*/
|
|
|
|
active_port++;
|
|
|
|
}
|
|
|
|
active = inb(active_port);
|
|
|
|
active |= (0x01 << (target & 0x07));
|
|
|
|
outb(active_port, active);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_unbusy_target(target, channel, iobase)
|
|
|
|
u_char target;
|
|
|
|
char channel;
|
|
|
|
u_long iobase;
|
|
|
|
{
|
|
|
|
u_char active;
|
1996-01-03 06:32:12 +00:00
|
|
|
u_long active_port = ACTIVE_A + iobase;
|
1995-07-31 08:25:36 +00:00
|
|
|
if(target > 0x07 || channel == 'B') {
|
|
|
|
/*
|
|
|
|
* targets on the Second channel or
|
|
|
|
* above id 7 store info in byte two
|
|
|
|
* of HA_ACTIVE
|
|
|
|
*/
|
|
|
|
active_port++;
|
|
|
|
}
|
|
|
|
active = inb(active_port);
|
|
|
|
active &= ~(0x01 << (target & 0x07));
|
|
|
|
outb(active_port, active);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static void
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_reset_current_bus(iobase)
|
|
|
|
u_long iobase;
|
|
|
|
{
|
|
|
|
outb(SCSISEQ + iobase, SCSIRSTO);
|
|
|
|
DELAY(1000);
|
|
|
|
outb(SCSISEQ + iobase, 0);
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static int
|
1996-01-03 06:32:12 +00:00
|
|
|
ahc_reset_channel(ahc, channel, timedout_scb, xs_error)
|
1995-07-31 08:25:36 +00:00
|
|
|
struct ahc_data *ahc;
|
|
|
|
char channel;
|
|
|
|
u_char timedout_scb;
|
|
|
|
u_int32 xs_error;
|
|
|
|
{
|
|
|
|
u_long iobase = ahc->baseport;
|
|
|
|
u_char sblkctl;
|
|
|
|
char cur_channel;
|
|
|
|
u_long offset, offset_max;
|
|
|
|
int found;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up all the state information for the
|
|
|
|
* pending transactions on this bus.
|
|
|
|
*/
|
1996-01-03 06:32:12 +00:00
|
|
|
found = ahc_reset_device(ahc, ALL_TARGETS, channel,
|
1995-07-31 08:25:36 +00:00
|
|
|
timedout_scb, xs_error);
|
|
|
|
if(channel == 'B'){
|
|
|
|
ahc->needsdtr |= (ahc->needsdtr_orig & 0xff00);
|
|
|
|
ahc->sdtrpending &= 0x00ff;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(ACTIVE_B + iobase, 0);
|
|
|
|
offset = TARG_SCRATCH + iobase + 8;
|
|
|
|
offset_max = TARG_SCRATCH + iobase + 16;
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
|
|
|
else if (ahc->type & AHC_WIDE){
|
|
|
|
ahc->needsdtr = ahc->needsdtr_orig;
|
|
|
|
ahc->needwdtr = ahc->needwdtr_orig;
|
|
|
|
ahc->sdtrpending = 0;
|
|
|
|
ahc->wdtrpending = 0;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(ACTIVE_A + iobase, 0);
|
|
|
|
outb(ACTIVE_B + iobase, 0);
|
|
|
|
offset = TARG_SCRATCH + iobase;
|
|
|
|
offset_max = TARG_SCRATCH + iobase + 16;
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
|
|
|
else{
|
|
|
|
ahc->needsdtr |= (ahc->needsdtr_orig & 0x00ff);
|
|
|
|
ahc->sdtrpending &= 0xff00;
|
1996-01-03 06:32:12 +00:00
|
|
|
outb(ACTIVE_A + iobase, 0);
|
|
|
|
offset = TARG_SCRATCH + iobase;
|
|
|
|
offset_max = TARG_SCRATCH + iobase + 8;
|
1995-07-31 08:25:36 +00:00
|
|
|
}
|
|
|
|
for(;offset < offset_max;offset++) {
|
|
|
|
/*
|
|
|
|
* Revert to async/narrow transfers
|
|
|
|
* until we renegotiate.
|
|
|
|
*/
|
|
|
|
u_char targ_scratch;
|
|
|
|
targ_scratch = inb(offset);
|
|
|
|
targ_scratch &= SXFR;
|
|
|
|
outb(offset, targ_scratch);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the bus and unpause/restart the controller
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Case 1: Command for another bus is active */
|
|
|
|
sblkctl = inb(SBLKCTL + iobase);
|
|
|
|
cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
|
|
|
|
if(cur_channel != channel)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Stealthily reset the other bus
|
|
|
|
* without upsetting the current bus
|
|
|
|
*/
|
|
|
|
outb(SBLKCTL + iobase, sblkctl ^ SELBUSB);
|
|
|
|
ahc_reset_current_bus(iobase);
|
|
|
|
outb(SBLKCTL + iobase, sblkctl);
|
|
|
|
UNPAUSE_SEQUENCER(ahc);
|
|
|
|
}
|
|
|
|
/* Case 2: A command from this bus is active or we're idle */
|
|
|
|
else {
|
|
|
|
ahc_reset_current_bus(iobase);
|
|
|
|
RESTART_SEQUENCER(ahc);
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
1995-10-31 18:41:49 +00:00
|
|
|
static int
|
1995-07-31 08:25:36 +00:00
|
|
|
ahc_match_scb (scb, target, channel)
|
|
|
|
struct scb *scb;
|
|
|
|
int target;
|
|
|
|
char channel;
|
|
|
|
{
|
|
|
|
int targ = (scb->target_channel_lun >> 4) & 0x0f;
|
|
|
|
char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
|
|
|
|
|
|
|
|
if (target == ALL_TARGETS)
|
|
|
|
return (chan == channel);
|
|
|
|
else
|
|
|
|
return ((chan == channel) && (targ == target));
|
|
|
|
}
|