/* $FreeBSD: head/sys/dev/ct/ct.c 78210 2001-06-14 11:09:11Z nyan $ */ /* $NecBSD: ct.c,v 1.13 1999/07/23 20:54:00 honda Exp $ */ /* $NetBSD$ */ #define CT_DEBUG #define CT_USE_CCSEQ /* * [NetBSD for NEC PC-98 series] * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999 * NetBSD/pc98 porting staff. All rights reserved. * * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999 * Naofumi HONDA. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef __NetBSD__ #include #include #include #include #include #include #include #include #include #include #include #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #include #include #include #include #include #include #endif /* __FreeBSD__ */ /*************************************************** * DEBUG ***************************************************/ #define CT_NTARGETS 8 #define CT_NLUNS 8 #define CT_RESET_DEFAULT 2000 #ifndef DDB #define Debugger() panic("should call debugger here (ct.c)") #else /* ! DDB */ #ifdef __FreeBSD__ #define Debugger() Debugger("ct") #endif /* __FreeBSD__ */ #endif #ifdef CT_DEBUG int ct_debug; #endif /* CT_DEBUG */ /*************************************************** * default data ***************************************************/ u_int8_t cthw_cmdlevel[256] = { /* 0 1 2 3 4 5 6 7 8 9 A B C E D F */ /*0*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 , /*1*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*2*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,0 ,0 ,0 ,0 ,0 , /*3*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*4*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*5*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*6*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*7*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*8*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*9*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*A*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*B*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*C*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*D*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*E*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , /*F*/0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , }; /* default synch data table */ /* A 10 6.6 5.0 4.0 3.3 2.8 2.5 2.0 M/s */ /* X 100 150 200 250 300 350 400 500 ns */ static struct ct_synch_data ct_synch_data_20MHz[] = { {25, 0xa0}, {37, 0xb0}, {50, 0x20}, {62, 0xd0}, {75, 0x30}, {87, 0xf0}, {100, 0x40}, {125, 0x50}, {0, 0} }; extern unsigned int delaycount; /***************************************************************** * Interface functions *****************************************************************/ static int ct_xfer __P((struct ct_softc *, u_int8_t *, int, int)); static void ct_io_xfer __P((struct ct_softc *)); static __inline int ct_reselected __P((struct ct_softc *)); static void ct_phase_error __P((struct ct_softc *, u_int8_t)); static int ct_start_selection __P((struct ct_softc *, struct slccb *)); static int ct_msg __P((struct ct_softc *, struct targ_info *, u_int)); static int ct_world_start __P((struct ct_softc *, int)); static __inline void cthw_phase_bypass __P((struct ct_softc *, u_int8_t)); static int cthw_chip_reset __P((bus_space_tag_t, bus_space_handle_t, int, int)); static void cthw_bus_reset __P((struct ct_softc *)); static int ct_nexus __P((struct ct_softc *, struct targ_info *)); static void cthw_attention __P((struct ct_softc *)); static int ct_targ_init __P((struct ct_softc *, struct targ_info *)); struct scsi_low_funcs ct_funcs = { SC_LOW_INIT_T ct_world_start, SC_LOW_BUSRST_T cthw_bus_reset, SC_LOW_TARG_INIT_T ct_targ_init, SC_LOW_SELECT_T ct_start_selection, SC_LOW_NEXUS_T ct_nexus, SC_LOW_ATTEN_T cthw_attention, SC_LOW_MSG_T ct_msg, SC_LOW_POLL_T ctintr, NULL, /* SC_LOW_POWER_T cthw_power, */ }; /************************************************** * HW functions **************************************************/ static __inline void cthw_phase_bypass(ct, ph) struct ct_softc *ct; u_int8_t ph; { bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; ct_cr_write_1(bst, bsh, wd3s_cph, ph); ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_SELECT_ATN_TFR); ct->sc_satgo = CT_SAT_GOING; } static void cthw_bus_reset(ct) struct ct_softc *ct; { /* * wd33c93 does not have bus reset function. */ if (ct->ct_bus_reset != NULL) ((*ct->ct_bus_reset) (ct)); } static int cthw_chip_reset(bst, bsh, chipclk, hostid) bus_space_tag_t bst; bus_space_handle_t bsh; int chipclk, hostid; { #define CT_SELTIMEOUT_20MHz_REGV (0x80) u_int8_t aux, regv; u_int seltout; int wc; /* issue abort cmd */ ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_ABORT); delay(1000); /* 1ms wait */ (void) ct_stat_read_1(bst, bsh); (void) ct_cr_read_1(bst, bsh, wd3s_stat); /* setup chip registers */ regv = 0; seltout = CT_SELTIMEOUT_20MHz_REGV; switch (chipclk) { case 10: seltout = (seltout * chipclk) / 20; regv = 0; break; case 15: seltout = (seltout * chipclk) / 20; regv = IDR_FS_12_15; break; case 20: seltout = (seltout * chipclk) / 20; regv = IDR_FS_16_20; break; default: panic("ct: illegal chip clk rate\n"); break; } regv |= IDR_EHP | hostid; ct_cr_write_1(bst, bsh, wd3s_oid, regv); ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_RESET); for (wc = CT_RESET_DEFAULT; wc > 0; wc --) { aux = ct_stat_read_1(bst, bsh); if (aux != 0xff && (aux & STR_INT)) { if (ct_cr_read_1(bst, bsh, wd3s_stat) == 0) break; ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_RESET); } delay(1); } if (wc == 0) return ENXIO; ct_cr_write_1(bst, bsh, wd3s_tout, seltout); ct_cr_write_1(bst, bsh, wd3s_sid, SIDR_RESEL); ct_cr_write_1(bst, bsh, wd3s_ctrl, CR_DEFAULT); ct_cr_write_1(bst, bsh, wd3s_synch, 0); (void) ct_stat_read_1(bst, bsh); (void) ct_cr_read_1(bst, bsh, wd3s_stat); return 0; } /************************************************** * Attach & Probe **************************************************/ int ctprobesubr(bst, bsh, dvcfg, hsid, chipclk) bus_space_tag_t bst; bus_space_handle_t bsh; u_int dvcfg, chipclk; int hsid; { #if 0 if ((ct_stat_read_1(bst, bsh) & STR_BUSY) != 0) return 0; #endif if (cthw_chip_reset(bst, bsh, chipclk, hsid) != 0) return 0; return 1; } int ctprint(aux, name) void *aux; const char *name; { if (name != NULL) printf("%s: scsibus ", name); return UNCONF; } void ctattachsubr(ct) struct ct_softc *ct; { struct scsi_low_softc *slp = &ct->sc_sclow; ct->sc_wc = delaycount * 2000; /* 2 sec */ slp->sl_funcs = &ct_funcs; (void) scsi_low_attach(slp, 2, CT_NTARGETS, CT_NLUNS, sizeof(struct ct_targ_info)); } /************************************************** * SCSI LOW interface functions **************************************************/ static void cthw_attention(ct) struct ct_softc *ct; { bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; if ((ct_stat_read_1(bst, bsh) & STR_BUSY) != 0) { ct->sc_atten = 1; return; } ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_ASSERT_ATN); delay(10); if ((ct_stat_read_1(bst, bsh) & STR_LCI) != 0) { ct->sc_atten = 1; return; } ct->sc_atten = 0; } static int ct_targ_init(ct, ti) struct ct_softc *ct; struct targ_info *ti; { struct ct_targ_info *cti = (void *) ti; if (ct->sc_chiprev == CT_WD33C93_A) { ti->ti_maxsynch.period = 200 / 4; /* 5MHz */ ti->ti_maxsynch.offset = 8; } else { ti->ti_maxsynch.period = 100 / 4; /* 10MHz */ ti->ti_maxsynch.offset = 12; } cti->cti_syncreg = 0; return 0; } static int ct_world_start(ct, fdone) struct ct_softc *ct; int fdone; { struct scsi_low_softc *slp = &ct->sc_sclow; bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; intrmask_t s; if (ct->sc_sdp == NULL) ct->sc_sdp = &ct_synch_data_20MHz[0]; slp->sl_cfgflags |= CFG_MSGUNIFY; if (slp->sl_cfgflags & CFG_NOPARITY) ct->sc_creg = CR_DEFAULT; else ct->sc_creg = CR_DEFAULT_HP; if (ct->sc_dma & CT_DMA_DMASTART) (*ct->ct_dma_xfer_stop) (ct); if (ct->sc_dma & CT_DMA_PIOSTART) (*ct->ct_pio_xfer_stop) (ct); ct->sc_dma = 0; ct->sc_atten = 0; s = splcam(); cthw_chip_reset(bst, bsh, ct->sc_chipclk, slp->sl_hostid); scsi_low_bus_reset(slp); cthw_chip_reset(bst, bsh, ct->sc_chipclk, slp->sl_hostid); splx(s); SOFT_INTR_REQUIRED(slp); return 0; } static int ct_start_selection(ct, cb) struct ct_softc *ct; struct slccb *cb; { struct scsi_low_softc *slp = &ct->sc_sclow; bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; struct targ_info *ti = slp->sl_nexus; struct lun_info *li = ti->ti_li; int s; u_int8_t cmd; ct->sc_atten = 0; if (cthw_cmdlevel[slp->sl_scp.scp_cmd[0]] != 0) { /* * This completely violates scsi protocols, * however some old devices do not work * properly with scsi attentions. */ if ((li->li_flags & SCSI_LOW_DISC) != 0) cmd = WD3S_SELECT_ATN_TFR; else cmd = WD3S_SELECT_NO_ATN_TFR; ct->sc_satgo = CT_SAT_GOING; } else { cmd = WD3S_SELECT_ATN; ct->sc_satgo = 0; } if ((ct_stat_read_1(bst, bsh) & STR_BUSY) != 0) return SCSI_LOW_START_FAIL; scsi_low_cmd(slp, ti); if ((ct->sc_satgo & CT_SAT_GOING) != 0) ct_write_cmds(bst, bsh, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen); s = splhigh(); if ((ct_stat_read_1(bst, bsh) & STR_BUSY) == 0) { /* XXX: * Reload a lun again here. */ ct_cr_write_1(bst, bsh, wd3s_lun, li->li_lun); ct_cr_write_1(bst, bsh, wd3s_cmd, cmd); if ((ct_stat_read_1(bst, bsh) & STR_LCI) == 0) { splx(s); SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); return SCSI_LOW_START_OK; } } splx(s); return SCSI_LOW_START_FAIL; } static int ct_msg(ct, ti, msg) struct ct_softc *ct; struct targ_info *ti; u_int msg; { struct lun_info *li = ti->ti_li; struct ct_targ_info *cti = (void *) ti; struct ct_synch_data *csp = ct->sc_sdp; u_int offset, period; if (msg != SCSI_LOW_MSG_SYNCH) return 0; offset = ti->ti_maxsynch.offset; period = ti->ti_maxsynch.period; for ( ; csp->cs_period != 0; csp ++) { if (period == csp->cs_period) break; } if (ti->ti_maxsynch.period != 0 && csp->cs_period == 0) { ti->ti_maxsynch.period = 0; ti->ti_maxsynch.offset = 0; cti->cti_syncreg = 0; return EINVAL; } cti->cti_syncreg = ((offset & 0x0f) | csp->cs_syncr); if (ct->ct_synch_setup != 0) (*ct->ct_synch_setup) (ct, li); return 0; } /************************************************* * *************************************************/ static int ct_xfer(ct, data, len, direction) struct ct_softc *ct; u_int8_t *data; int len, direction; { bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; int wc; register u_int8_t aux; if (len == 1) { ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_SBT | WD3S_TFR_INFO); } else { cthw_set_count(bst, bsh, len); ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_TFR_INFO); } aux = ct_stat_read_1(bst, bsh); if ((aux & STR_LCI) != 0) { cthw_set_count(bst, bsh, 0); return len; } for (wc = ct->sc_wc ; wc > 0; wc --) { /* check data ready */ if ((aux & (STR_BSY | STR_DBR)) == (STR_BSY | STR_DBR)) { if (direction == SCSI_LOW_READ) *data = ct_cr_read_1(bst, bsh, wd3s_data); else ct_cr_write_1(bst, bsh, wd3s_data, *data); len --; if (len <= 0) break; data ++; } /* check phase miss */ aux = ct_stat_read_1(bst, bsh); if ((aux & STR_INT) != 0) break; } return len; } static void ct_io_xfer(ct) struct ct_softc *ct; { struct scsi_low_softc *slp = &ct->sc_sclow; bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; struct sc_p *sp = &slp->sl_scp; u_int dummy; int len; /* io polling mode */ ct_cr_write_1(bst, bsh, wd3s_ctrl, ct->sc_creg); if (sp->scp_datalen <= 0) { slp->sl_error |= PDMAERR; dummy = 0; len = ct_xfer(ct, (u_int8_t *) &dummy, 1, sp->scp_direction); } else len = ct_xfer(ct, sp->scp_data, sp->scp_datalen, sp->scp_direction); sp->scp_data += (sp->scp_datalen - len); sp->scp_datalen = len; } /************************************************** * **************************************************/ struct ct_err { u_char *pe_msg; u_int pe_err; u_int pe_errmsg; int pe_done; }; struct ct_err ct_cmderr[] = { /*0*/ { "illegal cmd", FATALIO, SCSI_LOW_MSG_ABORT, 1}, /*1*/ { "unexpected bus free", FATALIO, 0, 1}, /*2*/ { NULL, SELTIMEOUTIO, 0, 1}, /*3*/ { "scsi bus parity error", PARITYERR, SCSI_LOW_MSG_ERROR, 0}, /*4*/ { "scsi bus parity error", PARITYERR, SCSI_LOW_MSG_ERROR, 0}, /*5*/ { "unknown" , FATALIO, SCSI_LOW_MSG_ABORT, 1}, /*6*/ { "miss reselection (target mode)", FATALIO, SCSI_LOW_MSG_ABORT, 0}, /*7*/ { "wrong status byte", PARITYERR, SCSI_LOW_MSG_ERROR, 0}, }; static void ct_phase_error(ct, scsi_status) struct ct_softc *ct; u_int8_t scsi_status; { struct scsi_low_softc *slp = &ct->sc_sclow; struct targ_info *ti = slp->sl_nexus; struct ct_err *pep; u_int msg = 0; if ((scsi_status & BSR_CM) == BSR_CMDERR && (scsi_status & BSR_PHVALID) == 0) { pep = &ct_cmderr[scsi_status & BSR_PM]; slp->sl_error |= pep->pe_err; if ((pep->pe_err & PARITYERR) != 0) { if (ti->ti_phase == PH_MSGIN) msg = SCSI_LOW_MSG_PARITY; else msg = SCSI_LOW_MSG_ERROR; } else msg = pep->pe_errmsg; if (msg != 0) scsi_low_assert_msg(slp, slp->sl_nexus, msg, 1); if (pep->pe_msg != NULL) { printf("%s: phase error: %s", slp->sl_xname, pep->pe_msg); scsi_low_print(slp, slp->sl_nexus); } if (pep->pe_done != 0) scsi_low_disconnected(slp, ti); } else { slp->sl_error |= FATALIO; scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "phase error"); } } /************************************************** * ### SCSI PHASE SEQUENCER ### **************************************************/ static __inline int ct_reselected(ct) struct ct_softc *ct; { struct scsi_low_softc *slp = &ct->sc_sclow; bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; struct targ_info *ti; u_int sid; ct->sc_atten = 0; sid = (ct_cr_read_1(bst, bsh, wd3s_sid) & SIDR_IDM); if ((ti = scsi_low_reselected(slp, sid)) == NULL) return EJUSTRETURN; ct_cr_write_1(bst, bsh, wd3s_did, sid); ct_cr_write_1(bst, bsh, wd3s_lun, 0); /* temp */ ct_cr_write_1(bst, bsh, wd3s_ctrl, ct->sc_creg | CR_DMA); cthw_set_count(bst, bsh, 0); return EJUSTRETURN; } static int ct_nexus(ct, ti) struct ct_softc *ct; struct targ_info *ti; { bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; struct lun_info *li = ti->ti_li; struct ct_targ_info *cti = (void *) ti; if ((li->li_flags & SCSI_LOW_NOPARITY) != 0) ct->sc_creg = CR_DEFAULT; else ct->sc_creg = CR_DEFAULT_HP; ct_cr_write_1(bst, bsh, wd3s_did, ti->ti_id); ct_cr_write_1(bst, bsh, wd3s_lun, li->li_lun); ct_cr_write_1(bst, bsh, wd3s_ctrl, ct->sc_creg | CR_DMA); ct_cr_write_1(bst, bsh, wd3s_cph, 0); ct_cr_write_1(bst, bsh, wd3s_synch, cti->cti_syncreg); cthw_set_count(bst, bsh, 0); ct_cr_write_1(bst, bsh, wd3s_lun, li->li_lun); /* XXX */ return 0; } int ctintr(arg) void *arg; { struct ct_softc *ct = arg; struct scsi_low_softc *slp = &ct->sc_sclow; bus_space_tag_t bst = ct->sc_iot; bus_space_handle_t bsh = ct->sc_ioh; struct targ_info *ti; struct physio_proc *pp; struct buf *bp; int len, satgo; u_int8_t scsi_status, regv; if (slp->sl_flags & HW_INACTIVE) return 0; /************************************************** * Get status & bus phase **************************************************/ if ((ct_stat_read_1(bst, bsh) & STR_INT) == 0) return 0; scsi_status = ct_cr_read_1(bst, bsh, wd3s_stat); if (scsi_status == ((u_int8_t) -1)) return 1; /************************************************** * Check reselection, or nexus **************************************************/ if (scsi_status == BSR_RESEL) { if (ct_reselected(ct) == EJUSTRETURN) return 1; } if ((ti = slp->sl_nexus) == NULL) return 1; /************************************************** * Debug section **************************************************/ #ifdef CT_DEBUG if (ct_debug > 0) { scsi_low_print(slp, NULL); printf("%s: scsi_status 0x%x\n\n", slp->sl_xname, (u_int) scsi_status); if (ct_debug > 1) Debugger(); } #endif /* CT_DEBUG */ /************************************************** * Internal scsi phase **************************************************/ satgo = ct->sc_satgo; ct->sc_satgo = 0; 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, ti); SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_IDENTIFY, 0); return 1; } else { scsi_low_arbit_win(slp, ti); SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); } 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 { (*ct->ct_pio_xfer_stop) (ct); ct->sc_dma &= ~CT_DMA_PIOSTART; } } 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) return 1; 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) return 1; common_data_phase: if (slp->sl_scp.scp_datalen <= 0) { ct_io_xfer(ct); return 1; } slp->sl_flags |= HW_PDMASTART; if ((ct->sc_xmode & CT_XMODE_PIO) != 0 && (slp->sl_scp.scp_datalen % DEV_BSIZE) == 0) { pp = physio_proc_enter(bp); ct->sc_dma |= CT_DMA_PIOSTART; (*ct->ct_pio_xfer_start) (ct); physio_proc_leave(pp); return 1; } else { ct->sc_dma |= CT_DMA_DMASTART; (*ct->ct_dma_xfer_start) (ct); ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_TFR_INFO); } return 1; case BSR_CMDOUT: SCSI_LOW_SETUP_PHASE(ti, PH_CMD); if (scsi_low_cmd(slp, ti) != 0) break; if (ct_xfer(ct, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen, SCSI_LOW_WRITE) != 0) { printf("%s: scsi cmd xfer short\n", slp->sl_xname); } return 1; case BSR_STATIN: SCSI_LOW_SETUP_PHASE(ti, PH_STAT); #ifdef CT_USE_CCSEQ if (scsi_low_is_msgout_continue(ti) != 0 || ct->sc_atten != 0) { ct_xfer(ct, &ti->ti_status, 1, SCSI_LOW_READ); } else { cthw_set_count(bst, bsh, 0); cthw_phase_bypass(ct, 0x41); } #else /* !CT_USE_CCSEQ */ ct_xfer(ct, &ti->ti_status, 1, SCSI_LOW_READ); #endif /* !CT_USE_CCSEQ */ return 1; case BSR_UNSPINFO0: case BSR_UNSPINFO1: printf("%s: illegal bus phase (0x%x)\n", slp->sl_xname, (u_int) scsi_status); scsi_low_print(slp, ti); return 1; case BSR_MSGOUT: SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); len = scsi_low_msgout(slp, ti); if (ct_xfer(ct, ti->ti_msgoutstr, len, SCSI_LOW_WRITE)) { printf("%s: scsi msgout xfer short\n", slp->sl_xname); scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); } return 1; case BSR_MSGIN:/* msg in */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); ct_xfer(ct, ®v, 1, SCSI_LOW_READ); scsi_low_msgin(slp, ti, regv); return 1; } } else { /************************************************** * Special SCSI phase **************************************************/ switch (scsi_status) { case BSR_SATSDP: /* SAT with save data pointer */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); scsi_low_msgin(slp, ti, MSG_SAVESP); cthw_phase_bypass(ct, 0x41); return 1; case BSR_SATFIN: /* SAT COMPLETE */ /* * emulate statusin => msgin */ ti->ti_status = ct_cr_read_1(bst, bsh, wd3s_lun); SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_CMDC); scsi_low_disconnected(slp, ti); return 1; case BSR_ACKREQ: /* negate ACK */ if (ct->sc_atten != 0) cthw_attention(ct); ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_NEGATE_ACK); 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_SETUP_MSGPHASE(slp, MSGPH_DISC); } scsi_low_disconnected(slp, ti); return 1; default: break; } } ct_phase_error(ct, scsi_status); return 1; }