/************************************************* * <DATA PHASE> *************************************************/ static int ct_xfer(struct ct_softc *ct, u_int8_t *data, int len, int direction, u_int *statp) { struct ct_bus_access_handle *chp = &ct->sc_ch; int wc; register u_int8_t aux; *statp = 0; if (len == 1) { ct_cr_write_1(chp, wd3s_cmd, WD3S_SBT | WD3S_TFR_INFO); } else { cthw_set_count(chp, len); ct_cr_write_1(chp, wd3s_cmd, WD3S_TFR_INFO); } aux = ct_stat_read_1(chp); if ((aux & STR_LCI) != 0) { cthw_set_count(chp, 0); return len; } for (wc = 0; wc < ct->sc_tmaxcnt; wc ++) { /* check data ready */ if ((aux & (STR_BSY | STR_DBR)) == (STR_BSY | STR_DBR)) { if (direction == SCSI_LOW_READ) { *data = ct_cr_read_1(chp, wd3s_data); if ((aux & STR_PE) != 0) *statp |= SCSI_LOW_DATA_PE; } else { ct_cr_write_1(chp, wd3s_data, *data); } len --; if (len <= 0) break; data ++; } else { DELAY(1); } /* check phase miss */ aux = ct_stat_read_1(chp); if ((aux & STR_INT) != 0) break; } return len; }
static __inline void bshw_lc_smit_start(struct ct_softc *ct, int count, u_int direction) { struct ct_bus_access_handle *chp = &ct->sc_ch; u_int8_t pval, val; val = ct_cr_read_1(chp, BSHW_LC_FSET); cthw_set_count(chp, count); pval = FCTRL_EN; if (direction == SCSI_LOW_WRITE) pval |= (val & 0xe0) | FCTRL_WRITE; ct_cr_write_1(chp, BSHW_LC_FCTRL, pval); ct_cr_write_1(chp, wd3s_cmd, WD3S_TFR_INFO); }
static int ct_target_nexus_establish(struct ct_softc *ct, int lun, int dir) { 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 ct_targ_info *cti = (void *) ti; if (dir == SCSI_LOW_WRITE) ct_cr_write_1(chp, wd3s_did, ti->ti_id); else ct_cr_write_1(chp, wd3s_did, ti->ti_id | DIDR_DPD); ct_cr_write_1(chp, wd3s_lun, lun); ct_cr_write_1(chp, wd3s_ctrl, ct->sc_creg | CR_DMA); ct_cr_write_1(chp, wd3s_cph, 0); ct_cr_write_1(chp, wd3s_synch, cti->cti_syncreg); cthw_set_count(chp, 0); return 0; }
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; }