#include <sys/cdefs.h>
#include <lib/libsa/stand.h>
#ifndef INDIGO_R3K_MODE
#include <dev/arcbios/arcbios.h>
#endif
#include "iris_machdep.h"
#include "iris_scsivar.h"
#include "iris_scsireg.h"
#include "iris_scsicmd.h"
#include <dev/scsipi/scsi_message.h>
#define SBIC_WAIT(regs, until, timeo) wd33c93_wait(regs, until, timeo)
int wd33c93_cmd_wait = SBIC_CMD_WAIT;
int wd33c93_data_wait = SBIC_DATA_WAIT;
int wd33c93_init_wait = SBIC_INIT_WAIT;
#define STATUS_UNKNOWN 0xff
void wd33c93_reset(struct wd33c93_softc *);
int wd33c93_wait(struct wd33c93_softc *, uint8_t, int);
uint8_t wd33c93_selectbus(struct wd33c93_softc *, uint8_t *, size_t, uint8_t *,
size_t *);
void wd33c93_setsync(struct wd33c93_softc *);
int wd33c93_nextstate(struct wd33c93_softc *, uint8_t *, size_t, uint8_t *,
size_t *, uint8_t, uint8_t);
size_t wd33c93_xfout(struct wd33c93_softc *, void *, size_t *);
int wd33c93_intr(struct wd33c93_softc *, uint8_t *, size_t, uint8_t *,
size_t *);
size_t wd33c93_xfin(struct wd33c93_softc *, void *, size_t *);
void wd33c93_xferdone(struct wd33c93_softc *);
int wd33c93_abort(struct wd33c93_softc *, uint8_t *, size_t, uint8_t *,
size_t *);
int wd33c93_poll(struct wd33c93_softc *, uint8_t *, size_t, uint8_t *,
size_t *);
void wd33c93_timeout(struct wd33c93_softc *, uint8_t *, size_t, uint8_t *,
size_t *);
int wd33c93_msgin_phase(struct wd33c93_softc *);
void wd33c93_scsistart(struct wd33c93_softc *);
void wd33c93_scsidone(struct wd33c93_softc *);
void wd33c93_error(struct wd33c93_softc *);
void
wd33c93_init(void *aaddr, void *daddr)
{
struct wd33c93_softc *sc;
sc = &wd33c93_softc[scsi_ctlr];
sc->sc_asr_regh = aaddr;
sc->sc_data_regh = daddr;
sc->sc_target = scsi_id;
sc->sc_flags = 0;
sc->sc_state = SBIC_IDLE;
wd33c93_reset(sc);
}
void
wd33c93_reset(struct wd33c93_softc *sc)
{
u_int my_id;
uint8_t csr;
SET_SBIC_cmd(sc, SBIC_CMD_ABORT);
WAIT_CIP(sc);
my_id = sc->sc_target & SBIC_ID_MASK;
my_id |= SBIC_ID_FS_8_10;
sc->sc_syncperiods = 2 * 2 * 1250 / SCSI_CLKFREQ;
SET_SBIC_myid(sc, my_id);
SET_SBIC_cmd(sc, SBIC_CMD_RESET);
DELAY(25);
SBIC_WAIT(sc, SBIC_ASR_INT, 0);
SBIC_TC_PUT(sc, 0);
SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
GET_SBIC_csr(sc, csr);
__USE(csr);
SET_SBIC_rselid(sc, SBIC_RID_ER);
SET_SBIC_syn(sc, 0);
sc->sc_flags = 0;
sc->sc_state = SBIC_IDLE;
}
int
wd33c93_wait(struct wd33c93_softc *sc, uint8_t until, int timeo)
{
uint8_t val;
if (timeo == 0)
timeo = 1000000;
GET_SBIC_asr(sc, val);
while ((val & until) == 0) {
if (timeo-- == 0) {
return val;
break;
}
DELAY(1);
GET_SBIC_asr(sc, val);
}
return val;
}
int
wd33c93_go(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen, uint8_t *buf,
size_t *lenp)
{
int i;
uint8_t csr, asr;
wd33c93_scsistart(sc);
sc->sc_status = STATUS_UNKNOWN;
sc->sc_flags = 0;
if ((csr = wd33c93_selectbus(sc, cbuf, clen, buf, lenp)) == 0)
return 1;
GET_SBIC_asr(sc, asr);
do {
i = wd33c93_nextstate(sc, cbuf, clen, buf, lenp, csr, asr);
WAIT_CIP(sc);
if (sc->sc_state == SBIC_CONNECTED) {
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_LCI) != 0)
DELAY(5000);
if ((asr & SBIC_ASR_INT) != 0)
GET_SBIC_csr(sc, csr);
}
} while (sc->sc_state == SBIC_CONNECTED &&
(asr & (SBIC_ASR_INT|SBIC_ASR_LCI)) != 0);
if (i == SBIC_STATE_DONE) {
if (sc->sc_status == STATUS_UNKNOWN) {
printf("wd33c93_go: stat == UNKNOWN\n");
return 1;
}
}
if (wd33c93_poll(sc, cbuf, clen, buf, lenp)) {
wd33c93_timeout(sc, cbuf, clen, buf, lenp);
if (wd33c93_poll(sc, cbuf, clen, buf, lenp)) {
wd33c93_timeout(sc, cbuf, clen, buf, lenp);
}
}
return 0;
}
uint8_t
wd33c93_selectbus(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen,
uint8_t *buf, size_t *lenp)
{
uint8_t asr, csr, id, lun, target;
static int i = 0;
sc->sc_state = SBIC_SELECTING;
target = sc->sc_target;
lun = SCSI_LUN;
SBIC_TC_PUT(sc, 0);
SET_SBIC_selid(sc, target);
SET_SBIC_timeo(sc, SBIC_TIMEOUT(250, SCSI_CLKFREQ));
GET_SBIC_asr(sc, asr);
if ((asr & (SBIC_ASR_INT|SBIC_ASR_BSY)) != 0) {
return 0;
}
SET_SBIC_cmd(sc, SBIC_CMD_SEL_ATN);
WAIT_CIP(sc);
do {
asr = SBIC_WAIT(sc, SBIC_ASR_INT | SBIC_ASR_LCI, 0);
if ((asr & SBIC_ASR_LCI) != 0) {
return 0;
}
GET_SBIC_csr(sc, csr);
if (csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) {
wd33c93_nextstate(sc, cbuf, clen, buf, lenp, csr, asr);
return 0;
}
if (csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) {
return 0;
}
} while (csr != (SBIC_CSR_MIS_2 | MESG_OUT_PHASE) &&
csr != (SBIC_CSR_MIS_2 | CMD_PHASE) &&
csr != SBIC_CSR_SEL_TIMEO);
if (csr == SBIC_CSR_SEL_TIMEO) {
return 0;
}
GET_SBIC_selid(sc, id);
if (id != target) {
printf("wd33c93_selectbus: wrong target selected WANTED %d GOT %d \n",
target, id);
printf("Boot failed! Halting...\n");
reboot();
}
sc->sc_flags |= SBICF_SELECTED;
sc->sc_state = SBIC_CONNECTED;
wd33c93_setsync(sc);
SET_SBIC_rselid(sc, SBIC_RID_ER);
if (csr == (SBIC_CSR_MIS_2 | MESG_OUT_PHASE)) {
if (i < 6) {
SEND_BYTE(sc, MSG_IDENTIFY(lun, 0));
DELAY(200000);
i++;
} else {
sc->sc_omsg[0] = MSG_IDENTIFY(lun, 0);
sc->sc_omsg[1] = MSG_EXTENDED;
sc->sc_omsg[2] = MSG_EXT_SDTR_LEN;
sc->sc_omsg[3] = MSG_EXT_SDTR;
sc->sc_omsg[4] = sc->sc_syncperiods;
sc->sc_omsg[5] = SBIC_SYN_93AB_MAX_OFFSET;
size_t foo = 6;
size_t *bar;
bar = &foo;
wd33c93_xfout(sc, sc->sc_omsg, bar);
sc->sc_flags |= SBICF_SYNCNEGO;
}
SBIC_WAIT(sc, SBIC_ASR_INT , 0);
GET_SBIC_csr(sc, csr);
}
return csr;
}
void
wd33c93_setsync(struct wd33c93_softc *sc)
{
uint8_t syncreg;
syncreg = SBIC_SYN(0, 0, 0);
SET_SBIC_syn(sc, syncreg);
}
int
wd33c93_nextstate(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen,
uint8_t *buf, size_t *lenp, uint8_t csr, uint8_t asr)
{
size_t *clenp;
size_t resid;
switch (csr) {
case SBIC_CSR_XFERRED | CMD_PHASE:
case SBIC_CSR_MIS | CMD_PHASE:
case SBIC_CSR_MIS_1 | CMD_PHASE:
case SBIC_CSR_MIS_2 | CMD_PHASE:
clenp = &clen;
if (wd33c93_xfout(sc, cbuf, clenp))
goto abort;
break;
case SBIC_CSR_XFERRED | STATUS_PHASE:
case SBIC_CSR_MIS | STATUS_PHASE:
case SBIC_CSR_MIS_1 | STATUS_PHASE:
case SBIC_CSR_MIS_2 | STATUS_PHASE:
SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
wd33c93_xferdone(sc);
wd33c93_scsidone(sc);
return SBIC_STATE_DONE;
case SBIC_CSR_XFERRED | DATA_IN_PHASE:
case SBIC_CSR_MIS | DATA_IN_PHASE:
case SBIC_CSR_MIS_1 | DATA_IN_PHASE:
case SBIC_CSR_MIS_2 | DATA_IN_PHASE:
case SBIC_CSR_XFERRED | DATA_OUT_PHASE:
case SBIC_CSR_MIS | DATA_OUT_PHASE:
case SBIC_CSR_MIS_1 | DATA_OUT_PHASE:
case SBIC_CSR_MIS_2 | DATA_OUT_PHASE:
if (SBIC_PHASE(csr) == DATA_IN_PHASE){
resid = wd33c93_xfin(sc, buf, lenp);
*lenp = resid;
wd33c93_intr(sc, cbuf, clen, buf, lenp);
} else {
resid = wd33c93_xfout(sc, buf, lenp);
*lenp = resid;
}
break;
case SBIC_CSR_XFERRED | MESG_IN_PHASE:
case SBIC_CSR_MIS | MESG_IN_PHASE:
case SBIC_CSR_MIS_1 | MESG_IN_PHASE:
case SBIC_CSR_MIS_2 | MESG_IN_PHASE:
return wd33c93_msgin_phase(sc);
case SBIC_CSR_MSGIN_W_ACK:
SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
break;
case SBIC_CSR_XFERRED | MESG_OUT_PHASE:
case SBIC_CSR_MIS | MESG_OUT_PHASE:
case SBIC_CSR_MIS_1 | MESG_OUT_PHASE:
case SBIC_CSR_MIS_2 | MESG_OUT_PHASE:
return SBIC_STATE_RUNNING;
case SBIC_CSR_DISC:
case SBIC_CSR_DISC_1:
sc->sc_state = SBIC_IDLE;
sc->sc_flags = 0;
return SBIC_STATE_DISCONNECT;
case SBIC_CSR_RSLT_NI:
case SBIC_CSR_RSLT_IFY:
{
sc->sc_state = SBIC_RESELECTED;
if (csr == SBIC_CSR_RSLT_IFY)
SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
break;
}
default:
abort:
printf("wd33c93_nextstate:abort\n");
printf("Boot failed! Halting...\n");
reboot();
}
return SBIC_STATE_RUNNING;
}
size_t
wd33c93_xfout(struct wd33c93_softc *sc, void *bp, size_t *lenp)
{
int wait = wd33c93_data_wait;
uint8_t asr, *buf = bp;
size_t len = *lenp;
SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
SBIC_TC_PUT(sc, (unsigned int)len);
WAIT_CIP(sc);
SET_SBIC_cmd(sc, SBIC_CMD_XFER_INFO);
do {
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_DBR) != 0) {
if (len != 0) {
SET_SBIC_data(sc, *buf);
buf++;
len--;
} else {
SET_SBIC_data(sc, 0);
}
wait = wd33c93_data_wait;
}
} while (len != 0 && (asr & SBIC_ASR_INT) == 0 && wait-- > 0);
return len;
}
int
wd33c93_intr(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen,
uint8_t *buf, size_t *lenp)
{
uint8_t asr, csr;
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_INT) == 0)
return 0;
GET_SBIC_csr(sc, csr);
do {
(void)wd33c93_nextstate(sc, cbuf, clen, buf, lenp, csr, asr);
WAIT_CIP(sc);
if (sc->sc_state == SBIC_CONNECTED) {
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_INT) != 0)
GET_SBIC_csr(sc, csr);
}
} while (sc->sc_state == SBIC_CONNECTED &&
(asr & (SBIC_ASR_INT|SBIC_ASR_LCI)) != 0);
return 1;
}
size_t
wd33c93_xfin(struct wd33c93_softc *sc, void *bp, size_t *lenp)
{
size_t len = *lenp;
int wait = wd33c93_data_wait;
uint8_t *buf = bp;
uint8_t asr;
SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
SBIC_TC_PUT(sc, (unsigned int)len);
WAIT_CIP(sc);
SET_SBIC_cmd(sc, SBIC_CMD_XFER_INFO);
do {
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_DBR) != 0) {
if (len != 0) {
GET_SBIC_data(sc, *buf);
buf++;
len--;
} else {
uint8_t foo;
GET_SBIC_data(sc, foo);
__USE(foo);
}
wait = wd33c93_data_wait;
}
} while ((asr & SBIC_ASR_INT) == 0 && wait-- > 0);
SBIC_TC_PUT(sc, 0);
return len;
}
void
wd33c93_xferdone(struct wd33c93_softc *sc)
{
uint8_t phase, csr;
SBIC_TC_PUT(sc, 0);
SET_SBIC_cmd_phase(sc, 0x46);
SET_SBIC_cmd(sc, SBIC_CMD_SEL_ATN_XFER);
do {
SBIC_WAIT(sc, SBIC_ASR_INT, 0);
GET_SBIC_csr(sc, csr);
} while ((csr != SBIC_CSR_DISC) &&
(csr != SBIC_CSR_DISC_1) &&
(csr != SBIC_CSR_S_XFERRED));
sc->sc_flags &= ~SBICF_SELECTED;
sc->sc_state = SBIC_DISCONNECT;
GET_SBIC_cmd_phase(sc, phase);
if (phase == 0x60)
GET_SBIC_tlun(sc, sc->sc_status);
else
wd33c93_error(sc);
}
int
wd33c93_abort(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen,
uint8_t *buf, size_t *lenp)
{
uint8_t csr, asr;
GET_SBIC_asr(sc, asr);
GET_SBIC_csr(sc, csr);
wd33c93_timeout(sc, cbuf, clen, buf, lenp);
while ((asr & SBIC_ASR_DBR) != 0) {
GET_SBIC_data(sc, asr);
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_DBR) != 0)
SET_SBIC_data(sc, asr);
GET_SBIC_asr(sc, asr);
}
WAIT_CIP(sc);
SET_SBIC_cmd(sc, SBIC_CMD_ABORT);
WAIT_CIP(sc);
GET_SBIC_asr(sc, asr);
if ((asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) != 0) {
wd33c93_reset(sc);
} else {
SET_SBIC_cmd(sc, SBIC_CMD_DISC);
WAIT_CIP(sc);
do {
SBIC_WAIT(sc, SBIC_ASR_INT, 0);
GET_SBIC_asr(sc, asr);
GET_SBIC_csr(sc, csr);
} while ((csr != SBIC_CSR_DISC) &&
(csr != SBIC_CSR_DISC_1) &&
(csr != SBIC_CSR_CMD_INVALID));
sc->sc_state = SBIC_ERROR;
sc->sc_flags = 0;
}
return SBIC_STATE_ERROR;
}
void
wd33c93_timeout(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen,
uint8_t *buf, size_t *lenp)
{
uint8_t asr;
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_INT) != 0) {
wd33c93_intr(sc, cbuf, clen, buf, lenp);
} else {
wd33c93_abort(sc, cbuf, clen, buf, lenp);
}
}
int
wd33c93_poll(struct wd33c93_softc *sc, uint8_t *cbuf, size_t clen,
uint8_t *buf, size_t *lenp)
{
uint8_t asr, csr = 0;
int count;
SBIC_WAIT(sc, SBIC_ASR_INT, wd33c93_cmd_wait);
for (count = SBIC_ABORT_TIMEOUT; count;) {
GET_SBIC_asr(sc, asr);
if ((asr & SBIC_ASR_LCI) != 0)
DELAY(5000);
if ((asr & SBIC_ASR_INT) != 0) {
GET_SBIC_csr(sc, csr);
(void)wd33c93_nextstate(sc, cbuf, clen, buf, lenp, csr,
asr);
WAIT_CIP(sc);
} else {
DELAY(5000);
count--;
}
if ((sc->xs_status & XS_STS_DONE) != 0)
return 0;
}
return 1;
}
static inline int
__verify_msg_format(uint8_t *p, int len)
{
if (len == 1 && MSG_IS1BYTE(p[0]))
return 1;
if (len == 2 && MSG_IS2BYTE(p[0]))
return 1;
if (len >= 3 && MSG_ISEXTENDED(p[0]) &&
len == p[1] + 2)
return 1;
return 0;
}
int
wd33c93_msgin_phase(struct wd33c93_softc *sc)
{
int len;
uint8_t asr, csr, *msg;
GET_SBIC_asr(sc, asr);
__USE(asr);
GET_SBIC_selid(sc, csr);
SET_SBIC_selid(sc, csr | SBIC_SID_FROM_SCSI);
SBIC_TC_PUT(sc, 0);
SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
msg = sc->sc_imsg;
len = 0;
do {
RECV_BYTE(sc, *msg++);
len++;
SBIC_WAIT(sc, SBIC_ASR_INT, 0);
GET_SBIC_csr(sc, csr);
if (__verify_msg_format(sc->sc_imsg, len))
break;
SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
SBIC_WAIT(sc, SBIC_ASR_INT, 0);
GET_SBIC_csr(sc, csr);
} while (len < SBIC_MAX_MSGLEN);
SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
SBIC_WAIT(sc, SBIC_ASR_INT, 0);
return SBIC_STATE_RUNNING;
}
void
wd33c93_scsistart(struct wd33c93_softc *sc)
{
sc->xs_status = 0;
}
void
wd33c93_scsidone(struct wd33c93_softc *sc)
{
sc->xs_status = XS_STS_DONE;
}
void
wd33c93_error(struct wd33c93_softc *sc)
{
}