summaryrefslogtreecommitdiffstats
path: root/sys/dev/ncv/ncr53c500.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ncv/ncr53c500.c')
-rw-r--r--sys/dev/ncv/ncr53c500.c1197
1 files changed, 1197 insertions, 0 deletions
diff --git a/sys/dev/ncv/ncr53c500.c b/sys/dev/ncv/ncr53c500.c
new file mode 100644
index 0000000..dfbfd3f
--- /dev/null
+++ b/sys/dev/ncv/ncr53c500.c
@@ -0,0 +1,1197 @@
+/* $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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/disklabel.h>
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500001
+#include <sys/bio.h>
+#endif
+#include <sys/buf.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/device_port.h>
+#include <sys/errno.h>
+
+#include <vm/vm.h>
+
+#ifdef __NetBSD__
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/scsipi/scsi_all.h>
+#include <dev/scsipi/scsipi_all.h>
+#include <dev/scsipi/scsiconf.h>
+#include <dev/scsipi/scsi_disk.h>
+
+#include <machine/dvcfg.h>
+#include <machine/physio_proc.h>
+
+#include <i386/Cbus/dev/scsi_low.h>
+
+#include <i386/Cbus/dev/ncr53c500reg.h>
+#include <i386/Cbus/dev/ncr53c500hw.h>
+#include <i386/Cbus/dev/ncr53c500var.h>
+
+#include <i386/Cbus/dev/ncr53c500hwtab.h>
+#endif /* __NetBSD__ */
+
+#ifdef __FreeBSD__
+#include <machine/clock.h>
+#define delay(time) DELAY(time)
+
+#include <machine/cpu.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+
+#include <machine/dvcfg.h>
+#include <machine/physio_proc.h>
+
+#include <cam/scsi/scsi_low.h>
+
+#include <dev/ncv/ncr53c500reg.h>
+#include <dev/ncv/ncr53c500hw.h>
+#include <dev/ncv/ncr53c500var.h>
+
+#include <dev/ncv/ncr53c500hwtab.h>
+
+#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((struct scsi_low_softc *) sc);
+
+ 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;
+}
OpenPOWER on IntegriCloud