* Copyright (c) 1992 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Ralph Campbell and Rick Macklem.
* %sccs.include.redist.c%
* @(#)asc.c 7.13 (Berkeley) %G%
* Copyright (c) 1991,1990,1989 Carnegie Mellon University
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
* Carnegie Mellon requests users of this software to return to
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
* $Log: scsi_53C94_hdw.c,v $
* Revision 2.5 91/02/05 17:45:07 mrt
* [91/02/04 11:18:43 mrt]
* Changed to use new Mach copyright
* [91/02/02 12:17:20 mrt]
* Revision 2.4 91/01/08 15:48:24 rpd
* Added continuation argument to thread_block.
* Revision 2.3 90/12/05 23:34:48 af
* Recovered from pmax merge.. and from the destruction of a disk.
* Revision 2.1.1.1 90/11/01 03:39:09 af
* Created, from the DEC specs:
* "PMAZ-AA TURBOchannel SCSI Module Functional Specification"
* Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990.
* And from the NCR data sheets
* "NCR 53C94, 53C95, 53C96 Advances SCSI Controller"
* Author: Alessandro Forin, Carnegie Mellon University
* Bottom layer of the SCSI driver: chip-dependent functions
* This file contains the code that is specific to the NCR 53C94
* SCSI chip (Host Bus Adapter in SCSI parlance): probing, start
* operation, and interrupt routine.
* This layer works based on small simple 'scripts' that are installed
* at the start of the command and drive the chip to completion.
* The idea comes from the specs of the NCR 53C700 'script' processor.
* There are various reasons for this, mainly
* - Performance: identify the common (successful) path, and follow it;
* at interrupt time no code is needed to find the current status
* - Code size: it should be easy to compact common operations
* - Adaptability: the code skeleton should adapt to different chips without
* terrible complications.
* - Error handling: and it is easy to modify the actions performed
* by the scripts to cope with strange but well identified sequences
#include <machine/machConst.h>
#include <pmax/dev/device.h>
#include <pmax/dev/scsi.h>
#include <pmax/dev/ascreg.h>
#include <pmax/pmax/asic.h>
#include <pmax/pmax/kmin.h>
#include <pmax/pmax/pmaxtype.h>
#define readback(a) { register int foo; foo = (a); }
extern int pmax_boardtype
;
int asc_to_scsi_period
[] = {
* Internal forward declarations.
static void asc_startcmd();
} asc_log
[NLOG
], *asc_logp
= asc_log
;
#define PACK(unit, status, ss, ir) \
((unit << 24) | (status << 16) | (ss << 8) | ir)
* Scripts are entries in a state machine table.
* A script has four parts: a pre-condition, an action, a command to the chip,
* and an index into asc_scripts for the next state. The first triggers error
* handling if not satisfied and in our case it is formed by the
* values of the interrupt register and status register, this
* basically captures the phase of the bus and the TC and BS
* bits. The action part is just a function pointer, and the
* command is what the 53C94 should be told to do at the end
* of the action processing. This command is only issued and the
* script proceeds if the action routine returns TRUE.
* See asc_intr() for how and where this is all done.
int condition
; /* expected state at interrupt time */
int (*action
)(); /* extra operations */
int command
; /* command to the chip */
struct script
*next
; /* index into asc_scripts for next state */
/* Matching on the condition value */
#define SCRIPT_MATCH(ir, csr) ((ir) | (((csr) & 0x67) << 8))
/* forward decls of script actions */
static int script_nop(); /* when nothing needed */
static int asc_end(); /* all come to an end */
static int asc_get_status(); /* get status from target */
static int asc_dma_in(); /* start reading data from target */
static int asc_last_dma_in(); /* cleanup after all data is read */
static int asc_resume_in(); /* resume data in after a message */
static int asc_resume_dma_in(); /* resume DMA after a disconnect */
static int asc_dma_out(); /* send data to target via dma */
static int asc_last_dma_out(); /* cleanup after all data is written */
static int asc_resume_out(); /* resume data out after a message */
static int asc_resume_dma_out(); /* resume DMA after a disconnect */
static int asc_sendsync(); /* negotiate sync xfer */
static int asc_replysync(); /* negotiate sync xfer */
static int asc_msg_in(); /* process a message byte */
static int asc_disconnect(); /* process an expected disconnect */
/* Define the index into asc_scripts for various state transitions */
#define SCRIPT_CONTINUE_IN 2
#define SCRIPT_DATA_OUT 3
#define SCRIPT_CONTINUE_OUT 5
#define SCRIPT_GET_STATUS 7
#define SCRIPT_REPLY_SYNC 11
#define SCRIPT_TRY_SYNC 12
#define SCRIPT_DISCONNECT 15
#define SCRIPT_RESUME_IN 17
#define SCRIPT_RESUME_DMA_IN 18
#define SCRIPT_RESUME_OUT 19
#define SCRIPT_RESUME_DMA_OUT 20
#define SCRIPT_RESUME_NO_DATA 21
script_t asc_scripts
[] = {
{SCRIPT_MATCH(ASC_INT_FC
| ASC_INT_BS
, ASC_PHASE_DATAI
), /* 0 */
asc_dma_in
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_IN
+ 1]},
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_STATUS
), /* 1 */
asc_last_dma_in
, ASC_CMD_I_COMPLETE
,
&asc_scripts
[SCRIPT_GET_STATUS
]},
/* continue data in after a chunk is finished */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_DATAI
), /* 2 */
asc_dma_in
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_IN
+ 1]},
{SCRIPT_MATCH(ASC_INT_FC
| ASC_INT_BS
, ASC_PHASE_DATAO
), /* 3 */
asc_dma_out
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_OUT
+ 1]},
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_STATUS
), /* 4 */
asc_last_dma_out
, ASC_CMD_I_COMPLETE
,
&asc_scripts
[SCRIPT_GET_STATUS
]},
/* continue data out after a chunk is finished */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_DATAO
), /* 5 */
asc_dma_out
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_OUT
+ 1]},
/* simple command with no data transfer */
{SCRIPT_MATCH(ASC_INT_FC
| ASC_INT_BS
, ASC_PHASE_STATUS
), /* 6 */
script_nop
, ASC_CMD_I_COMPLETE
,
&asc_scripts
[SCRIPT_GET_STATUS
]},
/* get status and finish command */
{SCRIPT_MATCH(ASC_INT_FC
, ASC_PHASE_MSG_IN
), /* 7 */
asc_get_status
, ASC_CMD_MSG_ACPT
,
&asc_scripts
[SCRIPT_GET_STATUS
+ 1]},
{SCRIPT_MATCH(ASC_INT_DISC
, 0), /* 8 */
&asc_scripts
[SCRIPT_GET_STATUS
+ 1]},
{SCRIPT_MATCH(ASC_INT_FC
, ASC_PHASE_MSG_IN
), /* 9 */
asc_msg_in
, ASC_CMD_MSG_ACPT
,
&asc_scripts
[SCRIPT_MSG_IN
+ 1]},
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_MSG_IN
), /* 10 */
script_nop
, ASC_CMD_XFER_INFO
,
&asc_scripts
[SCRIPT_MSG_IN
]},
/* send synchonous negotiation reply */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_MSG_OUT
), /* 11 */
asc_replysync
, ASC_CMD_XFER_INFO
,
&asc_scripts
[SCRIPT_REPLY_SYNC
]},
/* try to negotiate synchonous transfer parameters */
{SCRIPT_MATCH(ASC_INT_FC
| ASC_INT_BS
, ASC_PHASE_MSG_OUT
), /* 12 */
asc_sendsync
, ASC_CMD_XFER_INFO
,
&asc_scripts
[SCRIPT_TRY_SYNC
+ 1]},
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_MSG_IN
), /* 13 */
script_nop
, ASC_CMD_XFER_INFO
,
&asc_scripts
[SCRIPT_MSG_IN
]},
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_COMMAND
), /* 14 */
script_nop
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_RESUME_NO_DATA
]},
/* handle a disconnect */
{SCRIPT_MATCH(ASC_INT_DISC
, ASC_PHASE_DATAO
), /* 15 */
asc_disconnect
, ASC_CMD_ENABLE_SEL
,
&asc_scripts
[SCRIPT_RESEL
]},
/* reselect sequence: this is just a placeholder so match fails */
{SCRIPT_MATCH(0, ASC_PHASE_MSG_IN
), /* 16 */
script_nop
, ASC_CMD_MSG_ACPT
,
&asc_scripts
[SCRIPT_RESEL
]},
/* resume data in after a message */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_DATAI
), /* 17 */
asc_resume_in
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_IN
+ 1]},
/* resume partial DMA data in after a message */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_DATAI
), /* 18 */
asc_resume_dma_in
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_IN
+ 1]},
/* resume data out after a message */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_DATAO
), /* 19 */
asc_resume_out
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_OUT
+ 1]},
/* resume partial DMA data out after a message */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_DATAO
), /* 20 */
asc_resume_dma_out
, ASC_CMD_XFER_INFO
| ASC_CMD_DMA
,
&asc_scripts
[SCRIPT_DATA_OUT
+ 1]},
/* resume after a message when there is no more data */
{SCRIPT_MATCH(ASC_INT_BS
, ASC_PHASE_STATUS
), /* 21 */
script_nop
, ASC_CMD_I_COMPLETE
,
&asc_scripts
[SCRIPT_GET_STATUS
]},
* State kept for each active SCSI device.
typedef struct scsi_state
{
script_t
*script
; /* saved script while processing error */
int statusByte
; /* status byte returned during STATUS_PHASE */
int error
; /* errno to pass back to device driver */
u_char
*dmaBufAddr
; /* DMA buffer address */
u_int dmaBufSize
; /* DMA buffer size */
int dmalen
; /* amount to transfer in this chunk */
int dmaresid
; /* amount not transfered if chunk suspended */
int buflen
; /* total remaining amount of data to transfer */
char *buf
; /* current pointer within scsicmd->buf */
int flags
; /* see below */
int msglen
; /* number of message bytes to read */
int msgcnt
; /* number of message bytes received */
u_char sync_period
; /* DMA synchronous period */
u_char sync_offset
; /* DMA synchronous xfer offset or 0 if async */
u_char msg_out
; /* next MSG_OUT byte to send */
u_char msg_in
[16]; /* buffer for multibyte messages */
#define DISCONN 0x01 /* true if currently disconnected from bus */
#define DMA_IN_PROGRESS 0x02 /* true if data DMA started */
#define DMA_IN 0x04 /* true if reading from SCSI device */
#define DMA_OUT 0x10 /* true if writing to SCSI device */
#define DID_SYNC 0x20 /* true if synchronous offset was negotiated */
#define TRY_SYNC 0x40 /* true if try neg. synchronous offset */
#define PARITY_ERR 0x80 /* true if parity error seen */
* State kept for each active SCSI host interface (53C94).
asc_regmap_t
*regs
; /* chip address */
volatile int *dmar
; /* DMA address register address */
u_char
*buff
; /* RAM buffer address (uncached) */
int myid
; /* SCSI ID of this interface */
int myidmask
; /* ~(1 << myid) */
int state
; /* current SCSI connection state */
int target
; /* target SCSI ID if busy */
script_t
*script
; /* next expected interrupt & action */
ScsiCmd
*cmd
[ASC_NCMD
]; /* active command indexed by SCSI ID */
State st
[ASC_NCMD
]; /* state info for each active command */
void (*dma_start
)(); /* Start dma routine */
void (*dma_end
)(); /* End dma routine */
int dma_xfer
; /* Dma len still to go */
int min_period
; /* Min transfer period clk/byte */
int max_period
; /* Max transfer period clk/byte */
int ccf
; /* CCF, whatever that really is? */
int timeout_250
; /* 250ms timeout */
int tb_ticks
; /* 4ns. ticks/tb channel ticks */
#define ASC_STATE_IDLE 0 /* idle state */
#define ASC_STATE_BUSY 1 /* selecting or currently connected */
#define ASC_STATE_TARGET 2 /* currently selected as target */
#define ASC_STATE_RESEL 3 /* currently waiting for reselect */
typedef struct asc_softc
*asc_softc_t
;
static void tb_dma_start(), tb_dma_end(), asic_dma_start(), asic_dma_end();
* Definition of the controller for the auto-configuration program.
struct driver ascdriver
= {
"asc", asc_probe
, asc_start
, 0, asc_intr
,
* Test to see if device is present.
* Return true if found and initialized ok.
register struct pmax_ctlr
*cp
;
register asc_softc_t asc
;
register asc_regmap_t
*regs
;
if ((unit
= cp
->pmax_unit
) >= NASC
)
if (badaddr(cp
->pmax_addr
+ ASC_OFFSET_53C94
, 1))
* Initialize hw descriptor, cache some pointers
asc
->regs
= (asc_regmap_t
*)(cp
->pmax_addr
+ ASC_OFFSET_53C94
);
* Set up machine dependencies.
* 2) timing based on turbochannel frequency
switch (pmax_boardtype
) {
asc
->buff
= (u_char
*)MACH_PHYS_TO_UNCACHED(asc_iomem
);
*((volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base
)) = -1;
*((volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base
)) = -1;
*((volatile int *)ASIC_REG_SCSI_SCR(asic_base
)) = 0;
asc
->dma_start
= asic_dma_start
;
asc
->dma_end
= asic_dma_end
;
* Fall through for turbochannel option.
asc
->dmar
= (volatile int *)(cp
->pmax_addr
+ ASC_OFFSET_DMAR
);
asc
->buff
= (u_char
*)(cp
->pmax_addr
+ ASC_OFFSET_RAM
);
bufsiz
= PER_TGT_DMA_SIZE
;
asc
->dma_start
= tb_dma_start
;
asc
->dma_end
= tb_dma_end
;
* Now for timing. The 3max has a 25Mhz tb whereas the 3min and
switch (pmax_boardtype
) {
asc
->min_period
= ASC_MIN_PERIOD25
;
asc
->max_period
= ASC_MAX_PERIOD25
;
asc
->timeout_250
= ASC_TIMEOUT_250(25, asc
->ccf
);
asc
->min_period
= ASC_MIN_PERIOD12
;
asc
->max_period
= ASC_MAX_PERIOD12
;
asc
->timeout_250
= ASC_TIMEOUT_250(13, asc
->ccf
);
asc
->state
= ASC_STATE_IDLE
;
* Reset chip, fully. Note that interrupts are already enabled.
/* preserve our ID for now */
asc
->myid
= regs
->asc_cnfg1
& ASC_CNFG1_MY_BUS_ID
;
asc
->myidmask
= ~(1 << asc
->myid
);
* Our SCSI id on the bus.
* The user can set this via the prom on 3maxen/pmaxen.
* If this changes it is easy to fix: make a default that
* can be changed as boot arg.
regs
->asc_cnfg1
= (regs
->asc_cnfg1
& ~ASC_CNFG1_MY_BUS_ID
) |
(scsi_initiator_id
[unit
] & 0x7);
id
= regs
->asc_cnfg1
& ASC_CNFG1_MY_BUS_ID
;
* Statically partition the DMA buffer between targets.
* This way we will eventually be able to attach/detach
* drives on-fly. And 18k/target is plenty for normal use.
* Give each target its own DMA buffer region.
* We may want to try ping ponging buffers later.
for (i
= 0; i
< ASC_NCMD
; i
++) {
asc
->st
[i
].dmaBufAddr
= asc
->buff
+ bufsiz
* i
;
asc
->st
[i
].dmaBufSize
= bufsiz
;
printf("asc%d at nexus0 csr 0x%x priority %d SCSI id %d\n",
unit
, cp
->pmax_addr
, cp
->pmax_pri
, id
);
* Start activity on a SCSI device.
* We maintain information on each device separately since devices can
* connect/disconnect during an operation.
register ScsiCmd
*scsicmd
; /* command to start */
register struct scsi_device
*sdp
= scsicmd
->sd
;
register asc_softc_t asc
= &asc_softc
[sdp
->sd_ctlr
];
* Check if another command is already in progress.
* We may have to change this if we allow SCSI devices with
if (asc
->cmd
[sdp
->sd_drive
]) {
printf("asc%d: device %s busy at start\n", sdp
->sd_ctlr
,
(*sdp
->sd_driver
->d_done
)(scsicmd
->unit
, EBUSY
,
asc
->cmd
[sdp
->sd_drive
] = scsicmd
;
asc_startcmd(asc
, sdp
->sd_drive
);
* Reset chip and wait till done
regs
->asc_cmd
= ASC_CMD_RESET
;
MachEmptyWriteBuffer(); DELAY(25);
/* spec says this is needed after reset */
regs
->asc_cmd
= ASC_CMD_NOP
;
MachEmptyWriteBuffer(); DELAY(25);
* Set up various chip parameters
regs
->asc_ccf
= asc
->ccf
;
MachEmptyWriteBuffer(); DELAY(25);
regs
->asc_sel_timo
= asc
->timeout_250
;
regs
->asc_cnfg1
= asc
->myid
| ASC_CNFG1_P_CHECK
;
/* include ASC_CNFG2_SCSI2 if you want to allow SCSI II commands */
regs
->asc_cnfg2
= /* ASC_CNFG2_RFB | ASC_CNFG2_SCSI2 | */ ASC_CNFG2_EPL
;
regs
->asc_syn_p
= asc
->min_period
;
regs
->asc_syn_o
= 0; /* async for now */
* Start a SCSI command on a target.
asc_startcmd(asc
, target
)
register asc_regmap_t
*regs
;
register ScsiCmd
*scsicmd
;
* See if another target is currently selected on this SCSI bus.
* If a reselection is in progress, it is Ok to ignore it since
* the ASC will automatically cancel the command and flush
* the FIFO if the ASC is reselected before the command starts.
* If we try to use ASC_CMD_DISABLE_SEL, we can hang the system if
* a reselect occurs before starting the command.
asc
->state
= ASC_STATE_BUSY
;
/* cache some pointers */
scsicmd
= asc
->cmd
[target
];
state
= &asc
->st
[target
];
printf("asc_startcmd: %s target %d cmd %x len %d\n",
scsicmd
->sd
->sd_driver
->d_name
, target
,
scsicmd
->cmd
[0], scsicmd
->buflen
);
* Init the chip and target state.
state
->flags
= state
->flags
& DID_SYNC
;
state
->script
= (script_t
*)0;
state
->msg_out
= SCSI_NO_OP
;
* Copy command data to the DMA buffer.
bcopy(scsicmd
->cmd
, state
->dmaBufAddr
, len
);
/* check for simple SCSI command with no data transfer */
if ((state
->buflen
= scsicmd
->buflen
) == 0) {
/* check for sync negotiation */
if ((scsicmd
->flags
& SCSICMD_USE_SYNC
) &&
!(state
->flags
& DID_SYNC
)) {
asc
->script
= &asc_scripts
[SCRIPT_TRY_SYNC
];
state
->flags
|= TRY_SYNC
;
asc
->script
= &asc_scripts
[SCRIPT_SIMPLE
];
} else if (scsicmd
->flags
& SCSICMD_DATA_TO_DEVICE
) {
asc
->script
= &asc_scripts
[SCRIPT_DATA_OUT
];
state
->buf
= scsicmd
->buf
;
asc
->script
= &asc_scripts
[SCRIPT_DATA_IN
];
state
->buf
= scsicmd
->buf
;
asc_debug_cmd
= scsicmd
->cmd
[0];
if (scsicmd
->cmd
[0] == SCSI_READ_EXT
) {
asc_debug_bn
= (scsicmd
->cmd
[2] << 24) |
(scsicmd
->cmd
[3] << 16) |
asc_debug_sz
= (scsicmd
->cmd
[7] << 8) | scsicmd
->cmd
[8];
asc_logp
->status
= PACK(asc
- asc_softc
, 0, 0, asc_debug_cmd
);
asc_logp
->target
= asc
->target
;
asc_logp
->state
= asc
->script
- asc_scripts
;
asc_logp
->msg
= SCSI_DIS_REC_IDENTIFY
;
asc_logp
->resid
= scsicmd
->buflen
;
if (++asc_logp
>= &asc_log
[NLOG
])
/* preload the FIFO with the message to be sent */
regs
->asc_fifo
= SCSI_DIS_REC_IDENTIFY
;
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
, ASCDMA_WRITE
);
regs
->asc_dbus_id
= target
;
readback(regs
->asc_dbus_id
);
regs
->asc_syn_p
= state
->sync_period
;
readback(regs
->asc_syn_p
);
regs
->asc_syn_o
= state
->sync_offset
;
readback(regs
->asc_syn_o
);
if (state
->flags
& TRY_SYNC
)
regs
->asc_cmd
= ASC_CMD_SEL_ATN_STOP
;
regs
->asc_cmd
= ASC_CMD_SEL_ATN
| ASC_CMD_DMA
;
* Take interrupts from the chip
* Move along the current command's script if
* all is well, invoke error handler if not.
register asc_softc_t asc
= &asc_softc
[unit
];
register asc_regmap_t
*regs
= asc
->regs
;
register int ss
, ir
, status
;
/* collect ephemeral information */
status
= regs
->asc_status
;
ir
= regs
->asc_intr
; /* this resets the previous two */
asc_logp
->status
= PACK(unit
, status
, ss
, ir
);
asc_logp
->target
= (asc
->state
== ASC_STATE_BUSY
) ? asc
->target
: -1;
asc_logp
->state
= scpt
- asc_scripts
;
if (++asc_logp
>= &asc_log
[NLOG
])
printf("asc_intr: status %x ss %x ir %x cond %d:%x\n",
status
, ss
, ir
, scpt
- asc_scripts
, scpt
->condition
);
/* check the expected state */
if (SCRIPT_MATCH(ir
, status
) == scpt
->condition
) {
* Perform the appropriate operation, then proceed.
if ((*scpt
->action
)(asc
, status
, ss
, ir
)) {
regs
->asc_cmd
= scpt
->command
;
asc
->script
= scpt
->next
;
* Check for parity error.
* Hardware will automatically set ATN
* to request the device for a MSG_OUT phase.
if (status
& ASC_CSR_PE
) {
printf("asc%d: SCSI device %d: incomming parity error seen\n",
asc
- asc_softc
, asc
->target
);
asc
->st
[asc
->target
].flags
|= PARITY_ERR
;
* Probably a bug in a device driver.
if (status
& ASC_CSR_GE
) {
printf("asc%d: SCSI device %d: gross error\n",
asc
- asc_softc
, asc
->target
);
/* check for message in or out */
if ((ir
& ~ASC_INT_FC
) == ASC_INT_BS
) {
state
= &asc
->st
[asc
->target
];
switch (ASC_PHASE(status
)) {
fifo
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
;
printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n",
state
->buflen
, state
->dmalen
, len
, fifo
);
* Check for parity error.
* Hardware will automatically set ATN
* to request the device for a MSG_OUT phase.
if (state
->flags
& PARITY_ERR
) {
state
->flags
&= ~PARITY_ERR
;
state
->msg_out
= SCSI_MESSAGE_PARITY_ERROR
;
/* reset message in counter */
state
->msg_out
= SCSI_NO_OP
;
regs
->asc_fifo
= state
->msg_out
;
regs
->asc_cmd
= ASC_CMD_XFER_INFO
;
/* probably an error in the SCSI command */
asc
->script
= &asc_scripts
[SCRIPT_GET_STATUS
];
regs
->asc_cmd
= ASC_CMD_I_COMPLETE
;
/* check for DMA in progress */
fifo
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
;
/* flush any data in the FIFO */
if (state
->flags
& DMA_OUT
)
else if (state
->flags
& DMA_IN
) {
printf("asc_intr: IN: dmalen %d len %d fifo %d\n",
state
->dmalen
, len
, fifo
); /* XXX */
cp
= state
->dmaBufAddr
+ (state
->dmalen
- len
);
printf("asc_intr: dmalen %d len %d fifo %d\n",
state
->dmalen
, len
, fifo
); /* XXX */
regs
->asc_cmd
= ASC_CMD_FLUSH
;
/* save number of bytes still to be sent or received */
asc_log
[NLOG
- 1].resid
= len
;
asc_logp
[-1].resid
= len
;
/* setup state to resume to */
if (state
->flags
& DMA_IN
) {
* Since the ASC_CNFG3_SRB bit of the
* cnfg3 register bit is not set,
* we just transferred an extra byte.
* Since we can't resume on an odd byte
* boundary, we copy the valid data out
* and resume DMA at the start address.
printf("asc_intr: msg in len %d (fifo %d)\n",
len
= state
->dmalen
- len
;
&asc_scripts
[SCRIPT_RESUME_DMA_IN
];
} else if (state
->flags
& DMA_OUT
)
&asc_scripts
[SCRIPT_RESUME_DMA_OUT
];
state
->script
= asc
->script
;
/* setup state to resume to */
if (state
->flags
& DMA_IN
) {
if (state
->flags
& DMA_IN_PROGRESS
) {
state
->flags
&= ~DMA_IN_PROGRESS
;
(*asc
->dma_end
)(asc
, state
, ASCDMA_READ
);
bcopy(state
->dmaBufAddr
, state
->buf
,
&asc_scripts
[SCRIPT_RESUME_IN
];
&asc_scripts
[SCRIPT_RESUME_NO_DATA
];
} else if (state
->flags
& DMA_OUT
) {
* If this is the last chunk, the next expected
* state is to get status.
if (state
->flags
& DMA_IN_PROGRESS
) {
state
->flags
&= ~DMA_IN_PROGRESS
;
(*asc
->dma_end
)(asc
, state
, ASCDMA_WRITE
);
&asc_scripts
[SCRIPT_RESUME_OUT
];
&asc_scripts
[SCRIPT_RESUME_NO_DATA
];
} else if (asc
->script
== &asc_scripts
[SCRIPT_SIMPLE
])
&asc_scripts
[SCRIPT_RESUME_NO_DATA
];
state
->script
= asc
->script
;
/* setup to receive a message */
asc
->script
= &asc_scripts
[SCRIPT_MSG_IN
];
regs
->asc_cmd
= ASC_CMD_XFER_INFO
;
/* check for SCSI bus reset */
if (ir
& ASC_INT_RESET
) {
printf("asc%d: SCSI bus reset!!\n", asc
- asc_softc
);
/* need to flush any pending commands */
for (i
= 0; i
< ASC_NCMD
; i
++) {
/* rearbitrate synchronous offset */
for (i
= 0; i
< ASC_NCMD
; i
++) {
asc
->st
[i
].sync_offset
= 0;
/* check for command errors */
/* check for disconnect */
state
= &asc
->st
[asc
->target
];
case 0: /* device did not respond */
/* check for one of the starting scripts */
switch (asc
->script
- asc_scripts
) {
if (regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
) {
regs
->asc_cmd
= ASC_CMD_FLUSH
;
asc_end(asc
, status
, ss
, ir
);
printf("asc%d: SCSI device %d: unexpected disconnect\n",
asc
- asc_softc
, asc
->target
);
* On rare occasions my RZ24 does a disconnect during
* data in phase and the following seems to keep it
* XXX Should a scsi disk ever do this??
asc
->script
= &asc_scripts
[SCRIPT_RESEL
];
asc
->state
= ASC_STATE_RESEL
;
regs
->asc_cmd
= ASC_CMD_ENABLE_SEL
;
if (ir
& ASC_INT_RESEL
) {
fifo
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
;
/* read unencoded SCSI ID and convert to binary */
msg
= regs
->asc_fifo
& asc
->myidmask
;
for (id
= 0; (msg
& 1) == 0; id
++)
/* read identify message */
asc_log
[NLOG
- 1].msg
= msg
;
asc
->state
= ASC_STATE_BUSY
;
asc
->script
= state
->script
;
state
->script
= (script_t
*)0;
if (!(state
->flags
& DISCONN
))
state
->flags
&= ~DISCONN
;
regs
->asc_syn_p
= state
->sync_period
;
regs
->asc_syn_o
= state
->sync_offset
;
regs
->asc_cmd
= ASC_CMD_MSG_ACPT
;
/* check if we are being selected as a target */
if (ir
& (ASC_INT_SEL
| ASC_INT_SEL_ATN
))
* 'ir' must be just ASC_INT_FC.
* This is normal if canceling an ASC_ENABLE_SEL.
/* watch out for HW race conditions and setup & hold time violations */
while (ir
!= (status
= regs
->asc_status
))
if (status
& ASC_CSR_INT
)
* All the many little things that the interrupt
* routine might switch to.
script_nop(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
asc_get_status(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
* Get the last two bytes in the FIFO.
if ((data
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
) != 2) {
printf("asc_get_status: fifo cnt %d\n", data
); /* XXX */
asc_DumpLog("get_status"); /* XXX */
asc
->regs
->asc_cmd
= ASC_CMD_MSG_ACPT
;
readback(asc
->regs
->asc_cmd
);
} while ((regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
) > 2);
/* save the status byte */
asc
->st
[asc
->target
].statusByte
= data
= regs
->asc_fifo
;
asc_log
[NLOG
- 1].msg
= data
;
/* get the (presumed) command_complete message */
if ((data
= regs
->asc_fifo
) == SCSI_COMMAND_COMPLETE
)
printf("asc_get_status: status %x cmd %x\n",
asc
->st
[asc
->target
].statusByte
, data
);
asc_DumpLog("asc_get_status");
asc_end(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register ScsiCmd
*scsicmd
;
asc
->state
= ASC_STATE_IDLE
;
scsicmd
= asc
->cmd
[target
];
asc
->cmd
[target
] = (ScsiCmd
*)0;
state
= &asc
->st
[target
];
printf("asc_end: %s target %d cmd %x err %d resid %d\n",
scsicmd
->sd
->sd_driver
->d_name
, target
,
scsicmd
->cmd
[0], state
->error
, state
->buflen
);
if (target
< 0 || !scsicmd
)
/* look for disconnected devices */
for (i
= 0; i
< ASC_NCMD
; i
++) {
if (!asc
->cmd
[i
] || !(asc
->st
[i
].flags
& DISCONN
))
asc
->regs
->asc_cmd
= ASC_CMD_ENABLE_SEL
;
readback(asc
->regs
->asc_cmd
);
asc
->state
= ASC_STATE_RESEL
;
asc
->script
= &asc_scripts
[SCRIPT_RESEL
];
* Look for another device that is ready.
* May want to keep last one started and increment for fairness
* rather than always starting at zero.
for (i
= 0; i
< ASC_NCMD
; i
++) {
/* don't restart a disconnected command */
if (!asc
->cmd
[i
] || (asc
->st
[i
].flags
& DISCONN
))
/* signal device driver that the command is done */
(*scsicmd
->sd
->sd_driver
->d_done
)(scsicmd
->unit
, state
->error
,
state
->buflen
, state
->statusByte
);
asc_dma_in(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* check for previous chunk in buffer */
if (state
->flags
& DMA_IN_PROGRESS
) {
* Only count bytes that have been copied to memory.
* There may be some bytes in the FIFO if synchonous transfers
(*asc
->dma_end
)(asc
, state
, ASCDMA_READ
);
len
= state
->dmalen
- len
;
bcopy(state
->dmaBufAddr
, state
->buf
, len
);
/* setup to start reading the next chunk */
if (len
> state
->dmaBufSize
)
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
, ASCDMA_READ
);
printf("asc_dma_in: buflen %d, len %d\n", state
->buflen
, len
);
/* check for next chunk */
state
->flags
|= DMA_IN_PROGRESS
;
if (len
!= state
->buflen
) {
regs
->asc_cmd
= ASC_CMD_XFER_INFO
| ASC_CMD_DMA
;
asc
->script
= &asc_scripts
[SCRIPT_CONTINUE_IN
];
asc_last_dma_in(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* copy data from buffer to main memory */
(*asc
->dma_end
)(asc
, state
, ASCDMA_READ
);
fifo
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
;
printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n",
state
->buflen
, state
->dmalen
, len
, fifo
);
/* device must be trying to send more than we expect */
regs
->asc_cmd
= ASC_CMD_FLUSH
;
state
->flags
&= ~DMA_IN_PROGRESS
;
len
= state
->dmalen
- len
;
bcopy(state
->dmaBufAddr
, state
->buf
, len
);
asc_resume_in(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* setup to start reading the next chunk */
if (len
> state
->dmaBufSize
)
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
, ASCDMA_READ
);
printf("asc_resume_in: buflen %d, len %d\n", state
->buflen
,
/* check for next chunk */
state
->flags
|= DMA_IN_PROGRESS
;
if (len
!= state
->buflen
) {
regs
->asc_cmd
= ASC_CMD_XFER_INFO
| ASC_CMD_DMA
;
asc
->script
= &asc_scripts
[SCRIPT_CONTINUE_IN
];
asc_resume_dma_in(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* setup to finish reading the current chunk */
off
= state
->dmalen
- len
;
if ((off
& 1) && state
->sync_offset
) {
printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n",
state
->dmalen
, len
, off
); /* XXX */
regs
->asc_res_fifo
= state
->dmaBufAddr
[off
];
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
+ off
, ASCDMA_READ
);
printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n",
state
->dmalen
, state
->buflen
, len
, off
);
/* check for next chunk */
state
->flags
|= DMA_IN_PROGRESS
;
if (state
->dmalen
!= state
->buflen
) {
regs
->asc_cmd
= ASC_CMD_XFER_INFO
| ASC_CMD_DMA
;
asc
->script
= &asc_scripts
[SCRIPT_CONTINUE_IN
];
asc_dma_out(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
if (state
->flags
& DMA_IN_PROGRESS
) {
/* check to be sure previous chunk was finished */
fifo
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
;
printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
state
->buflen
, state
->dmalen
, len
, fifo
); /* XXX */
len
= state
->dmalen
- len
;
/* setup for this chunk */
if (len
> state
->dmaBufSize
)
bcopy(state
->buf
, state
->dmaBufAddr
, len
);
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
, ASCDMA_WRITE
);
printf("asc_dma_out: buflen %d, len %d\n", state
->buflen
, len
);
/* check for next chunk */
state
->flags
|= DMA_IN_PROGRESS
;
if (len
!= state
->buflen
) {
regs
->asc_cmd
= ASC_CMD_XFER_INFO
| ASC_CMD_DMA
;
asc
->script
= &asc_scripts
[SCRIPT_CONTINUE_OUT
];
asc_last_dma_out(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
fifo
= regs
->asc_flags
& ASC_FLAGS_FIFO_CNT
;
printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
state
->buflen
, state
->dmalen
, len
, fifo
);
regs
->asc_cmd
= ASC_CMD_FLUSH
;
printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n",
state
->buflen
, state
->dmalen
, len
, fifo
);
state
->flags
&= ~DMA_IN_PROGRESS
;
len
= state
->dmalen
- len
;
asc_resume_out(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* setup for this chunk */
if (len
> state
->dmaBufSize
)
bcopy(state
->buf
, state
->dmaBufAddr
, len
);
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
, ASCDMA_WRITE
);
printf("asc_resume_out: buflen %d, len %d\n", state
->buflen
,
/* check for next chunk */
state
->flags
|= DMA_IN_PROGRESS
;
if (len
!= state
->buflen
) {
regs
->asc_cmd
= ASC_CMD_XFER_INFO
| ASC_CMD_DMA
;
asc
->script
= &asc_scripts
[SCRIPT_CONTINUE_OUT
];
asc_resume_dma_out(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* setup to finish writing this chunk */
off
= state
->dmalen
- len
;
printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n",
state
->dmalen
, len
, off
); /* XXX */
regs
->asc_fifo
= state
->dmaBufAddr
[off
];
(*asc
->dma_start
)(asc
, state
, state
->dmaBufAddr
+ off
, ASCDMA_WRITE
);
printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n",
state
->dmalen
, state
->buflen
, len
, off
);
/* check for next chunk */
state
->flags
|= DMA_IN_PROGRESS
;
if (state
->dmalen
!= state
->buflen
) {
regs
->asc_cmd
= ASC_CMD_XFER_INFO
| ASC_CMD_DMA
;
asc
->script
= &asc_scripts
[SCRIPT_CONTINUE_OUT
];
asc_sendsync(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* send the extended synchronous negotiation message */
regs
->asc_fifo
= SCSI_EXTENDED_MSG
;
regs
->asc_fifo
= SCSI_SYNCHRONOUS_XFER
;
regs
->asc_fifo
= SCSI_MIN_PERIOD
;
regs
->asc_fifo
= ASC_MAX_OFFSET
;
/* state to resume after we see the sync reply message */
state
->script
= asc
->script
+ 2;
asc_replysync(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
printf("asc_replysync: %x %x\n",
asc_to_scsi_period
[state
->sync_period
] * asc
->tb_ticks
,
/* send synchronous transfer in response to a request */
regs
->asc_fifo
= SCSI_EXTENDED_MSG
;
regs
->asc_fifo
= SCSI_SYNCHRONOUS_XFER
;
regs
->asc_fifo
= asc_to_scsi_period
[state
->sync_period
] * asc
->tb_ticks
;
regs
->asc_fifo
= state
->sync_offset
;
regs
->asc_cmd
= ASC_CMD_XFER_INFO
;
/* return to the appropriate script */
asc_DumpLog("asc_replsync");
asc
->script
= state
->script
;
state
->script
= (script_t
*)0;
asc_msg_in(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register asc_regmap_t
*regs
= asc
->regs
;
register State
*state
= &asc
->st
[asc
->target
];
/* read one message byte */
asc_log
[NLOG
- 1].msg
= msg
;
/* check for multi-byte message */
if (state
->msglen
!= 0) {
/* first byte is the message length */
if (state
->msgcnt
>= state
->msglen
)
state
->msg_in
[state
->msgcnt
++] = msg
;
/* did we just read the last byte of the message? */
if (state
->msgcnt
!= state
->msglen
)
/* process an extended message */
printf("asc_msg_in: msg %x %x %x\n",
switch (state
->msg_in
[0]) {
case SCSI_SYNCHRONOUS_XFER
:
state
->flags
|= DID_SYNC
;
state
->sync_offset
= state
->msg_in
[2];
/* convert SCSI period to ASC period */
i
= state
->msg_in
[1] / asc
->tb_ticks
;
else if (i
>= asc
->max_period
) {
/* can't do sync transfer, period too long */
printf("asc%d: SCSI device %d: sync xfer period too long (%d)\n",
asc
- asc_softc
, asc
->target
, i
);
if ((i
* asc
->tb_ticks
) != state
->msg_in
[1])
state
->sync_period
= i
& 0x1F;
* If this is a request, check minimums and
* send back an acknowledge.
if (!(state
->flags
& TRY_SYNC
)) {
regs
->asc_cmd
= ASC_CMD_SET_ATN
;
if (state
->sync_period
< asc
->min_period
)
if (state
->sync_offset
> ASC_MAX_OFFSET
)
asc
->script
= &asc_scripts
[SCRIPT_REPLY_SYNC
];
regs
->asc_syn_p
= state
->sync_period
;
readback(regs
->asc_syn_p
);
regs
->asc_syn_o
= state
->sync_offset
;
readback(regs
->asc_syn_o
);
regs
->asc_cmd
= ASC_CMD_MSG_ACPT
;
regs
->asc_syn_p
= state
->sync_period
;
readback(regs
->asc_syn_p
);
regs
->asc_syn_o
= state
->sync_offset
;
readback(regs
->asc_syn_o
);
printf("asc%d: SCSI device %d: rejecting extended message 0x%x\n",
asc
- asc_softc
, asc
->target
,
/* process first byte of a message */
printf("asc_msg_in: msg %x\n", msg
);
case SCSI_MESSAGE_REJECT
:
printf(" did not like SYNCH xfer "); /* XXX */
state
->flags
|= DID_SYNC
;
regs
->asc_cmd
= ASC_CMD_MSG_ACPT
;
status
= asc_wait(regs
, ASC_CSR_INT
);
/* some just break out here, some dont */
if (ASC_PHASE(status
) == ASC_PHASE_MSG_OUT
) {
regs
->asc_fifo
= SCSI_ABORT
;
regs
->asc_cmd
= ASC_CMD_XFER_INFO
;
status
= asc_wait(regs
, ASC_CSR_INT
);
asc_end(asc
, status
, 0, ir
);
case SCSI_EXTENDED_MSG
: /* read an extended message */
/* setup to read message length next */
case SCSI_SAVE_DATA_POINTER
:
/* expect another message */
case SCSI_RESTORE_POINTERS
:
* Need to do the following if resuming synchonous data in
* on an odd byte boundary.
regs->asc_cnfg2 |= ASC_CNFG2_RFB;
if (state
->flags
& DISCONN
)
regs
->asc_cmd
= ASC_CMD_MSG_ACPT
;
asc
->script
= &asc_scripts
[SCRIPT_DISCONNECT
];
printf("asc%d: SCSI device %d: rejecting message 0x%x\n",
asc
- asc_softc
, asc
->target
, msg
);
/* request a message out before acknowledging this message */
state
->msg_out
= SCSI_MESSAGE_REJECT
;
regs
->asc_cmd
= ASC_CMD_SET_ATN
;
/* return to original script */
regs
->asc_cmd
= ASC_CMD_MSG_ACPT
;
asc_DumpLog("asc_msg_in");
asc
->script
= state
->script
;
state
->script
= (script_t
*)0;
asc_disconnect(asc
, status
, ss
, ir
)
register asc_softc_t asc
;
register int status
, ss
, ir
;
register State
*state
= &asc
->st
[asc
->target
];
if (!(state
->flags
& DISCONN
)) {
printf("asc_disconnect: device %d: DISCONN not set!\n",
asc
->state
= ASC_STATE_RESEL
;
* DMA handling routines. For a turbochannel device, just set the dmar.
* For the I/O ASIC, handle the actual DMA interface.
tb_dma_start(asc
, state
, cp
, flag
)
if (flag
== ASCDMA_WRITE
)
*asc
->dmar
= ASC_DMAR_WRITE
| ASC_DMA_ADDR(cp
);
*asc
->dmar
= ASC_DMA_ADDR(cp
);
tb_dma_end(asc
, state
, flag
)
asic_dma_start(asc
, state
, cp
, flag
)
register volatile u_int
*ssr
= (volatile u_int
*)
/* stop DMA engine first */
*ssr
&= ~ASIC_CSR_DMAEN_SCSI
;
*((volatile int *)ASIC_REG_SCSI_SCR(asic_base
)) = 0;
phys
= MACH_CACHED_TO_PHYS(cp
);
cp
= (caddr_t
)pmax_trunc_page(cp
+ NBPG
);
nphys
= MACH_CACHED_TO_PHYS(cp
);
asc
->dma_xfer
= state
->dmalen
- (nphys
- phys
);
*(volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base
) =
*(volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base
) =
*ssr
|= ASIC_CSR_SCSI_DIR
| ASIC_CSR_DMAEN_SCSI
;
*ssr
= (*ssr
& ~ASIC_CSR_SCSI_DIR
) | ASIC_CSR_DMAEN_SCSI
;
asic_dma_end(asc
, state
, flag
)
register volatile u_int
*ssr
= (volatile u_int
*)
register volatile u_int
*dmap
= (volatile u_int
*)
ASIC_REG_SCSI_DMAPTR(asic_base
);
*ssr
&= ~ASIC_CSR_DMAEN_SCSI
;
to
= (u_short
*)MACH_PHYS_TO_CACHED(*dmap
>> 3);
*((volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base
)) = -1;
if (flag
== ASCDMA_READ
) {
MachFlushDCache(MACH_PHYS_TO_CACHED(
MACH_UNCACHED_TO_PHYS(state
->dmaBufAddr
)), state
->dmalen
);
if (nb
= *((int *)ASIC_REG_SCSI_SCR(asic_base
))) {
/* pick up last upto6 bytes, sigh. */
/* Last byte really xferred is.. */
w
= *(int *)ASIC_REG_SCSI_SDR0(asic_base
);
w
= *(int *)ASIC_REG_SCSI_SDR1(asic_base
);
* Called by asic_intr() for scsi dma pointer update interrupts.
asc_softc_t asc
= &asc_softc
[0];
if (asc
->dma_xfer
<= -NBPG
) {
volatile u_int
*ssr
= (volatile u_int
*)
*ssr
&= ~ASIC_CSR_DMAEN_SCSI
;
next_phys
= MACH_CACHED_TO_PHYS(asc
->dma_next
);
*(volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base
) =
ASIC_DMA_ADDR(next_phys
);
register struct asc_log
*lp
;
printf("asc: %s: cmd %x bn %d cnt %d\n", str
, asc_debug_cmd
,
asc_debug_bn
, asc_debug_sz
);
printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x resid %d\n",
asc_scripts
[lp
->state
].condition
,
if (++lp
>= &asc_log
[NLOG
])
} while (lp
!= asc_logp
);