NetBSD/sys/arch/amiga/dev/scsi.c

1694 lines
44 KiB
C

/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Van Jacobson of Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)scsi.c 7.5 (Berkeley) 5/4/91
*
* MULTICONTROLLER support only working for multiple controllers of the
* same kind at the moment !!
*
*/
/*
* AMIGA AMD 33C93 scsi adaptor driver
*/
#include "a3000scsi.h"
#include "a2091scsi.h"
#include "gvp11scsi.h"
#define NSCSI (NA3000SCSI + NA2091SCSI + NGVP11SCSI)
#if NSCSI > 0
#ifndef lint
static char rcsid[] = "$Header: /cvsroot/src/sys/arch/amiga/dev/Attic/scsi.c,v 1.4 1993/10/30 23:41:33 mw Exp $";
#endif
#include "sys/param.h"
#include "sys/systm.h"
#include "sys/buf.h"
#include "device.h"
#include "dmavar.h"
#include "scsivar.h"
#include "scsireg.h"
#include "../amiga/custom.h"
#include "machine/cpu.h"
static int sbic_wait (volatile sbic_padded_regmap_t *regs, char until, int timeo, int line);
static void scsiabort (register struct scsi_softc *dev, register volatile sbic_padded_regmap_t *regs, char *where);
static void scsierror (register struct scsi_softc *dev, register volatile sbic_padded_regmap_t *regs, u_char csr);
static int issue_select (register struct scsi_softc *dev, register volatile sbic_padded_regmap_t *regs, u_char target, u_char our_addr);
static int wait_for_select (register struct scsi_softc *dev, register volatile sbic_padded_regmap_t *regs);
static int ixfer_start (register volatile sbic_padded_regmap_t *regs, int len, u_char phase, register int wait);
static int ixfer_out (register volatile sbic_padded_regmap_t *regs, int len, register u_char *buf, int phase);
static void ixfer_in (register volatile sbic_padded_regmap_t *regs, int len, register u_char *buf);
static int scsiicmd (struct scsi_softc *dev, int target, u_char *cbuf, int clen, u_char *buf, int len, u_char xferphase);
static void finishxfer (struct scsi_softc *dev, register volatile sbic_padded_regmap_t *regs, int target);
/*
* SCSI delays
* In u-seconds, primarily for state changes on the SPC.
*/
#define SCSI_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */
#define SCSI_DATA_WAIT 50000 /* wait per data in/out step */
#define SCSI_INIT_WAIT 50000 /* wait per step (both) during init */
extern void _insque();
extern void _remque();
#if 0
int scsiinit(), scsigo(), scsiintr(), scsixfer();
void scsistart(), scsidone(), scsifree(), scsireset();
struct driver scsidriver = {
scsiinit, "scsi", (int (*)())scsistart, scsigo, scsiintr,
(int (*)())scsidone,
};
#endif
#if NA3000SCSI > 0
int a3000scsiinit();
struct driver a3000scsidriver = {
a3000scsiinit, "a3000scsi", (int (*)())scsistart, scsigo, scsiintr,
(int (*)())scsidone,
};
#endif
#if NA2091SCSI > 0
int a2091scsiinit();
struct driver a2091scsidriver = {
a2091scsiinit, "a2091scsi", (int (*)())scsistart, scsigo, scsiintr,
(int (*)())scsidone,
};
#endif
#if NGVP11SCSI > 0
int gvp11scsiinit();
struct driver gvp11scsidriver = {
gvp11scsiinit, "GVPIIscsi", (int (*)())scsistart, scsigo, scsiintr,
(int (*)())scsidone,
};
#endif
struct scsi_softc scsi_softc[NSCSI];
int scsi_cmd_wait = SCSI_CMD_WAIT;
int scsi_data_wait = SCSI_DATA_WAIT;
int scsi_init_wait = SCSI_INIT_WAIT;
/*
* Synch xfer parameters, and timing conversions
*/
static int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */
static int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */
static int
sbic_to_scsi_period(dev, regs, a)
struct scsi_softc *dev;
sbic_padded_regmap_t *regs;
{
unsigned int fs;
/* cycle = DIV / (2*CLK) */
/* DIV = FS+2 */
/* best we can do is 200ns at 20Mhz, 2 cycles */
GET_SBIC_myid(regs,fs);
fs = (fs >>6) + 2; /* DIV */
fs = (fs * 10000) / (dev->sc_clock_freq<<1); /* Cycle, in ns */
if (a < 2) a = 8; /* map to Cycles */
return ((fs*a)>>2); /* in 4 ns units */
}
static int
scsi_period_to_sbic(dev, regs, p)
struct scsi_softc *dev;
sbic_padded_regmap_t *regs;
{
register unsigned int fs, ret;
/* Just the inverse of the above */
GET_SBIC_myid(regs,fs);
fs = (fs >>6) + 2; /* DIV */
fs = (fs * 10000) / (dev->sc_clock_freq<<1); /* Cycle, in ns */
ret = p << 2; /* in ns units */
ret = ret / fs; /* in Cycles */
if (ret < sbic_min_period)
return sbic_min_period;
/* verify rounding */
if (sbic_to_scsi_period(dev, regs, ret) < p)
ret++;
return (ret >= 8) ? 0 : ret;
}
/* default to not inhibit sync negotiation on any drive */
u_char inhibit_sync[NSCSI][8] = { 0, 0, 0, 0, 1, 0, 0 }; /* initialize, so patchable */
int scsi_no_dma = 0;
#ifdef DEBUG
int scsi_debug = 0;
int sync_debug = 0;
#define WAITHIST
#define QUASEL
#endif
#ifdef QUASEL
#define QPRINTF(a) if (scsi_debug > 1) printf a
#else
#define QPRINTF
#endif
#ifdef WAITHIST
#define MAXWAIT 1022
u_int ixstart_wait[MAXWAIT+2];
u_int ixin_wait[MAXWAIT+2];
u_int ixout_wait[MAXWAIT+2];
u_int mxin_wait[MAXWAIT+2];
u_int mxin2_wait[MAXWAIT+2];
u_int cxin_wait[MAXWAIT+2];
u_int fxfr_wait[MAXWAIT+2];
u_int sgo_wait[MAXWAIT+2];
#define HIST(h,w) (++h[((w)>MAXWAIT? MAXWAIT : ((w) < 0 ? -1 : (w))) + 1]);
#else
#define HIST(h,w)
#endif
#define b_cylin b_resid
static sbic_wait (regs, until, timeo, line)
volatile sbic_padded_regmap_t *regs;
char until;
int timeo;
int line;
{
register unsigned char val;
if (! timeo)
timeo = 1000000; /* some large value.. */
GET_SBIC_asr (regs,val);
while ((val & until) == 0)
{
if (!timeo--)
{
int csr;
GET_SBIC_csr (regs, csr);
printf("sbic_wait TIMEO @%d with asr=x%x csr=x%x\n", line, val, csr);
break;
}
DELAY(1);
GET_SBIC_asr(regs,val);
}
return val;
}
#define SBIC_WAIT(regs, until, timeo) sbic_wait (regs, until, timeo, __LINE__)
static void
scsiabort(dev, regs, where)
register struct scsi_softc *dev;
volatile register sbic_padded_regmap_t *regs;
char *where;
{
u_char csr, asr;
GET_SBIC_csr (regs, csr);
GET_SBIC_asr (regs, asr);
printf ("scsi%d: abort %s: csr = 0x%02x, asr = 0x%02x\n",
dev->sc_ac->amiga_unit,
where, csr, asr);
if (dev->sc_flags & SCSI_SELECTED)
{
SET_SBIC_cmd (regs, SBIC_CMD_ABORT);
WAIT_CIP (regs);
GET_SBIC_asr (regs, asr);
if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI))
{
/* ok, get more drastic.. */
SET_SBIC_cmd (regs, SBIC_CMD_RESET);
DELAY(25);
SBIC_WAIT(regs, SBIC_ASR_INT, 0);
GET_SBIC_csr (regs, csr); /* clears interrupt also */
dev->sc_flags &= ~SCSI_SELECTED;
return;
}
do
{
SBIC_WAIT (regs, SBIC_ASR_INT, 0);
GET_SBIC_csr (regs, csr);
}
while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
&& (csr != SBIC_CSR_CMD_INVALID));
/* lets just hope it worked.. */
dev->sc_flags &= ~SCSI_SELECTED;
}
}
/*
* XXX Set/reset long delays.
*
* if delay == 0, reset default delays
* if delay < 0, set both delays to default long initialization values
* if delay > 0, set both delays to this value
*
* Used when a devices is expected to respond slowly (e.g. during
* initialization).
*/
void
scsi_delay(delay)
int delay;
{
static int saved_cmd_wait, saved_data_wait;
if (delay) {
saved_cmd_wait = scsi_cmd_wait;
saved_data_wait = scsi_data_wait;
if (delay > 0)
scsi_cmd_wait = scsi_data_wait = delay;
else
scsi_cmd_wait = scsi_data_wait = scsi_init_wait;
} else {
scsi_cmd_wait = saved_cmd_wait;
scsi_data_wait = saved_data_wait;
}
}
static int initialized[NSCSI];
int scsi_clock_override = 0;
#if NA3000SCSI > 0
int
a3000scsiinit(ac)
register struct amiga_ctlr *ac;
{
register struct scsi_softc *dev = &scsi_softc[ac->amiga_unit];
register sbic_padded_regmap_t *regs;
if (! ac->amiga_addr)
return 0;
if (initialized[ac->amiga_unit])
return 0;
initialized[ac->amiga_unit] = 1;
/* initialize dma */
a3000dmainit (ac, &dev->dmareq, &dev->dmafree, &dev->dmago,
&dev->dmanext, &dev->dmastop);
/* advance ac->amiga_addr to point to the real sbic-registers */
ac->amiga_addr = (caddr_t) ((int)ac->amiga_addr + 0x41);
regs = (sbic_padded_regmap_t *) ac->amiga_addr;
dev->sc_clock_freq = scsi_clock_override ? scsi_clock_override : 143;
/* hardwired IPL */
ac->amiga_ipl = 2;
dev->sc_ac = ac;
dev->sc_dq.dq_driver = &a3000scsidriver;
dev->sc_sq.dq_forw = dev->sc_sq.dq_back = &dev->sc_sq;
scsireset (ac->amiga_unit);
/* make sure IPL2 interrupts are delivered to the cpu when the sbic
generates some. Note that this does not yet enable sbic-interrupts,
this is handled in dma.c, which selectively enables interrupts only
while DMA requests are pending.
Note that enabling PORTS interrupts also enables keyboard interrupts
as soon as the corresponding int-enable bit in CIA-A is set. */
custom.intreq = INTF_PORTS;
custom.intena = INTF_SETCLR | INTF_PORTS;
return(1);
}
#endif
#if NA2091SCSI > 0
int
a2091scsiinit(ac)
register struct amiga_ctlr *ac;
{
register struct scsi_softc *dev = &scsi_softc[ac->amiga_unit];
register sbic_padded_regmap_t *regs;
if (! ac->amiga_addr)
return 0;
if (initialized[ac->amiga_unit])
return 0;
initialized[ac->amiga_unit] = 1;
/* initialize dma */
a2091dmainit (ac, &dev->dmareq, &dev->dmafree, &dev->dmago,
&dev->dmanext, &dev->dmastop);
/* advance ac->amiga_addr to point to the real sbic-registers */
ac->amiga_addr = (caddr_t) ((int)ac->amiga_addr + 0x91);
regs = (sbic_padded_regmap_t *) ac->amiga_addr;
dev->sc_clock_freq = scsi_clock_override ? scsi_clock_override : 77;
/* hardwired IPL */
ac->amiga_ipl = 2;
dev->sc_ac = ac;
dev->sc_dq.dq_driver = &a2091scsidriver;
dev->sc_sq.dq_forw = dev->sc_sq.dq_back = &dev->sc_sq;
scsireset (ac->amiga_unit);
/* make sure IPL2 interrupts are delivered to the cpu when the sbic
generates some. Note that this does not yet enable sbic-interrupts,
this is handled in dma.c, which selectively enables interrupts only
while DMA requests are pending.
Note that enabling PORTS interrupts also enables keyboard interrupts
as soon as the corresponding int-enable bit in CIA-A is set. */
custom.intreq = INTF_PORTS;
custom.intena = INTF_SETCLR | INTF_PORTS;
return(1);
}
#endif
#if NGVP11SCSI > 0
int
gvp11scsiinit(ac)
register struct amiga_ctlr *ac;
{
register struct scsi_softc *dev = &scsi_softc[ac->amiga_unit];
register sbic_padded_regmap_t *regs;
if (! ac->amiga_addr)
return 0;
if (initialized[ac->amiga_unit])
return 0;
initialized[ac->amiga_unit] = 1;
/* initialize dma */
gvp11dmainit (ac, &dev->dmareq, &dev->dmafree, &dev->dmago,
&dev->dmanext, &dev->dmastop);
/* advance ac->amiga_addr to point to the real sbic-registers */
ac->amiga_addr = (caddr_t) ((int)ac->amiga_addr + 0x61);
regs = (sbic_padded_regmap_t *) ac->amiga_addr;
dev->sc_clock_freq = scsi_clock_override ? scsi_clock_override : 77;
/* hardwired IPL */
ac->amiga_ipl = 2;
dev->sc_ac = ac;
dev->sc_dq.dq_driver = &gvp11scsidriver;
dev->sc_sq.dq_forw = dev->sc_sq.dq_back = &dev->sc_sq;
scsireset (ac->amiga_unit);
/* make sure IPL2 interrupts are delivered to the cpu when the sbic
generates some. Note that this does not yet enable sbic-interrupts,
this is handled in dma.c, which selectively enables interrupts only
while DMA requests are pending.
Note that enabling PORTS interrupts also enables keyboard interrupts
as soon as the corresponding int-enable bit in CIA-A is set. */
custom.intreq = INTF_PORTS;
custom.intena = INTF_SETCLR | INTF_PORTS;
return(1);
}
#endif
void
scsireset(unit)
register int unit;
{
register struct scsi_softc *dev = &scsi_softc[unit];
volatile register sbic_padded_regmap_t *regs =
(sbic_padded_regmap_t *)dev->sc_ac->amiga_addr;
u_int i, s;
u_char my_id, csr;
if (dev->sc_flags & SCSI_ALIVE)
scsiabort(dev, regs, "reset");
printf("scsi%d: ", unit);
s = splbio();
/* preserve our ID for now */
GET_SBIC_myid (regs, my_id);
my_id &= SBIC_ID_MASK;
if (dev->sc_clock_freq < 110)
my_id |= SBIC_ID_FS_8_10;
else if (dev->sc_clock_freq < 160)
my_id |= SBIC_ID_FS_12_15;
else if (dev->sc_clock_freq < 210)
my_id |= SBIC_ID_FS_16_20;
my_id |= SBIC_ID_EAF/* |SBIC_ID_EHP*/;
SET_SBIC_myid (regs, my_id);
/*
* Disable interrupts (in dmainit) then reset the chip
*/
SET_SBIC_cmd (regs, SBIC_CMD_RESET);
DELAY(25);
SBIC_WAIT(regs, SBIC_ASR_INT, 0);
GET_SBIC_csr (regs, csr); /* clears interrupt also */
/*
* Set up various chip parameters
*/
SET_SBIC_control (regs, (/*SBIC_CTL_HHP |*/ SBIC_CTL_EDI | SBIC_CTL_IDI |
/* | SBIC_CTL_HSP | */ SBIC_MACHINE_DMA_MODE));
/* don't allow (re)selection (SBIC_RID_ES) until we can handle target mode!! */
SET_SBIC_rselid (regs, 0 /* | SBIC_RID_ER | SBIC_RID_ES | SBIC_RID_DSP */);
SET_SBIC_syn (regs, 0); /* asynch for now */
/* anything else was zeroed by reset */
#if 0
/* go async for now */
dev->sc_sync = 0;
printf ("async, ");
#endif
splx (s);
printf("scsi id %d\n", my_id & SBIC_ID_MASK);
dev->sc_flags |= SCSI_ALIVE;
dev->sc_flags &= ~SCSI_SELECTED;
}
static void
scsierror(dev, regs, csr)
register struct scsi_softc *dev;
volatile register sbic_padded_regmap_t *regs;
u_char csr;
{
int unit = dev->sc_ac->amiga_unit;
char *sep = "";
printf("scsi%d: ", unit);
#if 0
if (ints & INTS_RST) {
DELAY(100);
if (regs->scsi_aconf & HCONF_SD)
printf("spurious RST interrupt");
else
printf("hardware error - check fuse");
sep = ", ";
}
if ((ints & INTS_HARD_ERR) || regs->scsi_serr) {
if (regs->scsi_serr & SERR_SCSI_PAR) {
printf("%sparity err", sep);
sep = ", ";
}
if (regs->scsi_serr & SERR_SPC_PAR) {
printf("%sSPC parity err", sep);
sep = ", ";
}
if (regs->scsi_serr & SERR_TC_PAR) {
printf("%sTC parity err", sep);
sep = ", ";
}
if (regs->scsi_serr & SERR_PHASE_ERR) {
printf("%sphase err", sep);
sep = ", ";
}
if (regs->scsi_serr & SERR_SHORT_XFR) {
printf("%ssync short transfer err", sep);
sep = ", ";
}
if (regs->scsi_serr & SERR_OFFSET) {
printf("%ssync offset error", sep);
sep = ", ";
}
}
if (ints & INTS_TIMEOUT)
printf("%sSPC select timeout error", sep);
if (ints & INTS_SRV_REQ)
printf("%sspurious SRV_REQ interrupt", sep);
if (ints & INTS_CMD_DONE)
printf("%sspurious CMD_DONE interrupt", sep);
if (ints & INTS_DISCON)
printf("%sspurious disconnect interrupt", sep);
if (ints & INTS_RESEL)
printf("%sspurious reselect interrupt", sep);
if (ints & INTS_SEL)
printf("%sspurious select interrupt", sep);
#else
printf ("csr == 0x%02x", csr); /* XXX */
#endif
printf("\n");
}
static int
issue_select(dev, regs, target, our_addr)
register struct scsi_softc *dev;
volatile register sbic_padded_regmap_t *regs;
u_char target, our_addr;
{
u_char asr, csr;
QPRINTF (("issue_select %d\n", target));
/* if we're already selected, return */
if (dev->sc_flags & SCSI_SELECTED) /* XXXX */
return 1;
SBIC_TC_PUT (regs, 0);
SET_SBIC_selid (regs, target);
SET_SBIC_timeo (regs, SBIC_TIMEOUT(250,dev->sc_clock_freq));
if (dev->sc_sync[target].state == SYNC_DONE)
{
/* set to negotiated values */
SET_SBIC_syn (regs, SBIC_SYN (dev->sc_sync[target].offset,
dev->sc_sync[target].period));
}
else
{
/* set to async */
SET_SBIC_syn (regs, SBIC_SYN (0, sbic_min_period));
}
SET_SBIC_cmd (regs, SBIC_CMD_SEL_ATN);
return (0);
}
static int
wait_for_select(dev, regs)
register struct scsi_softc *dev;
volatile register sbic_padded_regmap_t *regs;
{
u_char asr, csr;
QPRINTF (("wait_for_select: "));
WAIT_CIP (regs);
do
{
SBIC_WAIT (regs, SBIC_ASR_INT, 0);
GET_SBIC_csr (regs, csr);
QPRINTF (("%02x ", csr));
}
while (csr != (SBIC_CSR_MIS_2|MESG_OUT_PHASE)
&& csr != SBIC_CSR_SEL_TIMEO);
/* Send identify message (SCSI-2 requires an identify msg (?)) */
if (csr == (SBIC_CSR_MIS_2|MESG_OUT_PHASE))
{
u_char id;
GET_SBIC_selid (regs, id);
/* handle drives that don't want to be asked whether to go
sync at all. */
if (inhibit_sync[dev->sc_ac->amiga_unit][id]
&& dev->sc_sync[id].state == SYNC_START)
{
#ifdef DEBUG
if (sync_debug)
printf ("Forcing target %d asynchronous.\n", id);
#endif
dev->sc_sync[id].offset = 0;
dev->sc_sync[id].period = sbic_min_period;
dev->sc_sync[id].state = SYNC_DONE;
}
if (dev->sc_sync[id].state == SYNC_START)
{
/* then try to initiate a sync transfer.
So compose the sync message we're going to send to the target */
#ifdef DEBUG
if (sync_debug)
printf ("Sending sync request to target %d ... ", id);
#endif
dev->sc_msg[0] = MSG_IDENTIFY; /* no MSG_IDENTIFY_DR yet */
dev->sc_msg[1] = MSG_EXT_MESSAGE; /* sync request is extended message */
dev->sc_msg[2] = 3; /* length */
dev->sc_msg[3] = MSG_SYNC_REQ;
dev->sc_msg[4] = sbic_to_scsi_period (dev, regs, sbic_min_period);
dev->sc_msg[5] = sbic_max_offset;
if (ixfer_start (regs, 6, MESG_OUT_PHASE, scsi_cmd_wait))
ixfer_out (regs, 6, dev->sc_msg, MESG_OUT_PHASE);
dev->sc_sync[id].state = SYNC_SENT;
#ifdef DEBUG
if (sync_debug)
printf ("sent\n");
#endif
}
else
{
/* just send identify then */
SEND_BYTE (regs, MSG_IDENTIFY);
}
SBIC_WAIT (regs, SBIC_ASR_INT, 0);
GET_SBIC_csr (regs, csr);
QPRINTF (("[%02x]", csr));
#ifdef DEBUG
if (sync_debug && dev->sc_sync[id].state == SYNC_SENT)
printf ("csr-result of last msgout: 0x%x\n", csr);
#endif
if (csr != SBIC_CSR_SEL_TIMEO)
dev->sc_flags |= SCSI_SELECTED;
}
QPRINTF(("\n"));
return csr == SBIC_CSR_SEL_TIMEO;
}
static int
ixfer_start(regs, len, phase, wait)
volatile register sbic_padded_regmap_t *regs;
int len;
u_char phase;
register int wait;
{
#if 0
regs->scsi_tch = len >> 16;
regs->scsi_tcm = len >> 8;
regs->scsi_tcl = len;
regs->scsi_pctl = phase;
regs->scsi_tmod = 0; /*XXX*/
regs->scsi_scmd = SCMD_XFR | SCMD_PROG_XFR;
/* wait for xfer to start or svc_req interrupt */
while ((regs->scsi_ssts & SSTS_BUSY) == 0) {
if (regs->scsi_ints || --wait < 0) {
#ifdef DEBUG
if (scsi_debug)
printf("ixfer_start fail: i%x, w%d\n",
regs->scsi_ints, wait);
#endif
HIST(ixstart_wait, wait)
return (0);
}
DELAY(1);
}
HIST(ixstart_wait, wait)
return (1);
#else
if (phase == DATA_IN_PHASE || phase == MESG_IN_PHASE)
{
u_char id;
GET_SBIC_selid (regs, id);
id |= SBIC_SID_FROM_SCSI;
SET_SBIC_selid (regs, id);
SBIC_TC_PUT (regs, (unsigned)len);
}
else if (phase == DATA_OUT_PHASE || phase == MESG_OUT_PHASE
|| phase == CMD_PHASE )
{
SBIC_TC_PUT (regs, (unsigned)len);
}
else
{
SBIC_TC_PUT (regs, 0);
}
QPRINTF(("ixfer_start %d, %d, %d\n", len, phase, wait));
return 1;
#endif
}
static int
ixfer_out(regs, len, buf, phase)
volatile register sbic_padded_regmap_t *regs;
int len;
register u_char *buf;
int phase;
{
register int wait = scsi_data_wait;
u_char orig_csr, csr, asr;
QPRINTF(("ixfer_out {%d} %02x %02x %02x %02x %02x %02x\n",
len, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));
GET_SBIC_csr (regs, orig_csr);
/* sigh.. WD-PROTO strikes again.. sending the command in one go causes
the chip to lock up if talking to certain (misbehaving?) targets.
Anyway, this procedure should work for all targets, but it's slightly
slower due to the overhead */
#if 0
if (phase == CMD_PHASE)
{
WAIT_CIP (regs);
#if 0
for (; len > 0; len--)
{
/* clear possible last interrupt */
GET_SBIC_csr (regs, csr);
/* send the byte, and expect an interrupt afterwards */
SEND_BYTE (regs, *buf);
buf++;
SBIC_WAIT (regs, SBIC_ASR_INT, 0); /* XXX */
}
#else
SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
for (;len > 0; len--)
{
WAIT_CIP (regs);
GET_SBIC_csr (regs, csr);
SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO | SBIC_CMD_SBT);
GET_SBIC_asr (regs, asr);
while (!(asr & SBIC_ASR_DBR))
{
DELAY(1);
GET_SBIC_asr (regs, asr);
}
SET_SBIC_data (regs, *buf);
buf++;
while (!(asr & SBIC_ASR_INT))
{
DELAY(1);
GET_SBIC_asr (regs, asr);
}
}
#endif
}
else
#endif
{
WAIT_CIP (regs);
SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
for (;len > 0; len--)
{
GET_SBIC_asr (regs, asr);
while (!(asr & SBIC_ASR_DBR))
{
if ((asr & SBIC_ASR_INT) || --wait < 0)
{
#ifdef DEBUG
if (scsi_debug)
printf("ixfer_out fail: l%d i%x w%d\n",
len, asr, wait);
#endif
HIST(ixout_wait, wait)
return (len);
}
DELAY(1);
GET_SBIC_asr (regs, asr);
}
SET_SBIC_data (regs, *buf);
buf++;
}
}
QPRINTF(("ixfer_out done\n"));
/* this leaves with one csr to be read */
HIST(ixout_wait, wait)
return (0);
}
static void
ixfer_in(regs, len, buf)
volatile register sbic_padded_regmap_t *regs;
int len;
register u_char *buf;
{
register int wait = scsi_data_wait;
u_char *obp = buf;
u_char orig_csr, csr, asr;
GET_SBIC_csr (regs, orig_csr);
QPRINTF(("ixfer_in %d, csr=%02x\n", len, orig_csr));
WAIT_CIP (regs);
SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
for (;len > 0; len--)
{
GET_SBIC_asr (regs, asr);
while (!(asr & SBIC_ASR_DBR))
{
if ((asr & SBIC_ASR_INT) || --wait < 0)
{
#ifdef DEBUG
if (scsi_debug)
printf("ixfer_in fail: l%d i%x w%d\n",
len, asr, wait);
#endif
HIST(ixin_wait, wait)
return;
}
DELAY(1);
GET_SBIC_asr (regs, asr);
}
GET_SBIC_data (regs, *buf);
buf++;
}
QPRINTF(("ixfer_in {%d} %02x %02x %02x %02x %02x %02x\n",
len, obp[0], obp[1], obp[2], obp[3], obp[4], obp[5]));
/* this leaves with one csr to be read */
HIST(ixin_wait, wait)
}
/*
* SCSI 'immediate' command: issue a command to some SCSI device
* and get back an 'immediate' response (i.e., do programmed xfer
* to get the response data). 'cbuf' is a buffer containing a scsi
* command of length clen bytes. 'buf' is a buffer of length 'len'
* bytes for data. The transfer direction is determined by the device
* (i.e., by the scsi bus data xfer phase). If 'len' is zero, the
* command must supply no data. 'xferphase' is the bus phase the
* caller expects to happen after the command is issued. It should
* be one of DATA_IN_PHASE, DATA_OUT_PHASE or STATUS_PHASE.
*/
static int
scsiicmd(dev, target, cbuf, clen, buf, len, xferphase)
struct scsi_softc *dev;
int target;
u_char *cbuf;
int clen;
u_char *buf;
int len;
u_char xferphase;
{
volatile register sbic_padded_regmap_t *regs =
(sbic_padded_regmap_t *)dev->sc_ac->amiga_addr;
u_char phase, csr, asr;
register int wait;
/* set the sbic into non-DMA mode */
SET_SBIC_control (regs, (/*SBIC_CTL_HHP |*/ SBIC_CTL_EDI | SBIC_CTL_IDI |
/* | SBIC_CTL_HSP | */ 0));
retry_selection:
/* select the SCSI bus (it's an error if bus isn't free) */
if (issue_select (dev, regs, target, dev->sc_scsi_addr))
return -1;
if (wait_for_select (dev, regs))
return -1;
/*
* Wait for a phase change (or error) then let the device
* sequence us through the various SCSI phases.
*/
dev->sc_stat[0] = 0xff;
dev->sc_msg[0] = 0xff;
phase = CMD_PHASE;
while (1)
{
wait = scsi_cmd_wait;
GET_SBIC_csr (regs, csr);
QPRINTF((">CSR:%02x<", csr));
HIST(cxin_wait, wait);
if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08)) /* requesting some new phase */
phase = csr & PHASE;
else if ((csr == SBIC_CSR_DISC) || (csr == SBIC_CSR_DISC_1)
|| (csr == SBIC_CSR_S_XFERRED))
{
dev->sc_flags &= ~SCSI_SELECTED;
GET_SBIC_cmd_phase (regs, phase);
if (phase == 0x60)
GET_SBIC_tlun (regs, dev->sc_stat[0]);
else
return -1;
goto out;
}
else
{
scsierror(dev, regs, csr);
goto abort;
}
switch (phase)
{
case CMD_PHASE:
if (ixfer_start (regs, clen, phase, wait))
if (ixfer_out (regs, clen, cbuf, phase))
goto abort;
phase = xferphase;
break;
case DATA_IN_PHASE:
if (len <= 0)
goto abort;
wait = scsi_data_wait;
if (ixfer_start (regs, len, phase, wait))
ixfer_in (regs, len, buf);
phase = STATUS_PHASE;
break;
case MESG_IN_PHASE:
if (ixfer_start (regs, sizeof (dev->sc_msg), phase, wait))
{
dev->sc_msg[0] = 0xff;
ixfer_in (regs, sizeof (dev->sc_msg), dev->sc_msg);
/* get the command completion interrupt, or we can't send
a new command (LCI) */
SBIC_WAIT (regs, SBIC_ASR_INT, wait);
GET_SBIC_csr (regs, csr);
#ifdef DEBUG
if (sync_debug)
printf ("msgin finished with csr 0x%x\n", csr);
#endif
/* test whether this is a reply to our sync request */
if (dev->sc_msg[0] == MSG_EXT_MESSAGE
&& dev->sc_msg[1] == 3
&& dev->sc_msg[2] == MSG_SYNC_REQ)
{
dev->sc_sync[target].period = scsi_period_to_sbic (dev, regs, dev->sc_msg[3]);
dev->sc_sync[target].offset = dev->sc_msg[4];
dev->sc_sync[target].state = SYNC_DONE;
SET_SBIC_syn (regs, SBIC_SYN (dev->sc_sync[target].offset,
dev->sc_sync[target].period));
/* ACK the message */
SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
WAIT_CIP (regs);
phase = CMD_PHASE; /* or whatever */
printf ("scsi%d: target %d now synchronous, period=%dns, offset=%d.\n",
dev->sc_ac->amiga_unit, target,
dev->sc_msg[3] * 4, dev->sc_msg[4]);
}
else if (dev->sc_msg[0] == MSG_REJECT
&& dev->sc_sync[target].state == SYNC_SENT)
{
#ifdef DEBUG
if (sync_debug)
printf ("target %d rejected sync, going async\n", target);
#endif
dev->sc_sync[target].period = sbic_min_period;
dev->sc_sync[target].offset = 0;
dev->sc_sync[target].state = SYNC_DONE;
SET_SBIC_syn (regs, SBIC_SYN (dev->sc_sync[target].offset,
dev->sc_sync[target].period));
/* ACK the message */
SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
WAIT_CIP (regs);
phase = CMD_PHASE; /* or whatever */
}
else if (dev->sc_msg[0] == MSG_REJECT)
{
/* coming to think of it, we'll never REJECt a REJECT
message.. :-) */
/* ACK the message */
SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
WAIT_CIP (regs);
phase = CMD_PHASE; /* or whatever */
}
else if (dev->sc_msg[0] == MSG_CMD_COMPLETE)
{
/* !! KLUDGE ALERT !!
quite a few drives don't seem to really like the current
way of sending the sync-handshake together with the
ident-message, and they react by sending command-complete
and disconnecting right after returning the valid sync
handshake. So, all I can do is reselect the drive, and
hope it won't disconnect again. I don't think this is valid
behavior, but I can't help fixing a problem that apparently
exists.
Note: we should not get here on `normal' command completion,
as that condition is handled by the high-level sel&xfer resume
command used to walk thru status/cc-phase. */
#ifdef DEBUG
if (sync_debug)
printf ("GOT CMD-COMPLETE! %d acting weird.. waiting for disconnect...\n", target);
#endif
/* ACK the message */
SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
WAIT_CIP (regs);
/* wait for disconnect */
while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1))
{
DELAY (1);
GET_SBIC_csr (regs, csr);
}
#ifdef DEBUG
if (sync_debug)
printf ("ok.\nRetrying selection.\n");
#endif
dev->sc_flags &= ~SCSI_SELECTED;
goto retry_selection;
}
else
{
#ifdef DEBUG
if (scsi_debug || sync_debug)
printf ("Rejecting message 0x%02x\n", dev->sc_msg[0]);
#endif
/* prepare to reject the message, NACK */
SET_SBIC_cmd (regs, SBIC_CMD_SET_ATN);
WAIT_CIP (regs);
SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
WAIT_CIP (regs);
phase = MESG_OUT_PHASE;
}
}
break;
case MESG_OUT_PHASE:
#ifdef DEBUG
if (sync_debug)
printf ("Sending REJECT message to last received message.\n");
#endif
/* should only get here on reject, since it's always US that
initiate a sync transfer */
SEND_BYTE (regs, MSG_REJECT);
phase = STATUS_PHASE;
break;
case DATA_OUT_PHASE:
if (len <= 0)
goto abort;
wait = scsi_data_wait;
if (ixfer_start (regs, len, phase, wait))
{
if (ixfer_out (regs, len, buf, phase))
goto abort;
}
phase = STATUS_PHASE;
break;
case STATUS_PHASE:
/* the sbic does the status/cmd-complete reading ok, so do this
with its hi-level commands. */
SBIC_TC_PUT (regs, 0);
SET_SBIC_cmd_phase (regs, 0x46);
SET_SBIC_cmd (regs, SBIC_CMD_SEL_ATN_XFER);
phase = BUS_FREE_PHASE;
break;
case BUS_FREE_PHASE:
goto out;
default:
printf("scsi: unexpected phase %d in icmd from %d\n",
phase, target);
goto abort;
}
/* make sure the last command was taken, ie. we're not hunting after
an ignored command.. */
GET_SBIC_asr (regs, asr);
if (asr & SBIC_ASR_LCI)
goto abort;
/* tapes may take a loooong time.. */
while (asr & SBIC_ASR_BSY)
{
DELAY(1);
GET_SBIC_asr (regs, asr);
}
#if 0
if (wait <= 0)
goto abort;
#endif
/* wait for last command to complete */
SBIC_WAIT (regs, SBIC_ASR_INT, wait);
}
abort:
scsiabort(dev, regs, "icmd");
out:
return (dev->sc_stat[0]);
}
/*
* Finish SCSI xfer command: After the completion interrupt from
* a read/write operation, sequence through the final phases in
* programmed i/o. This routine is a lot like scsiicmd except we
* skip (and don't allow) the select, cmd out and data in/out phases.
*/
static void
finishxfer(dev, regs, target)
struct scsi_softc *dev;
volatile register sbic_padded_regmap_t *regs;
int target;
{
u_char phase, csr;
int s;
QPRINTF(("{"));
s = splbio();
/* have the sbic complete on its own */
SBIC_TC_PUT (regs, 0);
SET_SBIC_cmd_phase (regs, 0x46);
SET_SBIC_cmd (regs, SBIC_CMD_SEL_ATN_XFER);
do
{
SBIC_WAIT (regs, SBIC_ASR_INT, 0);
GET_SBIC_csr (regs, csr);
QPRINTF(("%02x:", csr));
}
while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
&& (csr != SBIC_CSR_S_XFERRED));
dev->sc_flags &= ~SCSI_SELECTED;
GET_SBIC_cmd_phase (regs, phase);
QPRINTF(("}%02x\n", phase));
if (phase == 0x60)
GET_SBIC_tlun (regs, dev->sc_stat[0]);
else
scsierror (dev, regs, csr);
splx (s);
}
int
scsi_test_unit_rdy(ctlr, slave, unit)
int ctlr, slave, unit;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY };
cdb.lun = unit;
return (scsiicmd(dev, slave, (u_char *)&cdb, sizeof(cdb), (u_char *)0, 0,
STATUS_PHASE));
}
int
scsi_start_stop_unit (ctlr, slave, unit, start)
int ctlr, slave, unit;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
static struct scsi_cdb6 cdb = { CMD_LOADUNLOAD };
cdb.lun = unit;
/* we don't set the immediate bit, so we wait for the
command to succeed.
We also don't touch the LoEj bit, which is primarily meant
for floppies. */
cdb.len = start & 0x01;
return (scsiicmd(dev, slave, (u_char *)&cdb, sizeof(cdb), (u_char *)0, 0,
STATUS_PHASE));
}
int
scsi_request_sense(ctlr, slave, unit, buf, len)
int ctlr, slave, unit;
u_char *buf;
unsigned len;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE };
cdb.lun = unit;
cdb.len = len;
return (scsiicmd(dev, slave, (u_char *)&cdb, sizeof(cdb), buf, len, DATA_IN_PHASE));
}
int
scsi_immed_command(ctlr, slave, unit, cdb, buf, len, rd)
int ctlr, slave, unit;
struct scsi_fmt_cdb *cdb;
u_char *buf;
unsigned len;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
cdb->cdb[1] |= (unit << 5);
return (scsiicmd(dev, slave, (u_char *) cdb->cdb, cdb->len, buf, len,
rd != 0? DATA_IN_PHASE : DATA_OUT_PHASE));
}
/*
* The following routines are test-and-transfer i/o versions of read/write
* for things like reading disk labels and writing core dumps. The
* routine scsigo should be used for normal data transfers, NOT these
* routines.
*/
int
scsi_tt_read(ctlr, slave, unit, buf, len, blk, bshift)
int ctlr, slave, unit;
u_char *buf;
u_int len;
daddr_t blk;
int bshift;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
struct scsi_cdb10 cdb;
int stat;
int old_wait = scsi_data_wait;
scsi_data_wait = 300000;
bzero(&cdb, sizeof(cdb));
cdb.cmd = CMD_READ_EXT;
cdb.lun = unit;
blk >>= bshift;
cdb.lbah = blk >> 24;
cdb.lbahm = blk >> 16;
cdb.lbalm = blk >> 8;
cdb.lbal = blk;
cdb.lenh = len >> (8 + DEV_BSHIFT + bshift);
cdb.lenl = len >> (DEV_BSHIFT + bshift);
stat = scsiicmd(dev, slave, (u_char *) &cdb, sizeof(cdb), buf, len, DATA_IN_PHASE);
scsi_data_wait = old_wait;
return (stat);
}
int
scsi_tt_write(ctlr, slave, unit, buf, len, blk, bshift)
int ctlr, slave, unit;
u_char *buf;
u_int len;
daddr_t blk;
int bshift;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
struct scsi_cdb10 cdb;
int stat;
int old_wait = scsi_data_wait;
scsi_data_wait = 300000;
bzero(&cdb, sizeof(cdb));
cdb.cmd = CMD_WRITE_EXT;
cdb.lun = unit;
blk >>= bshift;
cdb.lbah = blk >> 24;
cdb.lbahm = blk >> 16;
cdb.lbalm = blk >> 8;
cdb.lbal = blk;
cdb.lenh = len >> (8 + DEV_BSHIFT + bshift);
cdb.lenl = len >> (DEV_BSHIFT + bshift);
stat = scsiicmd(dev, slave, (u_char *) &cdb, sizeof(cdb), buf, len, DATA_OUT_PHASE);
scsi_data_wait = old_wait;
return (stat);
}
int
scsireq(dq)
register struct devqueue *dq;
{
register struct devqueue *hq;
hq = &scsi_softc[dq->dq_ctlr].sc_sq;
insque(dq, hq->dq_back);
if (dq->dq_back == hq)
return(1);
return(0);
}
int
scsiustart (int unit)
{
register struct scsi_softc *dev = &scsi_softc[unit];
if (dev->dmareq(&dev->sc_dq))
return(1);
return(0);
}
void
scsistart (int unit)
{
register struct devqueue *dq;
dq = scsi_softc[unit].sc_sq.dq_forw;
(dq->dq_driver->d_go)(dq->dq_unit);
}
int
scsigo(ctlr, slave, unit, bp, cdb, pad)
int ctlr, slave, unit;
struct buf *bp;
struct scsi_fmt_cdb *cdb;
int pad;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
volatile register sbic_padded_regmap_t *regs =
(sbic_padded_regmap_t *)dev->sc_ac->amiga_addr;
int i, dmaflags;
u_char phase, csr, asr, cmd;
cdb->cdb[1] |= unit << 5;
/* this should only happen on character devices, and there only if user
programs have not been written taking care of not passing odd aligned
buffers. dd was a bad example of this sin.. */
/* XXXX do all with polled I/O */
if (scsi_no_dma || (((int)bp->b_un.b_addr & 3) || (bp->b_bcount & 1)))
{
register struct devqueue *dq;
dev->dmafree(&dev->sc_dq);
/* in this case do the transfer with programmed I/O :-( This is
probably still faster than doing the transfer with DMA into a
buffer and copying it later to its final destination, comments? */
scsiicmd (dev, slave, (u_char *) cdb->cdb, cdb->len,
bp->b_un.b_addr, bp->b_bcount,
bp->b_flags & B_READ ? DATA_IN_PHASE : DATA_OUT_PHASE);
dq = dev->sc_sq.dq_forw;
dev->sc_flags &=~ SCSI_IO;
(dq->dq_driver->d_intr)(dq->dq_unit, dev->sc_stat[0]);
return dev->sc_stat[0];
}
/* set the sbic into DMA mode */
SET_SBIC_control (regs, (/*SBIC_CTL_HHP |*/ SBIC_CTL_EDI | SBIC_CTL_IDI |
/* | SBIC_CTL_HSP | */ SBIC_MACHINE_DMA_MODE));
/* select the SCSI bus (it's an error if bus isn't free) */
if (issue_select(dev, regs, slave, dev->sc_scsi_addr) || wait_for_select(dev, regs))
{
dev->dmafree(&dev->sc_dq);
return (1);
}
/*
* Wait for a phase change (or error) then let the device
* sequence us through command phase (we may have to take
* a msg in/out before doing the command). If the disk has
* to do a seek, it may be a long time until we get a change
* to data phase so, in the absense of an explicit phase
* change, we assume data phase will be coming up and tell
* the SPC to start a transfer whenever it does. We'll get
* a service required interrupt later if this assumption is
* wrong. Otherwise we'll get a service required int when
* the transfer changes to status phase.
*/
phase = CMD_PHASE;
while (1)
{
register int wait = scsi_cmd_wait;
register struct devqueue *dq;
switch (phase)
{
case CMD_PHASE:
if (ixfer_start(regs, cdb->len, phase, wait))
if (ixfer_out(regs, cdb->len, cdb->cdb, phase))
goto abort;
break;
case MESG_IN_PHASE:
if (ixfer_start (regs, sizeof (dev->sc_msg), phase, wait))
{
ixfer_in (regs, sizeof (dev->sc_msg), dev->sc_msg);
/* prepare to reject any mesgin, no matter what it might be.. */
SET_SBIC_cmd (regs, SBIC_CMD_SET_ATN);
WAIT_CIP (regs);
SET_SBIC_cmd (regs, SBIC_CMD_CLR_ACK);
phase = MESG_OUT_PHASE;
}
break;
case MESG_OUT_PHASE:
SEND_BYTE (regs, MSG_REJECT);
phase = STATUS_PHASE;
break;
case DATA_IN_PHASE:
case DATA_OUT_PHASE:
goto out;
/* status phase can happen, if the issued read/write command is
illegal (for example, reading after EOT on tape) and the device
doesn't even go to data in/out phase. So handle this here
normally, instead of going thru abort-handling. */
case STATUS_PHASE:
dev->dmafree(&dev->sc_dq);
finishxfer (dev, regs, slave);
dq = dev->sc_sq.dq_forw;
dev->sc_flags &=~ SCSI_IO;
(dq->dq_driver->d_intr)(dq->dq_unit, dev->sc_stat[0]);
return 0;
default:
printf("scsi: unexpected phase %d in go from %d\n",
phase, slave);
goto abort;
}
/* make sure the last command was taken, ie. we're not hunting after
an ignored command.. */
GET_SBIC_asr (regs, asr);
if (asr & SBIC_ASR_LCI)
goto abort;
/* tapes may take a loooong time.. */
while (asr & SBIC_ASR_BSY)
{
DELAY(1);
GET_SBIC_asr (regs, asr);
}
if (wait <= 0)
goto abort;
/* wait for last command to complete */
SBIC_WAIT (regs, SBIC_ASR_INT, wait);
GET_SBIC_csr (regs, csr);
QPRINTF((">CSR:%02x<", csr));
HIST(sgo_wait, wait);
if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08)) /* requesting some new phase */
phase = csr & PHASE;
else
{
scsierror(dev, regs, csr);
goto abort;
}
}
out:
dmaflags = DMAGO_NOINT;
if (bp->b_flags & B_READ)
dmaflags |= DMAGO_READ;
if ((int)bp->b_un.b_addr & 3)
panic ("not long-aligned buffer address in scsi_go");
if (bp->b_bcount & 1)
panic ("odd transfer count in scsi_go");
/* dmago() also enables interrupts for the sbic */
i = dev->dmago(ctlr, bp->b_un.b_addr, bp->b_bcount, dmaflags);
SBIC_TC_PUT (regs, (unsigned)i);
SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
return (0);
abort:
scsiabort(dev, regs, "go");
dev->dmafree(&dev->sc_dq);
return (1);
}
void
scsidone (int unit)
{
volatile register sbic_padded_regmap_t *regs =
(sbic_padded_regmap_t *)scsi_softc[unit].sc_ac->amiga_addr;
#ifdef DEBUG
if (scsi_debug)
printf("scsi%d: done called!\n", unit);
#endif
}
int
scsiintr (int unit)
{
register struct scsi_softc *dev = &scsi_softc[unit];
volatile register sbic_padded_regmap_t *regs =
(sbic_padded_regmap_t *)dev->sc_ac->amiga_addr;
register u_char asr, csr, phase;
register struct devqueue *dq;
int i;
GET_SBIC_asr (regs, asr);
if (! (asr & SBIC_ASR_INT))
return 0;
GET_SBIC_csr (regs, csr);
QPRINTF(("[0x%x]", csr));
if (csr == (SBIC_CSR_XFERRED|STATUS_PHASE)
|| csr == (SBIC_CSR_MIS|STATUS_PHASE)
|| csr == (SBIC_CSR_MIS_1|STATUS_PHASE)
|| csr == (SBIC_CSR_MIS_2|STATUS_PHASE))
{
/*
* this should be the normal i/o completion case.
* get the status & cmd complete msg then let the
* device driver look at what happened.
*/
dq = dev->sc_sq.dq_forw;
finishxfer(dev, regs, dq->dq_slave);
dev->sc_flags &=~ SCSI_IO;
dev->dmafree (&dev->sc_dq);
(dq->dq_driver->d_intr)(dq->dq_unit, dev->sc_stat[0]);
}
else if (csr == (SBIC_CSR_XFERRED|DATA_OUT_PHASE) || csr == (SBIC_CSR_XFERRED|DATA_IN_PHASE)
|| csr == (SBIC_CSR_MIS|DATA_OUT_PHASE) || csr == (SBIC_CSR_MIS|DATA_IN_PHASE)
|| csr == (SBIC_CSR_MIS_1|DATA_OUT_PHASE) || csr == (SBIC_CSR_MIS_1|DATA_IN_PHASE)
|| csr == (SBIC_CSR_MIS_2|DATA_OUT_PHASE) || csr == (SBIC_CSR_MIS_2|DATA_IN_PHASE))
{
/* do scatter-gather dma hacking the controller chip, ouch.. */
i = dev->dmanext (unit);
SBIC_TC_PUT (regs, (unsigned)i);
SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
}
else
{
/* Something unexpected happened -- deal with it. */
dev->dmastop (unit);
scsierror(dev, regs, csr);
scsiabort(dev, regs, "intr");
if (dev->sc_flags & SCSI_IO)
{
dev->sc_flags &=~ SCSI_IO;
dev->dmafree (&dev->sc_dq);
dq = dev->sc_sq.dq_forw;
(dq->dq_driver->d_intr)(dq->dq_unit, -1);
}
}
return 1;
}
void
scsifree(dq)
register struct devqueue *dq;
{
register struct devqueue *hq;
hq = &scsi_softc[dq->dq_ctlr].sc_sq;
remque(dq);
if ((dq = hq->dq_forw) != hq)
(dq->dq_driver->d_start)(dq->dq_unit);
}
/*
* (XXX) The following routine is needed for the SCSI tape driver
* to read odd-size records.
*/
#include "st.h"
#if NST > 0
int
scsi_tt_oddio(ctlr, slave, unit, buf, len, b_flags, freedma)
int ctlr, slave, unit, b_flags;
u_char *buf;
u_int len;
{
register struct scsi_softc *dev = &scsi_softc[ctlr];
struct scsi_cdb6 cdb;
u_char iphase;
int stat;
/*
* First free any DMA channel that was allocated.
* We can't use DMA to do this transfer.
*/
if (freedma)
dev->dmafree(&dev->sc_dq);
/*
* Initialize command block
*/
bzero(&cdb, sizeof(cdb));
cdb.lun = unit;
cdb.lbam = (len >> 16) & 0xff;
cdb.lbal = (len >> 8) & 0xff;
cdb.len = len & 0xff;
if (buf == 0) {
cdb.cmd = CMD_SPACE;
cdb.lun |= 0x00;
len = 0;
iphase = MESG_IN_PHASE;
} else if (b_flags & B_READ) {
cdb.cmd = CMD_READ;
iphase = DATA_IN_PHASE;
} else {
cdb.cmd = CMD_WRITE;
iphase = DATA_OUT_PHASE;
}
/*
* Perform command (with very long delays)
*/
scsi_delay(30000000);
stat = scsiicmd(dev, slave, (u_char *) &cdb, sizeof(cdb), buf, len, iphase);
scsi_delay(0);
return (stat);
}
#endif
#endif