/* $FreeBSD$ */ /* $NecBSD: ncr53c500.c,v 1.30 1999/07/23 21:00:04 honda Exp $ */ /* $NetBSD$ */ #define NCV_DEBUG #define NCV_STATICS /* * [NetBSD for NEC PC-98 series] * Copyright (c) 1995, 1996, 1997, 1998, 1999 * NetBSD/pc98 porting staff. All rights reserved. * Copyright (c) 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 #if defined(__FreeBSD__) && __FreeBSD_version >= 500001 #include #endif #include #include #include #include #include #include #ifdef __NetBSD__ #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #include #define delay(time) DELAY(time) #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version < 400001 #include "ncv.h" struct ncv_softc *ncvdata[NNCV]; #endif #endif /* __FreeBSD__ */ /*************************************************** * DEBUG ***************************************************/ #ifndef DDB #define Debugger() panic("should call debugger here (ncr53c500.c)") #else /* ! DDB */ #ifdef __FreeBSD__ #define Debugger() Debugger("ncv") #endif /* __FreeBSD__ */ #endif #ifdef NCV_DEBUG int ncv_debug; #endif /* NCV_DEBUG */ #ifdef NCV_STATICS struct ncv_statics { int disconnect; int reselect; } ncv_statics[NCV_NTARGETS]; #endif /* NCV_STATICS */ /*************************************************** * ISA DEVICE STRUCTURE ***************************************************/ extern struct cfdriver ncv_cd; /************************************************************** * DECLARE **************************************************************/ /* static */ static void ncv_pio_read __P((struct ncv_softc *, u_int8_t *, u_int)); static void ncv_pio_write __P((struct ncv_softc *, u_int8_t *, u_int)); static int ncv_msg __P((struct ncv_softc *, struct targ_info *, u_int)); static __inline int ncv_reselected __P((struct ncv_softc *)); static __inline int ncv_disconnected __P((struct ncv_softc *, struct targ_info *)); static __inline void ncv_pdma_end __P((struct ncv_softc *sc, struct targ_info *)); static __inline void ncvhw_set_count __P((bus_space_tag_t, bus_space_handle_t, int)); static __inline u_int ncvhw_get_count __P((bus_space_tag_t, bus_space_handle_t)); static __inline void ncvhw_select_register_0 __P((bus_space_tag_t, bus_space_handle_t, struct ncv_hw *)); static __inline void ncvhw_select_register_1 __P((bus_space_tag_t, bus_space_handle_t, struct ncv_hw *)); static __inline void ncvhw_fpush __P((bus_space_tag_t, bus_space_handle_t, u_int8_t *, int)); static int ncv_world_start __P((struct ncv_softc *, int)); static void ncvhw_bus_reset __P((struct ncv_softc *)); static void ncvhw_reset __P((bus_space_tag_t, bus_space_handle_t, struct ncv_hw *)); static int ncvhw_check __P((bus_space_tag_t, bus_space_handle_t, struct ncv_hw *)); static void ncvhw_init __P((bus_space_tag_t, bus_space_handle_t, struct ncv_hw *)); static int ncvhw_start_selection __P((struct ncv_softc *sc, struct slccb *)); static void ncvhw_attention __P((struct ncv_softc *)); static int ncv_nexus __P((struct ncv_softc *, struct targ_info *)); #ifdef NCV_POWER_CONTROL static int ncvhw_power __P((struct ncv_softc *, u_int)); #endif static int ncv_lun_init __P((struct ncv_softc *, struct targ_info *, struct lun_info *)); static void settimeout __P((void *)); struct scsi_low_funcs ncv_funcs = { SC_LOW_INIT_T ncv_world_start, SC_LOW_BUSRST_T ncvhw_bus_reset, SC_LOW_LUN_INIT_T ncv_lun_init, SC_LOW_SELECT_T ncvhw_start_selection, SC_LOW_NEXUS_T ncv_nexus, SC_LOW_ATTEN_T ncvhw_attention, SC_LOW_MSG_T ncv_msg, SC_LOW_POLL_T ncvintr, NULL, /* SC_LOW_POWER_T ncvhw_power, */ }; /************************************************************** * hwfuncs **************************************************************/ static __inline void ncvhw_select_register_0(iot, ioh, hw) bus_space_tag_t iot; bus_space_handle_t ioh; struct ncv_hw *hw; { bus_space_write_1(iot, ioh, cr0_cfg4, hw->cfg4); } static __inline void ncvhw_select_register_1(iot, ioh, hw) bus_space_tag_t iot; bus_space_handle_t ioh; struct ncv_hw *hw; { bus_space_write_1(iot, ioh, cr1_cfg5, hw->cfg5); } static __inline void ncvhw_fpush(iot, ioh, buf, len) bus_space_tag_t iot; bus_space_handle_t ioh; u_int8_t *buf; int len; { int ptr; for (ptr = 0; ptr < len; ptr ++) bus_space_write_1(iot, ioh, cr0_sfifo, buf[ptr]); } static int ncvhw_check(iot, ioh, hw) bus_space_tag_t iot; bus_space_handle_t ioh; struct ncv_hw *hw; { u_int8_t stat; ncvhw_select_register_0(iot, ioh, hw); bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP | CMD_DMA); if (bus_space_read_1(iot, ioh, cr0_cmd) != (CMD_NOP | CMD_DMA)) { #ifdef NCV_DEBUG printf("ncv: cr0_cmd CMD_NOP|CMD_DMA failed\n"); #endif /* NCV_DEBUG */ return ENODEV; } bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP); if (bus_space_read_1(iot, ioh, cr0_cmd) != CMD_NOP) { #ifdef NCV_DEBUG printf("ncv: cr0_cmd CMD_NOP failed\n"); #endif /* NCV_DEBUG */ return ENODEV; } /* hardware reset */ ncvhw_reset(iot, ioh, hw); ncvhw_init(iot, ioh, hw); /* bus reset */ ncvhw_select_register_0(iot, ioh, hw); bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTSCSI); bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP | CMD_DMA); delay(100 * 1000); /* check response */ bus_space_read_1(iot, ioh, cr0_stat); stat = bus_space_read_1(iot, ioh, cr0_istat); delay(1000); if (((stat & INTR_SBR) == 0) || (bus_space_read_1(iot, ioh, cr0_istat) & INTR_SBR)) { #ifdef NCV_DEBUG printf("ncv: cr0_istat SCSI BUS RESET failed\n"); #endif /* NCV_DEBUG */ return ENODEV; } return 0; } static void ncvhw_reset(iot, ioh, hw) bus_space_tag_t iot; bus_space_handle_t ioh; struct ncv_hw *hw; { ncvhw_select_register_0(iot, ioh, hw); /* dummy cmd twice */ bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP); bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP); /* chip reset */ bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTCHIP); /* again dummy cmd twice */ bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP); bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP); } static void ncvhw_init(iot, ioh, hw) bus_space_tag_t iot; bus_space_handle_t ioh; struct ncv_hw *hw; { ncvhw_select_register_0(iot, ioh, hw); bus_space_write_1(iot, ioh, cr0_clk, hw->clk); bus_space_write_1(iot, ioh, cr0_srtout, SEL_TOUT); bus_space_write_1(iot, ioh, cr0_period, 0); bus_space_write_1(iot, ioh, cr0_offs, 0); bus_space_write_1(iot, ioh, cr0_cfg1, hw->cfg1); bus_space_write_1(iot, ioh, cr0_cfg2, hw->cfg2); bus_space_write_1(iot, ioh, cr0_cfg3, hw->cfg3); bus_space_write_1(iot, ioh, cr0_tchsb, 0); ncvhw_select_register_1(iot, ioh, hw); bus_space_write_1(iot, ioh, cr1_fstat, 0x0); bus_space_write_1(iot, ioh, cr1_pflag, 0x0); bus_space_write_1(iot, ioh, cr1_atacmd, ATACMD_ENGAGE); ncvhw_select_register_0(iot, ioh, hw); } #ifdef NCV_POWER_CONTROL static int ncvhw_power(sc, flags) struct ncv_softc *sc; u_int flags; { struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; if (flags == SCSI_LOW_POWDOWN) { printf("%s power down\n", slp->sl_xname); ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_atacmd, ATACMD_POWDOWN); } else { switch (sc->sc_rstep) { case 0: printf("%s resume step O\n", slp->sl_xname); ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_atacmd, ATACMD_ENGAGE); break; case 1: printf("%s resume step I\n", slp->sl_xname); ncvhw_reset(iot, ioh, &sc->sc_hw); ncvhw_init(iot, ioh, &sc->sc_hw); break; } } return 0; } #endif /* NCV_POWER_CONTROL */ /************************************************************** * scsi low interface **************************************************************/ static void ncvhw_attention(sc) struct ncv_softc *sc; { bus_space_write_1(sc->sc_iot, sc->sc_ioh, cr0_cmd, CMD_SETATN); delay(10); } static void ncvhw_bus_reset(sc) struct ncv_softc *sc; { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; ncvhw_select_register_0(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTSCSI); bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP | CMD_DMA); } static int ncvhw_start_selection(sc, cb) struct ncv_softc *sc; struct slccb *cb; { 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 = cb->ti; int s; u_int8_t msg; msg = ID_MSG_SETUP(ti); sc->sc_compseq = 0; ncvhw_select_register_0(iot, ioh, &sc->sc_hw); s = splhigh(); if (slp->sl_disc > 0 && (bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_stat) & STAT_INT)) { splx(s); return SCSI_LOW_START_FAIL; } bus_space_write_1(iot, ioh, cr0_dstid, ti->ti_id); bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); bus_space_write_1(iot, ioh, cr0_sfifo, msg); if (scsi_low_is_msgout_continue(ti) != 0) { bus_space_write_1(iot, ioh, cr0_cmd, CMD_SELATNS); sc->sc_selstop = 1; } else { /* XXX: * emulate nexus call because ncv bypasses CMD phase. */ scsi_low_cmd(slp, ti); ncvhw_fpush(iot, ioh, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen); bus_space_write_1(iot, ioh, cr0_cmd, CMD_SELATN); sc->sc_selstop = 0; } splx(s); SCSI_LOW_TARGET_ASSERT_ATN(ti); SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); return SCSI_LOW_START_OK; } static int ncv_world_start(sc, fdone) struct ncv_softc *sc; int fdone; { struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int8_t stat; intrmask_t s; ncvhw_reset(iot, ioh, &sc->sc_hw); ncvhw_init(iot, ioh, &sc->sc_hw); s = splcam(); scsi_low_bus_reset(slp); ncvhw_select_register_0(iot, ioh, &sc->sc_hw); bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_stat); stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_istat); splx(s); delay(1000); if (((stat & INTR_SBR) == 0) || (bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_istat) & INTR_SBR)) return ENODEV; SOFT_INTR_REQUIRED(slp); return 0; } static int ncv_msg(sc, ti, msg) struct ncv_softc *sc; struct targ_info *ti; u_int msg; { struct lun_info *li = ti->ti_li; struct ncv_lun_info *nli = (void *) li; u_int hwcycle, period; if ((msg & SCSI_LOW_MSG_SYNCH) == 0) return 0; period = li->li_maxsynch.period; hwcycle = 1000 / ((sc->sc_hw.clk == 0) ? 40 : (5 * sc->sc_hw.clk)); if (period < 200 / 4 && period >= 100 / 4) nli->nli_reg_cfg3 |= C3_FSCSI; else nli->nli_reg_cfg3 &= ~C3_FSCSI; period = ((period * 40 / hwcycle) + 5) / 10; nli->nli_reg_period = period & 0x1f; nli->nli_reg_offset = li->li_maxsynch.offset; return 0; } static int ncv_lun_init(sc, ti, li) struct ncv_softc *sc; struct targ_info *ti; struct lun_info *li; { struct ncv_lun_info *nli = (void *) li; li->li_maxsynch.period = sc->sc_hw.mperiod; li->li_maxsynch.offset = sc->sc_hw.moffset; nli->nli_reg_cfg3 = sc->sc_hw.cfg3; nli->nli_reg_period = 0; nli->nli_reg_offset = 0; return 0; } /************************************************************** * General probe attach **************************************************************/ static int ncv_setup_img __P((struct ncv_hw *, u_int, int)); static int ncv_setup_img(hw, dvcfg, hsid) struct ncv_hw *hw; u_int dvcfg; int hsid; { if (NCV_CLKFACTOR(dvcfg) > CLK_35M_F) { printf("ncv: invalid dvcfg flags\n"); return EINVAL; } if (NCV_C5IMG(dvcfg) != 0) { hw->cfg5 = NCV_C5IMG(dvcfg); hw->clk = NCV_CLKFACTOR(dvcfg); if (NCV_SPECIAL(dvcfg) & NCVHWCFG_MAX10M) hw->mperiod = 100 / 4; /* XXX: * RATOC scsi cards have fatal fifo asic bug. * To avoid it, currently make sync offset 0 (async)! */ if (NCV_SPECIAL(dvcfg) & NCVHWCFG_FIFOBUG) { hw->mperiod = 0; hw->moffset = 0; } if (NCV_SPECIAL(dvcfg) & NCVHWCFG_SCSI1) hw->cfg2 &= ~C2_SCSI2; if (NCV_SPECIAL(dvcfg) & NCVHWCFG_SLOW) hw->cfg1 |= C1_SLOW; } /* setup configuration image 3 */ if (hw->clk != CLK_40M_F && hw->clk <= CLK_25M_F) hw->cfg3 &= ~C3_FCLK; /* setup configuration image 1 */ hw->cfg1 = (hw->cfg1 & 0xf0) | hsid; return 0; } int ncvprobesubr(iot, ioh, dvcfg, hsid) bus_space_tag_t iot; bus_space_handle_t ioh; u_int dvcfg; int hsid; { struct ncv_hw hwtab; hwtab = ncv_template; if (ncv_setup_img(&hwtab, dvcfg, hsid)) return 0; if (ncvhw_check(iot, ioh, &hwtab) != 0) return 0; return 1; } int ncvprint(aux, name) void *aux; const char *name; { if (name != NULL) printf("%s: scsibus ", name); return UNCONF; } void ncvattachsubr(sc) struct ncv_softc *sc; { struct scsi_low_softc *slp = &sc->sc_sclow; printf("\n"); sc->sc_hw = ncv_template; ncv_setup_img(&sc->sc_hw, slp->sl_cfgflags, slp->sl_hostid); slp->sl_funcs = &ncv_funcs; (void) scsi_low_attach(slp, 2, NCV_NTARGETS, NCV_NLUNS, sizeof(struct ncv_lun_info)); } /************************************************************** * PDMA **************************************************************/ static __inline void ncvhw_set_count(iot, ioh, count) bus_space_tag_t iot; bus_space_handle_t ioh; int count; { bus_space_write_1(iot, ioh, cr0_tclsb, (u_int8_t) count); bus_space_write_1(iot, ioh, cr0_tcmsb, (u_int8_t) (count >> NBBY)); bus_space_write_1(iot, ioh, cr0_tchsb, (u_int8_t) (count >> (NBBY * 2))); } static __inline u_int ncvhw_get_count(iot, ioh) bus_space_tag_t iot; bus_space_handle_t ioh; { u_int count; count = (u_int) bus_space_read_1(iot, ioh, cr0_tclsb); count |= ((u_int) bus_space_read_1(iot, ioh, cr0_tcmsb)) << NBBY; count |= ((u_int) bus_space_read_1(iot, ioh, cr0_tchsb)) << (NBBY * 2); return count; } static __inline void ncv_pdma_end(sc, ti) struct ncv_softc *sc; struct targ_info *ti; { struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int len; slp->sl_flags &= ~HW_PDMASTART; if (ti->ti_phase == PH_DATA) { len = ncvhw_get_count(sc->sc_iot, sc->sc_ioh); if (slp->sl_scp.scp_direction == SCSI_LOW_WRITE) len += (bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_sffl) & CR0_SFFLR_BMASK); if ((u_int) len <= (u_int) slp->sl_scp.scp_datalen) { slp->sl_scp.scp_data += (slp->sl_scp.scp_datalen - len); slp->sl_scp.scp_datalen = len; if ((slp->sl_scp.scp_direction == SCSI_LOW_READ) && sc->sc_tdatalen != len) goto bad; } else { bad: slp->sl_error |= PDMAERR; printf("%s stragne count hw 0x%x soft 0x%x tlen 0x%x\n", slp->sl_xname, len, slp->sl_scp.scp_datalen, sc->sc_tdatalen); } } else { printf("%s data phase miss\n", slp->sl_xname); slp->sl_error |= PDMAERR; } ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_fstat, 0); ncvhw_select_register_0(iot, ioh, &sc->sc_hw); } static void ncv_pio_read(sc, buf, reqlen) struct ncv_softc *sc; u_int8_t *buf; u_int reqlen; { struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int s; int tout = 0; register u_int8_t fstat; #ifdef __FreeBSD__ struct callout_handle ch; #endif ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_pflag, 0); ncvhw_select_register_0(iot, ioh, &sc->sc_hw); ncvhw_set_count(iot, ioh, reqlen); bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS | CMD_DMA); ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_fstat, FIFO_EN); slp->sl_flags |= HW_PDMASTART; #ifdef __FreeBSD__ ch = timeout(settimeout, &tout, 2 * hz); #else timeout(settimeout, &tout, 2 * hz); #endif while (reqlen >= FIFO_F_SZ && tout == 0) { fstat = bus_space_read_1(iot, ioh, cr1_fstat); if (fstat & FIFO_F) { #define NCV_FAST32_ACCESS #ifdef NCV_FAST32_ACCESS bus_space_read_multi_4(iot, ioh, cr1_fdata, (u_int32_t *) buf, FIFO_F_SZ / 4); #else /* !NCV_FAST32_ACCESS */ bus_space_read_multi_2(iot, ioh, cr1_fdata, (u_int16_t *) buf, FIFO_F_SZ / 2); #endif /* !NCV_FAST32_ACCESS */ buf += FIFO_F_SZ; reqlen -= FIFO_F_SZ; continue; } else if (fstat & FIFO_BRK) break; } if (reqlen >= FIFO_2_SZ) { fstat = bus_space_read_1(iot, ioh, cr1_fstat); if (fstat & FIFO_2) { #ifdef NCV_FAST32_ACCESS bus_space_read_multi_4(iot, ioh, cr1_fdata, (u_int32_t *) buf, FIFO_2_SZ / 4); #else /* !NCV_FAST32_ACCESS */ bus_space_read_multi_2(iot, ioh, cr1_fdata, (u_int16_t *) buf, FIFO_2_SZ / 2); #endif /* !NCV_FAST32_ACCESS */ buf += FIFO_2_SZ; reqlen -= FIFO_2_SZ; } } while (reqlen > 0 && tout == 0) { fstat = bus_space_read_1(iot, ioh, cr1_fstat); if ((fstat & FIFO_E) == 0) { *buf++ = bus_space_read_1(iot, ioh, cr1_fdata); reqlen --; continue; } else if (fstat & FIFO_BRK) break; } ncvhw_select_register_0(iot, ioh, &sc->sc_hw); sc->sc_tdatalen = reqlen; s = splhigh(); if (tout == 0) { #ifdef __FreeBSD__ untimeout(settimeout, &tout, ch); #else untimeout(settimeout, &tout); #endif splx(s); } else { splx(s); printf("%s pio read timeout\n", slp->sl_xname); } } static void ncv_pio_write(sc, buf, reqlen) struct ncv_softc *sc; u_int8_t *buf; u_int reqlen; { struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int s; int tout = 0; register u_int8_t fstat; #ifdef __FreeBSD__ struct callout_handle ch; #endif ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_pflag, 0); ncvhw_select_register_0(iot, ioh, &sc->sc_hw); ncvhw_set_count(iot, ioh, reqlen); bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS | CMD_DMA); ncvhw_select_register_1(iot, ioh, &sc->sc_hw); bus_space_write_1(iot, ioh, cr1_fstat, FIFO_EN); slp->sl_flags |= HW_PDMASTART; #ifdef __FreeBSD__ ch = timeout(settimeout, &tout, 2 * hz); #else timeout(settimeout, &tout, 2 * hz); #endif while (reqlen >= FIFO_F_SZ && tout == 0) { fstat = bus_space_read_1(iot, ioh, cr1_fstat); if (fstat & FIFO_BRK) goto done; if (fstat & FIFO_E) { #ifdef NCV_FAST32_ACCESS bus_space_write_multi_4(iot, ioh, cr1_fdata, (u_int32_t *) buf, FIFO_F_SZ / 4); #else /* !NCV_FAST32_ACCESS */ bus_space_write_multi_2(iot, ioh, cr1_fdata, (u_int16_t *) buf, FIFO_F_SZ / 2); #endif /* !NCV_FAST32_ACCESS */ buf += FIFO_F_SZ; reqlen -= FIFO_F_SZ; } } while (reqlen > 0 && tout == 0) { fstat = bus_space_read_1(iot, ioh, cr1_fstat); if (fstat & FIFO_BRK) break; if ((fstat & FIFO_F) == 0) /* fifo not full */ { bus_space_write_1(iot, ioh, cr1_fdata, *buf++); reqlen --; } } done: ncvhw_select_register_0(iot, ioh, &sc->sc_hw); s = splhigh(); if (tout == 0) { #ifdef __FreeBSD__ untimeout(settimeout, &tout, ch); #else untimeout(settimeout, &tout); #endif splx(s); } else { splx(s); printf("%s pio write timeout\n", slp->sl_xname); } } static void settimeout(arg) void *arg; { int *tout = arg; *tout = 1; } /************************************************************** * disconnect & reselect (HW low) **************************************************************/ static __inline int ncv_reselected(sc) struct ncv_softc *sc; { 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; u_int sid; if ((bus_space_read_1(iot, ioh, cr0_sffl) & CR0_SFFLR_BMASK) != 2) { printf("%s illegal fifo bytes\n", slp->sl_xname); scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "chip confused"); return EJUSTRETURN; } sid = (u_int) bus_space_read_1(iot, ioh, cr0_sfifo); sid = ffs(sid) - 1; ti = scsi_low_reselected((struct scsi_low_softc *) sc, sid); if (ti == NULL) return EJUSTRETURN; #ifdef NCV_STATICS ncv_statics[sid].reselect ++; #endif /* NCV_STATICS */ bus_space_write_1(iot, ioh, cr0_dstid, sid); return 0; } static __inline int ncv_disconnected(sc, ti) struct ncv_softc *sc; struct targ_info *ti; { struct scsi_low_softc *slp = &sc->sc_sclow; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); bus_space_write_1(iot, ioh, cr0_cfg1, sc->sc_hw.cfg1); bus_space_write_1(iot, ioh, cr0_cmd, CMD_ENSEL); #ifdef NCV_STATICS if (slp->sl_msgphase == MSGPH_DISC) ncv_statics[ti->ti_id].disconnect ++; #endif /* NCV_STATICS */ scsi_low_disconnected(slp, ti); return 1; } /************************************************************** * SEQUENCER **************************************************************/ static int ncv_nexus(sc, ti) struct ncv_softc *sc; struct targ_info *ti; { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct lun_info *li = ti->ti_li; struct ncv_lun_info *nli = (void *) li; if (li->li_flags & SCSI_LOW_NOPARITY) bus_space_write_1(iot, ioh, cr0_cfg1, sc->sc_hw.cfg1); else bus_space_write_1(iot, ioh, cr0_cfg1, sc->sc_hw.cfg1 | C1_PARENB); bus_space_write_1(iot, ioh, cr0_period, nli->nli_reg_period); bus_space_write_1(iot, ioh, cr0_offs, nli->nli_reg_offset); bus_space_write_1(iot, ioh, cr0_cfg3, nli->nli_reg_cfg3); return 0; } int ncvintr(arg) void *arg; { struct ncv_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 physio_proc *pp; struct buf *bp; int len, identify; u_int8_t regv, status, ireason; if (slp->sl_flags & HW_INACTIVE) return 0; /******************************************** * Status ********************************************/ ncvhw_select_register_0(iot, ioh, &sc->sc_hw); status = bus_space_read_1(iot, ioh, cr0_stat); if ((status & STAT_INT) == 0) return 0; ireason = bus_space_read_1(iot, ioh, cr0_istat); if (ireason & INTR_SBR) { u_int8_t val; /* avoid power off hangup */ val = bus_space_read_1(iot, ioh, cr0_cfg1); bus_space_write_1(iot, ioh, cr0_cfg1, val | C1_SRR); /* status init */ scsi_low_restart(slp, SCSI_LOW_RESTART_SOFT, "bus reset (power off?)"); return 1; } /******************************************** * Debug section ********************************************/ #ifdef NCV_DEBUG if (ncv_debug) { scsi_low_print(slp, NULL); printf("%s st %x ist %x\n\n", slp->sl_xname, status, ireason); if (ncv_debug > 1) Debugger(); } #endif /* NCV_DEBUG */ /******************************************** * Reselect or Disconnect or Nexus check ********************************************/ /* (I) reselect */ if (ireason == INTR_RESELECT) { if (ncv_reselected(sc) == EJUSTRETURN) return 1; } /* (II) nexus */ if ((ti = slp->sl_nexus) == NULL) return 0; if ((status & (STAT_PE | STAT_GE)) != 0) { slp->sl_error |= PARITYERR; if (ti->ti_phase == PH_MSGIN) scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_PARITY, 1); else scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ERROR, 1); } if ((ireason & (INTR_DIS | INTR_ILL)) != 0) { if ((ireason & INTR_ILL) == 0) return ncv_disconnected(sc, ti); slp->sl_error |= FATALIO; scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "illegal cmd"); return 1; } /******************************************** * Internal scsi phase ********************************************/ switch (ti->ti_phase) { case PH_SELSTART: scsi_low_arbit_win(slp, ti); SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); identify = 0; if (sc->sc_selstop == 0) { /* XXX: * Here scsi phases expected are * DATA PHASE: * MSGIN : target wants to disconnect the host. * STATUSIN : immediate command completed. * MSGOUT : identify command failed. */ if ((status & PHASE_MASK) != MESSAGE_OUT_PHASE) break; identify = 1; } else { /* XXX: * Here scsi phase should be MSGOUT. * The driver NEVER supports devices * which neglect ATN singal. */ if ((status & PHASE_MASK) != MESSAGE_OUT_PHASE) { slp->sl_error |= FATALIO; scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "msgout error"); return 1; } if ((ireason & INTR_FC) == 0) identify = 1; } if (identify != 0) { printf("%s msg identify failed\n", slp->sl_xname); scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_IDENTIFY, 0); } break; case PH_RESEL: if ((status & PHASE_MASK) != MESSAGE_IN_PHASE) { scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); return 1; } break; default: if (slp->sl_flags & HW_PDMASTART) ncv_pdma_end(sc, ti); break; } /******************************************** * Scsi phase sequencer ********************************************/ switch (status & PHASE_MASK) { case DATA_OUT_PHASE: /* data out */ SCSI_LOW_SETUP_PHASE(ti, PH_DATA); if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0) break; pp = physio_proc_enter(bp); ncv_pio_write(sc, slp->sl_scp.scp_data, slp->sl_scp.scp_datalen); physio_proc_leave(pp); break; case DATA_IN_PHASE: /* data in */ SCSI_LOW_SETUP_PHASE(ti, PH_DATA); if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0) break; pp = physio_proc_enter(bp); ncv_pio_read(sc, slp->sl_scp.scp_data, slp->sl_scp.scp_datalen); physio_proc_leave(pp); break; case COMMAND_PHASE: /* cmd out */ SCSI_LOW_SETUP_PHASE(ti, PH_CMD); if (scsi_low_cmd(slp, ti) != 0) break; bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); ncvhw_fpush(iot, ioh, slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen); bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS); break; case STATUS_PHASE: /* status in */ SCSI_LOW_SETUP_PHASE(ti, PH_STAT); bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); bus_space_write_1(iot, ioh, cr0_cmd, CMD_ICCS); sc->sc_compseq = 1; break; default: break; case MESSAGE_OUT_PHASE: /* msg out */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); len = scsi_low_msgout(slp, ti); ncvhw_fpush(iot, ioh, ti->ti_msgoutstr, len); bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS); if (scsi_low_is_msgout_continue(ti) == 0) bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTATN); break; case MESSAGE_IN_PHASE: /* msg in */ SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); len = bus_space_read_1(iot, ioh, cr0_sffl) & CR0_SFFLR_BMASK; if (sc->sc_compseq != 0) { sc->sc_compseq = 0; if ((ireason & INTR_FC) && len == 2) { ti->ti_status = bus_space_read_1(iot, ioh, cr0_sfifo); len --; } else { scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "compseq error"); break; } } else if (ireason & INTR_BS) { bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH); bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS); break; } if ((ireason & INTR_FC) && len == 1) { regv = bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_sfifo); scsi_low_msgin(slp, ti, regv); bus_space_write_1(sc->sc_iot, sc->sc_ioh, cr0_cmd, CMD_MSGOK); } else { slp->sl_error |= MSGERR; printf("%s st %x ist %x\n\n", slp->sl_xname, status, ireason); scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "hw msgin error"); } break; } return 1; }