* Written by Julian Elischer (julian@tfs.com)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
* $Id: bt742a.c,v 1.7 1993/08/28 03:07:42 rgrimes Exp $
#ifdef MACH /* EITHER CMU OR OSF */
#include <i386at/scsiconf.h>
#ifdef OSF /* OSF ONLY */
#include <i386/handler.h>
#include <i386/dispatcher.h>
#include <i386/AT386/atbus.h>
#include <i386at/atbus.h>
#endif MACH /* end of MACH specific */
#ifdef __386BSD__ /* 386BSD specific */
#define isa_dev isa_device
#define dev_addr id_iobase
#include <i386/isa/isa_device.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#define Debugger() panic("should call debugger here (bt742a.c)")
extern int delaycount
; /* from clock setup code */
typedef unsigned long int physaddr
;
#define BT_BASE bt_base[unit]
#define BT_CTRL_STAT_PORT (BT_BASE + 0x0) /* control & status */
#define BT_CMD_DATA_PORT (BT_BASE + 0x1) /* cmds and datas */
#define BT_INTR_PORT (BT_BASE + 0x2) /* Intr. stat */
* BT_CTRL_STAT bits (write)
#define BT_HRST 0x80 /* Hardware reset */
#define BT_SRST 0x40 /* Software reset */
#define BT_IRST 0x20 /* Interrupt reset */
#define BT_SCRST 0x10 /* SCSI bus reset */
* BT_CTRL_STAT bits (read)
#define BT_STST 0x80 /* Self test in Progress */
#define BT_DIAGF 0x40 /* Diagnostic Failure */
#define BT_INIT 0x20 /* Mbx Init required */
#define BT_IDLE 0x10 /* Host Adapter Idle */
#define BT_CDF 0x08 /* cmd/data out port full */
#define BT_DF 0x04 /* Data in port full */
#define BT_INVDCMD 0x01 /* Invalid command */
* BT_CMD_DATA bits (write)
#define BT_NOP 0x00 /* No operation */
#define BT_MBX_INIT 0x01 /* Mbx initialization */
#define BT_START_SCSI 0x02 /* start scsi command */
#define BT_START_BIOS 0x03 /* start bios command */
#define BT_INQUIRE 0x04 /* Adapter Inquiry */
#define BT_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
#define BT_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
#define BT_BUS_ON_TIME_SET 0x07 /* set bus-on time */
#define BT_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
#define BT_SPEED_SET 0x09 /* set transfer speed */
#define BT_DEV_GET 0x0a /* return installed devices */
#define BT_CONF_GET 0x0b /* return configuration data */
#define BT_TARGET_EN 0x0c /* enable target mode */
#define BT_SETUP_GET 0x0d /* return setup data */
#define BT_WRITE_CH2 0x1a /* write channel 2 buffer */
#define BT_READ_CH2 0x1b /* read channel 2 buffer */
#define BT_WRITE_FIFO 0x1c /* write fifo buffer */
#define BT_READ_FIFO 0x1d /* read fifo buffer */
#define BT_ECHO 0x1e /* Echo command data */
#define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */
#define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */
/* Follows command appeared at FirmWare 3.31 */
#define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */
#define BT_DISABLE 0x00 /* Parameter value for Disable */
#define BT_ENABLE 0x01 /* Parameter value for Enable */
* BT_INTR_PORT bits (read)
#define BT_ANY_INTR 0x80 /* Any interrupt */
#define BT_SCRD 0x08 /* SCSI reset detected */
#define BT_HACC 0x04 /* Command complete */
#define BT_MBOA 0x02 /* MBX out empty */
#define BT_MBIF 0x01 /* MBX in full */
#define BT_MBX_SIZE 255 /* mail box size (MAX 255 MBxs) */
#define BT_CCB_SIZE 32 /* store up to 32CCBs at any one time */
/* in bt742a H/W ( Not MAX ? ) */
#define bt_nextmbx( wmb, mbx, mbio ) \
if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) { \
(wmb) = &((mbx)->mbio[0]); \
typedef struct bt_mbx_out
{
typedef struct bt_mbx_in
{
BT_MBO
*tmbo
; /* Target Mail Box out */
BT_MBI
*tmbi
; /* Target Mail Box in */
#define BT_MBO_FREE 0x0 /* MBO entry is free */
#define BT_MBO_START 0x1 /* MBO activate entry */
#define BT_MBO_ABORT 0x2 /* MBO abort entry */
#define BT_MBI_FREE 0x0 /* MBI entry is free */
#define BT_MBI_OK 0x1 /* completed without error */
#define BT_MBI_ABORT 0x2 /* aborted ccb */
#define BT_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
#define BT_MBI_ERROR 0x4 /* Completed with error */
extern struct bt_mbx bt_mbx
[];
/* #define BT_NSEG 8192 /* Number of scatter gather segments - to much vm */
unsigned char :3,data_in
:1,data_out
:1,:3;
unsigned char scsi_cmd_length
;
unsigned char req_sense_length
;
/*------------------------------------longword boundary */
unsigned long data_length
;
/*------------------------------------longword boundary */
/*------------------------------------longword boundary */
unsigned char target_stat
;
/*------------------------------------longword boundary */
unsigned char scsi_cmd
[12]; /* 12 bytes (bytes only)*/
/*------------------------------------4 longword boundary */
/*------------------------------------longword boundary */
/*------------------------------------longword boundary */
struct scsi_sense_data scsi_sense
;
/*------------------------------------longword boundary */
struct bt_scat_gath scat_gath
[BT_NSEG
];
/*------------------------------------longword boundary */
/*------------------------------------longword boundary */
struct scsi_xfer
*xfer
; /* the scsi_xfer for this cmd */
/*------------------------------------longword boundary */
struct bt_mbx_out
*mbx
; /* pointer to mail box */
/*------------------------------------longword boundary */
#define BT_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
#define BT_TARGET_CCB 0x01 /* SCSI Target CCB */
#define BT_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather*/
#define BT_RESET_CCB 0x81 /* SCSI Bus reset */
* bt_ccb.host_stat values
#define BT_OK 0x00 /* cmd ok */
#define BT_LINK_OK 0x0a /* Link cmd ok */
#define BT_LINK_IT 0x0b /* Link cmd ok + int */
#define BT_SEL_TIMEOUT 0x11 /* Selection time out */
#define BT_OVER_UNDER 0x12 /* Data over/under run */
#define BT_BUS_FREE 0x13 /* Bus dropped at unexpected time */
#define BT_INV_BUS 0x14 /* Invalid bus phase/sequence */
#define BT_BAD_MBO 0x15 /* Incorrect MBO cmd */
#define BT_BAD_CCB 0x16 /* Incorrect ccb opcode */
#define BT_BAD_LINK 0x17 /* Not same values of LUN for links */
#define BT_INV_TARGET 0x18 /* Invalid target direction */
#define BT_CCB_DUP 0x19 /* Duplicate CCB received */
#define BT_INV_CCB 0x1a /* Invalid CCB or segment list */
#define BT_ABORTED 42 /* pseudo value from driver */
extern physaddr
kvtophys();
#define PHYSTOKV(x) phystokv(x)
#define KVTOPHYS(x) kvtophys(x)
#define KVTOPHYS(x) vtophys(x)
#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
u_char bt_scratch_buf
[256];
caddr_t bt_base
[NBT
]; /* base port for each board */
short bt_base
[NBT
]; /* base port for each board */
struct bt_mbx bt_mbx
[NBT
];
struct bt_ccb
*bt_ccb_free
[NBT
];
struct bt_ccb bt_ccb
[NBT
][BT_CCB_SIZE
];
struct scsi_xfer bt_scsi_xfer
[NBT
];
struct isa_dev
*btinfo
[NBT
];
struct bt_ccb
*bt_get_ccb();
#endif /* defined(OSF) */
/***********debug values *************/
int btprobe(), btattach();
struct isa_driver btdriver
= { btprobe
, 0, btattach
, "bt", 0, 0, 0};
int (*btintrs
[])() = {btintr
, 0};
struct isa_driver btdriver
= { btprobe
, btattach
, "bt"};
long int bt_adapter_info();
struct scsi_switch bt_switch
=
#define BT_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */
#define BT_RESET_TIMEOUT 1000000
#define BT_SCSI_TIMEOUT_FUDGE 20 /* divided by for mSecs */
/***********************************************************************\
* bt_cmd(unit,icnt, ocnt,wait, retval, opcode, args) *
* Activate Adapter command *
* icnt: number of args (outbound bytes written after opcode) *
* ocnt: number of expected returned bytes *
* wait: number of seconds to wait for response *
* retval: buffer where to place returned bytes *
* opcode: opcode BT_NOP, BT_MBX_INIT, BT_START_SCSI ... *
* Performs an adapter command through the ports. Not to be confused *
* with a scsi command, which is read in via the dma *
* One of the adapter commands tells it to read in a scsi command *
\***********************************************************************/
bt_cmd(unit
,icnt
, ocnt
, wait
,retval
, opcode
, args
)
/*******************************************************\
* multiply the wait argument by a big constant *
\*******************************************************/
wait
= BT_CMD_TIMEOUT_FUDGE
* delaycount
;
wait
*= BT_CMD_TIMEOUT_FUDGE
* delaycount
;
/*******************************************************\
* Wait for the adapter to go idle, unless it's one of *
* the commands which don't need this *
\*******************************************************/
if (opcode
!= BT_MBX_INIT
&& opcode
!= BT_START_SCSI
)
i
= BT_CMD_TIMEOUT_FUDGE
* delaycount
; /* 1 sec?*/
sts
= inb(BT_CTRL_STAT_PORT
);
printf("bt%d: bt_cmd, host not idle(0x%x)\n",unit
,sts
);
/*******************************************************\
* Now that it is idle, if we expect output, preflush the*
\*******************************************************/
while((inb(BT_CTRL_STAT_PORT
)) & BT_DF
)
/*******************************************************\
* Output the command and the number of arguments given *
* for each byte, first check the port is empty. *
\*******************************************************/
icnt
++; /* include the command */
sts
= inb(BT_CTRL_STAT_PORT
);
sts
= inb(BT_CTRL_STAT_PORT
);
printf("bt%d: bt_cmd, cmd/data port full\n",unit
);
outb(BT_CTRL_STAT_PORT
, BT_SRST
);
outb(BT_CMD_DATA_PORT
, (u_char
)(*ic
++));
/*******************************************************\
* If we expect input, loop that many times, each time, *
* looking for the data register to have valid data *
\*******************************************************/
sts
= inb(BT_CTRL_STAT_PORT
);
sts
= inb(BT_CTRL_STAT_PORT
);
printf("bt%d: bt_cmd, cmd/data port empty %d\n",
oc
= inb(BT_CMD_DATA_PORT
);
/*******************************************************\
* Wait for the board to report a finised instruction *
\*******************************************************/
i
=BT_CMD_TIMEOUT_FUDGE
* delaycount
; /* 1 sec? */
printf("bt%d: bt_cmd, host not finished(0x%x)\n",unit
,sts
);
outb(BT_CTRL_STAT_PORT
, BT_IRST
);
/*******************************************************\
* Check if the device can be found at the port given *
* and if so, set it up ready for further work *
* as an argument, takes the isa_dev structure from *
\*******************************************************/
/***********************************************\
* find unit and check we have that many defined *
\***********************************************/
static ihandler_t bt_handler
[NBT
];
static ihandler_id_t
*bt_handler_id
[NBT
];
register ihandler_t
*chp
= &bt_handler
[unit
];;
#endif /* defined(OSF) */
bt_base
[unit
] = dev
->dev_addr
;
printf("bt%d: unit number too high\n",unit
);
/***********************************************\
* Try initialise a unit at this location *
* sets up dma and bus speed, loads bt_int[unit]*
\***********************************************/
/***********************************************\
* If it's there, put in it's interrupt vectors *
\***********************************************/
dev
->dev_pic
= bt_int
[unit
];
#if defined(OSF) /* OSF */
chp
->ih_level
= dev
->dev_pic
;
chp
->ih_handler
= dev
->dev_intr
[0];
chp
->ih_resolver
= i386_resolver
;
chp
->ih_stats
.intr_type
= INTR_DEVICE
;
chp
->ih_stats
.intr_cnt
= 0;
chp
->ih_hparam
[0].intparam
= unit
;
if ((bt_handler_id
[unit
] = handler_add(chp
)) != NULL
)
handler_enable(bt_handler_id
[unit
]);
panic("Unable to add bt interrupt handler");
#endif /* !defined(OSF) */
printf("port=%x spl=%d\n", dev
->dev_addr
, dev
->dev_spl
);
#ifdef __386BSD__ /* 386BSD */
dev
->id_irq
= (1 << bt_int
[unit
]);
dev
->id_drq
= bt_dma
[unit
];
/***********************************************\
* Attach all the sub-devices we can find *
\***********************************************/
int unit
= dev
->dev_unit
;
/***********************************************\
* ask the adapter what subunits are present *
\***********************************************/
scsi_attachdevs( unit
, bt_scsi_dev
[unit
], &bt_switch
);
#endif /* defined(OSF) */
/***********************************************\
* Return some information to the caller about *
* the adapter and it's capabilities *
\***********************************************/
long int bt_adapter_info(unit
)
return(2); /* 2 outstanding requests at a time per device */
/***********************************************\
* Catch an interrupt from the adaptor *
\***********************************************/
if(scsi_debug
& PRINTROUTINES
)
/***********************************************\
* First acknowlege the interrupt, Then if it's *
* not telling about a completed operation *
\***********************************************/
stat
= inb(BT_INTR_PORT
);
/* Mail Box out empty ? */
printf("bt%d: Available Free mbo post\n",unit
);
/* Disable MBO available interrupt */
outb(BT_CMD_DATA_PORT
,BT_MBO_INTR_EN
);
wait
= BT_CMD_TIMEOUT_FUDGE
* delaycount
;
if (!(inb(BT_CTRL_STAT_PORT
) & BT_CDF
))
printf("bt%d: bt_intr, cmd/data port full\n",unit
);
outb(BT_CTRL_STAT_PORT
, BT_SRST
);
outb(BT_CMD_DATA_PORT
, 0x00); /* Disable */
outb(BT_CTRL_STAT_PORT
, BT_IRST
);
if (! (stat
& BT_MBIF
)) {
outb(BT_CTRL_STAT_PORT
, BT_IRST
);
#endif /* defined(OSF) */
/***********************************************\
* If it IS then process the competed operation *
\***********************************************/
while ( wmbi
->stat
!= BT_MBI_FREE
) {
ccb
= (struct bt_ccb
*)PHYSTOKV((wmbi
->ccb_addr
));
if((stat
= wmbi
->stat
) != BT_MBI_OK
)
if(bt_debug
& BT_SHOWMISC
)
ccb
->host_stat
= BT_ABORTED
;
ccb
= (struct bt_ccb
*)0;
if(bt_debug
& BT_SHOWMISC
)
printf("unknown ccb for abort");
panic("Impossible mbxi status");
if((bt_debug
& BT_SHOWCMDS
) && ccb
)
printf("op=%x %x %x %x %x %x\n",
printf("stat %x for mbi addr = 0x%08x\n"
printf("addr = 0x%x\n", ccb
);
wmbi
->stat
= BT_MBI_FREE
;
untimeout(bt_timeout
,ccb
);
/* Set the IN mail Box pointer for next */
bt_nextmbx( wmbi
, wmbx
, mbi
);
for ( i
= 0; i
< BT_MBX_SIZE
; i
++) {
if ( wmbi
->stat
!= BT_MBI_FREE
) {
bt_nextmbx( wmbi
, wmbx
, mbi
);
printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n",
outb(BT_CTRL_STAT_PORT
, BT_IRST
);
/***********************************************\
* A ccb (and hence a mbx-out is put onto the *
\***********************************************/
bt_free_ccb(unit
,ccb
, flags
)
if(scsi_debug
& PRINTROUTINES
)
printf("ccb%d(0x%x)> ",unit
,flags
);
if (!(flags
& SCSI_NOMASK
))
ccb
->next
= bt_ccb_free
[unit
];
/***********************************************\
* If there were none, wake abybody waiting for *
* one to come free, starting with queued entries*
\***********************************************/
wakeup(&bt_ccb_free
[unit
]);
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
\***********************************************/
struct bt_mbx
*wmbx
; /* Mail Box pointer specified unit */
BT_MBO
*wmbo
; /* Out Mail Box pointer */
if(scsi_debug
& PRINTROUTINES
)
printf("<ccb%d(0x%x) ",unit
,flags
);
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
* If we can and have to, sleep waiting for one *
\***********************************************/
while ((!(rc
= bt_ccb_free
[unit
])) && (!(flags
& SCSI_NOSLEEP
)))
sleep(&bt_ccb_free
[unit
], PRIBIO
);
/* Get CCB from from free list */
bt_ccb_free
[unit
] = rc
->next
;
/* Get the Target OUT mail Box pointer */
while ( wmbo
->cmd
!= BT_MBO_FREE
) {
/* Enable MBO available interrupt */
outb(BT_CMD_DATA_PORT
,BT_MBO_INTR_EN
);
printf("Wait free mbo.."); /* AMURAI */
printf("Got free mbo\n"); /* AMURAI */
/* Link CCB to the Mail Box */
wmbo
->ccb_addr
= KVTOPHYS(rc
);
/* Set the OUT mail Box pointer for next */
bt_nextmbx( wmbo
, wmbx
, mbo
);
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
* Get a MBO and then Send it *
\***********************************************/
BT_MBO
*bt_send_mbo( int unit
,
BT_MBO
*wmbo
; /* Mail Box Out pointer */
struct bt_mbx
*wmbx
; /* Mail Box pointer specified unit */
if (!(flags
& SCSI_NOMASK
))
/* Get the Target OUT mail Box pointer and move to Next */
wmbx
->tmbo
= ( wmbo
== &( wmbx
->mbo
[BT_MBX_SIZE
- 1 ] ) ?
&(wmbx
->mbo
[0]) : wmbo
+ 1 );
* Check the outmail box is free or not
* Note: Under the normal operation, it shuld NOT happen to wait.
while ( wmbo
->cmd
!= BT_MBO_FREE
) {
wait
= BT_CMD_TIMEOUT_FUDGE
* delaycount
;
/* Enable MBO available interrupt */
outb(BT_CMD_DATA_PORT
,BT_MBO_INTR_EN
);
if (!(inb(BT_CTRL_STAT_PORT
) & BT_CDF
))
printf("bt%d: bt_send_mbo, cmd/data port full\n",unit
);
outb(BT_CTRL_STAT_PORT
, BT_SRST
);
outb(BT_CMD_DATA_PORT
, 0x01); /* Enable */
/* Link CCB to the Mail Box */
wmbo
->ccb_addr
= KVTOPHYS(ccb
);
outb(BT_CMD_DATA_PORT
, BT_START_SCSI
);
if (!(flags
& SCSI_NOMASK
))
/***********************************************\
* We have a ccb which has been processed by the *
* adaptor, now we look to see how the operation *
* went. Wake up the owner if waiting *
\***********************************************/
struct scsi_sense_data
*s1
,*s2
;
struct scsi_xfer
*xs
= ccb
->xfer
;
if(scsi_debug
& (PRINTROUTINES
| TRACEINTERRUPTS
))
/***********************************************\
* Otherwise, put the results of the operation *
* into the xfer and call whoever started it *
\***********************************************/
if ( ( ccb
->host_stat
!= BT_OK
|| ccb
->target_stat
!= SCSI_OK
)
&& (!(xs
->flags
& SCSI_ERR_OK
)))
case BT_ABORTED
: /* No response */
case BT_SEL_TIMEOUT
: /* No response */
if (bt_debug
& BT_SHOWMISC
)
printf("timeout reported back\n");
default: /* Other scsi protocol messes */
xs
->error
= XS_DRIVER_STUFFUP
;
if (bt_debug
& BT_SHOWMISC
)
printf("unexpected host_stat: %x\n",
if (bt_debug
& BT_SHOWMISC
)
printf("unexpected target_stat: %x\n",
xs
->error
= XS_DRIVER_STUFFUP
;
else /* All went correctly OR errors expected */
bt_free_ccb(unit
,ccb
, xs
->flags
);
(*(xs
->when_done
))(xs
->done_arg
,xs
->done_arg2
);
/***********************************************\
* Start the board, ready for normal operation *
\***********************************************/
/***********************************************\
* reset board, If it doesn't respond, assume *
* that it's not there.. good for the probe *
\***********************************************/
outb(BT_CTRL_STAT_PORT
, BT_HRST
|BT_SRST
);
for (i
=0; i
< BT_RESET_TIMEOUT
; i
++)
sts
= inb(BT_CTRL_STAT_PORT
) ;
if ( sts
== (BT_IDLE
| BT_INIT
))
if (i
>= BT_RESET_TIMEOUT
)
if (bt_debug
& BT_SHOWMISC
)
printf("bt_init: No answer from bt742a board\n");
/***********************************************\
* Assume we have a board at this stage *
* setup dma channel from jumpers and save int *
\***********************************************/
printf("bt%d: reading board settings, ",unit
);
bt_cmd(unit
,0, sizeof(conf
), 0 ,&conf
, BT_CONF_GET
);
printf("illegal dma setting %x\n",conf
.chan
);
printf("dma=%d, ",bt_dma
[unit
]);
printf("illegal int setting\n");
printf("int=%d\n",bt_int
[unit
]);
printf("int=%d ",bt_int
[unit
]);
/* who are we on the scsi bus */
bt_scsi_dev
[unit
] = conf
.scsi_dev
;
/***********************************************\
\***********************************************/
*((physaddr
*)ad
) = KVTOPHYS(&bt_mbx
[unit
]);
bt_cmd(unit
,5, 0, 0, 0, BT_MBX_INIT_EXTENDED
/***********************************************\
* Set Pointer chain null for just in case *
* Link the ccb's into a free-list W/O mbox *
* Initilize Mail Box stat to Free *
\***********************************************/
if ( bt_ccb_free
[unit
] != (struct bt_ccb
*)0 ) {
printf("bt%d: bt_ccb_free is NOT initialized but init here\n",
bt_ccb_free
[unit
] = (struct bt_ccb
*)0;
for (i
=0; i
< BT_CCB_SIZE
; i
++) {
bt_ccb
[unit
][i
].next
= bt_ccb_free
[unit
];
bt_ccb_free
[unit
] = &bt_ccb
[unit
][i
];
bt_ccb_free
[unit
]->flags
= CCB_FREE
;
for (i
=0; i
< BT_MBX_SIZE
; i
++) {
bt_mbx
[unit
].mbo
[i
].cmd
= BT_MBO_FREE
;
bt_mbx
[unit
].mbi
[i
].stat
= BT_MBI_FREE
;
/***********************************************\
* Set up Initial mail box for round-robin *
\***********************************************/
bt_mbx
[unit
].tmbo
= &bt_mbx
[unit
].mbo
[0];
bt_mbx
[unit
].tmbi
= &bt_mbx
[unit
].mbi
[0];
bt_inquire_setup_information( unit
);
/* Enable round-robin scheme - appeared at FirmWare 3.31 */
bt_cmd(unit
, 1, 0, 0, 0, BT_ROUND_ROBIN
, BT_ENABLE
);
/***********************************************\
* Note that we are going and return (to probe) *
\***********************************************/
bt_inquire_setup_information( unit
)
/* Inquire Board ID to Bt742 for FirmWare Version */
bt_cmd(unit
, 0, sizeof(bID
), 0, &bID
, BT_INQUIRE
);
printf("bt%d: version %c.%c, ",
unit
, bID
.firm_revision
, bID
.firm_version
);
/* Ask setup information to Bt742 */
bt_cmd(unit
, 1, sizeof(setup
), 0, &setup
, BT_SETUP_GET
, sizeof(setup
) );
printf("%d mbxs, %d ccbs\n", setup
.num_mbx
,
sizeof(bt_ccb
)/(sizeof(struct bt_ccb
) * NBT
) );
for ( i
= 0; i
< 8; i
++ ) {
if( !setup
.sync
[i
].offset
&&
printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s",
setup
.sync
[i
].offset
, setup
.sync
[i
].period
,
setup
.sync
[i
].valid
? "Yes" : "No" );
#define min(x,y) (x < y ? x : y)
bp
->b_flags
|= B_NPAGES
; /* can support scat/gather */
#endif /* defined(OSF) */
if(bp
->b_bcount
> ((BT_NSEG
-1) * PAGESIZ
))
bp
->b_bcount
= ((BT_NSEG
-1) * PAGESIZ
);
/***********************************************\
* start a scsi operation given the command and *
* the data address. Also needs the unit, target *
\***********************************************/
struct scsi_sense_data
*s1
,*s2
;
int seg
; /* scatter gather seg being worked on */
physaddr thisphys
,nextphys
;
int bytes_this_seg
,bytes_this_page
,datalen
,flags
;
if(scsi_debug
& PRINTROUTINES
)
/***********************************************\
* get a ccb (mbox-out) to use. If the transfer *
* is from a buf (possibly from interrupt time) *
* then we can't allow it to sleep *
\***********************************************/
if(xs
->bp
) flags
|= (SCSI_NOSLEEP
); /* just to be sure */
printf("bt%d: Already done?\n",unit
);
printf("bt%d: Not in use?\n",unit
);
if (!(ccb
= bt_get_ccb(unit
,flags
)))
xs
->error
= XS_DRIVER_STUFFUP
;
if(bt_debug
& BT_SHOWCCBS
)
printf("<start ccb(%x)>",ccb
);
/***********************************************\
* Put all the arguments for the xfer in the ccb *
\***********************************************/
ccb
->opcode
= BT_RESET_CCB
;
/* can't use S/G if zero length */
ccb
->opcode
= (xs
->datalen
?
ccb
->scsi_cmd_length
= xs
->cmdlen
;
ccb
->sense_ptr
= KVTOPHYS(&(ccb
->scsi_sense
));
ccb
->req_sense_length
= sizeof(ccb
->scsi_sense
);
if((xs
->datalen
) && (!(flags
& SCSI_RESET
)))
{ /* can use S/G only if not zero length */
ccb
->data_addr
= KVTOPHYS(ccb
->scat_gath
);
if(flags
& SCSI_DATA_UIO
)
iovp
= ((struct uio
*)xs
->data
)->uio_iov
;
datalen
= ((struct uio
*)xs
->data
)->uio_iovcnt
;
while ((datalen
) && (seg
< BT_NSEG
))
sg
->seg_addr
= (physaddr
)iovp
->iov_base
;
xs
->datalen
+= sg
->seg_len
= iovp
->iov_len
;
if(scsi_debug
& SHOWSCATGATH
)
/***********************************************\
* Set up the scatter gather block *
\***********************************************/
if(scsi_debug
& SHOWSCATGATH
)
printf("%d @0x%x:- ",xs
->datalen
,xs
->data
);
thisphys
= KVTOPHYS(thiskv
);
while ((datalen
) && (seg
< BT_NSEG
))
/* put in the base address */
if(scsi_debug
& SHOWSCATGATH
)
/* do it at least once */
while ((datalen
) && (thisphys
== nextphys
))
/*********************************************\
* This page is contiguous (physically) with *
* the the last, just extend the length *
\*********************************************/
/* how far to the end of the page */
nextphys
= (thisphys
& (~(PAGESIZ
- 1)))
bytes_this_page
= nextphys
- thisphys
;
bytes_this_page
= min(bytes_this_page
bytes_this_seg
+= bytes_this_page
;
datalen
-= bytes_this_page
;
/* get more ready for the next page */
thiskv
= (thiskv
& (~(PAGESIZ
- 1)))
thisphys
= KVTOPHYS(thiskv
);
/********************************************\
* next page isn't contiguous, finish the seg *
\********************************************/
if(scsi_debug
& SHOWSCATGATH
)
printf("(0x%x)",bytes_this_seg
);
sg
->seg_len
= bytes_this_seg
;
} /*end of iov/kv decision */
ccb
->data_length
= seg
* sizeof(struct bt_scat_gath
);
if(scsi_debug
& SHOWSCATGATH
)
{ /* there's still data, must have run out of segs! */
printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n",
xs
->error
= XS_DRIVER_STUFFUP
;
bt_free_ccb(unit
,ccb
,flags
);
{ /* No data xfer, use non S/G values */
ccb
->data_addr
= (physaddr
)0;
ccb
->link_addr
= (physaddr
)0;
/***********************************************\
* Put the scsi command in the ccb and start it *
\***********************************************/
if(!(flags
& SCSI_RESET
))
bcopy(xs
->cmd
, ccb
->scsi_cmd
, ccb
->scsi_cmd_length
);
if(scsi_debug
& SHOWCOMMANDS
)
u_char
*b
= ccb
->scsi_cmd
;
if(!(flags
& SCSI_RESET
))
while(i
< ccb
->scsi_cmd_length
)
printf("bt%d:%d:%d-RESET- "
if ( bt_send_mbo( unit
, flags
, BT_MBO_START
, ccb
) == (BT_MBO
*)0 )
xs
->error
= XS_DRIVER_STUFFUP
;
bt_free_ccb(unit
,ccb
,flags
);
/***********************************************\
* Usually return SUCCESSFULLY QUEUED *
\***********************************************/
if(scsi_debug
& TRACEINTERRUPTS
)
if (!(flags
& SCSI_NOMASK
))
timeout(bt_timeout
,ccb
,(xs
->timeout
* hz
) / 1000);
return(SUCCESSFULLY_QUEUED
);
/***********************************************\
* If we can't use interrupts, poll on completion*
\***********************************************/
int count
= delaycount
* xs
->timeout
/ BT_SCSI_TIMEOUT_FUDGE
;
struct bt_mbx
*wmbx
= &bt_mbx
[unit
];
BT_MBI
*wmbi
= wmbx
->tmbi
;
if(scsi_debug
& TRACEINTERRUPTS
)
stat
= inb(BT_INTR_PORT
) & (BT_ANY_INTR
| BT_MBIF
);
if ( !( stat
& BT_ANY_INTR
) ||
( wmbi
->stat
== BT_MBI_FREE
)||
(PHYSTOKV(wmbi
->ccb_addr
)
wmbi
->stat
= BT_MBI_FREE
;
outb(BT_CTRL_STAT_PORT
, BT_IRST
);
/* Set the IN mail Box pointer for next */
bt_nextmbx( wmbi
, wmbx
, mbi
);
if (!(xs
->flags
& SCSI_SILENT
))
bt_send_mbo( unit
, flags
, BT_MBO_ABORT
, ccb
);
count
= delaycount
* 2000 / BT_SCSI_TIMEOUT_FUDGE
;
if ( !( stat
& BT_ANY_INTR
) ||
( wmbi
->stat
== BT_MBI_FREE
)||
( PHYSTOKV(wmbi
->ccb_addr
)
wmbi
->stat
= BT_MBI_FREE
;
outb(BT_CTRL_STAT_PORT
, BT_IRST
);
/* Set the IN mail Box pointer for next */
bt_nextmbx( wmbi
, wmbx
, mbi
);
printf("bt%d: abort failed in wait\n", unit
);
ccb
->mbx
->cmd
= BT_MBO_FREE
;
bt_free_ccb(unit
,ccb
,flags
);
xs
->error
= XS_DRIVER_STUFFUP
;
if(xs
->error
) return(HAD_ERROR
);
bt_timeout(struct bt_ccb
*ccb
)
unit
= ccb
->xfer
->adapter
;
printf("bt%d: %d device timed out\n",unit
if(bt_debug
& BT_SHOWCCBS
)
bt_print_active_ccbs(unit
);
/***************************************\
* If The ccb's mbx is not free, then *
* the board has gone Far East ? *
\***************************************/
if((struct bt_ccb
*)PHYSTOKV(ccb
->mbx
->ccb_addr
)==ccb
&&
ccb
->mbx
->cmd
!= BT_MBO_FREE
)
printf("bt%d: not taking commands!\n"
/***************************************\
* If it has been through before, then *
* a previous abort has failed, don't *
\***************************************/
if(ccb
->flags
== CCB_ABORTED
) /* abort timed out */
printf("bt%d: Abort Operation has timed out\n",unit
);
ccb
->xfer
->retries
= 0; /* I MEAN IT ! */
ccb
->host_stat
= BT_ABORTED
;
else /* abort the operation that has timed out */
printf("bt%d: Try to abort\n",unit
);
bt_send_mbo( unit
, ~SCSI_NOMASK
,
/* 2 secs for the abort */
timeout(bt_timeout
,ccb
,2 * hz
);
ccb
->flags
= CCB_ABORTED
;
printf("ccb:%x op:%x cmdlen:%d senlen:%d\n"
printf(" datlen:%d hstat:%x tstat:%x flags:%x\n"
bt_print_active_ccbs(int unit
)
ccb
= &(bt_ccb
[unit
][0]);
if(ccb
->flags
!= CCB_FREE
)