* Copyright (c) 1992 The Regents of the University of California.
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
* %sccs.include.redist.c%
* @(#)esp.c 7.1 (Berkeley) %G%
* from: $Header: esp.c,v 1.22 92/06/17 06:59:33 torek Exp $ (LBL)
* Loosely derived from Mary Baker's devSCSIC90.c from the Berkeley
* Sprite project, which is:
* Copyright 1988 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
* from /sprite/src/kernel/dev/sun4c.md/RCS/devSCSIC90.c,v 1.4
* 90/12/19 12:37:58 mgbaker Exp $ SPRITE (Berkeley)
* Sbus ESP/DMA driver. A single driver must be used for both devices
* as they are physically tied to each other: The DMA chip can only
* be used to assist ESP SCSI transactions; the ESP interrupt enable is
* Since DMA and SCSI interrupts are handled in the same routine, the
* DMA device does not declare itself as an sbus device. This saves
#include "scsi/scsivar.h"
#include "machine/autoconf.h"
* This driver is organized as a collection of state machines. The
* primary machine is the SCSI sequencer:
* Given some previous SCSI state (as set up or tracked by us earlier)
* and the interrupt registers provided on the chips (dmacsr, espstat,
* espstep, and espintr), derive an action. In many cases this is
* just a matter of reading the target's phase and following its orders,
* which sets a new state.
* This sequencing is done in espact(); the state is primed in espselect().
* There will be (update this comment when there is) another state machine
* used to handle transfers that fall afoul of chip limits (16 bit DMA
* counter; 24 bit address counter in 32 bit address field).
* Another state bit is used to recover from bus resets:
* A single TEST UNIT READY is attempted on each target before any
* real communication begins; this TEST UNIT READY is allowed to
* fail in any way. This is required for the Quantum ProDrive 100
* MB disks, for instance, which respond to their first selection
* with status phase, and for anything that insists on implementing
* the broken SCSI-2 synch transfer initial message.
* This is done in espclear() (which calls espselect(); functions that
* call espselect() must check for clearing first).
* The state machines actually intermingle, as some SCSI sequences are
* only allowed during clearing.
struct device sc_dev
; /* base device */
volatile struct dmareg
*sc_dma
; /* register virtual address */
void dmaattach(struct device
*, struct device
*, void *);
{ NULL
, "dma", matchbyname
, dmaattach
, DV_DULL
, sizeof(struct dma_softc
) };
struct hba_softc sc_hba
; /* base device + hba, must be first */
struct sbusdev sc_sd
; /* sbus device */
struct intrhand sc_ih
; /* interrupt entry */
int sc_interrupts
; /* total number of interrupts taken */
struct dma_softc
*sc_dsc
; /* pointer to corresponding dma sc */
* Addresses mapped to hardware registers.
volatile struct espreg
*sc_esp
;
volatile struct dmareg
*sc_dma
;
* Copies of registers cleared/unlatched by reading.
int sc_clockfreq
; /* clock frequency */
u_char sc_sel_timeout
; /* select timeout */
u_char sc_id
; /* initiator ID (default = 7) */
u_char sc_needclear
; /* uncleared targets (1 bit each) */
u_char sc_esptype
; /* 100, 100A, 2xx (see below) */
u_char sc_ccf
; /* clock conversion factor */
u_char sc_conf1
; /* value for config reg 1 */
u_char sc_conf2
; /* value for config reg 2 */
u_char sc_conf3
; /* value for config reg 3 */
* Information pertaining to the current transfer,
* The size of sc_msg is the size of the ESP fifo,
* since we do message-in simply by allowing the fifo to fill.
char sc_probing
; /* used during autoconf; see below */
char sc_clearing
; /* true => cmd is just to clear targ */
char sc_state
; /* SCSI protocol state; see below */
char sc_sentcmd
; /* set once we get cmd out */
char sc_dmaactive
; /* true => doing dma */
u_char sc_sync
; /* synchronous transfer stuff (?) */
u_char sc_stat
[2]; /* status from last `status' phase */
u_char sc_msg
[16]; /* message from device */
u_short sc_dmactl
; /* control to load into dma csr */
u_long sc_dmaaddr
; /* addr to load into dma addr */
int sc_targ
; /* the target involved */
int sc_resid
; /* count of bytes not xferred */
struct scsi_cdb sc_cdb
; /* current command (not in dvma) */
* Values for sc_esptype (used to control configuration reset).
* The order is important; see espreset().
* Probe state. 0 means not probing. While looking for each target
* we set this to PROBE_TESTING and do a TEST UNIT READY on unit 0.
* If selection fails, this is changed to PROBE_NO_TARGET; otherwise
* we assume the target exists, regardless of the result of the test.
#define PROBE_NO_TARGET 2
* Note that S_CMDSVC is rare: normally we load the SCSI command into the
* ESP fifo and get interrupted only when the device has gone to data
* or status phase. If the device wants to play games, though, we end
* up doing things differently.
#define S_IDLE 0 /* not doing anything */
#define S_SEL 1 /* expecting select done interrupt */
#define S_CMDSVC 2 /* expecting service req interrupt */
"waiting for service request after command",
#define S_IOSVC 3 /* expecting service req interrupt */
"waiting for service request after io",
#define S_DI 4 /* expecting data-in done interrupt */
#define S_DO 5 /* expecting data-out done interrupt */
#define S_STAT 6 /* expecting status done interrupt */
#define S_MI 7 /* expecting message-in done interrupt */
#define S_FI 8 /* expecting final disconnect interrupt */
* Return values from espact().
#define ACT_CONT 0 /* espact() handled everything */
#define ACT_READ 1 /* target said it is sending us data */
#define ACT_WRITE 2 /* target said it is expecting data */
#define ACT_DONE 3 /* handled everything, and op is now done */
#define ACT_ERROR 4 /* an error occurred, op has been trashed */
#define ACT_RESET 5 /* please reset ESP, then do ACT_ERROR */
#define ACT_QUICKINTR 6 /* another interrupt is expected immediately */
/* autoconfiguration driver */
void espattach(struct device
*, struct device
*, void *);
{ NULL
, "esp", matchbyname
, espattach
, DV_DULL
, sizeof(struct esp_softc
) };
void espsbreset(struct device
*);
/* interrupt interface */
int espicmd(struct hba_softc
*, int, struct scsi_cdb
*, caddr_t
, int, int);
int espdump(struct hba_softc
*, int, struct scsi_cdb
*, caddr_t
, int);
void espstart(struct device
*, struct sq
*, struct buf
*,
scdgo_fn
, struct device
*);
int espgo(struct device
*, int, scintr_fn
, struct device
*,
void esprel(struct device
*);
void esphbareset(struct hba_softc
*, int);
static struct hbadriver esphbadriver
=
{ espicmd
, espdump
, espstart
, espgo
, esprel
, esphbareset
};
/* forward declarations */
static void espdoattach(int unit
);
static void espreset(struct esp_softc
*);
* The transfer size is limited to 16 bits since the scsi ctrl transfer
* counter is only 2 bytes. A 0 value means the biggest transfer size
#define MAX_TRANSFER_SIZE (64 * 1024)
/* Return true if this transfer will cross a dma boundary */
#define CROSS_DMA(addr, len) \
(((int)(addr) & 0xff000000) != (((int)(addr) + (len) - 1) & 0xff000000))
* Attach a found DMA chip.
* The second argument is really a pointer to an sbus_attach_args.
dmaattach(parent
, dev
, args
)
register struct dma_softc
*dsc
= (struct dma_softc
*)dev
;
register struct sbus_attach_args
*sa
= args
;
register volatile struct dmareg
*dma
;
dma
= (volatile struct dmareg
*)sa
->sa_ra
.ra_vaddr
;
dma
= (volatile struct dmareg
*)
mapiodev(sa
->sa_ra
.ra_paddr
, sizeof(struct dmareg
));
switch (rev
= DMA_REV(dma
->dma_csr
)) {
printf(": unknown revision %d\n", rev
);
espdoattach(dsc
->sc_dev
.dv_unit
);
* Attach a found ESP chip. Search for targets; attach each one found.
* The latter must be deferred if the corresponding dma chip has not yet
espattach(parent
, self
, args
)
register struct esp_softc
*sc
= (struct esp_softc
*)self
;
register struct sbus_attach_args
*sa
= args
;
register volatile struct espreg
*esp
;
if (sa
->sa_ra
.ra_nintr
!= 1) {
printf(": expected 1 interrupt, got %d\n", sa
->sa_ra
.ra_nintr
);
pri
= sa
->sa_ra
.ra_intr
[0].int_pri
;
esp
= (volatile struct espreg
*)sa
->sa_ra
.ra_vaddr
;
esp
= (volatile struct espreg
*)
mapiodev(sa
->sa_ra
.ra_paddr
, sizeof(struct espreg
));
node
= sa
->sa_ra
.ra_node
;
sc
->sc_id
= getpropint(node
, "initiator-id", 7);
freq
= getpropint(node
, "clock-frequency", -1);
freq
= ((struct sbus_softc
*)sc
->sc_hba
.hba_dev
.dv_parent
)->sc_clockfreq
;
/* MIGHT NEED TO RESET ESP CHIP HERE ...? */
* Find out whether we have a -100, -100A, or -2xx,
* and what speed it runs at.
sc
->sc_conf1
= sc
->sc_id
| ESPCONF1_PARENB
;
esp
->esp_conf1
= sc
->sc_conf1
;
esp
->esp_conf2
= ESPCONF2_SCSI2
| ESPCONF2_RPE
;
if ((esp
->esp_conf2
& ~ESPCONF2_RSVD
) !=
(ESPCONF2_SCSI2
| ESPCONF2_RPE
)) {
if (esp
->esp_conf3
!= 5) { /* XXX def bits */
sc
->sc_esptype
= ESP100A
;
printf(", clock = %s MHz, ID = %d\n", clockfreq(freq
), sc
->sc_id
);
* Set clock conversion factor and select timeout.
* N.B.: clock frequency is not actually used in the rest
* of the driver; I calculate it here for completeness only
* (so I can see it when debugging).
freq
= howmany(freq
, 1000 * 1000); /* convert to MHz */
t
= ESPCCF_FROMMHZ(freq
);
t
= ESPTIMO_REGVAL(250, t
, freq
); /* timeout = 250 ms. */
* Link into sbus; set interrupt handler.
sc
->sc_sd
.sd_reset
= espsbreset
;
sbus_establish(&sc
->sc_sd
, &sc
->sc_hba
.hba_dev
);
sc
->sc_ih
.ih_fun
= espintr
;
intr_establish(pri
, &sc
->sc_ih
);
espdoattach(sc
->sc_hba
.hba_dev
.dv_unit
);
* `Final' attach of esp occurs once esp and dma chips have been found
* and assigned virtual addresses. Set up the ESP SCSI data structures
* and probe the SCSI bus.
register struct esp_softc
*sc
;
register struct dma_softc
*dsc
;
/* make sure we have both */
if (espcd
.cd_ndevs
<= unit
||
dmacd
.cd_ndevs
<= unit
||
(sc
= espcd
.cd_devs
[unit
]) == NULL
||
(dsc
= dmacd
.cd_devs
[unit
]) == NULL
)
sc
->sc_dma
= dsc
->sc_dma
;
sc
->sc_hba
.hba_driver
= &esphbadriver
;
/* MAYBE THIS SHOULD BE MOVED TO scsi_subr.c? */
for (targ
= 0; targ
< 8; targ
++) {
sc
->sc_probing
= PROBE_TESTING
;
(void) scsi_test_unit_ready(&sc
->sc_hba
, targ
, 0);
if (sc
->sc_probing
!= PROBE_NO_TARGET
) {
SCSI_FOUNDTARGET(&sc
->sc_hba
, targ
);
register volatile struct dmareg
*dma
= sc
->sc_dma
;
dma
->dma_csr
|= DMA_RESET
;
dma
->dma_csr
&= ~DMA_RESET
; /* ??? */
dma
->dma_csr
|= DMA_IE
; /* enable interrupts */
* Reset the chip. N.B.: this causes a SCSI bus reset!
register struct esp_softc
*sc
;
register volatile struct espreg
*esp
= sc
->sc_esp
;
esp
->esp_cmd
= ESPCMD_RESET_CHIP
;
esp
->esp_cmd
= ESPCMD_NOP
;
* Reload configuration registers (cleared by RESET_CHIP command).
* Reloading conf2 on an ESP100 goofs it up, so out of paranoia
* we load only the registers that exist.
esp
->esp_conf1
= sc
->sc_conf1
;
if (sc
->sc_esptype
> ESP100
) { /* 100A, 2XX */
esp
->esp_conf2
= sc
->sc_conf2
;
if (sc
->sc_esptype
> ESP100A
) /* 2XX only */
esp
->esp_conf3
= sc
->sc_conf3
;
esp
->esp_ccf
= sc
->sc_ccf
;
esp
->esp_timeout
= sc
->sc_sel_timeout
;
/* We set synch offset later. */
* Reset the SCSI bus and, optionally, all attached targets.
* The chip should retain most of its parameters (including esp_ccf)
* across this kind of reset (see section 3.5 of Emulex documentation).
esphbareset(hba
, resetunits
)
register struct esp_softc
*sc
= (struct esp_softc
*)hba
;
register volatile struct espreg
*esp
= sc
->sc_esp
;
/* turn off scsi bus reset interrupts and reset scsi bus */
esp
->esp_conf1
= sc
->sc_conf1
| ESPCONF1_REPORT
;
esp
->esp_cmd
= ESPCMD_RESET_BUS
;
esp
->esp_cmd
= ESPCMD_NOP
;
esp
->esp_conf1
= sc
->sc_conf1
;
scsi_reset_units(&sc
->sc_hba
);
* Reset the esp, after an Sbus reset.
* Also resets corresponding dma chip.
* THIS ROUTINE MIGHT GO AWAY
struct esp_softc
*sc
= (struct esp_softc
*)dev
;
printf(" %s %s", sc
->sc_dsc
->sc_dev
.dv_xname
,
sc
->sc_hba
.hba_dev
.dv_xname
);
esphbareset(&sc
->sc_hba
, 1);
register struct esp_softc
*sc
;
printf("%s: %s (target=%d): stat=%b step=%x dmacsr=%b intr=%b\n",
sc
->sc_hba
.hba_dev
.dv_xname
, err
, sc
->sc_targ
,
sc
->sc_espstat
, ESPSTAT_BITS
, sc
->sc_espstep
,
sc
->sc_dmacsr
, DMA_BITS
, sc
->sc_espintr
, ESPINTR_BITS
);
* An interrupt has occurred. Sequence through the SCSI state machine.
* Return the action to take.
* Most of the work happens here.
* There are three interrupt sources:
* -- ESP interrupt request (typically, some device wants something).
* -- DMA byte count has reached 0 (we do not often want this one but
* can only turn it off in rev 2 DMA chips, it seems).
* DOES THIS OCCUR AT ALL HERE? THERE IS NOTHING TO HANDLE IT!
espact(sc
, esp
, dma
, cdb
)
register struct esp_softc
*sc
;
register volatile struct espreg
*esp
;
register volatile struct dmareg
*dma
;
register struct scsi_cdb
*cdb
;
register char *xname
= sc
->sc_hba
.hba_dev
.dv_xname
;
register int reg
, phase
, i
;
/* check various error conditions, using as little code as possible */
if (sc
->sc_dmacsr
& DMA_EP
) {
esperror(sc
, "DMA error");
dma
->dma_csr
|= DMA_FLUSH
;
* This often occurs when there is no target.
if (sc
->sc_espintr
& ESPINTR_DSC
&&
sc
->sc_state
== S_SEL
&& sc
->sc_probing
) {
sc
->sc_probing
= PROBE_NO_TARGET
;
esperror(sc
, "DIAGNOSTIC: gross error (ignored)");
esperror(sc
, "parity error");
#define ERR (ESPINTR_SBR|ESPINTR_ILC|ESPINTR_RSL|ESPINTR_SAT|ESPINTR_SEL)
esperror(sc
, "scsi bus reset");
else if (reg
& ESPINTR_ILC
)
esperror(sc
, "illegal command (driver bug)");
printf("%s: target %d", xname
, sc
->sc_targ
);
printf(" tried to reselect;");
printf(" selected with ATN;");
printf(" selected us as target;");
printf("we do not allow this yet\n");
* Disconnect currently only allowed in `final interrupt' states.
if (sc
->sc_state
== S_FI
)
* If we were doing a select just to test the existence
* of the target, note that it did not respond; otherwise
if (sc
->sc_state
== S_SEL
) {
sc
->sc_probing
= PROBE_NO_TARGET
;
/* flush fifo, in case we were selecting or sending data */
esp
->esp_cmd
= ESPCMD_FLUSH_FIFO
;
printf("%s: target %d not responding\n",
* Okay, things are moving along.
* What were we doing the last time we did something,
* and did it complete normally?
phase
= sc
->sc_espstat
& ESPSTAT_PHASE
;
* We were selecting. Arbitration and select are
* complete (because ESPINTR_DSC was not set), but
* there is no guarantee the command went out.
if ((reg
& (ESPINTR_SVC
|ESPINTR_CMP
)) !=
(ESPINTR_SVC
|ESPINTR_CMP
)) {
esperror(sc
, "selection failed");
if (sc
->sc_espstep
== ESPSTEP_DONE
) {
if (sc
->sc_espstep
== 2) {
* We got something other than command phase.
* Just pretend things are normal; the
* device will ask for the command later.
esperror(sc
, "DIAGNOSTIC: esp step 2");
} else if (sc
->sc_espstep
== 3) {
* Device entered command phase and then exited it
* before we finished handing out the command.
* Let this happen iff we are trying to clear the
esperror(sc
, "DIAGNOSTIC: esp step 3");
printf("%s: mysterious esp step %d\n",
* Part of the command may still be lodged in the FIFO.
esp
->esp_cmd
= ESPCMD_FLUSH_FIFO
;
* We were waiting for phase change after stuffing the command
* into the FIFO. Make sure it got out.
esperror(sc
, "DIAGNOSTIC: CMDSVC, fifo not empty");
printf("\tfifo count = %x\n", reg
);
esp
->esp_cmd
= ESPCMD_FLUSH_FIFO
;
* We were waiting for phase change after I/O.
* We were doing DMA data in, and expecting a
* transfer-count-zero interrupt or a phase change.
* We got that; drain the pack register and
* handle as for data out.
dma
->dma_csr
|= DMA_DRAIN
;
reg
= 0; /* FIFO auto flushed? */
* We were doing DMA data out. If there is data in the
* FIFO, it is stuff that got DMAed out but never made
* it to the device, so it counts as residual.
* XXX handle DMA IO with large count or address
* boundary condition by resuming here, or below?
if ((reg
= ESP_NFIFO(esp
)) != 0)
esp
->esp_cmd
= ESPCMD_FLUSH_FIFO
;
if (sc
->sc_dmaactive
== 0) {
printf("%s: dma done while %s, dmaactive==0\n",
xname
, espstates
[sc
->sc_state
]);
reg
+= esp
->esp_tcl
| (esp
->esp_tch
<< 8);
if (reg
== 0 && (sc
->sc_espstat
& ESPSTAT_TC
) == 0)
if (reg
> sc
->sc_resid
) {
printf("%s: xfer resid (%d) > xfer req (%d)\n",
xname
, reg
, sc
->sc_resid
);
* If data came in we must flush cache.
if (sc
->sc_state
== S_DI
)
cache_flush(sc
->sc_dmaaddr
, sc
->sc_resid
- reg
);
if ((sc
->sc_espintr
& ESPINTR_SVC
) == 0) {
printf("%s: no bus service req\n", xname
);
* The last thing we did was tell it `initiator complete'
* and so we expect to have gotten both the status byte
* and the final message byte. It is possible that we
* Apparently, BUS SERVICE is set if we got just status,
* while FUNCTION COMPLETE is set if we got both.
if ((reg
& (ESPINTR_SVC
|ESPINTR_CMP
)) != ESPINTR_CMP
) {
esperror(sc
, "bad status interrupt state");
"%s: command done but fifo count = %d; must be >= 2\n", xname
,
* Read the status and the first msg byte.
* It should be CMD_COMPLETE. Eventually we
* may handle IDENTIFY, DISCONNECT, etc., as well.
sc
->sc_stat
[0] = esp
->esp_fifo
;
sc
->sc_msg
[0] = reg
= esp
->esp_fifo
;
esp
->esp_cmd
= ESPCMD_MSG_ACCEPT
;
if (reg
== MSG_CMD_COMPLETE
) {
if (SCSIMSGLEN(reg
) != 1) {
printf("%s: target %d is naughty\n",
printf("%s: warning: target %d returned msg 0x%x\n",
xname
, sc
->sc_targ
, reg
);
if ((reg
& ESPINTR_SVC
) == 0) {
esperror(sc
, "missing phase after msg in");
for (i
= 0; i
< reg
; i
++)
sc
->sc_msg
[i
] = esp
->esp_fifo
;
esperror(sc
, "target did not disconnect");
* Things are still moving along. The phase tells us
* what the device wants next. Do it.
if (!sc
->sc_sentcmd
) esperror(sc
, "DIAGNOSTIC: data out without command");
if (!sc
->sc_sentcmd
) esperror(sc
, "DIAGNOSTIC: data in without command");
* Silly thing wants the command again.
* Load it into the FIFO and go to CMDSVC state.
printf("%s: redoing command\n", xname
);
reg
= SCSICMDLEN(cdb
->cdb_bytes
[0]);
for (i
= 0; i
< reg
; i
++)
esp
->esp_fifo
= cdb
->cdb_bytes
[i
];
esp
->esp_cmd
= ESPCMD_XFER_INFO
;
esp
->esp_cmd
= ESPCMD_INIT_COMP
;
printf("%s: accepting (& ignoring) msg from target %d\n", xname
, sc
->sc_targ
);
esp
->esp_cmd
= ESPCMD_MSG_ACCEPT
;
printf("%s: target %d asked for strange phase (%s)\n",
xname
, sc
->sc_targ
, espphases
[phase
]);
* Issue a select, loading command into the FIFO.
* Return nonzero on error, 0 if OK.
* Sets state to `selecting'; espact() will sequence state FSM.
espselect(sc
, esp
, targ
, cdb
)
register struct esp_softc
*sc
;
register volatile struct espreg
*esp
;
register struct scsi_cdb
*cdb
;
register int i
, cmdlen
= SCSICMDLEN(cdb
->cdb_bytes
[0]);
sc
->sc_stat
[0] = 0xff; /* ??? */
sc
->sc_msg
[0] = 0xff; /* ??? */
* Synch offset 0 => asynchronous transfer.
* Stuff the command bytes into the fifo.
* Select without attention since we do not do disconnect yet.
for (i
= 0; i
< cmdlen
; i
++)
esp
->esp_fifo
= cdb
->cdb_bytes
[i
];
esp
->esp_cmd
= ESPCMD_SEL_NATN
;
/* the rest is done elsewhere */
* THIS SHOULD BE ADJUSTABLE
/* name howlong purpose */
#define SELECT_WAIT 300000 /* wait for select to complete */
#define CMD_WAIT 1000 /* wait for next phase, generic */
#define IO_WAIT 1000000 /* time to xfer data in/out */
#define POSTDATA_WAIT 10000000 /* wait for next phase, after dataio */
* Transfer data out via polling. Return success (0) iff all
* the bytes were sent and we got an interrupt.
* This returns -1 on timeout, resid count on early interrupt,
* but no one really cares....
espixfer_out(sc
, esp
, dma
, buf
, len
)
register struct esp_softc
*sc
;
register volatile struct espreg
*esp
;
register volatile struct dmareg
*dma
;
panic("espixfer_out: 16MB boundary");
/* set dma address and transfer count */
dma
->dma_addr
= (int)buf
;
/* load count into counter via DMA NOP */
esp
->esp_cmd
= ESPCMD_DMA
| ESPCMD_NOP
;
/* enable dma (but not interrupts) */
esp
->esp_cmd
= ESPCMD_DMA
| ESPCMD_XFER_INFO
;
/* wait for completion */
for (wait
= IO_WAIT
; wait
> 0; --wait
) {
sc
->sc_espstat
= esp
->esp_stat
;
sc
->sc_espstep
= esp
->esp_step
& ESPSTEP_MASK
;
sc
->sc_espintr
= esp
->esp_intr
;
n
= esp
->esp_tcl
| (esp
->esp_tch
<< 8);
if (n
== 0 && (sc
->sc_espstat
& ESPSTAT_TC
) == 0)
* Transfer data in via polling.
* Return resid count on interrupt, -1 if timed out.
espixfer_in(sc
, esp
, dma
, buf
, len
)
register struct esp_softc
*sc
;
register volatile struct espreg
*esp
;
register volatile struct dmareg
*dma
;
panic("espixfer_in: 16MB boundary");
/* set dma address and transfer count */
dma
->dma_addr
= (int)buf
;
/* load count into counter via DMA NOP */
esp
->esp_cmd
= ESPCMD_DMA
| ESPCMD_NOP
;
/* enable dma (but not interrupts) */
dma
->dma_csr
= DMA_ENA
| DMA_READ
;
esp
->esp_cmd
= ESPCMD_DMA
| ESPCMD_XFER_INFO
;
/* wait for completion */
for (wait
= IO_WAIT
; wait
> 0; --wait
) {
sc
->sc_espstat
= esp
->esp_stat
;
sc
->sc_espstep
= esp
->esp_step
& ESPSTEP_MASK
;
sc
->sc_espintr
= esp
->esp_intr
;
dma
->dma_csr
|= DMA_DRAIN
;
n
= esp
->esp_tcl
| (esp
->esp_tch
<< 8);
if (n
== 0 && (sc
->sc_espstat
& ESPSTAT_TC
) == 0)
cache_flush(buf
, (u_int
)len
- n
);
* Clear out target state by doing a special TEST UNIT READY.
* Note that this calls espicmd (possibly recursively).
register struct esp_softc
*sc
;
/* turn off needclear immediately since this calls espicmd() again */
sc
->sc_needclear
&= ~(1 << targ
);
(void) scsi_test_unit_ready(&sc
->sc_hba
, targ
, 0);
* Send an `immediate' command, i.e., poll until the whole thing is done.
* Return the status byte from the device, or -1 if we timed out.
espicmd(hba
, targ
, cdb
, buf
, len
, rw
)
register struct hba_softc
*hba
;
register struct scsi_cdb
*cdb
;
register struct esp_softc
*sc
= (struct esp_softc
*)hba
;
register volatile struct espreg
*esp
= sc
->sc_esp
;
register volatile struct dmareg
*dma
= sc
->sc_dma
;
if ((unsigned)len
> MAX_TRANSFER_SIZE
) {
printf("%s: bad length %d\n", sc
->sc_hba
.hba_dev
.dv_xname
, len
);
* Clear the target if necessary.
if (sc
->sc_needclear
& (1 << targ
) && !sc
->sc_probing
)
* Disable hardware interrupts, start select sequence.
* Wait for interrupt-pending bit, then call espact() to
* sequence the state machine. When it tells us to do
* data transfer, we do programmed I/O.
* In any case, we loop calling espact() until done.
dma
->dma_csr
= 0; /* disable hardware interrupts */
espselect(sc
, esp
, targ
, cdb
);
msg
= "timeout waiting for phase change";
sc
->sc_espstat
= esp
->esp_stat
;
sc
->sc_espstep
= esp
->esp_step
& ESPSTEP_MASK
;
sc
->sc_espintr
= esp
->esp_intr
;
* The action happens `twice around' for read and write.
* All the rest `goto loop' or return or some such.
switch (r
= espact(sc
, esp
, dma
, cdb
)) {
if (len
== 0 || (rw
& B_READ
) == 0) {
r
= espixfer_in(sc
, esp
, dma
, buf
, len
);
msg
= "timeout reading from device";
/* we did the io, expecting `generic service' */
if (len
== 0 || rw
& B_READ
) {
if (espixfer_out(sc
, esp
, dma
, buf
, len
)) {
msg
= "timeout writing to device";
printf("%s: target %d: %s (phase = %s)\n",
sc
->sc_hba
.hba_dev
.dv_xname
, targ
, msg
,
espphases
[sc
->sc_espstat
& ESPSTAT_PHASE
]);
* Dump (write memory, possibly physmem).
* SPARC higher-level dump code always provides virtual addresses,
* so we need not do any I/O mapping here.
espdump(hba
, targ
, cdb
, buf
, len
)
register struct hba_softc
*hba
;
register struct scsi_cdb
*cdb
;
return (espicmd(hba
, targ
, cdb
, buf
, len
, B_WRITE
));
* Allocate resources (SCSI bus and DVMA space) for the given transfer.
* Must be called at splbio().
* THIS SHOULD RETURN SUCCESS/FAIL INDICATION
espstart(self
, sq
, bp
, dgo
, dev
)
register struct esp_softc
*sc
= (struct esp_softc
*)self
;
if (sc
->sc_hba
.hba_busy
== 0) {
* Bus not busy, nothing to do here, just tell
* this target or unit that it has the SCSI bus.
(*dgo
)(dev
, &sc
->sc_cdb
);
* Bus is busy; just enqueue.
if (sc
->sc_hba
.hba_head
== NULL
)
sc
->sc_hba
.hba_head
= sq
;
sc
->sc_hba
.hba_tail
->sq_forw
= sq
;
sc
->sc_hba
.hba_tail
= sq
;
* Send a `dma' command, i.e., send the cdb and use DMA to send the data.
* Return 0 on success, 1 on failure.
espgo(self
, targ
, intr
, dev
, bp
, pad
)
register struct esp_softc
*sc
= (struct esp_softc
*)self
;
register int len
= bp
->b_bcount
;
if ((unsigned)len
> MAX_TRANSFER_SIZE
) {
printf("%s: %s\n", sc
->sc_hba
.hba_dev
.dv_xname
,
len
< 0 ? "negative length" : "transfer too big");
if (sc
->sc_needclear
& (1 << targ
))
* Set dma registers later, on data transfer,
* but compute the contents now.
* COULD JUST REMEMBER bp HERE...?
* The DMA chip cannot cross a 16 MB address boundary.
* We should do this as multiple DMA transactions on a
* single SCSI command, but I have not written that yet.
sc
->sc_dmactl
= bp
->b_flags
& B_READ
? DMA_ENA
| DMA_READ
| DMA_IE
:
addr
= (u_long
)bp
->b_un
.b_addr
;
/* dma chip cannot cross 16MB boundary XXX */
if (CROSS_DMA(addr
, len
))
panic("dma crosses 16MB boundary: fix esp.c");
* Enable interrupts and start selection.
* The rest is done in our interrupt handler.
sc
->sc_hba
.hba_intr
= intr
; /* remember dev done function */
sc
->sc_hba
.hba_intrdev
= dev
; /* and its first arg */
sc
->sc_dma
->dma_csr
= DMA_IE
;
espselect(sc
, sc
->sc_esp
, targ
, &sc
->sc_cdb
);
* Handle interrupt. Return 1 if taken.
register struct esp_softc
*sc
= (struct esp_softc
*)sc0
;
register volatile struct espreg
*esp
= sc
->sc_esp
;
register volatile struct dmareg
*dma
= sc
->sc_dma
;
return (0); /* not ours */
sc
->sc_espstat
= esp
->esp_stat
;
sc
->sc_espstep
= esp
->esp_step
& ESPSTEP_MASK
;
sc
->sc_espintr
= esp
->esp_intr
;
if (sc
->sc_state
== S_IDLE
) {
printf("%s: stray interrupt\n", sc
->sc_hba
.hba_dev
.dv_xname
);
dma
->dma_csr
&= ~DMA_IE
; /* ??? */
switch (r
= espact(sc
, esp
, dma
, &sc
->sc_cdb
)) {
case ACT_CONT
: /* just return */
* We have to do this ourselves since another
* user of espact() wants to do programmed I/O.
* If we already did dma, and are done, stop.
printf("%s: target %d sent too much data\n",
sc
->sc_hba
.hba_dev
.dv_xname
, sc
->sc_targ
);
dma
->dma_addr
= sc
->sc_dmaaddr
;
esp
->esp_tch
= sc
->sc_resid
>> 8;
esp
->esp_tcl
= sc
->sc_resid
;
/* load count into counter via DMA NOP */
esp
->esp_cmd
= ESPCMD_DMA
| ESPCMD_NOP
;
dma
->dma_csr
= sc
->sc_dmactl
;
esp
->esp_cmd
= ESPCMD_DMA
| ESPCMD_XFER_INFO
;
case ACT_RESET
: /* please reset esp */
case ACT_DONE
: /* this one is done, successfully */
case ACT_ERROR
: /* this one is done due to `severe' error */
if (!sc
->sc_hba
.hba_busy
)
* This transaction is done.
* Call the driver's intr routine,
* then start the next guy if any.
(*sc
->sc_hba
.hba_intr
)(sc
->sc_hba
.hba_intrdev
,
r
== ACT_DONE
? sc
->sc_stat
[0] : -1, sc
->sc_resid
);
if ((sq
= sc
->sc_hba
.hba_head
) != NULL
) {
sc
->sc_hba
.hba_head
= sq
->sq_forw
;
(*sq
->sq_dgo
)(sq
->sq_dev
, &sc
->sc_cdb
);
case ACT_QUICKINTR
: /* wait a short while for another interrupt */
printf("%s: quickintr: ", sc
->sc_hba
.hba_dev
.dv_xname
);
printf("got one, wait=%d\n", wait
);
printf("did not get one\n");
* Target or unit decided to let go of the bus early.
register struct esp_softc
*sc
= (struct esp_softc
*)self
;
/* if there is someone else waiting, give them a crack at it */
if ((sq
= sc
->sc_hba
.hba_head
) != NULL
)
(*sq
->sq_dgo
)(sq
->sq_dev
, &sc
->sc_cdb
);