/************************************************** * ### SCSI PHASE SEQUENCER ### **************************************************/ static int ct_reselected(struct ct_softc *ct, u_int8_t scsi_status) { struct scsi_low_softc *slp = &ct->sc_sclow; struct ct_bus_access_handle *chp = &ct->sc_ch; struct targ_info *ti; u_int sid; u_int8_t regv; ct->sc_atten = 0; ct->sc_satgo &= ~CT_SAT_GOING; regv = ct_cr_read_1(chp, wd3s_sid); if ((regv & SIDR_VALID) == 0) return EJUSTRETURN; sid = regv & SIDR_IDM; if ((ti = scsi_low_reselected(slp, sid)) == NULL) return EJUSTRETURN; ct_target_nexus_establish(ct, 0, SCSI_LOW_READ); if (scsi_status != BSR_AFM_RESEL) return EJUSTRETURN; SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); regv = ct_cr_read_1(chp, wd3s_data); if (scsi_low_msgin(slp, ti, (u_int) regv) == 0) { if (scsi_low_is_msgout_continue(ti, 0) != 0) { /* XXX: scsi_low_attetion */ scsi_low_attention(slp); } } if (ct->sc_atten != 0) { ct_attention(ct); } ct_cr_write_1(chp, wd3s_cmd, WD3S_NEGATE_ACK); return EJUSTRETURN; }
int stgintr(void *arg) { struct stg_softc *sc = arg; struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct targ_info *ti; struct buf *bp; u_int derror, flags; int len; u_int8_t status, astatus, regv; /******************************************* * interrupt check *******************************************/ if (slp->sl_flags & HW_INACTIVE) return 0; astatus = bus_space_read_1(iot, ioh, tmc_astat); status = bus_space_read_1(iot, ioh, tmc_bstat); if ((astatus & ASTAT_STATMASK) == 0 || astatus == (u_int8_t) -1) return 0; bus_space_write_1(iot, ioh, tmc_ictl, 0); if (astatus & ASTAT_SCSIRST) { bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit | FCTL_CLRFIFO); bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); bus_space_write_1(iot, ioh, tmc_ictl, 0); scsi_low_restart(slp, SCSI_LOW_RESTART_SOFT, "bus reset (power off?)"); return 1; } /******************************************* * debug section *******************************************/ #ifdef STG_DEBUG if (stg_debug) { scsi_low_print(slp, NULL); kprintf("%s: st %x ist %x\n\n", slp->sl_xname, status, astatus); #ifdef DDB if (stg_debug > 1) SCSI_LOW_DEBUGGER("stg"); #endif /* DDB */ } #endif /* STG_DEBUG */ /******************************************* * reselection & nexus *******************************************/ if ((status & RESEL_PHASE_MASK)== PHASE_RESELECTED) { if (stg_reselected(sc) == EJUSTRETURN) goto out; } if ((ti = slp->sl_Tnexus) == NULL) return 0; derror = 0; if ((astatus & ASTAT_PARERR) != 0 && ti->ti_phase != PH_ARBSTART && (sc->sc_fcRinit & FCTL_PARENB) != 0) { slp->sl_error |= PARITYERR; derror = SCSI_LOW_DATA_PE; if ((status & PHASE_MASK) == MESSAGE_IN_PHASE) scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_PARITY, 0); else scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ERROR, 1); } /******************************************* * aribitration & selection *******************************************/ switch (ti->ti_phase) { case PH_ARBSTART: if ((astatus & ASTAT_ARBIT) == 0) { #ifdef STG_STATICS stg_statics.arbit_fail_0 ++; #endif /* STG_STATICS */ goto arb_fail; } status = bus_space_read_1(iot, ioh, tmc_bstat); if ((status & BSTAT_IO) != 0) { /* XXX: * Selection vs Reselection conflicts. */ #ifdef STG_STATICS stg_statics.arbit_fail_1 ++; #endif /* STG_STATICS */ arb_fail: bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); stghw_bcr_write_1(sc, BCTL_BUSFREE); scsi_low_arbit_fail(slp, slp->sl_Qnexus); goto out; } /* * selection assert start. */ SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); scsi_low_arbit_win(slp); crit_enter(); bus_space_write_1(iot, ioh, tmc_scsiid, sc->sc_idbit | (1 << ti->ti_id)); stghw_bcr_write_1(sc, sc->sc_imsg | sc->sc_busc | BCTL_SEL); bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcWinit); if ((stg_io_control & STG_WAIT_FOR_SELECT) != 0) { /* selection abort delay 200 + 100 micro sec */ if (stghw_select_targ_wait(sc, 300) == 0) { SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); stg_selection_done_and_expect_msgout(sc); } } crit_exit(); goto out; case PH_SELSTART: if ((status & BSTAT_BSY) == 0) { /* selection timeout delay 250 ms */ if (stghw_select_targ_wait(sc, 250 * 1000) != 0) { stg_disconnected(sc, ti); goto out; } } SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); stg_selection_done_and_expect_msgout(sc); goto out; case PH_SELECTED: if ((status & BSTAT_REQ) == 0) goto out; stg_target_nexus_establish(sc); break; case PH_RESEL: if ((status & BSTAT_REQ) == 0) goto out; /* clear a busy line */ bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); stghw_bcr_write_1(sc, sc->sc_busc); stg_target_nexus_establish(sc); if ((status & PHASE_MASK) != MESSAGE_IN_PHASE) { kprintf("%s: unexpected phase after reselect\n", slp->sl_xname); slp->sl_error |= FATALIO; scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); goto out; } break; } /******************************************* * data phase *******************************************/ if ((slp->sl_flags & HW_PDMASTART) && STG_IS_PHASE_DATA(status) == 0) { if (slp->sl_scp.scp_direction == SCSI_LOW_READ) stg_pio_read(sc, ti, 0); stg_pdma_end(sc, ti); } /******************************************* * scsi seq *******************************************/ switch (status & PHASE_MASK) { case COMMAND_PHASE: if (stg_expect_signal(sc, COMMAND_PHASE, BSTAT_REQ) <= 0) break; SCSI_LOW_SETUP_PHASE(ti, PH_CMD); if (scsi_low_cmd(slp, ti) != 0) { scsi_low_attention(slp); } if (stg_xfer(sc, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen, COMMAND_PHASE, 0) != 0) { kprintf("%s: CMDOUT short\n", slp->sl_xname); } break; case DATA_OUT_PHASE: SCSI_LOW_SETUP_PHASE(ti, PH_DATA); if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0) { scsi_low_attention(slp); } if ((sc->sc_icinit & ICTL_FIFO) != 0) stg_pio_write(sc, ti, sc->sc_wthold); else stg_pio_write(sc, ti, 0); break; case DATA_IN_PHASE: SCSI_LOW_SETUP_PHASE(ti, PH_DATA); if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0) { scsi_low_attention(slp); } if ((sc->sc_icinit & ICTL_FIFO) != 0) stg_pio_read(sc, ti, sc->sc_rthold); else stg_pio_read(sc, ti, 0); break; case STATUS_PHASE: regv = stg_expect_signal(sc, STATUS_PHASE, BSTAT_REQ); if (regv <= 0) break; SCSI_LOW_SETUP_PHASE(ti, PH_STAT); regv = bus_space_read_1(iot, ioh, tmc_sdna); if (scsi_low_statusin(slp, ti, regv | derror) != 0) { scsi_low_attention(slp); } if (regv != bus_space_read_1(iot, ioh, tmc_rdata)) { kprintf("%s: STATIN: data mismatch\n", slp->sl_xname); } stg_negate_signal(sc, BSTAT_ACK, "statin<ACK>"); break; case MESSAGE_OUT_PHASE: if (stg_expect_signal(sc, MESSAGE_OUT_PHASE, BSTAT_REQ) <= 0) break; SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); flags = (ti->ti_ophase != ti->ti_phase) ? SCSI_LOW_MSGOUT_INIT : 0; len = scsi_low_msgout(slp, ti, flags); if (len > 1 && slp->sl_atten == 0) { scsi_low_attention(slp); } if (stg_xfer(sc, ti->ti_msgoutstr, len, MESSAGE_OUT_PHASE, slp->sl_clear_atten) != 0) { kprintf("%s: MSGOUT short\n", slp->sl_xname); } else { if (slp->sl_msgphase >= MSGPH_ABORT) { stg_disconnected(sc, ti); } } break; case MESSAGE_IN_PHASE: /* confirm phase and req signal */ if (stg_expect_signal(sc, MESSAGE_IN_PHASE, BSTAT_REQ) <= 0) break; SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); /* read data with NOACK */ regv = bus_space_read_1(iot, ioh, tmc_sdna); if (scsi_low_msgin(slp, ti, derror | regv) == 0) { if (scsi_low_is_msgout_continue(ti, 0) != 0) { scsi_low_attention(slp); } } /* read data with ACK */ if (regv != bus_space_read_1(iot, ioh, tmc_rdata)) { kprintf("%s: MSGIN: data mismatch\n", slp->sl_xname); } /* wait for the ack negated */ stg_negate_signal(sc, BSTAT_ACK, "msgin<ACK>"); if (slp->sl_msgphase != 0 && slp->sl_msgphase < MSGPH_ABORT) { stg_disconnected(sc, ti); } break; case BUSFREE_PHASE: kprintf("%s: unexpected disconnect\n", slp->sl_xname); stg_disconnected(sc, ti); break; default: slp->sl_error |= FATALIO; kprintf("%s: unknown phase bus %x intr %x\n", slp->sl_xname, status, astatus); break; } out: bus_space_write_1(iot, ioh, tmc_ictl, sc->sc_icinit); return 1; }
static int ct_poll(void *arg) { struct ct_softc *ct = arg; struct scsi_low_softc *slp = &ct->sc_sclow; struct ct_bus_access_handle *chp = &ct->sc_ch; struct targ_info *ti; struct buf *bp; u_int derror, flags; int len, satgo, error; u_int8_t scsi_status, regv; again: if (slp->sl_flags & HW_INACTIVE) return 0; /************************************************** * Get status & bus phase **************************************************/ if ((ct_stat_read_1(chp) & STR_INT) == 0) return 0; scsi_status = ct_cr_read_1(chp, wd3s_stat); if (scsi_status == ((u_int8_t) -1)) return 1; /************************************************** * Check reselection, or nexus **************************************************/ if (scsi_status == BSR_RESEL || scsi_status == BSR_AFM_RESEL) { if (ct_reselected(ct, scsi_status) == EJUSTRETURN) return 1; } if ((ti = slp->sl_Tnexus) == NULL) return 1; /************************************************** * Debug section **************************************************/ #ifdef CT_DEBUG if (ct_debug > 0) { scsi_low_print(slp, NULL); device_printf(slp->sl_dev, "scsi_status 0x%x\n\n", (u_int) scsi_status); #ifdef KDB if (ct_debug > 1) kdb_enter(KDB_WHY_CAM, "ct"); #endif /* KDB */ } #endif /* CT_DEBUG */ /************************************************** * Internal scsi phase **************************************************/ satgo = ct->sc_satgo; ct->sc_satgo &= ~CT_SAT_GOING; switch (ti->ti_phase) { case PH_SELSTART: if ((satgo & CT_SAT_GOING) == 0) { if (scsi_status != BSR_SELECTED) { ct_phase_error(ct, scsi_status); return 1; } scsi_low_arbit_win(slp); SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); return 1; } else { scsi_low_arbit_win(slp); SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); /* XXX */ } break; case PH_RESEL: if ((scsi_status & BSR_PHVALID) == 0 || (scsi_status & BSR_PM) != BSR_MSGIN) { scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "phase miss after reselect"); return 1; } break; default: if (slp->sl_flags & HW_PDMASTART) { slp->sl_flags &= ~HW_PDMASTART; if (ct->sc_dma & CT_DMA_DMASTART) { (*ct->ct_dma_xfer_stop) (ct); ct->sc_dma &= ~CT_DMA_DMASTART; } else if (ct->sc_dma & CT_DMA_PIOSTART) { (*ct->ct_pio_xfer_stop) (ct); ct->sc_dma &= ~CT_DMA_PIOSTART; } else { scsi_low_data_finish(slp); } } break; } /************************************************** * parse scsi phase **************************************************/ if (scsi_status & BSR_PHVALID) { /************************************************** * Normal SCSI phase. **************************************************/ if ((scsi_status & BSR_CM) == BSR_CMDABT) { ct_phase_error(ct, scsi_status); return 1; } switch (scsi_status & BSR_PM) { case BSR_DATAOUT: SCSI_LOW_SETUP_PHASE(ti, PH_DATA); if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0) { ct_attention(ct); } goto common_data_phase; case BSR_DATAIN: SCSI_LOW_SETUP_PHASE(ti, PH_DATA); if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0) { ct_attention(ct); } common_data_phase: if (slp->sl_scp.scp_datalen > 0) { slp->sl_flags |= HW_PDMASTART; if ((ct->sc_xmode & CT_XMODE_PIO) != 0) { error = (*ct->ct_pio_xfer_start) (ct); if (error == 0) { ct->sc_dma |= CT_DMA_PIOSTART; return 1; } } if ((ct->sc_xmode & CT_XMODE_DMA) != 0) { error = (*ct->ct_dma_xfer_start) (ct); if (error == 0) { ct->sc_dma |= CT_DMA_DMASTART; return 1; } } } else { if (slp->sl_scp.scp_direction == SCSI_LOW_READ) { if (!(slp->sl_flags & HW_READ_PADDING)) { device_printf(slp->sl_dev, "read padding required\n"); return 1; } } else { if (!(slp->sl_flags & HW_WRITE_PADDING)) { device_printf(slp->sl_dev, "write padding required\n"); return 1; } } slp->sl_flags |= HW_PDMASTART; } ct_io_xfer(ct); return 1; case BSR_CMDOUT: SCSI_LOW_SETUP_PHASE(ti, PH_CMD); if (scsi_low_cmd(slp, ti) != 0) { ct_attention(ct); } if (ct_xfer(ct, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen, SCSI_LOW_WRITE, &derror) != 0) { device_printf(slp->sl_dev, "scsi cmd xfer short\n"); } return 1; case BSR_STATIN: SCSI_LOW_SETUP_PHASE(ti, PH_STAT); if ((ct_io_control & CT_USE_CCSEQ) != 0) { if (scsi_low_is_msgout_continue(ti, 0) != 0 || ct->sc_atten != 0) { ct_xfer(ct, ®v, 1, SCSI_LOW_READ, &derror); scsi_low_statusin(slp, ti, regv | derror); } else { ct->sc_satgo |= CT_SAT_GOING; cthw_set_count(chp, 0); cthw_phase_bypass(ct, 0x41); } } else { ct_xfer(ct, ®v, 1, SCSI_LOW_READ, &derror); scsi_low_statusin(slp, ti, regv | derror); } return 1; case BSR_UNSPINFO0: case BSR_UNSPINFO1: device_printf(slp->sl_dev, "illegal bus phase (0x%x)\n", (u_int) scsi_status); scsi_low_print(slp, ti); return 1; case BSR_MSGOUT: SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); flags = SCSI_LOW_MSGOUT_UNIFY; if (ti->ti_ophase != ti->ti_phase) flags |= SCSI_LOW_MSGOUT_INIT; len = scsi_low_msgout(slp, ti, flags); if (len > 1 && slp->sl_atten == 0) { ct_attention(ct); } if (ct_xfer(ct, ti->ti_msgoutstr, len, SCSI_LOW_WRITE, &derror) != 0) { device_printf(slp->sl_dev, "scsi msgout xfer short\n"); } SCSI_LOW_DEASSERT_ATN(slp); ct->sc_atten = 0; return 1; case BSR_MSGIN:/* msg in */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); ct_xfer(ct, ®v, 1, SCSI_LOW_READ, &derror); if (scsi_low_msgin(slp, ti, regv | derror) == 0) { if (scsi_low_is_msgout_continue(ti, 0) != 0) { /* XXX: scsi_low_attetion */ scsi_low_attention(slp); } } if ((ct_io_control & CT_FAST_INTR) != 0) { if (ct_catch_intr(ct) == 0) goto again; } return 1; } } else { /************************************************** * Special SCSI phase **************************************************/ switch (scsi_status) { case BSR_SATSDP: /* SAT with save data pointer */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); ct->sc_satgo |= CT_SAT_GOING; scsi_low_msgin(slp, ti, MSG_SAVESP); cthw_phase_bypass(ct, 0x41); return 1; case BSR_SATFIN: /* SAT COMPLETE */ /* * emulate statusin => msgin */ SCSI_LOW_SETUP_PHASE(ti, PH_STAT); scsi_low_statusin(slp, ti, ct_cr_read_1(chp, wd3s_lun)); SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); scsi_low_msgin(slp, ti, MSG_COMP); scsi_low_disconnected(slp, ti); return 1; case BSR_ACKREQ: /* negate ACK */ if (ct->sc_atten != 0) { ct_attention(ct); } ct_cr_write_1(chp, wd3s_cmd, WD3S_NEGATE_ACK); if ((ct_io_control & CT_FAST_INTR) != 0) { /* XXX: * Should clear a pending interrupt and * sync with a next interrupt! */ ct_catch_intr(ct); } return 1; case BSR_DISC: /* disconnect */ if (slp->sl_msgphase == MSGPH_NULL && (satgo & CT_SAT_GOING) != 0) { /* * emulate disconnect msg */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); scsi_low_msgin(slp, ti, MSG_DISCON); } scsi_low_disconnected(slp, ti); return 1; default: break; } } ct_phase_error(ct, scsi_status); return 1; }
static int ct_start_selection(struct ct_softc *ct, struct slccb *cb) { struct scsi_low_softc *slp = &ct->sc_sclow; struct ct_bus_access_handle *chp = &ct->sc_ch; struct targ_info *ti = slp->sl_Tnexus; struct lun_info *li = slp->sl_Lnexus; int s, satok; u_int8_t cmd; ct->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000; ct->sc_atten = 0; satok = 0; if (scsi_low_is_disconnect_ok(cb) != 0) { if (ct->sc_chiprev >= CT_WD33C93_A) satok = 1; else if (cthw_cmdlevel[slp->sl_scp.scp_cmd[0]] != 0) satok = 1; } if (satok != 0 && scsi_low_is_msgout_continue(ti, SCSI_LOW_MSG_IDENTIFY) == 0) { cmd = WD3S_SELECT_ATN_TFR; ct->sc_satgo = CT_SAT_GOING; } else { cmd = WD3S_SELECT_ATN; ct->sc_satgo = 0; } if ((ct_stat_read_1(chp) & (STR_BSY | STR_INT | STR_CIP)) != 0) return SCSI_LOW_START_FAIL; if ((ct->sc_satgo & CT_SAT_GOING) != 0) { (void) scsi_low_msgout(slp, ti, SCSI_LOW_MSGOUT_INIT); scsi_low_cmd(slp, ti); ct_cr_write_1(chp, wd3s_oid, slp->sl_scp.scp_cmdlen); ct_write_cmds(chp, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen); } else { /* anyway attention assert */ SCSI_LOW_ASSERT_ATN(slp); } ct_target_nexus_establish(ct, li->li_lun, slp->sl_scp.scp_direction); s = splhigh(); if ((ct_stat_read_1(chp) & (STR_BSY | STR_INT | STR_CIP)) == 0) { /* XXX: * Reload a lun again here. */ ct_cr_write_1(chp, wd3s_lun, li->li_lun); ct_cr_write_1(chp, wd3s_cmd, cmd); if ((ct_stat_read_1(chp) & STR_LCI) == 0) { splx(s); SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); return SCSI_LOW_START_OK; } } splx(s); return SCSI_LOW_START_FAIL; }