#define NSC 2
#include <sys/param.h>
#include <luna68k/stand/boot/samachdep.h>
#include <luna68k/stand/boot/scsireg.h>
#include <luna68k/stand/boot/scsivar.h>
#define SCSI_ID 7
static void screset(struct scsi_softc *);
static void scprobe(struct scsi_softc *, uint, uint);
static int issue_select(struct scsidevice *, uint8_t);
static void ixfer_start(struct scsidevice *, int, uint8_t, int);
static void ixfer_out(struct scsidevice *, int, uint8_t *);
static void ixfer_in(struct scsidevice *, int, uint8_t *);
static int scrun(int, int, uint8_t *, int, uint8_t *, int, volatile int *);
static int scfinish(int);
static void scabort(struct scsi_softc *);
struct scsi_softc scsi_softc[NSC];
int
scinit(int ctlr, void *addr)
{
struct scsi_softc *hs;
uint id;
if (ctlr < 0 || ctlr >= NSC)
return 0;
hs = &scsi_softc[ctlr];
hs->sc_ctlr = ctlr;
hs->sc_spc = addr;
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);
for (id = 0; id < 7; id++)
scprobe(hs, id, 0);
return 1;
}
static void
screset(struct scsi_softc *hs)
{
struct scsidevice *hd = hs->sc_spc;
printf("sc%d at 0x%08lx: ", hs->sc_ctlr, (u_long)hs->sc_spc);
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;
printf("async");
hd->scsi_bdid = SCSI_ID;
hd->scsi_sctl = SCTL_DISABLE | SCTL_ABRT_ENAB|
SCTL_PARITY_ENAB | SCTL_RESEL_ENAB |
SCTL_INTR_ENAB;
printf(", parity");
DELAY(400);
hd->scsi_sctl &= ~SCTL_DISABLE;
printf(", ID %d\n", SCSI_ID);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
bool
scident(uint ctlr, uint target, uint lun, struct scsi_inquiry *inqout,
uint32_t *capout)
{
struct scsi_inquiry inqbuf;
struct scsi_generic_cdb inq = {
6,
{ CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 }
};
uint32_t capbuf[2];
struct scsi_generic_cdb cap = {
10,
{ CMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
int i;
int tries = 10;
while ((i = scsi_test_unit_rdy(ctlr, target, lun)) != 0) {
if (i < 0 || --tries < 0)
return false;
if (i == STS_CHECKCOND) {
uint8_t sensebuf[8];
struct scsi_xsense *sp = (struct scsi_xsense *)sensebuf;
scsi_request_sense(ctlr, target, lun, sensebuf, 8);
if (sp->class == 7 && sp->key == 6)
DELAY(1000000);
}
DELAY(1000);
}
if (scsi_immed_command(ctlr, target, lun, &inq, (uint8_t *)&inqbuf,
sizeof(inqbuf)) ||
scsi_immed_command(ctlr, target, lun, &cap, (uint8_t *)&capbuf,
sizeof(capbuf)))
return false;
switch (inqbuf.type) {
case 0:
case 4:
case 5:
case 7:
break;
default:
return false;
}
if (inqout != NULL)
*inqout = inqbuf;
if (capout != NULL) {
capout[0] = capbuf[0];
capout[1] = capbuf[1];
}
return true;
}
#pragma GCC diagnostic pop
static void
scprobe(struct scsi_softc *hs, uint target, uint lun)
{
struct scsi_inquiry inqbuf;
uint32_t capbuf[2], blocks, blksize;
char idstr[32];
int i;
if (!scident(hs->sc_ctlr, target, lun, &inqbuf, capbuf))
return;
blocks = capbuf[0] + 1;
blksize = capbuf[1];
memcpy(idstr, &inqbuf.vendor_id, 28);
for (i = 27; i > 23; --i)
if (idstr[i] != ' ')
break;
idstr[i + 1] = '\0';
for (i = 23; i > 7; --i)
if (idstr[i] != ' ')
break;
idstr[i + 1] = '\0';
for (i = 7; i >= 0; --i)
if (idstr[i] != ' ')
break;
idstr[i + 1] = '\0';
printf(" ID %d: %s %s rev %s", target, idstr, &idstr[8], &idstr[24]);
printf(", %d bytes/sect x %d sectors\n", blksize, blocks);
}
static int
issue_select(struct scsidevice *hd, uint8_t 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;
}
static void
ixfer_start(struct scsidevice *hd, int len, uint8_t 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;
}
static void
ixfer_out(struct scsidevice *hd, int len, uint8_t *buf)
{
for (; len > 0; len--) {
while (hd->scsi_ssts & SSTS_DREG_FULL) {
DELAY(5);
}
hd->scsi_dreg = *buf++;
}
}
static void
ixfer_in(struct scsidevice *hd, int len, uint8_t *buf)
{
for (; len > 0; len--) {
while (hd->scsi_ssts & SSTS_DREG_EMPTY) {
DELAY(5);
}
*buf++ = hd->scsi_dreg;
}
}
static int
scrun(int ctlr, int target, uint8_t *cdb, int cdblen, uint8_t *buf, int len,
volatile int *lock)
{
struct scsi_softc *hs;
struct scsidevice *hd;
if (ctlr < 0 || ctlr >= NSC)
return 0;
hs = &scsi_softc[ctlr];
hd = hs->sc_spc;
if (hd == NULL)
return 0;
if ((hd->scsi_ssts & (SSTS_INITIATOR | SSTS_TARGET | SSTS_BUSY)) != 0)
return 0;
hs->sc_flags = 0;
hs->sc_phase = ARB_SEL_PHASE;
hs->sc_target = 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;
}
static int
scfinish(int ctlr)
{
struct scsi_softc *hs = &scsi_softc[ctlr];
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;
}
static void
scabort(struct scsi_softc *hs)
{
struct scsidevice *hd = hs->sc_spc;
int len;
printf("sc%d: abort phase=0x%x, ssts=0x%x, ints=0x%x\n",
hs->sc_ctlr, 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)) != 0 && --len >= 0;) {
hd->scsi_scmd = SCMD_SET_ATN;
while ((hd->scsi_psns & PSNS_REQ) == 0) {
if ((hd->scsi_ssts & SSTS_INITIATOR) == 0)
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) != 0)
DELAY(1);
(void)hd->scsi_temp;
} else {
hd->scsi_temp = MSG_ABORT;
hd->scsi_scmd = SCMD_SET_ACK;
while ((hd->scsi_psns & PSNS_REQ) != 0)
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_ctlr, hd->scsi_psns, hd->scsi_ssts);
}
int
scsi_test_unit_rdy(int ctlr, int target, int lun)
{
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", ctlr, target, lun);
#endif
cdb.lun = lun;
if (scrun(ctlr, target, (void *)&cdb, 6, NULL, 0, &lock) == 0) {
#ifdef DEBUG
printf("scsi_test_unit_rdy: Command Transfer Failed.\n");
#endif
return -1;
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED))
DELAY(10);
status = scfinish(ctlr);
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(int ctlr, int target, int lun, uint8_t *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 = lun;
cdb.len = len;
if (scrun(ctlr, target, (void *)&cdb, 6, buf, len, &lock) == 0) {
#ifdef DEBUG
printf("scsi_request_sense: Command Transfer Failed.\n");
#endif
return -1;
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED))
DELAY(10);
status = scfinish(ctlr);
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(int ctlr, int target, int lun, struct scsi_generic_cdb *cdb,
uint8_t *buf, unsigned int len)
{
int status;
volatile int lock;
#ifdef DEBUG
printf("scsi_immed_command( %d, %d, %d, cdb(%d), buf, %d): Start\n",
ctlr, target, lun, cdb->len, len);
#endif
cdb->cdb[1] |= lun << 5;
if (scrun(ctlr, target, (void *)&cdb->cdb[0], cdb->len, buf, len,
&lock) == 0) {
#ifdef DEBUG
printf("scsi_immed_command: Command Transfer Failed.\n");
#endif
return -1;
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED))
DELAY(10);
status = scfinish(ctlr);
if (lock == SC_IO_COMPLETE) {
#ifdef DEBUG
printf("scsi_immed_command: Status -- 0x%x\n", status);
#endif
return status;
} else {
return lock;
}
}
int
scsi_format_unit(int ctlr, int target, int lun)
{
static struct scsi_cdb6 cdb = { CMD_FORMAT_UNIT, 0, 0, 0, 0, 0 };
int status;
volatile int lock;
#ifdef DEBUG
int count = 0;
#endif
#ifdef DEBUG
printf("scsi_format_unit( %d, %d, %d): Start\n", ctlr, target, lun);
#endif
cdb.lun = lun;
if (scrun(ctlr, target, (void *)&cdb, 6, NULL, 0, &lock) == 0) {
#ifdef DEBUG
printf("scsi_format_unit: Command Transfer Failed.\n");
#endif
return -1;
}
while ((lock == SC_IN_PROGRESS) || (lock == SC_DISCONNECTED)) {
DELAY(1000000);
#ifdef DEBUG
if ((++count % 60) == 0)
printf("scsi_format_unit: %d\n", count / 60);
#endif
}
status = scfinish(ctlr);
if (lock == SC_IO_COMPLETE) {
#ifdef DEBUG
printf("scsi_format_unit: Status -- 0x%x\n", status);
#endif
return status;
} else {
return lock;
}
}
int
scintr(void)
{
struct scsi_softc *hs;
struct scsidevice *hd;
uint8_t ints, temp;
int i;
uint8_t *buf;
int len;
for (i = 0; i < NSC; i++) {
hs = &scsi_softc[i];
hd = hs->sc_spc;
if ((ints = hd->scsi_ints) != 0)
goto get_intr;
}
return -1;
get_intr:
#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->scsi_ints = ints;
*(hs->sc_lock) = SC_IO_FAILED;
return -1;
}