void bshw_bus_reset(struct ct_softc *ct) { struct scsi_low_softc *slp = &ct->sc_sclow; struct ct_bus_access_handle *chp = &ct->sc_ch; struct bshw_softc *bs = ct->ct_hw; struct bshw *hw = bs->sc_hw; bus_addr_t offs; u_int8_t regv; int i; /* open hardware busmaster mode */ if (hw->hw_dma_init != NULL && ((*hw->hw_dma_init)(ct)) != 0) { device_printf(slp->sl_dev, "change mode using external DMA (%x)\n", (u_int)ct_cr_read_1(chp, 0x37)); } /* clear hardware synch registers */ offs = hw->hw_sregaddr; if (offs != 0) { for (i = 0; i < 8; i ++, offs ++) { ct_cr_write_1(chp, offs, 0); if ((hw->hw_flags & BSHW_DOUBLE_DMACHAN) != 0) ct_cr_write_1(chp, offs + 8, 0); } } /* disable interrupt & assert reset */ regv = ct_cr_read_1(chp, wd3s_mbank); regv |= MBR_RST; regv &= ~MBR_IEN; ct_cr_write_1(chp, wd3s_mbank, regv); DELAY(500000); /* reset signal off */ regv &= ~MBR_RST; ct_cr_write_1(chp, wd3s_mbank, regv); /* interrupt enable */ regv |= MBR_IEN; ct_cr_write_1(chp, wd3s_mbank, regv); }
/************************************************* * <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; }
/* probe */ int bshw_read_settings(struct ct_bus_access_handle *chp, struct bshw_softc *bs) { static int irq_tbl[] = { 3, 5, 6, 9, 12, 13 }; bs->sc_hostid = (ct_cr_read_1(chp, wd3s_auxc) & AUXCR_HIDM); bs->sc_irq = irq_tbl[(ct_cr_read_1(chp, wd3s_auxc) >> 3) & 7]; bs->sc_drq = ct_cmdp_read_1(chp) & 3; return 0; }
/************************************************** * ### 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; }
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_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 cthw_chip_reset(struct ct_bus_access_handle *chp, int *chiprevp, int chipclk, int hostid) { #define CT_SELTIMEOUT_20MHz_REGV (0x80) u_int8_t aux, regv; u_int seltout; int wc; /* issue abort cmd */ ct_cr_write_1(chp, wd3s_cmd, WD3S_ABORT); DELAY(1000); /* 1ms wait */ (void) ct_stat_read_1(chp); (void) ct_cr_read_1(chp, wd3s_stat); /* setup chip registers */ regv = 0; seltout = CT_SELTIMEOUT_20MHz_REGV; switch (chipclk) { case 8: case 10: seltout = (seltout * chipclk) / 20; regv = IDR_FS_8_10; break; case 12: case 15: seltout = (seltout * chipclk) / 20; regv = IDR_FS_12_15; break; case 16: case 20: seltout = (seltout * chipclk) / 20; regv = IDR_FS_16_20; break; default: panic("ct: illegal chip clk rate"); break; } regv |= IDR_EHP | hostid | IDR_RAF | IDR_EAF; ct_cr_write_1(chp, wd3s_oid, regv); ct_cr_write_1(chp, wd3s_cmd, WD3S_RESET); for (wc = CT_RESET_DEFAULT; wc > 0; wc --) { aux = ct_stat_read_1(chp); if (aux != 0xff && (aux & STR_INT)) { regv = ct_cr_read_1(chp, wd3s_stat); if (regv == BSR_RESET || regv == BSR_AFM_RESET) break; ct_cr_write_1(chp, wd3s_cmd, WD3S_RESET); } DELAY(1); } if (wc == 0) return ENXIO; ct_cr_write_1(chp, wd3s_tout, seltout); ct_cr_write_1(chp, wd3s_sid, SIDR_RESEL); ct_cr_write_1(chp, wd3s_ctrl, CR_DEFAULT); ct_cr_write_1(chp, wd3s_synch, 0); if (chiprevp != NULL) { *chiprevp = CT_WD33C93; if (regv == BSR_RESET) goto out; *chiprevp = CT_WD33C93_A; ct_cr_write_1(chp, wd3s_qtag, 0xaa); if (ct_cr_read_1(chp, wd3s_qtag) != 0xaa) { ct_cr_write_1(chp, wd3s_qtag, 0x0); goto out; } ct_cr_write_1(chp, wd3s_qtag, 0x55); if (ct_cr_read_1(chp, wd3s_qtag) != 0x55) { ct_cr_write_1(chp, wd3s_qtag, 0x0); goto out; } ct_cr_write_1(chp, wd3s_qtag, 0x0); *chiprevp = CT_WD33C93_B; } out: (void) ct_stat_read_1(chp); (void) ct_cr_read_1(chp, wd3s_stat); return 0; }