#include <sys/param.h>
#include <machine/board.h>
#include <luna88k/stand/boot/samachdep.h>
#include <luna88k/stand/boot/scsireg.h>
#include <luna88k/stand/boot/scsivar.h>
#define SCSI_ID 7
int scintr(struct scsi_softc *);
void screset(struct scsi_softc *);
int issue_select(struct scsidevice *, u_char);
void ixfer_start(struct scsidevice *, int, u_char, int);
void ixfer_out(struct scsidevice *, int, u_char *);
void ixfer_in(struct scsidevice *, int, u_char *);
int scrun(struct scsi_softc *, uint, u_char *, int, u_char *, int,
volatile int *);
int scfinish(struct scsi_softc *);
void scabort(struct scsi_softc *, struct scsidevice *);
int
scinit(struct scsi_softc *hs, uint unit)
{
void *reg;
switch (unit) {
case 0:
reg = (void *)SCSI_ADDR;
break;
case 1:
reg = (void *)(SCSI_ADDR + 0x40);
break;
default:
return 0;
}
if (unit != 0 && machtype != LUNA_88K2)
return 0;
hs->sc_sd = (struct scsidevice *)reg;
hs->sc_unit = unit;
hs->sc_flags = 0;
hs->sc_phase = BUS_FREE_PHASE;
hs->sc_target = SCSI_ID;
hs->sc_cdb = NULL;
hs->sc_cdblen = 0;
hs->sc_buf = NULL;
hs->sc_len = 0;
hs->sc_lock = NULL;
hs->sc_stat = 0;
hs->sc_msg[0] = 0;
screset(hs);
return(1);
}
void
screset(struct scsi_softc *hs)
{
struct scsidevice *hd = hs->sc_sd;
#ifdef DEBUG
printf("sc%d: ", hs->sc_unit);
#endif
hd->scsi_sctl = SCTL_DISABLE | SCTL_CTRLRST;
hd->scsi_scmd = 0;
hd->scsi_pctl = 0;
hd->scsi_temp = 0;
hd->scsi_tch = 0;
hd->scsi_tcm = 0;
hd->scsi_tcl = 0;
hd->scsi_ints = 0;
#ifdef DEBUG
printf("async");
#endif
hd->scsi_bdid = SCSI_ID;
hd->scsi_sctl = SCTL_DISABLE | SCTL_ABRT_ENAB|
SCTL_PARITY_ENAB | SCTL_RESEL_ENAB |
SCTL_INTR_ENAB;
#ifdef DEBUG
printf(", parity");
#endif
DELAY(400);
hd->scsi_sctl &= ~SCTL_DISABLE;
#ifdef DEBUG
printf(", scsi id %d\n", SCSI_ID);
#endif
}
int
issue_select(struct scsidevice *hd, u_char target)
{
hd->scsi_pctl = 0;
hd->scsi_temp = (1 << SCSI_ID) | (1 << target);
hd->scsi_tch = 2;
hd->scsi_tcm = 113;
hd->scsi_tcl = 3;
hd->scsi_scmd = SCMD_SELECT;
return (1);
}
void
ixfer_start(struct scsidevice *hd, int len, u_char phase, int wait)
{
hd->scsi_tch = ((len & 0xff0000) >> 16);
hd->scsi_tcm = ((len & 0x00ff00) >> 8);
hd->scsi_tcl = (len & 0x0000ff);
hd->scsi_pctl = phase;
hd->scsi_scmd = SCMD_XFR | SCMD_PROG_XFR;
}
void
ixfer_out(struct scsidevice *hd, int len, u_char *buf)
{
for(; len > 0; len--) {
while (hd->scsi_ssts & SSTS_DREG_FULL) {
DELAY(5);
}
hd->scsi_dreg = *buf++;
}
}
void
ixfer_in(struct scsidevice *hd, int len, u_char *buf)
{
for (; len > 0; len--) {
while (hd->scsi_ssts & SSTS_DREG_EMPTY) {
DELAY(5);
}
*buf++ = hd->scsi_dreg;
}
}
int
scrun(struct scsi_softc *hs, uint target, u_char *cdb, int cdblen, u_char *buf,
int len, volatile int *lock)
{
struct scsidevice *hd;
hd = hs->sc_sd;
if (hd->scsi_ssts & (SSTS_INITIATOR|SSTS_TARGET|SSTS_BUSY))
return(0);
if (target > 7)
return 0;
hs->sc_flags = 0;
hs->sc_phase = ARB_SEL_PHASE;
hs->sc_target = target >= 7 ? target : 6 - target;
hs->sc_cdb = cdb;
hs->sc_cdblen = cdblen;
hs->sc_buf = buf;
hs->sc_len = len;
hs->sc_lock = lock;
hs->sc_stat = 0;
hs->sc_msg[0] = 0;
*(hs->sc_lock) = SC_IN_PROGRESS;
issue_select(hd, hs->sc_target);
return(1);
}
int
scfinish(struct scsi_softc *hs)
{
int status = hs->sc_stat;
hs->sc_flags = 0;
hs->sc_phase = BUS_FREE_PHASE;
hs->sc_target = SCSI_ID;
hs->sc_cdb = NULL;
hs->sc_cdblen = 0;
hs->sc_buf = NULL;
hs->sc_len = 0;
hs->sc_lock = NULL;
hs->sc_stat = 0;
hs->sc_msg[0] = 0;
return(status);
}
void
scabort(struct scsi_softc *hs, struct scsidevice *hd)
{
int len;
u_char junk;
printf("sc%d: abort phase=0x%x, ssts=0x%x, ints=0x%x\n",
hs->sc_unit, hd->scsi_psns, hd->scsi_ssts, hd->scsi_ints);
if (hd->scsi_ints != 0)
hd->scsi_ints = hd->scsi_ints;
if (hd->scsi_psns == 0 || (hd->scsi_ssts & SSTS_INITIATOR) == 0)
return;
len = (hd->scsi_tch << 16) | (hd->scsi_tcm << 8) | hd->scsi_tcl;
for (len += 1024; (hd->scsi_ssts & SSTS_INITIATOR) && --len >= 0; ) {
hd->scsi_scmd = SCMD_SET_ATN;
while ((hd->scsi_psns & PSNS_REQ) == 0) {
if (! (hd->scsi_ssts & SSTS_INITIATOR))
goto out;
DELAY(1);
}
if ((hd->scsi_psns & PHASE) == MESG_OUT_PHASE)
hd->scsi_scmd = SCMD_RST_ATN;
hd->scsi_pctl = hs->sc_phase = hd->scsi_psns & PHASE;
if (hd->scsi_psns & PHASE_IO) {
hd->scsi_scmd = SCMD_SET_ACK;
while (hd->scsi_psns & PSNS_REQ)
DELAY(1);
junk = hd->scsi_temp;
} else {
hd->scsi_temp = MSG_ABORT;
hd->scsi_scmd = SCMD_SET_ACK;
while (hd->scsi_psns & PSNS_REQ)
DELAY(1);
}
hd->scsi_scmd = SCMD_RST_ACK;
}
out:
if (len < 0 && hs)
printf("sc%d: abort failed. phase=0x%x, ssts=0x%x\n",
hs->sc_unit, hd->scsi_psns, hd->scsi_ssts);
}
int
scsi_test_unit_rdy(struct scsi_softc *sc, int target, int unit)
{
static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY };
int status;
volatile int lock;
#ifdef DEBUG
printf("scsi_test_unit_rdy(%d,%d,%d): Start\n",
sc->sc_unit, target, unit);
#endif
cdb.lun = unit;
if (!(scrun(sc, target, (void *)&cdb, 6, NULL, 0, &lock))) {
#ifdef DEBUG
printf("scsi_test_unit_rdy: Command Transfer Failed.\n");
#endif
return(-1);
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED)) {
if (scintr(sc))
DELAY(10);
}
status = scfinish(sc);
if (lock == SC_IO_COMPLETE) {
#ifdef DEBUG
printf("scsi_test_unit_rdy: Status -- 0x%x\n", status);
#endif
return(status);
} else {
return(lock);
}
}
int
scsi_request_sense(struct scsi_softc *sc, int target, int unit, u_char *buf,
unsigned int len)
{
static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE };
int status;
volatile int lock;
#ifdef DEBUG
printf("scsi_request_sense: Start\n");
#endif
cdb.lun = unit;
cdb.len = len;
if (!(scrun(sc, target, (void *)&cdb, 6, buf, len, &lock))) {
#ifdef DEBUG
printf("scsi_request_sense: Command Transfer Failed.\n");
#endif
return(-1);
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED)) {
if (scintr(sc))
DELAY(10);
}
status = scfinish(sc);
if (lock == SC_IO_COMPLETE) {
#ifdef DEBUG
printf("scsi_request_sense: Status -- 0x%x\n", status);
#endif
return(status);
} else {
return(lock);
}
}
int
scsi_immed_command(struct scsi_softc *sc, int target, int unit,
struct scsi_generic_cdb *cdb, u_char *buf, unsigned int len)
{
int status;
volatile int lock;
#ifdef DEBUG
printf("scsi_immed_command( %d, %d, %d, cdb(%d), buf, %d): Start\n",
sc->sc_unit, target, unit, cdb->len, len);
#endif
cdb->cdb[1] |= unit << 5;
if (!(scrun(sc, target, (void *)&cdb->cdb[0], cdb->len, buf, len,
&lock))) {
#ifdef DEBUG
printf("scsi_immed_command: Command Transfer Failed.\n");
#endif
return(-1);
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED)) {
if (scintr(sc))
DELAY(10);
}
status = scfinish(sc);
if (lock == SC_IO_COMPLETE) {
#ifdef DEBUG
printf("scsi_immed_command: Status -- 0x%x\n", status);
#endif
return(status);
} else {
return(lock);
}
}
int
scintr(struct scsi_softc *hs)
{
struct scsidevice *hd;
u_char ints, temp;
u_char *buf;
int i, len;
hd = hs->sc_sd;
if ((ints = hd->scsi_ints) == 0)
return -1;
#ifdef DEBUG
printf("scintr: INTS 0x%x, SSTS 0x%x, PCTL 0x%x, PSNS 0x%x 0x%x\n",
ints, hd->scsi_ssts, hd->scsi_pctl, hd->scsi_psns,
hs->sc_phase);
#endif
if (ints & INTS_RESEL) {
if (hs->sc_phase == BUS_FREE_PHASE) {
temp = hd->scsi_temp & ~(1 << SCSI_ID);
for (i = 0; temp != 1; i++) {
temp >>= 1;
}
hs->sc_target = i;
*(hs->sc_lock) = SC_IN_PROGRESS;
} else
goto abort;
} else if (ints & INTS_DISCON) {
if (hs->sc_msg[0] == MSG_CMD_COMPLETE ||
hs->sc_msg[0] == MSG_DISCONNECT) {
hs->sc_phase = BUS_FREE_PHASE;
hs->sc_target = SCSI_ID;
if (hs->sc_msg[0] == MSG_CMD_COMPLETE)
*(hs->sc_lock) = SC_IO_COMPLETE;
else
*(hs->sc_lock) = SC_DISCONNECTED;
hd->scsi_ints = ints;
return 0;
} else
goto abort;
} else if (ints & INTS_CMD_DONE) {
if (hs->sc_phase == BUS_FREE_PHASE)
goto abort;
else if (hs->sc_phase == MESG_IN_PHASE) {
hd->scsi_scmd = SCMD_RST_ACK;
hd->scsi_ints = ints;
hs->sc_phase = hd->scsi_psns & PHASE;
return 0;
}
if (hs->sc_flags & SC_SEL_TIMEOUT)
hs->sc_flags &= ~SC_SEL_TIMEOUT;
} else if (ints & INTS_SRV_REQ) {
if (hs->sc_phase != MESG_IN_PHASE)
goto abort;
} else if (ints & INTS_TIMEOUT) {
if (hs->sc_phase == ARB_SEL_PHASE) {
if (hs->sc_flags & SC_SEL_TIMEOUT) {
hs->sc_flags &= ~SC_SEL_TIMEOUT;
hs->sc_phase = BUS_FREE_PHASE;
hs->sc_target = SCSI_ID;
*(hs->sc_lock) = SC_DEV_NOT_FOUND;
hd->scsi_ints = ints;
return 0;
} else {
hs->sc_flags |= SC_SEL_TIMEOUT;
hd->scsi_temp = 0;
hd->scsi_tch = 0;
hd->scsi_tcm = 0x06;
hd->scsi_tcl = 0x40;
hd->scsi_ints = ints;
return 0;
}
} else
goto abort;
} else
goto abort;
hd->scsi_ints = ints;
while ((hd->scsi_psns & PSNS_REQ) == 0) {
DELAY(1);
}
hs->sc_phase = hd->scsi_psns & PHASE;
if (hs->sc_phase == DATA_OUT_PHASE || hs->sc_phase == DATA_IN_PHASE) {
len = hs->sc_len;
buf = hs->sc_buf;
} else if (hs->sc_phase == CMD_PHASE) {
len = hs->sc_cdblen;
buf = hs->sc_cdb;
} else if (hs->sc_phase == STATUS_PHASE) {
len = 1;
buf = &hs->sc_stat;
} else {
len = 1;
buf = hs->sc_msg;
}
ixfer_start(hd, len, hs->sc_phase, 0);
if (hs->sc_phase & PHASE_IO)
ixfer_in(hd, len, buf);
else
ixfer_out(hd, len, buf);
return 0;
abort:
scabort(hs, hd);
hd->scsi_ints = ints;
*(hs->sc_lock) = SC_IO_FAILED;
return -1;
}