diff options
author | non <non@FreeBSD.org> | 2000-10-23 12:55:51 +0000 |
---|---|---|
committer | non <non@FreeBSD.org> | 2000-10-23 12:55:51 +0000 |
commit | 8e8d8815fad836120f63c84071f89cb9552db1e2 (patch) | |
tree | a86ab047759aa8e4af097dccf6d2615932b9bc7e /sys/dev | |
parent | 7885ad000050949f99433ec9baa0babf83709052 (diff) | |
download | FreeBSD-src-8e8d8815fad836120f63c84071f89cb9552db1e2.zip FreeBSD-src-8e8d8815fad836120f63c84071f89cb9552db1e2.tar.gz |
Add PC-Card/ISA SCSI host adpater drivers from NetBSD/pc98
(a NetBSD port for NEC PC-98x1 machines). They are ncv for NCR 53C500,
nsp for Workbit Ninja SCSI-3, and stg for TMC 18C30 and 18C50.
I thank NetBSD/pc98 and bsd-nomads people.
Obtained from: NetBSD/pc98
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ncv/ncr53c500.c | 1197 | ||||
-rw-r--r-- | sys/dev/ncv/ncr53c500_pccard.c | 414 | ||||
-rw-r--r-- | sys/dev/ncv/ncr53c500hw.h | 67 | ||||
-rw-r--r-- | sys/dev/ncv/ncr53c500hwtab.h | 47 | ||||
-rw-r--r-- | sys/dev/ncv/ncr53c500reg.h | 192 | ||||
-rw-r--r-- | sys/dev/ncv/ncr53c500var.h | 90 | ||||
-rw-r--r-- | sys/dev/nsp/nsp.c | 1373 | ||||
-rw-r--r-- | sys/dev/nsp/nsp_pccard.c | 414 | ||||
-rw-r--r-- | sys/dev/nsp/nspreg.h | 201 | ||||
-rw-r--r-- | sys/dev/nsp/nspvar.h | 102 | ||||
-rw-r--r-- | sys/dev/stg/tmc18c30.c | 1218 | ||||
-rw-r--r-- | sys/dev/stg/tmc18c30_isa.c | 298 | ||||
-rw-r--r-- | sys/dev/stg/tmc18c30_pccard.c | 399 | ||||
-rw-r--r-- | sys/dev/stg/tmc18c30reg.h | 144 | ||||
-rw-r--r-- | sys/dev/stg/tmc18c30var.h | 99 |
15 files changed, 6255 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; +} diff --git a/sys/dev/ncv/ncr53c500_pccard.c b/sys/dev/ncv/ncr53c500_pccard.c new file mode 100644 index 0000000..d1ffd89 --- /dev/null +++ b/sys/dev/ncv/ncr53c500_pccard.c @@ -0,0 +1,414 @@ +/* $FreeBSD$ */ +/* $NecBSD: ncr53c500_pisa.c,v 1.28 1998/11/26 01:59:11 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [Ported for FreeBSD] + * Copyright (c) 2000 + * Noriaki Mitsunaga, Mitsuru Iwasaki and Takanori Watanabe. + * All rights reserved. + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1995, 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1995, 1996, 1997, 1998 + * 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 <sys/param.h> +#include <sys/systm.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/errno.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <machine/bus_pio.h> +#include <i386/isa/isa_device.h> + +#include <machine/dvcfg.h> + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static struct ncv_softc *ncv_get_softc(int); +extern struct ncv_softc *ncvdata[]; +#define DEVPORT_ALLOCSOFTCFUNC ncv_get_softc +#define DEVPORT_SOFTCARRAY ncvdata +#endif +#include <sys/device_port.h> + +#include <cam/scsi/scsi_low.h> +#include <cam/scsi/scsi_low_pisa.h> + +#include <dev/ncv/ncr53c500reg.h> +#include <dev/ncv/ncr53c500hw.h> +#include <dev/ncv/ncr53c500var.h> +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 400001) +#include "ncv.h" +#endif + +#define KME_KXLC004_01 0x1 +#define OFFSET_KME_KXLC004_01 0x10 + +/* pccard support */ +#include "apm.h" +#if NAPM > 0 +#include <machine/apm_bios.h> +#endif /* NAPM > 0 */ + +#include "card.h" +#if NCARD > 0 +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/select.h> +#include <pccard/cardinfo.h> +#include <pccard/slot.h> + +static int ncvprobe(DEVPORT_PDEVICE devi); +static int ncvattach(DEVPORT_PDEVICE devi); + +static int ncv_card_intr __P((DEVPORT_PDEVICE)); +static void ncv_card_unload __P((DEVPORT_PDEVICE)); +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static int ncv_card_init __P((DEVPORT_PDEVICE)); +#endif + +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 +/* + * Additional code for FreeBSD new-bus PCCard frontend + */ + +static void +ncv_pccard_intr(void * arg) +{ + ncvintr(arg); +} + +static void +ncv_release_resource(DEVPORT_PDEVICE dev) +{ + struct ncv_softc *sc = device_get_softc(dev); + + if (sc->ncv_intrhand) { + bus_teardown_intr(dev, sc->irq_res, sc->ncv_intrhand); + } + + if (sc->port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->port_rid, sc->port_res); + } + + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid, sc->irq_res); + } + + if (sc->mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->mem_rid, sc->mem_res); + } +} + +static int +ncv_alloc_resource(DEVPORT_PDEVICE dev) +{ + struct ncv_softc *sc = device_get_softc(dev); + u_int32_t flags = DEVPORT_PDEVFLAGS(dev); + u_int iobase = DEVPORT_PDEVIOBASE(dev); + u_long maddr, msize; + int error; + bus_addr_t offset = 0; + + if(flags & KME_KXLC004_01) + offset = OFFSET_KME_KXLC004_01; + + sc->port_rid = 0; + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, + iobase+offset, ~0, NCVIOSZ, RF_ACTIVE); + if (sc->port_res == NULL) { + ncv_release_resource(dev); + return(ENOMEM); + } + + sc->irq_rid = 0; + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->irq_res == NULL) { + ncv_release_resource(dev); + return(ENOMEM); + } + + error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &maddr, &msize); + if (error) { + return(0); /* XXX */ + } + + /* no need to allocate memory if not configured */ + if (maddr == 0 || msize == 0) { + return(0); + } + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, + 0, ~0, msize, RF_ACTIVE); + if (sc->mem_res == NULL) { + ncv_release_resource(dev); + return(ENOMEM); + } + + return(0); +} + +static int +ncv_pccard_probe(DEVPORT_PDEVICE dev) +{ + struct ncv_softc *sc = device_get_softc(dev); + int error; + + bzero(sc, sizeof(struct ncv_softc)); + + error = ncv_alloc_resource(dev); + if (error) { + return(error); + } + + if (ncvprobe(dev) == 0) { + ncv_release_resource(dev); + return(ENXIO); + } + + ncv_release_resource(dev); + + return(0); +} + +static int +ncv_pccard_attach(DEVPORT_PDEVICE dev) +{ + struct ncv_softc *sc = device_get_softc(dev); + int error; + + error = ncv_alloc_resource(dev); + if (error) { + return(error); + } + + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CAM, + ncv_pccard_intr, (void *)sc, &sc->ncv_intrhand); + if (error) { + ncv_release_resource(dev); + return(error); + } + + if (ncvattach(dev) == 0) { + ncv_release_resource(dev); + return(ENXIO); + } + + return(0); +} + +static void +ncv_pccard_detach(DEVPORT_PDEVICE dev) +{ + ncv_card_unload(dev); + ncv_release_resource(dev); +} + +static device_method_t ncv_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ncv_pccard_probe), + DEVMETHOD(device_attach, ncv_pccard_attach), + DEVMETHOD(device_detach, ncv_pccard_detach), + + { 0, 0 } +}; + +static driver_t ncv_pccard_driver = { + "ncv", + ncv_pccard_methods, + sizeof(struct ncv_softc), +}; + +static devclass_t ncv_devclass; + +DRIVER_MODULE(ncv, pccard, ncv_pccard_driver, ncv_devclass, 0, 0); + +#else + +PCCARD_MODULE(ncv, ncv_card_init, ncv_card_unload, ncv_card_intr, 0, cam_imask); + +#endif + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static struct ncv_softc * +ncv_get_softc(int unit) +{ + struct ncv_softc *sc; + + if (unit >= NNCV) { + return(NULL); + } + + if (ncvdata[unit] == NULL) { + sc = malloc(sizeof(struct ncv_softc), M_TEMP,M_NOWAIT); + if (sc == NULL) { + printf("ncv_get_softc: cannot malloc!\n"); + return(NULL); + } + ncvdata[unit] = sc; + } else { + sc = ncvdata[unit]; + } + + return(sc); +} + +static int +ncv_card_init(DEVPORT_PDEVICE devi) +{ + int unit = DEVPORT_PDEVUNIT(devi); + + if (NNCV <= unit) + return (ENODEV); + + if (ncvprobe(devi) == 0) + return (ENXIO); + + if (ncvattach(devi) == 0) + return (ENXIO); + return (0); +} +#endif + +static void +ncv_card_unload(DEVPORT_PDEVICE devi) +{ + struct ncv_softc *sc = DEVPORT_PDEVGET_SOFTC(devi); + + printf("%s: unload\n", sc->sc_sclow.sl_xname); + scsi_low_deactivate((struct scsi_low_softc *)sc); + scsi_low_dettach(&sc->sc_sclow); +} + +static int +ncv_card_intr(DEVPORT_PDEVICE devi) +{ + + ncvintr(DEVPORT_PDEVGET_SOFTC(devi)); + return 1; +} + +static int +ncvprobe(DEVPORT_PDEVICE devi) +{ + int rv; + struct ncv_softc *sc = device_get_softc(devi); + u_int32_t flags = DEVPORT_PDEVFLAGS(devi); + +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + rv = ncvprobesubr(rman_get_bustag(sc->port_res), + rman_get_bushandle(sc->port_res), + flags, NCV_HOSTID); +#else + bus_addr_t offset = 0; + u_int iobase = DEVPORT_PDEVIOBASE(devi); + + if(flags & KME_KXLC004_01) + offset = OFFSET_KME_KXLC004_01; + + rv = ncvprobesubr(I386_BUS_SPACE_IO, + iobase + offset, + flags, NCV_HOSTID); +#endif + + return rv; +} + +static int +ncvattach(DEVPORT_PDEVICE devi) +{ + struct ncv_softc *sc; + struct scsi_low_softc *slp; + u_int32_t flags = DEVPORT_PDEVFLAGS(devi); +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + int unit = DEVPORT_PDEVUNIT(devi); + bus_addr_t offset = 0; + u_int iobase = DEVPORT_PDEVIOBASE(devi); +#endif + char dvname[16]; /* SCSI_LOW_DVNAME_LEN */ + + strcpy(dvname, "ncv"); + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + if (unit >= NNCV) + { + printf("%s: unit number too high\n", dvname); + return (0); + } + + if (iobase == 0) + { + printf("%s: no ioaddr is given\n", dvname); + return (0); + } + + if(flags & KME_KXLC004_01) + offset = OFFSET_KME_KXLC004_01; +#endif + + sc = DEVPORT_PDEVALLOC_SOFTC(devi); + if (sc == NULL) { + return(0); + } + + slp = &sc->sc_sclow; +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + slp->sl_dev = devi; + sc->sc_iot = rman_get_bustag(sc->port_res); + sc->sc_ioh = rman_get_bushandle(sc->port_res); +#else + bzero(sc, sizeof(struct ncv_softc)); + strcpy(slp->sl_dev.dv_xname, dvname); + slp->sl_dev.dv_unit = unit; + sc->sc_iot = I386_BUS_SPACE_IO; + sc->sc_ioh = iobase + offset; +#endif + + slp->sl_hostid = NCV_HOSTID; + slp->sl_cfgflags = flags; + + ncvattachsubr(sc); + + sc->sc_ih = ncvintr; + + return(NCVIOSZ); +} +#endif /* NCARD */ diff --git a/sys/dev/ncv/ncr53c500hw.h b/sys/dev/ncv/ncr53c500hw.h new file mode 100644 index 0000000..e318959 --- /dev/null +++ b/sys/dev/ncv/ncr53c500hw.h @@ -0,0 +1,67 @@ +/* $FreeBSD$ */ +/* $NecBSD: ncr53c500hw.h,v 1.6 1998/11/26 01:59:12 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * 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. + */ + +#ifndef __NCR53C500HW_H_ +#define __NCR53C500HW_H_ + +#include <machine/dvcfg.h> + +#define NCV_HOSTID 7 +#define NCV_NTARGETS 8 +#define NCV_NLUNS 8 + +struct ncv_hw { + /* configuration images */ + u_int8_t cfg1; + u_int8_t cfg2; + u_int8_t cfg3; + u_int8_t cfg4; + u_int8_t cfg5; + + /* synch */ + u_int8_t clk; + u_int8_t mperiod; + u_int8_t moffset; +}; + +/* dvcfg */ +#define NCV_C5IMG(flags) ((DVCFG_MAJOR(flags) >> 8) & 0xff) +#define NCV_CLKFACTOR(flags) (DVCFG_MAJOR(flags) & 0x0f) +#define NCVHWCFG_MAX10M 0x01 +#define NCVHWCFG_SCSI1 0x02 +#define NCVHWCFG_SLOW 0x04 +#define NCVHWCFG_FIFOBUG 0x08 +#define NCV_SPECIAL(flags) ((DVCFG_MAJOR(flags) >> 4) & 0x0f) +#endif /* !__NCR53C500HW_H_ */ diff --git a/sys/dev/ncv/ncr53c500hwtab.h b/sys/dev/ncv/ncr53c500hwtab.h new file mode 100644 index 0000000..c95787e9 --- /dev/null +++ b/sys/dev/ncv/ncr53c500hwtab.h @@ -0,0 +1,47 @@ +/* $FreeBSD$ */ +/* $NecBSD: ncr53c500hwtab.h,v 1.2 1998/11/26 01:59:13 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * 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. + */ + +static struct ncv_hw ncv_template = { + NCV_HOSTID, + C2_FE | C2_SCSI2, + C3_FCLK, + C4_ANE, + 0x80, + + CLK_40M_F, + + 200 / 4, + 15, +}; diff --git a/sys/dev/ncv/ncr53c500reg.h b/sys/dev/ncv/ncr53c500reg.h new file mode 100644 index 0000000..ac39e1b --- /dev/null +++ b/sys/dev/ncv/ncr53c500reg.h @@ -0,0 +1,192 @@ +/* $FreeBSD$ */ +/* $NecBSD: ncr53c500reg.h,v 1.5 1998/12/26 11:50:01 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1995, 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1995, 1996, 1997, 1998 + * Naofumi HONDA. All rights reserved. + * Copyright (c) 1995, 1996, 1997, 1998 + * Kouichi Matsuda. 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. + */ + +#ifndef _NCR53C500REG_H_ +#define _NCR53C500REG_H_ + +/* Control Register Set 0 */ +#define NCVIOSZ 0x10 + +#define cr0_tclsb 0x00 /* RW - Transfer Count Low */ +#define cr0_tcmsb 0x01 /* RW - Transfer Count Mid */ +#define cr0_sfifo 0x02 /* RW - FIFO data */ +#define cr0_cmd 0x03 /* RW - Command (2 deep) */ +#define cr0_stat 0x04 /* RO - Status */ +#define cr0_dstid 0x04 /* WO - Select/Reselect Bus ID */ +#define cr0_istat 0x05 /* RO - Interrupt */ +#define cr0_srtout 0x05 /* WO - Select/Reselect Timeout */ +#define cr0_seq 0x06 /* RO - Sequence Step */ +#define cr0_period 0x06 /* WO - Synch Transfer Period */ +#define cr0_sffl 0x07 /* RO - FIFO FLags */ +#define cr0_offs 0x07 /* WO - Synch Ofset */ +#define cr0_cfg1 0x08 /* RW - Configuration #1 */ +#define cr0_clk 0x09 /* WO - Clock Conversion Factor */ +#define cr0_tst 0x0a /* WO - Test (Chip Test Only) */ +#define cr0_cfg2 0x0b /* RW - Configuration #2 */ +#define cr0_cfg3 0x0c /* RW - Configuration #3 */ +#define cr0_cfg4 0x0d /* RW - Configuration #4 */ +#define cr0_tchsb 0x0e /* RW - Transfer Count High */ +#define cr0_fifo_bottom 0x0f /* WO - FIFO bottom */ + +/* Control Register Set 1 */ +#define cr1_jumper 0x00 /* RW - Jumper Sense Port */ +#define cr1_sram_ptr 0x01 /* RW - SRAM Address Pointer */ +#define cr1_sram_data 0x02 /* RW - SRAM Data */ +#define cr1_fdata 0x04 /* RW - PIO FIFO */ +#define cr1_fstat 0x08 /* RW - PIO Status */ +#define cr1_atacmd 0x09 /* RW - ATA Command/Status */ +#define cr1_ataerr 0x0a /* RW - ATA Features/Error */ +#define cr1_pflag 0x0b /* RW - PIO Flag Interrupt Enable */ +#define cr1_cfg5 0x0d /* RW - Configuration #5 */ +#define cr1_sig 0x0e /* RO - Signature */ +#define cr1_cfg6 0x0f /* RW - Configuration #6 */ + +/* atacmd (MPS110 ONLY) */ +#define ATACMD_POWDOWN 0x2d +#define ATACMD_ENGAGE 0x24 + +/* cr0_sffl regster */ +#define CR0_SFFLR_BMASK 0x1f /* scsi fifo byte mask */ + +/* cfg4 */ +#define C4_ANE 0x04 + +/* cfg3 */ +#define C3_NULL 0x00 +#define C3_FCLK 0x08 /* Fast SCSI */ +#define C3_FSCSI 0x10 /* Fast Clock (>25Mhz) */ + +/* cfg2 */ +#define C2_SCSI2 0x08 /* SCSI-2 Enable */ +#define C2_FE 0x40 /* Features Enable */ + +/* cfg1 */ +#define C1_SLOW 0x80 /* Slow Cable Mode */ +#define C1_SRR 0x40 /* SCSI Reset Rep Int Dis */ +#define C1_PARENB 0x10 /* Enable Parity Check */ + +/* clk factor */ +#define CLK_40M_F 0x00 +#define CLK_25M_F 0x05 +#define CLK_30M_F 0x06 +#define CLK_35M_F 0x07 + +/* interrupt status register */ +#define INTR_SBR 0x80 /* SCSI Bus Reset */ +#define INTR_ILL 0x40 /* Illegal Command */ +#define INTR_DIS 0x20 /* Disconnect */ +#define INTR_BS 0x10 /* Bus Service */ +#define INTR_FC 0x08 /* Function Complete */ +#define INTR_RESEL 0x04 /* Reselected */ +#define INTR_SELATN 0x02 /* Select with ATN */ +#define INTR_SEL 0x01 /* Selected */ +#define INTR_RESELECT (INTR_RESEL | INTR_FC) + +/* status register */ +#define STAT_INT 0x80 /* Interrupt */ +#define STAT_GE 0x40 /* Gross Error */ +#define STAT_PE 0x20 /* Parity Error */ +#define STAT_TC 0x10 /* Terminal Count */ + +/* phase bits */ +#define IOI 0x01 +#define CDI 0x02 +#define MSGI 0x04 + +/* Information transfer phases */ +#define DATA_OUT_PHASE (0) +#define DATA_IN_PHASE (IOI) +#define COMMAND_PHASE (CDI) +#define STATUS_PHASE (CDI|IOI) +#define MESSAGE_OUT_PHASE (MSGI|CDI) +#define MESSAGE_IN_PHASE (MSGI|CDI|IOI) + +#define PHASE_MASK (MSGI|CDI|IOI) + +/* fifo status register */ +#define FIFO_SMASK 0x1e +#define FIFO_E 0x10 /* fifo empty */ +#define FIFO_B 0x00 /* there exists any */ +#define FIFO_1 0x08 /* 1/3 <= bytes < 2/3 */ +#define FIFO_2 0x04 /* 2/3 <= bytes < full */ +#define FIFO_F 0x02 /* full */ +#define FIFO_EN 0x01 /* fifo direction */ +#define FIFO_BRK 0x40 /* phase miss */ + +#define FIFO_F_SZ 128 +#define FIFO_1_SZ 44 +#define FIFO_2_SZ 84 + +/* pflags */ +#define PFR_WRITE 0x01 + +/* Commands */ +#define CMD_DMA 0x80 /* DMA Bit */ +#define CMD_NOP 0x00 /* No Operation */ +#define CMD_FLUSH 0x01 /* Flush FIFO */ +#define CMD_RSTCHIP 0x02 /* Reset Chip */ +#define CMD_RSTSCSI 0x03 /* Reset SCSI Bus */ +#define CMD_RESEL 0x40 /* Reselect Sequence */ +#define CMD_SELNATN 0x41 /* Select without ATN */ +#define CMD_SELATN 0x42 /* Select with ATN */ +#define CMD_SELATNS 0x43 /* Select with ATN & Stop */ +#define CMD_ENSEL 0x44 /* Enable (Re)Selection */ +#define CMD_DISSEL 0x45 /* Disable (Re)Selection */ +#define CMD_SELATN3 0x46 /* Select with ATN3 */ +#define CMD_RESEL3 0x47 /* Reselect3 Sequence */ +#define CMD_SNDMSG 0x20 /* Send Message */ +#define CMD_SNDSTAT 0x21 /* Send Status */ +#define CMD_SNDDATA 0x22 /* Send Data */ +#define CMD_DISCSEQ 0x23 /* Disconnect Sequence */ +#define CMD_TERMSEQ 0x24 /* Terminate Sequence */ +#define CMD_TCCS 0x25 /* Target Command Comp Seq */ +#define CMD_DISC 0x27 /* Disconnect */ +#define CMD_RECMSG 0x28 /* Receive Message */ +#define CMD_RECCMD 0x29 /* Receive Command */ +#define CMD_RECDATA 0x2a /* Receive Data */ +#define CMD_RECCSEQ 0x2b /* Receive Command Sequence */ +#define CMD_ABORT 0x04 /* Target Abort DMA */ +#define CMD_TRANS 0x10 /* Transfer Information */ +#define CMD_ICCS 0x11 /* Initiator Cmd Comp Seq */ +#define CMD_MSGOK 0x12 /* Message Accepted */ +#define CMD_TRPAD 0x18 /* Transfer Pad */ +#define CMD_SETATN 0x1a /* Set ATN */ +#define CMD_RSTATN 0x1b /* Reset ATN */ + +/* Default timeout */ +#define SEL_TOUT 0xa3 +#endif /* !_NCR53C500REG_H_ */ diff --git a/sys/dev/ncv/ncr53c500var.h b/sys/dev/ncv/ncr53c500var.h new file mode 100644 index 0000000..07e85f8 --- /dev/null +++ b/sys/dev/ncv/ncr53c500var.h @@ -0,0 +1,90 @@ +/* $FreeBSD$ */ +/* $NecBSD: ncr53c500var.h,v 1.11 1998/11/28 18:42:42 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1995, 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1995, 1996, 1997, 1998 + * 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. + */ + +#ifndef _NCR53C500VAR_H_ +#define _NCR53C500VAR_H_ + +/***************************************************************** + * Host adapter structure + *****************************************************************/ +struct ncv_softc { + struct scsi_low_softc sc_sclow; /* generic data */ + + bus_space_tag_t sc_iot; + bus_space_tag_t sc_memt; + bus_space_handle_t sc_ioh; + + void *sc_ih; + int sc_selstop; /* sel atn stop asserted */ + int sc_compseq; /* completion seq cmd asserted */ + int sc_tdatalen; /* temp xfer data len */ + + struct ncv_hw sc_hw; /* hardware register images */ +#if defined (__FreeBSD__) && __FreeBSD_version >= 400001 + int port_rid; + int irq_rid; + int mem_rid; + struct resource *port_res; + struct resource *irq_res; + struct resource *mem_res; + void *ncv_intrhand; +#endif +}; + +/***************************************************************** + * Lun information + *****************************************************************/ +struct ncv_lun_info { + struct lun_info nli_li; + + u_int8_t nli_reg_cfg3; /* cfg3 images per lun */ + u_int8_t nli_reg_offset; /* synch offset register per lun */ + u_int8_t nli_reg_period; /* synch period register per lun */ +}; + +/***************************************************************** + * Proto + *****************************************************************/ +int ncvprobesubr __P((bus_space_tag_t, bus_space_handle_t ioh, u_int, int)); +void ncvattachsubr __P((struct ncv_softc *)); +int ncvprint __P((void *, const char *)); +int ncvintr __P((void *)); + +#if defined(i386) +#define SOFT_INTR_REQUIRED(slp) (softintr((slp)->sl_irq)) +#else /* !i386 */ +#define SOFT_INTR_REQUIRED(slp) +#endif /* !i386 */ +#endif /* !_NCR53C500VAR_H_ */ diff --git a/sys/dev/nsp/nsp.c b/sys/dev/nsp/nsp.c new file mode 100644 index 0000000..c2b6762 --- /dev/null +++ b/sys/dev/nsp/nsp.c @@ -0,0 +1,1373 @@ +/* $FreeBSD$ */ +/* $NecBSD: nsp.c,v 1.21 1999/07/23 21:00:05 honda Exp $ */ +/* $NetBSD$ */ + +#define NSP_DEBUG +#define NSP_STATICS + +/* + * Copyright (c) 1998 + * NetBSD/pc98 porting staff. 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/nspreg.h> +#include <i386/Cbus/dev/nspvar.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_memio.h> +#include <machine/bus.h> + +#include <machine/dvcfg.h> +#include <machine/physio_proc.h> + +#include <cam/scsi/scsi_low.h> +#include <dev/nsp/nspreg.h> +#include <dev/nsp/nspvar.h> + +#if __FreeBSD_version < 400001 +#include "nsp.h" +struct nsp_softc *nspdata[NNSP]; +#endif +#endif /* __FreeBSD__ */ + +/*************************************************** + * USER SETTINGS + ***************************************************/ +/* DEVICE CONFIGURATION FLAGS (MINOR) + * + * 0x01 DISCONECT OFF + * 0x02 PARITY LINE OFF + * 0x04 IDENTIFY MSG OFF ( = single lun) + * 0x08 SYNC TRANSFER OFF + */ + +/*************************************************** + * PARAMS + ***************************************************/ +#define NSP_NTARGETS 8 +#define NSP_NLUNS 8 + +#define NSP_SELTIMEOUT 200 + +/*************************************************** + * DEBUG + ***************************************************/ +#ifndef DDB +#define Debugger() panic("should call debugger here (nsp.c)") +#else /* ! DDB */ +#ifdef __FreeBSD__ +#define Debugger() Debugger("nsp") +#endif /* __FreeBSD__ */ +#endif + +#ifdef NSP_DEBUG +int nsp_debug; +#endif /* NSP_DEBUG */ + +#ifdef NSP_STATICS +struct nsp_statics { + int disconnect; + int reselect; + int data_phase_bypass; +} nsp_statics[NSP_NTARGETS]; +#endif /* NSP_STATICS */ + +/*************************************************** + * ISA DEVICE STRUCTURE + ***************************************************/ +extern struct cfdriver nsp_cd; + +/************************************************************** + * DECLARE + **************************************************************/ +/* static */ +static void nsp_pio_read __P((struct nsp_softc *, struct targ_info *)); +static void nsp_pio_write __P((struct nsp_softc *, struct targ_info *)); +static int nsp_xfer __P((struct nsp_softc *, u_int8_t *, int, int)); +static int nsp_msg __P((struct nsp_softc *, struct targ_info *, u_int)); +static int nsp_reselected __P((struct nsp_softc *)); +static __inline int nsp_disconnected __P((struct nsp_softc *, struct targ_info *)); +static __inline void nsp_pdma_end __P((struct nsp_softc *, struct targ_info *)); +static void nsphw_init __P((struct nsp_softc *)); +static int nsp_nexus __P((struct nsp_softc *, struct targ_info *)); +static int nsp_world_start __P((struct nsp_softc *, int)); +static int nsphw_start_selection __P((struct nsp_softc *sc, struct slccb *)); +static void nsphw_bus_reset __P((struct nsp_softc *)); +static void nsphw_attention __P((struct nsp_softc *)); +static u_int nsp_fifo_count __P((struct nsp_softc *)); +static int nsp_negate_signal __P((struct nsp_softc *, u_int8_t, u_char *)); +static int nsp_expect_signal __P((struct nsp_softc *, u_int8_t, u_int8_t)); +static __inline void nsp_start_timer __P((struct nsp_softc *, int)); +static int nsp_dataphase_bypass __P((struct nsp_softc *, struct targ_info *)); +static void nsp_setup_fifo __P((struct nsp_softc *, int)); +static int nsp_lun_init __P((struct nsp_softc *, struct targ_info *, struct lun_info *)); +static void settimeout __P((void *)); + +struct scsi_low_funcs nspfuncs = { + SC_LOW_INIT_T nsp_world_start, + SC_LOW_BUSRST_T nsphw_bus_reset, + SC_LOW_LUN_INIT_T nsp_lun_init, + + SC_LOW_SELECT_T nsphw_start_selection, + SC_LOW_NEXUS_T nsp_nexus, + + SC_LOW_ATTEN_T nsphw_attention, + SC_LOW_MSG_T nsp_msg, + + SC_LOW_POLL_T nspintr, + + NULL, +}; + +/**************************************************** + * hwfuncs + ****************************************************/ +static __inline u_int8_t nsp_cr_read_1 __P((bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t ofs)); +static __inline void nsp_cr_write_1 __P((bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t ofs, u_int8_t va)); + +static __inline u_int8_t +nsp_cr_read_1(bst, bsh, ofs) + bus_space_tag_t bst; + bus_space_handle_t bsh; + bus_addr_t ofs; +{ + + bus_space_write_1(bst, bsh, nsp_idxr, ofs); + return bus_space_read_1(bst, bsh, nsp_datar); +} + +static __inline void +nsp_cr_write_1(bst, bsh, ofs, va) + bus_space_tag_t bst; + bus_space_handle_t bsh; + bus_addr_t ofs; + u_int8_t va; +{ + + bus_space_write_1(bst, bsh, nsp_idxr, ofs); + bus_space_write_1(bst, bsh, nsp_datar, va); +} + +static int +nsp_expect_signal(sc, curphase, mask) + struct nsp_softc *sc; + u_int8_t curphase, mask; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int rv = -1; + int s; + int tout = 0; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + u_int8_t ph, isrc; + +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, hz/2); +#else + timeout(settimeout, &tout, hz/2); +#endif + do + { + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (ph == 0xff) { + rv = -1; + break; + } + isrc = bus_space_read_1(bst, bsh, nsp_irqsr); + if (isrc & IRQSR_SCSI) { + rv = 0; + break; + } + if ((ph & mask) != 0 && (ph & SCBUSMON_PHMASK) == curphase) { + rv = 1; + break; + } + } + while (tout == 0); + + s = splhigh(); + if (tout == 0) { +#ifdef __FreeBSD__ + untimeout(settimeout, &tout, ch); +#else + untimeout(settimeout, &tout); +#endif + splx(s); + } else { + splx(s); + printf("%s: nsp_expect_signal timeout\n", slp->sl_xname); + rv = -1; + } + + return rv; +} + +static void +nsphw_init(sc) + struct nsp_softc *sc; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + + /* block all interrupts */ + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_ALLMASK); + + /* setup SCSI interface */ + bus_space_write_1(bst, bsh, nsp_ifselr, IFSELR_IFSEL); + + nsp_cr_write_1(bst, bsh, NSPR_SCIENR, 0); + + nsp_cr_write_1(bst, bsh, NSPR_XFERMR, XFERMR_IO8); + nsp_cr_write_1(bst, bsh, NSPR_CLKDIVR, sc->sc_iclkdiv); + + nsp_cr_write_1(bst, bsh, NSPR_SCIENR, sc->sc_icr); + nsp_cr_write_1(bst, bsh, NSPR_PARITYR, 0); + nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, + PTCLRR_ACK | PTCLRR_REQ | PTCLRR_HOST | PTCLRR_RSS); + + /* setup fifo asic */ + bus_space_write_1(bst, bsh, nsp_ifselr, IFSELR_REGSEL); + nsp_cr_write_1(bst, bsh, NSPR_TERMPWRC, 0); + if ((nsp_cr_read_1(bst, bsh, NSPR_OCR) & OCR_TERMPWRS) == 0) + nsp_cr_write_1(bst, bsh, NSPR_TERMPWRC, TERMPWRC_POWON); + + nsp_cr_write_1(bst, bsh, NSPR_XFERMR, XFERMR_IO8); + nsp_cr_write_1(bst, bsh, NSPR_CLKDIVR, sc->sc_clkdiv); + nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0); + nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0); + + nsp_cr_write_1(bst, bsh, NSPR_SYNCR, 0); + nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, 0); + + /* enable interrupts and ack them */ + nsp_cr_write_1(bst, bsh, NSPR_SCIENR, SCIENR_SCCHG | SCIENR_RESEL | SCIENR_RST); + bus_space_write_1(bst, bsh, nsp_irqcr, IRQSR_MASK); + + nsp_setup_fifo(sc, 0); +} + +/**************************************************** + * scsi low interface + ****************************************************/ +static void +nsphw_attention(sc) + struct nsp_softc *sc; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + u_int8_t cr; + + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR)/* & ~SCBUSCR_ACK */; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr | SCBUSCR_ATN); +} + +static void +nsphw_bus_reset(sc) + struct nsp_softc *sc; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int i; + + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_ALLMASK); + + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, SCBUSCR_RST); + delay(100 * 1000); /* 100ms */ + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, 0); + for (i = 0; i < 5; i ++) + (void) nsp_cr_read_1(bst, bsh, NSPR_IRQPHS); + + bus_space_write_1(bst, bsh, nsp_irqcr, IRQSR_MASK); +} + +static int +nsphw_start_selection(sc, cb) + struct nsp_softc *sc; + struct slccb *cb; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + struct targ_info *ti = cb->ti; + register u_int8_t arbs, ph; + int s; + int tout = 0; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + + /* check bus free */ + if (slp->sl_disc > 0) + { + s = splhigh(); + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (ph != SCBUSMON_FREE) + { + splx(s); + return SCSI_LOW_START_FAIL; + } + splx(s); + } + + /* start arbitration */ + SCSI_LOW_SETUP_PHASE(ti, PH_ARBSTART); + nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_EXEC); +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, 2 * hz); +#else + timeout(settimeout, &tout, 2 * hz); +#endif + do + { + /* XXX: what a stupid chip! */ + arbs = nsp_cr_read_1(bst, bsh, NSPR_ARBITS); + delay(1); + } + while ((arbs & (ARBITS_WIN | ARBITS_FAIL)) == 0 && tout == 0); + + s = splhigh(); + if (tout == 0) { +#ifdef __FreeBSD__ + untimeout(settimeout, &tout, ch); +#else + untimeout(settimeout, &tout); +#endif + } + splx(s); + + if ((arbs & ARBITS_WIN) == 0) + { + nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_CLR); + return SCSI_LOW_START_FAIL; + } + + /* assert select line */ + SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); + scsi_low_arbit_win(slp, ti); + delay(3); + nsp_cr_write_1(bst, bsh, NSPR_DATA, + sc->sc_idbit | (1 << ti->ti_id)); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + SCBUSCR_SEL | SCBUSCR_BSY | sc->sc_busc); + delay(3); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, SCBUSCR_SEL | + SCBUSCR_BSY | SCBUSCR_DOUT | sc->sc_busc); + nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_CLR); + delay(3); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + SCBUSCR_SEL | SCBUSCR_DOUT | sc->sc_busc); + + /* check selection timeout */ + nsp_start_timer(sc, 1000 / 51); + sc->sc_seltout = 1; + + return SCSI_LOW_START_OK; +} + +static int +nsp_world_start(sc, fdone) + struct nsp_softc *sc; + int fdone; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + intrmask_t s; + + s = splcam(); + sc->sc_cnt = 0; + sc->sc_seltout = 0; + if ((slp->sl_cfgflags & CFG_NOATTEN) == 0) + sc->sc_busc = SCBUSCR_ATN; + else + sc->sc_busc = 0; + sc->sc_icr = (SCIENR_SCCHG | SCIENR_RESEL | SCIENR_RST); + + nsphw_init(sc); + scsi_low_bus_reset(slp); + splx(s); + + SOFT_INTR_REQUIRED(slp); + return 0; +} + +struct ncp_synch_data { + u_int min_period; + u_int max_period; + u_int chip_period; + u_int ack_width; +}; + +static struct ncp_synch_data ncp_sync_data_40M[] = { + {0x0c,0x0c,0x1,0}, /* 20MB 50ns*/ + {0x19,0x19,0x3,1}, /* 10MB 100ns*/ + {0x1a,0x25,0x5,2}, /* 7.5MB 150ns*/ + {0x26,0x32,0x7,3}, /* 5MB 200ns*/ + {0x0, 0, 0, 0} +}; + +static struct ncp_synch_data ncp_sync_data_20M[] = { + {0x19,0x19,0x1,0}, /* 10MB 100ns*/ + {0x1a,0x25,0x2,0}, /* 7.5MB 150ns*/ + {0x26,0x32,0x3,1}, /* 5MB 200ns*/ + {0x0, 0, 0, 0} +}; + +static int +nsp_msg(sc, ti, msg) + struct nsp_softc *sc; + struct targ_info *ti; + u_int msg; +{ + struct ncp_synch_data *sdp; + struct lun_info *li = ti->ti_li; + struct nsp_lun_info *nli = (void *) li; + u_int period, offset; + int i; + + if ((msg & SCSI_LOW_MSG_SYNCH) == 0) + return 0; + + period = li->li_maxsynch.period; + offset = li->li_maxsynch.offset; + if (sc->sc_iclkdiv == CLKDIVR_20M) + sdp = &ncp_sync_data_20M[0]; + else + sdp = &ncp_sync_data_40M[0]; + + for (i = 0; sdp->max_period != 0; i ++, sdp ++) + { + if (period >= sdp->min_period && period <= sdp->max_period) + break; + } + + if (period != 0 && sdp->max_period == 0) + { + /* + * NO proper period/offset found, + * Retry neg with the target. + */ + li->li_maxsynch.period = 0; + li->li_maxsynch.offset = 0; + nli->nli_reg_syncr = 0; + nli->nli_reg_ackwidth = 0; + return EINVAL; + } + + nli->nli_reg_syncr = (sdp->chip_period << SYNCR_PERS) | + (offset & SYNCR_OFFM); + nli->nli_reg_ackwidth = sdp->ack_width; + return 0; +} + +static int +nsp_lun_init(sc, ti, li) + struct nsp_softc *sc; + struct targ_info *ti; + struct lun_info *li; +{ + struct nsp_lun_info *nli = (void *) li; + + li->li_maxsynch.period = 200 / 4; + li->li_maxsynch.offset = 15; + nli->nli_reg_syncr = 0; + nli->nli_reg_ackwidth = 0; + return 0; +} + +static __inline void +nsp_start_timer(sc, time) + struct nsp_softc *sc; + int time; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + + sc->sc_timer = time; + nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, time); +} + +/************************************************************** + * General probe attach + **************************************************************/ +int +nspprobesubr(iot, ioh, dvcfg) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int dvcfg; +{ + u_int8_t regv; + + regv = bus_space_read_1(iot, ioh, nsp_fifosr); + if (regv < 0x11 || regv >= 0x20) + return 0; + return 1; +} + +int +nspprint(aux, name) + void *aux; + const char *name; +{ + + if (name != NULL) + printf("%s: scsibus ", name); + return UNCONF; +} + +void +nspattachsubr(sc) + struct nsp_softc *sc; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + + printf("\n"); + + sc->sc_idbit = (1 << slp->sl_hostid); + slp->sl_funcs = &nspfuncs; + if (sc->sc_memh != NULL) + sc->sc_xmode = NSP_MID_SMIT; + else + sc->sc_xmode = NSP_PIO; + + (void) scsi_low_attach(slp, 2, NSP_NTARGETS, NSP_NLUNS, + sizeof(struct nsp_lun_info)); +} + +/************************************************************** + * PDMA functions + **************************************************************/ +static u_int +nsp_fifo_count(sc) + struct nsp_softc *sc; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + u_int count; + + nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_PT); + count = bus_space_read_1(bst, bsh, nsp_datar); + count += (((u_int) bus_space_read_1(bst, bsh, nsp_datar)) << 8); + count += (((u_int) bus_space_read_1(bst, bsh, nsp_datar)) << 16); + return count; +} + +static void +nsp_setup_fifo(sc, on) + struct nsp_softc *sc; + int on; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + u_int8_t xfermode; + + if (on != 0) + xfermode = XFERMR_XEN | XFERMR_FIFOEN; + else + xfermode = 0; + + if ((slp->sl_scp.scp_datalen % DEV_BSIZE) != 0) + { + sc->sc_mask = 0; + xfermode |= XFERMR_IO8; + } + else + { + sc->sc_mask = 3; + if (sc->sc_xmode == NSP_MID_SMIT) + xfermode |= XFERMR_MEM32; + else + xfermode |= XFERMR_IO32; + } + + sc->sc_xfermr = xfermode; + nsp_cr_write_1(bst, bsh, NSPR_XFERMR, sc->sc_xfermr); +} + +static __inline void +nsp_pdma_end(sc, ti) + struct nsp_softc *sc; + struct targ_info *ti; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + struct slccb *cb = ti->ti_nexus; + u_int len = 0, cnt; + + slp->sl_flags &= ~HW_PDMASTART; + nsp_setup_fifo(sc, 0); + + if (ti->ti_phase == PH_DATA) + { + cnt = nsp_fifo_count(sc); + if (slp->sl_scp.scp_direction == SCSI_LOW_WRITE) + { + len = sc->sc_cnt - cnt; + if (slp->sl_scp.scp_datalen + len <= + cb->ccb_scp.scp_datalen) + { + slp->sl_scp.scp_data -= len; + slp->sl_scp.scp_datalen += len; + } + else + { + slp->sl_error |= PDMAERR; + printf("%s len %x >= datalen %x\n", + slp->sl_xname, + len, slp->sl_scp.scp_datalen); + } + } + else if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + { + if (sc->sc_cnt != cnt) + { + slp->sl_error |= PDMAERR; + printf("%s: data read count error %x != %x\n", + slp->sl_xname, sc->sc_cnt, cnt); + } + } + sc->sc_cnt = cnt; + } + else + { + + printf("%s data phase miss\n", slp->sl_xname); + slp->sl_error |= PDMAERR; + } +} + +#define RFIFO_CRIT 64 +#define WFIFO_CRIT 64 + +static void +nsp_pio_read(sc, ti) + struct nsp_softc *sc; + struct targ_info *ti; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int s; + int tout = 0; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + u_int res, ocount, mask = sc->sc_mask; + u_int8_t stat, fstat; + + slp->sl_flags |= HW_PDMASTART; + ocount = sc->sc_cnt; + +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, 2 * hz); +#else + timeout(settimeout, &tout, 2 * hz); +#endif + while (slp->sl_scp.scp_datalen > 0 && tout == 0) + { + stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + stat &= SCBUSMON_PHMASK; + res = nsp_fifo_count(sc) - ocount; + if (res == 0) + { + if (stat == PHASE_DATAIN) + continue; + break; + } + + fstat = bus_space_read_1(bst, bsh, nsp_fifosr); + if ((fstat & FIFOSR_FULLEMP) == 0 && stat == PHASE_DATAIN) + continue; + + if (res > slp->sl_scp.scp_datalen) + break; + + if (res >= NSP_BUFFER_SIZE) + res = NSP_BUFFER_SIZE; + else + res &= ~mask; + + if (sc->sc_xfermr & XFERMR_MEM32) + { + bus_space_read_region_4(sc->sc_memt, + sc->sc_memh, + 0, + (u_int32_t *) slp->sl_scp.scp_data, + res >> 2); + } + else + { + if (mask != 0) + bus_space_read_multi_4(bst, bsh, nsp_fifodr, + (u_int32_t *) slp->sl_scp.scp_data, + res >> 2); + else + bus_space_read_multi_1(bst, bsh, nsp_fifodr, + (u_int8_t *) slp->sl_scp.scp_data, + res); + } + + slp->sl_scp.scp_data += res; + slp->sl_scp.scp_datalen -= res; + ocount += res; + } + + sc->sc_cnt = ocount; + 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 +nsp_pio_write(sc, ti) + struct nsp_softc *sc; + struct targ_info *ti; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + u_int res, ocount, mask = sc->sc_mask; + int s; + int tout = 0; + register u_int8_t stat; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + + ocount = sc->sc_cnt; + slp->sl_flags |= HW_PDMASTART; +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, 2 * hz); +#else + timeout(settimeout, &tout, 2 * hz); +#endif + while (slp->sl_scp.scp_datalen > 0 && tout == 0) + { + stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + stat &= SCBUSMON_PHMASK; + if (stat != PHASE_DATAOUT) + break; + + res = ocount - nsp_fifo_count(sc); + if (res > 0) + continue; + + res = (slp->sl_scp.scp_datalen > WFIFO_CRIT) ? WFIFO_CRIT : + slp->sl_scp.scp_datalen; + + if (sc->sc_xfermr & XFERMR_MEM32) + { + bus_space_write_region_4(sc->sc_memt, + sc->sc_memh, + 0, + (u_int32_t *) slp->sl_scp.scp_data, + res >> 2); + } + else + { + if (mask != 0) + bus_space_write_multi_4(bst, bsh, nsp_fifodr, + (u_int32_t *) slp->sl_scp.scp_data, res >> 2); + else + bus_space_write_multi_1(bst, bsh, nsp_fifodr, + (u_int8_t *) slp->sl_scp.scp_data, res); + } + + slp->sl_scp.scp_datalen -= res; + slp->sl_scp.scp_data += res; + ocount += res; + } + + sc->sc_cnt = ocount; + 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; +} + +static int +nsp_negate_signal(sc, mask, s) + struct nsp_softc *sc; + u_int8_t mask; + u_char *s; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int tout = 0; + int s; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + u_int8_t regv; + +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, hz/2); +#else + timeout(settimeout, &tout, hz/2); +#endif + do + { + regv = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (regv == 0xff) + break; + } + while ((regv & mask) != 0 && tout == 0); + + s = splhigh(); + if (tout == 0) { +#ifdef __FreeBSD__ + untimeout(settimeout, &tout, ch); +#else + untimeout(settimeout, &tout); +#endif + splx(s); + } else { + splx(s); + printf("%s: %s singla off timeout \n", slp->sl_xname, s); + } + + return 0; +} + +static int +nsp_xfer(sc, buf, len, phase) + struct nsp_softc *sc; + u_int8_t *buf; + int len; + int phase; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int ptr, rv, atn; + + atn = (scsi_low_is_msgout_continue(slp->sl_nexus) != 0); + for (ptr = 0; len > 0; len --, ptr ++) + { + rv = nsp_expect_signal(sc, phase, SCBUSMON_REQ); + if (rv <= 0) + goto out; + + if (len == 1 && atn == 0) + { + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + SCBUSCR_ADIR | SCBUSCR_ACKEN); + } + + if (phase & SCBUSMON_IO) + { + buf[ptr] = nsp_cr_read_1(bst, bsh, NSPR_DATAACK); + } + else + { + nsp_cr_write_1(bst, bsh, NSPR_DATAACK, buf[ptr]); + } + nsp_negate_signal(sc, SCBUSMON_ACK, "xfer<ACK>"); + } + +out: + return len; +} + +static int +nsp_dataphase_bypass(sc, ti) + struct nsp_softc *sc; + struct targ_info *ti; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + struct slccb *cb = ti->ti_nexus; + u_int cnt; + + if (slp->sl_scp.scp_direction != SCSI_LOW_READ || + (slp->sl_scp.scp_datalen % DEV_BSIZE) == 0) + return 0; + + cnt = nsp_fifo_count(sc); + if (sc->sc_cnt == cnt) + return 0; + if (cnt >= DEV_BSIZE) + return EINVAL; + + if (cb == NULL) + return 0; + + /* + * XXX: NSP_QUIRK + * Data phase skip only occures in case of SCSI_LOW_READ. + */ + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + nsp_pio_read(sc, ti); + nsp_pdma_end(sc, ti); +#ifdef NSP_STATICS + nsp_statics[ti->ti_id].data_phase_bypass ++; +#endif /* NSP_STATICS */ + return 0; +} + +/************************************************************** + * disconnect & reselect (HW low) + **************************************************************/ +static int +nsp_reselected(sc) + struct nsp_softc *sc; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + struct targ_info *ti; + u_int sid; + u_int8_t cr; + + sid = (u_int) nsp_cr_read_1(bst, bsh, NSPR_RESELR); + sid &= ~sc->sc_idbit; + sid = ffs(sid) - 1; + if ((ti = scsi_low_reselected(slp, sid)) == NULL) + return EJUSTRETURN; + + nsp_negate_signal(sc, SCBUSMON_SEL, "reselect<SEL>"); + + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) & ~(SCBUSCR_BSY | SCBUSCR_ATN); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr | SCBUSCR_ADIR | SCBUSCR_ACKEN); + +#ifdef NSP_STATICS + nsp_statics[sid].reselect ++; +#endif /* NSP_STATCIS */ + return EJUSTRETURN; +} + +static __inline int +nsp_disconnected(sc, ti) + struct nsp_softc *sc; + struct targ_info *ti; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + +#ifdef NSP_STATICS + if (slp->sl_msgphase == MSGPH_DISC) + nsp_statics[ti->ti_id].disconnect ++; +#endif /* NSP_STATICS */ + + scsi_low_disconnected(slp, ti); + return 1; +} + +/************************************************************** + * SEQUENCER + **************************************************************/ +static void nspmsg __P((struct nsp_softc *, u_char *, u_int8_t, u_int8_t, u_int8_t)); + +static void +nspmsg(sc, s, isrc, ph, irqphs) + struct nsp_softc *sc; + u_char *s; + u_int8_t isrc, ph, irqphs; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + + printf("%s: %s\n", slp->sl_xname, s); + printf("%s: isrc 0x%x scmon 0x%x irqphs 0x%x\n", + slp->sl_xname, (u_int) isrc, (u_int) ph, (u_int) irqphs); +} + +static int +nsp_nexus(sc, ti) + struct nsp_softc *sc; + struct targ_info *ti; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + struct nsp_lun_info *nli = (void *) ti->ti_li; + + /* setup synch transfer registers */ + nsp_cr_write_1(bst, bsh, NSPR_SYNCR, nli->nli_reg_syncr); + nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, nli->nli_reg_ackwidth); + + /* setup pdma fifo */ + nsp_setup_fifo(sc, 1); + + /* clear ack counter */ + sc->sc_cnt = 0; + nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_PT | PTCLRR_ACK | + PTCLRR_REQ | PTCLRR_HOST); + return 0; +} + +int +nspintr(arg) + void *arg; +{ + struct nsp_softc *sc = arg; + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + struct targ_info *ti; + struct physio_proc *pp; + struct buf *bp; + int len, rv; + u_int8_t isrc, ph, irqphs, cr, regv; + + /******************************************* + * interrupt check + *******************************************/ + if (slp->sl_flags & HW_INACTIVE) + return 0; + + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_IRQDIS); + isrc = bus_space_read_1(bst, bsh, nsp_irqsr); + if (isrc == 0xff || (isrc & IRQSR_MASK) == 0) + { + bus_space_write_1(bst, bsh, nsp_irqcr, 0); + return 0; + } + + /* XXX: IMPORTANT + * Do not read an irqphs register if no scsi phase interrupt. + * Unless, you should lose a scsi phase interrupt. + */ + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if ((isrc & IRQSR_SCSI) != 0) + { + irqphs = nsp_cr_read_1(bst, bsh, NSPR_IRQPHS); + } + else + irqphs = 0; + + /* + * timer interrupt handler (scsi vs timer interrupts) + */ + if (sc->sc_timer != 0) + { + nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0); + nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0); + sc->sc_timer = 0; + } + + if ((isrc & IRQSR_MASK) == IRQSR_TIMER && sc->sc_seltout == 0) + { + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_TIMERCL); + return 1; + } + + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_TIMERCL | IRQCR_FIFOCL); + + /******************************************* + * debug section + *******************************************/ +#ifdef NSP_DEBUG + if (nsp_debug) + { + nspmsg(sc, "current status", isrc, ph, irqphs); + scsi_low_print(slp, NULL); + if (nsp_debug > 1) + Debugger(); + } +#endif /* NSP_DEBUG */ + + /******************************************* + * Parse hardware SCSI irq reasons register + *******************************************/ + if ((isrc & IRQSR_SCSI) != 0) + { + if ((irqphs & IRQPHS_RST) != 0) + { + scsi_low_restart(slp, SCSI_LOW_RESTART_SOFT, + "bus reset (power off?)"); + return 1; + } + + if ((irqphs & IRQPHS_RSEL) != 0) + { + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_RESCL); + if (nsp_reselected(sc) == EJUSTRETURN) + return 1; + } + + if ((irqphs & (IRQPHS_PCHG | IRQPHS_LBF)) == 0) + return 1; + } + + /******************************************* + * nexus check + *******************************************/ + if ((ti = slp->sl_nexus) == NULL) + { + /* unknown scsi phase changes */ + nspmsg(sc, "unknown scsi phase changes", isrc, ph, irqphs); + return 0; + } + + /******************************************* + * aribitration & selection + *******************************************/ + switch (ti->ti_phase) + { + case PH_SELSTART: + if ((ph & SCBUSMON_BSY) == 0) + { + if (sc->sc_seltout >= NSP_SELTIMEOUT) + { + sc->sc_seltout = 0; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, 0); + return nsp_disconnected(sc, ti); + } + sc->sc_seltout ++; + nsp_start_timer(sc, 1000 / 51); + return 1; + } + + /* attention assert */ + sc->sc_seltout = 0; + SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, sc->sc_busc); + delay(1); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + sc->sc_busc | SCBUSCR_ADIR | SCBUSCR_ACKEN); + + SCSI_LOW_TARGET_ASSERT_ATN(ti); + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_IDENTIFY, 0); + return 1; + + case PH_RESEL: + if ((ph & SCBUSMON_PHMASK) != PHASE_MSGIN) + { + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); + return 1; + } + /* fall */ + + default: + if ((isrc & (IRQSR_SCSI | IRQSR_FIFO)) == 0) + return 1; + break; + } + + /******************************************* + * scsi seq + *******************************************/ + if (slp->sl_flags & HW_PDMASTART) + nsp_pdma_end(sc, ti); + + /* normal disconnect */ + if (slp->sl_msgphase != 0 && (irqphs & IRQPHS_LBF) != 0) + return nsp_disconnected(sc, ti); + + /* check unexpected bus free state */ + if (ph == 0) + { + nspmsg(sc, "unexpected bus free", isrc, ph, irqphs); + return nsp_disconnected(sc, ti); + } + + /* check normal scsi phase */ + switch (ph & SCBUSMON_PHMASK) + { + case PHASE_CMD: + if ((ph & SCBUSMON_REQ) == 0) + return 1; + + SCSI_LOW_SETUP_PHASE(ti, PH_CMD); + if (scsi_low_cmd(slp, ti) != 0) + break; + + nsp_cr_write_1(bst, bsh, NSPR_CMDCR, CMDCR_PTCLR); + for (len = 0; len < slp->sl_scp.scp_cmdlen; len ++) + nsp_cr_write_1(bst, bsh, NSPR_CMDDR, + slp->sl_scp.scp_cmd[len]); + + nsp_cr_write_1(bst, bsh, NSPR_CMDCR, CMDCR_PTCLR | CMDCR_EXEC); + break; + + case PHASE_DATAOUT: + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0) + break; + + pp = physio_proc_enter(bp); + nsp_pio_write(sc, ti); + physio_proc_leave(pp); + break; + + case PHASE_DATAIN: + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0) + break; + + pp = physio_proc_enter(bp); + nsp_pio_read(sc, ti); + physio_proc_leave(pp); + break; + + case PHASE_STATUS: + nsp_dataphase_bypass(sc, ti); + if ((ph & SCBUSMON_REQ) == 0) + return 1; + + SCSI_LOW_SETUP_PHASE(ti, PH_STAT); + ti->ti_status = nsp_cr_read_1(bst, bsh, NSPR_DATAACK); + break; + + case PHASE_MSGOUT: + if ((ph & SCBUSMON_REQ) == 0) + goto timerout; + + /* + * XXX: NSP QUIRK + * NSP invoke interrupts only in the case of scsi phase changes, + * therefore we should poll the scsi phase here to catch + * the next "msg out" if exists (no scsi phase changes). + */ + rv = len = 16; + do { + SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); + + len = scsi_low_msgout(slp, ti); + if (nsp_xfer(sc, ti->ti_msgoutstr, len, PHASE_MSGOUT)) + { + scsi_low_assert_msg(slp, ti, + SCSI_LOW_MSG_RESET, 0); + nspmsg(sc, "MSGOUT: xfer short", + isrc, ph, irqphs); + } + + /* catch a next signal */ + rv = nsp_expect_signal(sc, PHASE_MSGOUT, SCBUSMON_REQ); + } + while (rv > 0 && len -- > 0); + break; + + case PHASE_MSGIN: + nsp_dataphase_bypass(sc, ti); + if ((ph & SCBUSMON_REQ) == 0) + goto timerout; + + SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); + + /* + * XXX: NSP QUIRK + * NSP invoke interrupts only in the case of scsi phase changes, + * therefore we should poll the scsi phase here to catch + * the next "msg in" if exists (no scsi phase changes). + */ + rv = len = 16; + do { + /* read a data */ + regv = nsp_cr_read_1(bst, bsh, NSPR_DATA); + + /* assert ack */ + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR); + cr |= SCBUSCR_ACK; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + nsp_negate_signal(sc, SCBUSMON_REQ, "msgin<REQ>"); + + scsi_low_msgin(slp, ti, regv); + + /* deassert ack */ + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR); + cr &= ~SCBUSCR_ACK; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + + /* catch a next signal */ + rv = nsp_expect_signal(sc, PHASE_MSGIN, SCBUSMON_REQ); + } + while (rv > 0 && len -- > 0); + break; + + case PHASE_SEL: + default: + nspmsg(sc, "unknown scsi phase", isrc, ph, irqphs); + break; + } + + return 1; + +timerout: + nsp_start_timer(sc, 1000 / 102); + return 0; +} diff --git a/sys/dev/nsp/nsp_pccard.c b/sys/dev/nsp/nsp_pccard.c new file mode 100644 index 0000000..10e5b1b --- /dev/null +++ b/sys/dev/nsp/nsp_pccard.c @@ -0,0 +1,414 @@ +/* $FreeBSD$ */ +/* $NecBSD: nsp_pisa.c,v 1.4 1999/04/15 01:35:54 kmatsuda Exp $ */ +/* $NetBSD$ */ + +/* + * [Ported for FreeBSD] + * Copyright (c) 2000 + * Noriaki Mitsunaga, Mitsuru Iwasaki and Takanori Watanabe. + * All rights reserved. + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1998 + * NetBSD/pc98 porting staff. 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 <sys/param.h> +#include <sys/systm.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/errno.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <i386/isa/isa_device.h> + +#include <machine/dvcfg.h> + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static struct nsp_softc *nsp_get_softc(int); +extern struct nsp_softc *nspdata[]; +#define DEVPORT_ALLOCSOFTCFUNC nsp_get_softc +#define DEVPORT_SOFTCARRAY nspdata +#endif +#include <sys/device_port.h> + +#include <cam/scsi/scsi_low.h> +#include <cam/scsi/scsi_low_pisa.h> + +#include <dev/nsp/nspreg.h> +#include <dev/nsp/nspvar.h> +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 400001) +#include "nsp.h" +#endif + +#define NSP_HOSTID 7 + +/* pccard support */ + +#include "apm.h" +#if NAPM > 0 +#include <machine/apm_bios.h> +#endif + +#include "card.h" +#if NCARD > 0 +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/select.h> +#include <pccard/cardinfo.h> +#include <pccard/slot.h> + +#define PIO_MODE 1 /* pd_flags */ + +static int nspprobe(DEVPORT_PDEVICE devi); +static int nspattach(DEVPORT_PDEVICE devi); + +static int nsp_card_intr __P((DEVPORT_PDEVICE)); +static void nsp_card_unload __P((DEVPORT_PDEVICE)); +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static int nsp_card_init __P((DEVPORT_PDEVICE)); +#endif + +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 +/* + * Additional code for FreeBSD new-bus PCCard frontend + */ + +static void +nsp_pccard_intr(void * arg) +{ + nspintr(arg); +} + +static void +nsp_release_resource(DEVPORT_PDEVICE dev) +{ + struct nsp_softc *sc = device_get_softc(dev); + + if (sc->nsp_intrhand) { + bus_teardown_intr(dev, sc->irq_res, sc->nsp_intrhand); + } + + if (sc->port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->port_rid, sc->port_res); + } + + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid, sc->irq_res); + } + + if (sc->mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->mem_rid, sc->mem_res); + } +} + +static int +nsp_alloc_resource(DEVPORT_PDEVICE dev) +{ + struct nsp_softc *sc = device_get_softc(dev); + u_long maddr, msize; + int error; + + sc->port_rid = 0; + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, + 0, ~0, NSP_IOSIZE,RF_ACTIVE); + if (sc->port_res == NULL) { + nsp_release_resource(dev); + return(ENOMEM); + } + + sc->irq_rid = 0; + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->irq_res == NULL) { + nsp_release_resource(dev); + return(ENOMEM); + } + + /* no need to allocate memory if PIO mode */ + if ((DEVPORT_PDEVFLAGS(dev) & PIO_MODE) != 0) { + return(0); + } + + error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &maddr, &msize); + if (error) { + return(0); /* XXX */ + } + + /* no need to allocate memory if not configured */ + if (maddr == 0 || msize == 0) { + return(0); + } + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, + 0, ~0, msize, RF_ACTIVE); + if (sc->mem_res == NULL) { + nsp_release_resource(dev); + return(ENOMEM); + } + + return(0); +} + +static int +nsp_pccard_probe(DEVPORT_PDEVICE dev) +{ + struct nsp_softc *sc = device_get_softc(dev); + int error; + + bzero(sc, sizeof(struct nsp_softc)); + + error = nsp_alloc_resource(dev); + if (error) { + return(error); + } + + if (nspprobe(dev) == 0) { + nsp_release_resource(dev); + return(ENXIO); + } + + nsp_release_resource(dev); + + return(0); +} + +static int +nsp_pccard_attach(DEVPORT_PDEVICE dev) +{ + struct nsp_softc *sc = device_get_softc(dev); + int error; + + error = nsp_alloc_resource(dev); + if (error) { + return(error); + } + + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CAM, + nsp_pccard_intr, (void *)sc, &sc->nsp_intrhand); + if (error) { + nsp_release_resource(dev); + return(error); + } + + if (nspattach(dev) == 0) { + nsp_release_resource(dev); + return(ENXIO); + } + + return(0); +} + +static void +nsp_pccard_detach(DEVPORT_PDEVICE dev) +{ + nsp_card_unload(dev); + nsp_release_resource(dev); +} + +static device_method_t nsp_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nsp_pccard_probe), + DEVMETHOD(device_attach, nsp_pccard_attach), + DEVMETHOD(device_detach, nsp_pccard_detach), + + { 0, 0 } +}; + +static driver_t nsp_pccard_driver = { + "nsp", + nsp_pccard_methods, + sizeof(struct nsp_softc), +}; + +static devclass_t nsp_devclass; + +DRIVER_MODULE(nsp, pccard, nsp_pccard_driver, nsp_devclass, 0, 0); + +#else + +PCCARD_MODULE(nsp, nsp_card_init,nsp_card_unload, nsp_card_intr,0, cam_imask); + +#endif + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static struct nsp_softc * +nsp_get_softc(int unit) +{ + struct nsp_softc *sc; + + if (unit >= NNSP) { + return(NULL); + } + + if (nspdata[unit] == NULL) { + sc = malloc(sizeof(struct nsp_softc), M_TEMP,M_NOWAIT); + if (sc == NULL) { + printf("nsp_get_softc: cannot malloc!\n"); + return(NULL); + } + nspdata[unit] = sc; + } else { + sc = nspdata[unit]; + } + + return(sc); +} + +static int +nsp_card_init(DEVPORT_PDEVICE devi) +{ + int unit = DEVPORT_PDEVUNIT(devi); + + if (NNSP <= unit) + return (ENODEV); + + if (nspprobe(devi) == 0) + return (ENXIO); + + if (nspattach(devi) == 0) + return (ENXIO); + + return (0); +} +#endif + +static void +nsp_card_unload(DEVPORT_PDEVICE devi) +{ + struct nsp_softc *sc = DEVPORT_PDEVGET_SOFTC(devi); + + printf("%s: unload\n",sc->sc_sclow.sl_xname); + scsi_low_deactivate((struct scsi_low_softc *)sc); + scsi_low_dettach(&sc->sc_sclow); +} + +static int +nsp_card_intr(DEVPORT_PDEVICE devi) +{ + nspintr(DEVPORT_PDEVGET_SOFTC(devi)); + return 1; +} + +static int +nspprobe(DEVPORT_PDEVICE devi) +{ + int rv; +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + struct nsp_softc *sc = device_get_softc(devi); + + rv = nspprobesubr(rman_get_bustag(sc->port_res), + rman_get_bushandle(sc->port_res), + DEVPORT_PDEVFLAGS(devi)); +#else + rv = nspprobesubr(I386_BUS_SPACE_IO, + DEVPORT_PDEVIOBASE(devi), DEVPORT_PDEVFLAGS(devi)); +#endif + + return rv; +} + +static int +nspattach(DEVPORT_PDEVICE devi) +{ +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + int unit = DEVPORT_PDEVUNIT(devi); +#endif + struct nsp_softc *sc; + struct scsi_low_softc *slp; + u_int32_t flags = DEVPORT_PDEVFLAGS(devi); + u_int iobase = DEVPORT_PDEVIOBASE(devi); + char dvname[16]; + + strcpy(dvname,"nsp"); + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + if (unit >= NNSP) + { + printf("%s: unit number too high\n",dvname); + return(0); + } +#endif + + if (iobase == 0) + { + printf("%s: no ioaddr is given\n", dvname); + return (0); + } + + sc = DEVPORT_PDEVALLOC_SOFTC(devi); + if (sc == NULL) { + return (0); + } + + slp = &sc->sc_sclow; +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + slp->sl_dev = devi; + sc->sc_iot = rman_get_bustag(sc->port_res); + sc->sc_ioh = rman_get_bushandle(sc->port_res); +#else + bzero(sc, sizeof(struct nsp_softc)); + strcpy(slp->sl_dev.dv_xname, dvname); + slp->sl_dev.dv_unit = unit; + sc->sc_iot = I386_BUS_SPACE_IO; + sc->sc_ioh = iobase; +#endif + + if((flags & PIO_MODE) == 0) { +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + sc->sc_memt = rman_get_bustag(sc->mem_res); + sc->sc_memh = rman_get_bushandle(sc->mem_res); +#else + sc->sc_memt = I386_BUS_SPACE_MEM; + sc->sc_memh = (bus_space_handle_t)DEVPORT_PDEVMADDR(devi); +#endif + } else { + sc->sc_memh = 0; + } + /* slp->sl_irq = devi->pd_irq; */ + sc->sc_iclkdiv = CLKDIVR_20M; + sc->sc_clkdiv = CLKDIVR_40M; + + slp->sl_hostid = NSP_HOSTID; + slp->sl_cfgflags = flags; + + nspattachsubr(sc); + + sc->sc_ih = nspintr; + + return(NSP_IOSIZE); +} +#endif /* NCARD>0 */ diff --git a/sys/dev/nsp/nspreg.h b/sys/dev/nsp/nspreg.h new file mode 100644 index 0000000..9912a13 --- /dev/null +++ b/sys/dev/nsp/nspreg.h @@ -0,0 +1,201 @@ +/* $FreeBSD$ */ +/* $NecBSD: nspreg.h,v 1.4 1999/04/15 01:35:55 kmatsuda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1998 + * NetBSD/pc98 porting staff. 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. + */ + +#ifndef _NSPREG_H_ +#define _NSPREG_H_ + +/* base registers */ +#define nsp_irqcr 0 +#define IRQCR_RESCL 0x01 +#define IRQCR_PHCL 0x02 +#define IRQCR_TIMERCL 0x04 +#define IRQCR_FIFOCL 0x08 +#define IRQCR_SCSIIDIS 0x10 +#define IRQCR_EXTIDIS 0x20 +#define IRQCR_TIMERIDIS 0x40 +#define IRQCR_FIFOIDIS 0x80 +#define IRQCR_ALLMASK 0xff +#define IRQCR_IRQDIS 0xf0 + +#define nsp_irqsr 0 +#define IRQSR_SCSI 0x01 +#define IRQSR_EXT 0x02 +#define IRQSR_TIMER 0x04 +#define IRQSR_FIFO 0x08 +#define IRQSR_MASK 0x0f + +#define nsp_ifselr 1 +#define IFSELR_IFSEL 0x01 +#define IFSELR_REGSEL 0x04 + +#define nsp_fifosr 1 +#define FIFOSR_CHIPREVM 0x0f +#define FIFOSR_CHIPIDM 0x70 +#define FIFOSR_FULLEMP 0x80 + +#define nsp_idxr 2 +#define nsp_datar 3 +#define nsp_fifodr 4 + +/* indexed registers */ +#define NSPR_EXTBUSC 0x10 + +#define NSPR_CLKDIVR 0x11 +#define CLKDIVR_40M 0x02 +#define CLKDIVR_20M 0x01 + +#define NSPR_TERMPWRC 0x13 +#define TERMPWRC_POWON 0x01 + +#define NSPR_SCIENR 0x15 +#define SCIENR_SCCHG 0x01 +#define SCIENR_RESEL 0x10 +#define SCIENR_FIFO 0x20 +#define SCIENR_RST 0x40 + +#define NSPR_IRQPHS 0x16 +#define IRQPHS_LMSG 0x01 +#define IRQPHS_LIO 0x02 +#define IRQPHS_LCD 0x04 +#define IRQPHS_LBF 0x08 +#define IRQPHS_PCHG 0x10 +#define IRQPHS_RSEL 0x20 +#define IRQPHS_FIFO 0x40 +#define IRQPHS_RST 0x80 + +#define NSPR_TIMERCNT 0x17 + +#define NSPR_SCBUSCR 0x18 +#define SCBUSCR_SEL 0x01 +#define SCBUSCR_RST 0x02 +#define SCBUSCR_DOUT 0x04 +#define SCBUSCR_ATN 0x08 +#define SCBUSCR_ACK 0x10 +#define SCBUSCR_BSY 0x20 +#define SCBUSCR_ADIR 0x40 +#define SCBUSCR_ACKEN 0x80 + +#define NSPR_SCBUSMON 0x19 +#define SCBUSMON_MSG 0x01 +#define SCBUSMON_IO 0x02 +#define SCBUSMON_CD 0x04 +#define SCBUSMON_BSY 0x08 +#define SCBUSMON_ACK 0x10 +#define SCBUSMON_REQ 0x20 +#define SCBUSMON_SEL 0x40 +#define SCBUSMON_ATN 0x80 + +#define NSPR_SETARBIT 0x1A + +#define NSPR_ARBITS 0x1A +#define ARBITS_EXEC 0x01 +#define ARBITS_CLR 0x02 +#define ARBITS_WIN 0x02 +#define ARBITS_FAIL 0x04 +#define ARBITS_RESEL 0x08 + +#define NSPR_PARITYR 0x1B /* (W/R) */ + +#define NSPR_CMDCR 0x1C /* (W) */ +#define CMDCR_PTCLR 0x01 +#define CMDCR_EXEC 0x02 + +#define NSPR_RESELR 0x1C /* (R) */ +#define NSPR_CMDDR 0x1D /* (W/R) */ + +#define NSPR_PTCLRR 0x1E /* (W) */ +#define PTCLRR_PT 0x01 +#define PTCLRR_ACK 0x02 +#define PTCLRR_REQ 0x04 +#define PTCLRR_HOST 0x08 +#define PTCLRR_RSS 0x30 + +#define NSPR_XFERCR 0x1E /* (R) */ + +#define NSPR_XFERMR 0x20 +#define XFERMR_MEM8 0x01 +#define XFERMR_MEM32 0x02 +#define XFERMR_ADR24 0x04 +#define XFERMR_ADR32 0x08 +#define XFERMR_IO8 0x10 +#define XFERMR_IO32 0x20 +#define XFERMR_XEN 0x40 +#define XFERMR_FIFOEN 0x80 + +#define NSPR_SYNCR 0x21 +#define SYNCR_OFFM 0x0f +#define SYNCR_PERM 0xf0 +#define SYNCR_PERS 4 + +#define NSPR_DATA 0x22 +#define NSPR_DATAACK 0x23 +#define NSPR_OCR 0x26 +#define OCR_ROMEN 0x01 +#define OCR_TERMPWROUT 0x02 +#define OCR_TERMPWRS 0x04 + +#define NSPR_ACKWIDTH 0x27 + +/* SCBUSMON phase defs */ +#define SCBUSMON_FREE 0 +#define SCBUSMON_CMD \ + (SCBUSMON_BSY | SCBUSMON_CD | SCBUSMON_REQ) +#define SCBUSMON_MSGIN \ + (SCBUSMON_BSY | SCBUSMON_MSG | SCBUSMON_IO | SCBUSMON_CD | SCBUSMON_REQ) +#define SCBUSMON_MSGOUT \ + (SCBUSMON_BSY | SCBUSMON_MSG | SCBUSMON_CD | SCBUSMON_REQ) +#define SCBUSMON_DATAIN \ + (SCBUSMON_BSY | SCBUSMON_IO | SCBUSMON_REQ) +#define SCBUSMON_DATAOUT \ + (SCBUSMON_BSY | SCBUSMON_REQ) +#define SCBUSMON_STATUS \ + (SCBUSMON_BSY | SCBUSMON_IO | SCBUSMON_CD | SCBUSMON_REQ) +#define SCBUSMON_RESELECT \ + (SCBUSMON_SEL | SCBUSMON_IO) +#define SCBUSMON_PHMASK \ + (SCBUSMON_SEL | SCBUSMON_CD | SCBUSMON_MSG | SCBUSMON_IO) + +/* SCSI phase */ +#define PHASE_CMD (SCBUSMON_CMD & SCBUSMON_PHMASK) +#define PHASE_DATAIN (SCBUSMON_DATAIN & SCBUSMON_PHMASK) +#define PHASE_DATAOUT (SCBUSMON_DATAOUT & SCBUSMON_PHMASK) +#define PHASE_STATUS (SCBUSMON_STATUS & SCBUSMON_PHMASK) +#define PHASE_MSGIN (SCBUSMON_MSGIN & SCBUSMON_PHMASK) +#define PHASE_MSGOUT (SCBUSMON_MSGOUT & SCBUSMON_PHMASK) +#define PHASE_SEL (SCBUSMON_SEL | SCBUSMON_IO) + +/* Size */ +#define NSP_MEMSIZE NBPG +#define NSP_IOSIZE 16 +#define NSP_BUFFER_SIZE 512 +#endif /* !_NSPREG_H_ */ diff --git a/sys/dev/nsp/nspvar.h b/sys/dev/nsp/nspvar.h new file mode 100644 index 0000000..078f1b9 --- /dev/null +++ b/sys/dev/nsp/nspvar.h @@ -0,0 +1,102 @@ +/* $FreeBSD$ */ +/* $NecBSD: nspvar.h,v 1.7 1999/04/15 01:35:55 kmatsuda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1998 + * NetBSD/pc98 porting staff. 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. + */ + +#ifndef _NSPVAR_H_ +#define _NSPVAR_H_ + +/***************************************************************** + * Host adapter structure + *****************************************************************/ +struct nsp_softc { + struct scsi_low_softc sc_sclow; /* generic data */ + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_space_tag_t sc_memt; + bus_space_handle_t sc_memh; + + void *sc_ih; + + int sc_seltout; /* selection timeout counter */ + int sc_timer; /* timer start */ + + int sc_xmode; +#define NSP_HIGH_SMIT 2 /* write address data mode */ +#define NSP_MID_SMIT 1 /* mem access */ +#define NSP_PIO 0 /* io access */ + + u_int sc_idbit; /* host id bit pattern */ + u_int sc_mask; /* bus width mask */ + u_int sc_cnt; /* fifo R/W count (host) */ + u_int8_t sc_iclkdiv; /* scsi chip clock divisor */ + u_int8_t sc_clkdiv; /* asic chip clock divisor */ + u_int8_t sc_xfermr; /* fifo control reg */ + u_int8_t sc_icr; /* interrupt control reg */ + + u_int8_t sc_busc; /* busc registers */ + u_long sc_ringp; /* data buffer ring pointer */ +#if defined (__FreeBSD__) && __FreeBSD_version >= 400001 + int port_rid; + int irq_rid; + int mem_rid; + struct resource *port_res; + struct resource *irq_res; + struct resource *mem_res; + void *nsp_intrhand; +#endif +}; + +/***************************************************************** + * Lun information + *****************************************************************/ +struct nsp_lun_info { + struct lun_info nli_li; /* generic lun info */ + + u_int8_t nli_reg_syncr; /* sync registers per devices */ + u_int8_t nli_reg_ackwidth; /* ackwidth per devices */ +}; + +/***************************************************************** + * Proto + *****************************************************************/ +int nspprobesubr __P((bus_space_tag_t, bus_space_handle_t, u_int)); +void nspattachsubr __P((struct nsp_softc *)); +int nspprint __P((void *, const char *)); +int nspintr __P((void *)); + +#if defined(i386) +#define SOFT_INTR_REQUIRED(slp) (softintr((slp)->sl_irq)) +#else /* !i386 */ +#define SOFT_INTR_REQUIRED(slp) +#endif /* !i386 */ +#endif /* !_NSPVAR_H_ */ diff --git a/sys/dev/stg/tmc18c30.c b/sys/dev/stg/tmc18c30.c new file mode 100644 index 0000000..dbfe567 --- /dev/null +++ b/sys/dev/stg/tmc18c30.c @@ -0,0 +1,1218 @@ +/* $FreeBSD$ */ +/* $NecBSD: tmc18c30.c,v 1.28 1999/07/23 21:00:06 honda Exp $ */ +/* $NetBSD$ */ + +#define STG_DEBUG +#define STG_STATICS + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998, 1999 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998, 1999 + * Naofumi HONDA. All rights reserved. + * Copyright (c) 1996, 1997, 1998, 1999 + * Kouichi Matsuda. 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/tmc18c30reg.h> +#include <i386/Cbus/dev/tmc18c30var.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/stg/tmc18c30reg.h> +#include <dev/stg/tmc18c30var.h> + +#if __FreeBSD_version < 400001 +#include "stg.h" +struct stg_softc *stgdata[NSTG]; +#endif +#endif /* __FreeBSD__ */ + +/*************************************************** + * USER SETTINGS + ***************************************************/ +/* DEVICE CONFIGURATION FLAGS (MINOR) + * + * 0x01 DISCONECT OFF + * 0x02 PARITY LINE OFF + * 0x04 IDENTIFY MSG OFF ( = single lun) + * 0x08 SYNC TRANSFER OFF + */ +/* #define STG_SYNC_SUPPORT */ /* NOT YET but easy */ + +/* For the 512 fifo type: change below */ +#define TMC18C30_FIFOSZ 0x800 +#define TMC18C30_FCB 1 + +#define TMC18C50_FIFOSZ 0x2000 +#define TMC18C50_FCB 2 + +/*************************************************** + * PARAMS + ***************************************************/ +#define STG_NTARGETS 8 +#define STG_NLUNS 8 + +/*************************************************** + * DEBUG + ***************************************************/ +#ifndef DDB +#define Debugger() panic("should call debugger here (tmc18c30.c)") +#else /* ! DDB */ +#ifdef __FreeBSD__ +#define Debugger() Debugger("stg") +#endif /* __FreeBSD__ */ +#endif + +#ifdef STG_DEBUG +int stg_debug; +#endif /* STG_DEBUG */ + +#ifdef STG_STATICS +struct stg_statics { + int disconnect; + int reselect; + int sprious_arbit_fail_0; + int sprious_arbit_fail_1; + int sprious_arbit_fail_2; +} stg_statics[STG_NTARGETS]; +#endif /* STG_STATICS */ + +/*************************************************** + * ISA DEVICE STRUCTURE + ***************************************************/ +extern struct cfdriver stg_cd; + +/************************************************************** + * DECLARE + **************************************************************/ +/* static */ +static void stg_pio_read __P((struct stg_softc *, struct targ_info *)); +static void stg_pio_write __P((struct stg_softc *, struct targ_info *)); +static int stg_xfer __P((struct stg_softc *, u_int8_t *, int, int)); +static int stg_msg __P((struct stg_softc *, struct targ_info *, u_int)); +static int stg_reselected __P((struct stg_softc *)); +static __inline int stg_disconnected __P((struct stg_softc *, struct targ_info *)); +static __inline void stg_pdma_end __P((struct stg_softc *, struct targ_info *)); +static int stghw_select_targ_wait __P((struct stg_softc *, int)); +static int stghw_check __P((struct stg_softc *)); +static void stghw_init __P((struct stg_softc *)); +static int stg_negate_signal __P((struct stg_softc *, u_int8_t, u_char *)); +static int stg_expect_signal __P((struct stg_softc *, u_int8_t, u_int8_t)); +static int stg_world_start __P((struct stg_softc *, int)); +static int stghw_start_selection __P((struct stg_softc *sc, struct slccb *)); +static void stghw_bus_reset __P((struct stg_softc *)); +static void stghw_attention __P((struct stg_softc *)); +static int stg_nexus __P((struct stg_softc *, struct targ_info *)); +static int stg_lun_init __P((struct stg_softc *, struct targ_info *, struct lun_info *)); +static __inline void stghw_bcr_write_1 __P((struct stg_softc *, u_int8_t)); +static void settimeout __P((void *)); + +struct scsi_low_funcs stgfuncs = { + SC_LOW_INIT_T stg_world_start, + SC_LOW_BUSRST_T stghw_bus_reset, + SC_LOW_LUN_INIT_T stg_lun_init, + + SC_LOW_SELECT_T stghw_start_selection, + SC_LOW_NEXUS_T stg_nexus, + + SC_LOW_ATTEN_T stghw_attention, + SC_LOW_MSG_T stg_msg, + + SC_LOW_POLL_T stgintr, + + NULL, +}; + +/**************************************************** + * hwfuncs + ****************************************************/ +static int +stghw_check(sc) + struct stg_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; + u_int16_t lsb, msb; + + sc->sc_chip = TMCCHIP_UNK; + sc->sc_fsz = TMC18C50_FIFOSZ; + sc->sc_fcb = TMC18C50_FCB; + sc->sc_fcsp = 0; + + sc->sc_fcRinit = FCTL_INTEN; + sc->sc_fcWinit = FCTL_PARENB | FCTL_INTEN; + + if (slp->sl_cfgflags & CFG_NOATTEN) + sc->sc_imsg = 0; + else + sc->sc_imsg = BCTL_ATN; + sc->sc_busc = BCTL_BUSEN; + + lsb = bus_space_read_1(iot, ioh, tmc_idlsb); + msb = bus_space_read_1(iot, ioh, tmc_idmsb); + switch (msb << 8 | lsb) + { + case 0x6127: + /* TMCCHIP_1800 not supported. (it's my policy) */ + sc->sc_chip = TMCCHIP_1800; + return EINVAL; + + case 0x60e9: + sc->sc_chip = TMCCHIP_18C50; + sc->sc_fcsp |= FCTL_CLRINT; + if (bus_space_read_1(iot, ioh, tmc_cfg2) & 0x02) + { + sc->sc_chip = TMCCHIP_18C30; + sc->sc_fsz = TMC18C30_FIFOSZ; + sc->sc_fcb = TMC18C30_FCB; + } + break; + + default: + return ENODEV; + } + + sc->sc_icinit = ICTL_ALLINT | sc->sc_fcb; + return 0; +} + +static void +stghw_init(sc) + struct stg_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + bus_space_write_1(iot, ioh, tmc_ictl, 0); + stghw_bcr_write_1(sc, BCTL_BUSFREE); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcsp | sc->sc_fcRinit | + FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + bus_space_write_1(iot, ioh, tmc_ictl, sc->sc_icinit); + + bus_space_write_1(iot, ioh, tmc_ssctl, 0); +} + +static int +stg_lun_init(sc, ti, li) + struct stg_softc *sc; + struct targ_info *ti; + struct lun_info *li; +{ + struct stg_lun_info *sli = (void *) li; + + li->li_maxsynch.period = 0; + li->li_maxsynch.offset = 8; + sli->sli_reg_synch = 0; + return 0; +} + +/**************************************************** + * scsi low interface + ****************************************************/ +static __inline void +stghw_bcr_write_1(sc, bcv) + struct stg_softc *sc; + u_int8_t bcv; +{ + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, tmc_bctl, bcv); + sc->sc_busimg = bcv; +} + +static void +stghw_attention(sc) + struct stg_softc *sc; +{ + + sc->sc_busc |= BCTL_ATN; + sc->sc_busimg |= BCTL_ATN; + bus_space_write_1(sc->sc_iot, sc->sc_ioh, tmc_bctl, sc->sc_busimg); +} + +static void +stghw_bus_reset(sc) + struct stg_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + bus_space_write_1(iot, ioh, tmc_ictl, 0); + bus_space_write_1(iot, ioh, tmc_fctl, 0); + stghw_bcr_write_1(sc, BCTL_RST); + delay(100000); + stghw_bcr_write_1(sc, BCTL_BUSFREE); +} + +static int +stghw_start_selection(sc, cb) + struct stg_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; + struct lun_info *li = ti->ti_li; + register u_int8_t stat; + int s; + + if (li->li_flags & SCSI_LOW_NOPARITY) + sc->sc_fcRinit &= ~FCTL_PARENB; + else + sc->sc_fcRinit |= FCTL_PARENB; + + stghw_bcr_write_1(sc, BCTL_BUSFREE); + + s = splhigh(); + if (slp->sl_disc > 0) + { + stat = bus_space_read_1(iot, ioh, tmc_bstat); + if (stat & (BSTAT_BSY | BSTAT_SEL | BSTAT_IO)) + { + splx(s); + return SCSI_LOW_START_FAIL; + } + } + + bus_space_write_1(iot, ioh, tmc_scsiid, sc->sc_idbit); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit | FCTL_ARBIT); + splx(s); + + SCSI_LOW_SETUP_PHASE(ti, PH_ARBSTART); + return SCSI_LOW_START_OK; +} + +static int +stg_world_start(sc, fdone) + struct stg_softc *sc; + int fdone; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + int error; + intrmask_t s; + + if ((error = stghw_check(sc)) != 0) + return error; + + s = splcam(); + stghw_init(sc); + scsi_low_bus_reset(slp); + stghw_init(sc); + splx(s); + + SOFT_INTR_REQUIRED(slp); + return 0; +} + +static int +stg_msg(sc, ti, msg) + struct stg_softc *sc; + struct targ_info *ti; + u_int msg; +{ + struct lun_info *li = ti->ti_li; + struct stg_lun_info *sli = (void *) li; + u_int period, offset; + + if (msg != SCSI_LOW_MSG_SYNCH) + return EINVAL; + + period = li->li_maxsynch.period; + offset = li->li_maxsynch.offset; + period = period << 2; + if (period >= 200) + { + sli->sli_reg_synch = (period - 200) / 50; + if (period % 50) + sli->sli_reg_synch ++; + sli->sli_reg_synch |= SSCTL_SYNCHEN; + } + else if (period >= 100) + { + sli->sli_reg_synch = (period - 100) / 50; + if (period % 50) + sli->sli_reg_synch ++; + sli->sli_reg_synch |= SSCTL_SYNCHEN | SSCTL_FSYNCHEN; + } + return 0; +} + +/************************************************************** + * General probe attach + **************************************************************/ +int +stgprobesubr(iot, ioh, dvcfg) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_int dvcfg; +{ + u_int16_t lsb, msb; + + lsb = bus_space_read_1(iot, ioh, tmc_idlsb); + msb = bus_space_read_1(iot, ioh, tmc_idmsb); + switch (msb << 8 | lsb) + { + default: + return 0; + case 0x6127: + /* not support! */ + return 0; + case 0x60e9: + return 1; + } + return 0; +} + +int +stgprint(aux, name) + void *aux; + const char *name; +{ + + if (name != NULL) + printf("%s: scsibus ", name); + return UNCONF; +} + +void +stgattachsubr(sc) + struct stg_softc *sc; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + + printf("\n"); + + sc->sc_idbit = (1 << slp->sl_hostid); + slp->sl_funcs = &stgfuncs; + + slp->sl_cfgflags |= CFG_ASYNC; /* XXX */ + + if (stghw_check(sc) != 0) + { + printf("stg: hardware missing\n"); + return; + } + + (void) scsi_low_attach(slp, 2, STG_NTARGETS, STG_NLUNS, + sizeof(struct stg_lun_info)); +} + +/************************************************************** + * PDMA functions + **************************************************************/ +static __inline void +stg_pdma_end(sc, ti) + struct stg_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; + struct slccb *cb = ti->ti_nexus; + u_int len, tres; + + slp->sl_flags &= ~HW_PDMASTART; + + if (ti->ti_phase == PH_DATA) + { + len = bus_space_read_2(iot, ioh, tmc_fdcnt); + if (slp->sl_scp.scp_direction == SCSI_LOW_WRITE) + { + if (len != 0) + { + tres = len + slp->sl_scp.scp_datalen; + if (tres <= (u_int) cb->ccb_scp.scp_datalen) + { + slp->sl_scp.scp_data -= len; + slp->sl_scp.scp_datalen = tres; + } + else + { + slp->sl_error |= PDMAERR; + printf("%s len %x >= datalen %x\n", + slp->sl_xname, + len, slp->sl_scp.scp_datalen); + } + } + } + else if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + { + if (len != 0) + { + slp->sl_error |= PDMAERR; + printf("%s: len %x left in fifo\n", + slp->sl_xname, len); + } + } + } + else + { + + printf("%s data phase miss\n", slp->sl_xname); + slp->sl_error |= PDMAERR; + } + + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); +} + +static void +stg_pio_read(sc, ti) + struct stg_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; + struct sc_p *sp = &slp->sl_scp; + int s; + int tout = 0; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + u_int res; + u_int8_t stat; + + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit | FCTL_FIFOEN); + + slp->sl_flags |= HW_PDMASTART; +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, 2 * hz); +#else + timeout(settimeout, &tout, 2 * hz); +#endif + while (sp->scp_datalen > 0 && tout == 0) + { + res = bus_space_read_2(iot, ioh, tmc_fdcnt); + if (res == 0) + { + stat = bus_space_read_1(iot, ioh, tmc_bstat); + if ((stat & BSTAT_PHMASK) == BSTAT_IO) + continue; + break; /* phase mismatch */ + } + + /* XXX */ + if (res > sp->scp_datalen) + { + slp->sl_error |= PDMAERR; + break; + } + + sp->scp_datalen -= res; + if (res & 1) + { + *sp->scp_data = bus_space_read_1(iot, ioh, tmc_rfifo); + sp->scp_data ++; + res --; + } + + bus_space_read_multi_2(iot, ioh, tmc_rfifo, + (u_int16_t *) sp->scp_data, res >> 1); + sp->scp_data += res; + } + + 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); + } +} + +#define WFIFO_CRIT 0x100 + +static void +stg_pio_write(sc, ti) + struct stg_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; + struct sc_p *sp = &slp->sl_scp; + u_int res; + int s; + int tout = 0; + register u_int8_t stat; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + + stat = sc->sc_fcWinit | FCTL_FIFOEN | FCTL_FIFOW; + bus_space_write_1(iot, ioh, tmc_fctl, stat | FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, stat); + + slp->sl_flags |= HW_PDMASTART; +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, 2 * hz); +#else + timeout(settimeout, &tout, 2 * hz); +#endif + while (sp->scp_datalen > 0 && tout == 0) + { + stat = bus_space_read_1(iot, ioh, tmc_bstat); + if ((stat & BSTAT_PHMASK) != 0) + break; + + if (bus_space_read_2(iot, ioh, tmc_fdcnt) >= WFIFO_CRIT) + continue; + + res = (sp->scp_datalen > WFIFO_CRIT) ? + WFIFO_CRIT : sp->scp_datalen; + sp->scp_datalen -= res; + if ((res & 0x1) != 0) + { + bus_space_write_1(iot, ioh, tmc_wfifo, *sp->scp_data); + sp->scp_data ++; + res --; + } + + bus_space_write_multi_2(iot, ioh, tmc_wfifo, + (u_int16_t *) sp->scp_data, res >> 1); + sp->scp_data += res; + } + + 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 int +stg_negate_signal(sc, mask, s) + struct stg_softc *sc; + u_int8_t mask; + u_char *s; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int s; + int tout = 0; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + u_int8_t regv; + +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, 2 * hz); +#else + timeout(settimeout, &tout, 2 * hz); +#endif + do + { + regv = bus_space_read_1(bst, bsh, tmc_bstat); + if (regv == 0xff) { + s = splhigh(); + if (tout == 0) { +#ifdef __FreeBSD__ + untimeout(settimeout, &tout, ch); +#else + untimeout(settimeout, &tout); +#endif + } + splx(s); + return EIO; + } + } + while ((regv & mask) != 0 && tout == 0); + + s = splhigh(); + if (tout == 0) { +#ifdef __FreeBSD__ + untimeout(settimeout, &tout, ch); +#else + untimeout(settimeout, &tout); +#endif + splx(s); + } else { + splx(s); + printf("%s: %s singal off timeout \n", slp->sl_xname, s); + return EIO; + } + return 0; +} + +static void +settimeout(arg) + void *arg; +{ + int *tout = arg; + + *tout = 1; +} + +static int +stg_expect_signal(sc, phase, mask) + struct stg_softc *sc; + u_int8_t phase, mask; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int rv = -1; + int s; + int tout = 0; +#ifdef __FreeBSD__ + struct callout_handle ch; +#endif + u_int8_t ph; + + phase &= BSTAT_PHMASK; +#ifdef __FreeBSD__ + ch = timeout(settimeout, &tout, hz/2); +#else + timeout(settimeout, &tout, hz/2); +#endif + do + { + ph = bus_space_read_1(bst, bsh, tmc_bstat); + if (ph == 0xff) { + rv = -1; + break; + } + if ((ph & BSTAT_PHMASK) != phase) { + rv = 0; + break; + } + if ((ph & mask) != 0) { + rv = 1; + break; + } + } + while (tout == 0); + + s = splhigh(); + if (tout == 0) { +#ifdef __FreeBSD__ + untimeout(settimeout, &tout, ch); +#else + untimeout(settimeout, &tout); +#endif + splx(s); + } else { + splx(s); + printf("%s: stg_expect_signal timeout\n", slp->sl_xname); + rv = -1; + } + return rv; +} + +static int +stg_xfer(sc, buf, len, phase) + struct stg_softc *sc; + u_int8_t *buf; + int len; + int phase; +{ + 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 rv, ptr, atn; + + atn = (scsi_low_is_msgout_continue(slp->sl_nexus) != 0); + if (phase & BSTAT_IO) + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + else + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcWinit); + + for (ptr = 0; len > 0; len --) + { + rv = stg_expect_signal(sc, phase, BSTAT_REQ); + if (rv <= 0) + goto bad; + + if (len == 1 && atn == 0) + { + sc->sc_busc &= ~BCTL_ATN; + stghw_bcr_write_1(sc, sc->sc_busc); + } + + if (phase & BSTAT_IO) + { + buf[ptr ++] = bus_space_read_1(iot, ioh, tmc_rdata); + } + else + { + bus_space_write_1(iot, ioh, tmc_wdata, buf[ptr ++]); + } + stg_negate_signal(sc, BSTAT_ACK, "xfer<ACK>"); + } + +bad: + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + return len; +} + +/************************************************************** + * disconnect & reselect (HW low) + **************************************************************/ +static int +stg_reselected(sc) + struct stg_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 (slp->sl_selid != NULL) + { + /* XXX: + * Selection vs Reselection conflicts. + */ +#ifdef STG_STATICS + stg_statics[slp->sl_selid->ti_id].sprious_arbit_fail_0 ++; +#endif /* STG_STATICS */ + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + stghw_bcr_write_1(sc, BCTL_BUSFREE); + } + + /* XXX: + * We should ack the reselection as soon as possible, + * becuase the target would abort the current reselection seq + * due to reselection timeout. + */ + sid = (u_int) bus_space_read_1(iot, ioh, tmc_scsiid); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcsp | + sc->sc_fcRinit | FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + stghw_bcr_write_1(sc, sc->sc_busc | BCTL_BSY); + + sid &= ~sc->sc_idbit; + sid = ffs(sid) - 1; + if ((ti = scsi_low_reselected(slp, sid)) == NULL) + return EJUSTRETURN; + +#ifdef STG_STATICS + stg_statics[sid].reselect ++; +#endif /* STG_STATICS */ + return EJUSTRETURN; +} + +static __inline int +stg_disconnected(sc, ti) + struct stg_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; + + /* clear bus status & fifo */ + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit | FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + stghw_bcr_write_1(sc, BCTL_BUSFREE); + sc->sc_fcRinit &= ~FCTL_PARENB; + sc->sc_busc &= ~BCTL_ATN; + +#ifdef STG_STATICS + if (slp->sl_msgphase == MSGPH_DISC) + stg_statics[ti->ti_id].disconnect ++; +#endif /* STG_STATICS */ + scsi_low_disconnected(slp, ti); + return 1; +} + +/************************************************************** + * SEQUENCER + **************************************************************/ +static int +stg_nexus(sc, ti) + struct stg_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 stg_lun_info *sli = (void *) ti->ti_li; + + if (li->li_flags & SCSI_LOW_NOPARITY) + sc->sc_fcRinit &= ~FCTL_PARENB; + else + sc->sc_fcRinit |= FCTL_PARENB; + + bus_space_write_1(iot, ioh, tmc_ssctl, sli->sli_reg_synch); + return 0; +} + +static int +stghw_select_targ_wait(sc, id) + struct stg_softc *sc; + int id; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int wc, error = EIO; + + bus_space_write_1(iot, ioh, tmc_scsiid, sc->sc_idbit | (1 << id)); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcWinit & (~FCTL_INTEN)); + stghw_bcr_write_1(sc, sc->sc_busc | sc->sc_imsg | BCTL_SEL); + + for (wc = 50000; wc; wc--) + { + if (bus_space_read_1(iot, ioh, tmc_bstat) & BSTAT_BSY) + { + error = 0; + break; + } + + delay(1); + } + + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit | FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + return error; +} + +int +stgintr(arg) + void *arg; +{ + struct stg_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; + u_int8_t status, astatus, regv; + + /******************************************* + * interrupt check + *******************************************/ + if (slp->sl_flags & HW_INACTIVE) + return 0; + + astatus = bus_space_read_1(iot, ioh, tmc_astat); + status = bus_space_read_1(iot, ioh, tmc_bstat); + + if ((astatus & ASTAT_STATMASK) == 0) + return 0; + + if (astatus & ASTAT_SCSIRST) + { + bus_space_write_1(iot, ioh, tmc_fctl, + sc->sc_fcRinit | FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + + scsi_low_restart(slp, SCSI_LOW_RESTART_SOFT, + "bus reset (power off?)"); + return 1; + } + + /******************************************* + * debug section + *******************************************/ +#ifdef STG_DEBUG + if (stg_debug) + { + scsi_low_print(slp, NULL); + printf("%s st %x ist %x\n\n", slp->sl_xname, + status, astatus); + if (stg_debug > 1) + Debugger(); + } +#endif /* STG_DEBUG */ + + /******************************************* + * reselection & nexus + *******************************************/ + if ((status & RESEL_PHASE_MASK)== PHASE_RESELECTED) + { + if (stg_reselected(sc) == EJUSTRETURN) + return 1; + } + + if ((ti = slp->sl_nexus) == NULL) + { + status = bus_space_read_1(iot, ioh, tmc_bstat); + if ((status & PHASE_MASK) != MESSAGE_IN_PHASE) + return 1; + + /* XXX: + * Some scsi devices overrun scsi phase. + */ + if (stg_reselected(sc) == EJUSTRETURN) + { +#ifdef STG_STATICS + if ((ti = slp->sl_nexus) != NULL) + stg_statics[ti->ti_id].sprious_arbit_fail_1 ++; +#endif /* STG_STATICS */ + return 1; + } + } + + if ((astatus & ASTAT_PARERR) != 0 && ti->ti_phase != PH_ARBSTART && + (sc->sc_fcRinit & FCTL_PARENB) != 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); + } + + /******************************************* + * aribitration & selection + *******************************************/ + switch (ti->ti_phase) + { + case PH_ARBSTART: + if ((astatus & ASTAT_ARBIT) == 0) + goto arb_fail; + + status = bus_space_read_1(iot, ioh, tmc_bstat); + if ((status & BSTAT_IO) != 0) + { + /* XXX: + * Selection vs Reselection conflicts. + */ +#ifdef STG_STATICS + stg_statics[ti->ti_id].sprious_arbit_fail_2 ++; +#endif /* STG_STATICS */ +arb_fail: + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + stghw_bcr_write_1(sc, BCTL_BUSFREE); + SCSI_LOW_SETUP_PHASE(ti, PH_NULL); + scsi_low_clear_nexus(slp, ti); + return 1; + } + + /* + * selection assert start. + */ + SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); + scsi_low_arbit_win(slp, ti); +#ifdef STG_ALT_SELECTION + bus_space_write_1(iot, ioh, tmc_scsiid, + sc->sc_idbit | (1 << ti->ti_id)); + /* assert busy */ + stghw_bcr_write_1(sc, sc->sc_imsg | BCTL_BSY | sc->sc_busc); + /* arb flag clear */ + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcWinit); + /* assert sel */ + stghw_bcr_write_1(sc, sc->sc_imsg | BCTL_BSY | sc->sc_busc | BCTL_SEL); + delay(3); + /* deassert busy */ + stghw_bcr_write_1(sc, sc->sc_imsg | sc->sc_busc | BCTL_SEL); +#else /* !STG_ALT_SELECTION */ + bus_space_write_1(iot, ioh, tmc_scsiid, + sc->sc_idbit | (1 << ti->ti_id)); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcWinit); + stghw_bcr_write_1(sc, sc->sc_imsg | sc->sc_busc | BCTL_SEL); +#endif /* !STG_ALT_SELECTION */ + return 1; + + case PH_SELSTART: + if ((status & BSTAT_BSY) == 0) + { + if (stghw_select_targ_wait(sc, ti->ti_id) != 0) + { + return stg_disconnected(sc, ti); + } + } + + /* + * attention assert. + */ + SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit | + FCTL_CLRFIFO); + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + stghw_bcr_write_1(sc, sc->sc_imsg | sc->sc_busc); + SCSI_LOW_TARGET_ASSERT_ATN(ti); + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_IDENTIFY, 0); + return 1; + + case PH_RESEL: + /* clear a busy line */ + bus_space_write_1(iot, ioh, tmc_fctl, sc->sc_fcRinit); + stghw_bcr_write_1(sc, sc->sc_busc); + if ((status & PHASE_MASK) != MESSAGE_IN_PHASE) + { + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); + return 1; + } + break; + } + + /******************************************* + * scsi seq + *******************************************/ + if (slp->sl_flags & HW_PDMASTART) + stg_pdma_end(sc, ti); + + switch (status & PHASE_MASK) + { + case COMMAND_PHASE: + SCSI_LOW_SETUP_PHASE(ti, PH_CMD); + if (scsi_low_cmd(slp, ti) != 0) + break; + + if (stg_xfer(sc, slp->sl_scp.scp_cmd, + slp->sl_scp.scp_cmdlen, COMMAND_PHASE) != 0) + { + printf("%s: MSGOUT short\n", slp->sl_xname); + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_RESET, 0); + } + break; + + case DATA_OUT_PHASE: + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0) + break; + + pp = physio_proc_enter(bp); + stg_pio_write(sc, ti); + physio_proc_leave(pp); + break; + + case DATA_IN_PHASE: + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0) + break; + + pp = physio_proc_enter(bp); + stg_pio_read(sc, ti); + physio_proc_leave(pp); + break; + + case STATUS_PHASE: + SCSI_LOW_SETUP_PHASE(ti, PH_STAT); + ti->ti_status = bus_space_read_1(iot, ioh, tmc_rdata); + break; + + case MESSAGE_OUT_PHASE: + SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); + len = scsi_low_msgout(slp, ti); + if (stg_xfer(sc, ti->ti_msgoutstr, len, MESSAGE_OUT_PHASE)) + { + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_RESET, 0); + printf("%s: MSGOUT short\n", slp->sl_xname); + } + break; + + case MESSAGE_IN_PHASE: + SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); + + /* confirm REQ signal */ + regv = stg_expect_signal(sc, MESSAGE_IN_PHASE, BSTAT_REQ); + if (regv <= 0) + { + printf("%s: MSGIN: no req\n", slp->sl_xname); + break; + } + /* read data with NOACK */ + regv = bus_space_read_1(sc->sc_iot, sc->sc_ioh, tmc_sdna); + + scsi_low_msgin(slp, ti, regv); + + /* read data with ACK */ + if (regv != bus_space_read_1(sc->sc_iot, sc->sc_ioh, tmc_rdata)) + { + printf("%s: MSGIN: data mismatch\n", slp->sl_xname); + } + + if (slp->sl_msgphase != 0) + { + stg_negate_signal(sc, BSTAT_ACK, "discon<ACK>"); + return stg_disconnected(sc, ti); + } + break; + + case BUSFREE_PHASE: + printf("%s unexpected disconnection\n", slp->sl_xname); + return stg_disconnected(sc, ti); + + default: + printf("%s unknown phase bus %x intr %x\n", + slp->sl_xname, status, astatus); + break; + } + + return 1; +} diff --git a/sys/dev/stg/tmc18c30_isa.c b/sys/dev/stg/tmc18c30_isa.c new file mode 100644 index 0000000..83ab6a8 --- /dev/null +++ b/sys/dev/stg/tmc18c30_isa.c @@ -0,0 +1,298 @@ +/* $FreeBSD$ */ +/* $NecBSD: tmc18c30_pisa.c,v 1.22 1998/11/26 01:59:21 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [Ported for FreeBSD] + * Copyright (c) 2000 + * Noriaki Mitsunaga, Mitsuru Iwasaki and Takanori Watanabe. + * All rights reserved. + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Naofumi HONDA. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.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/errno.h> + +#include <vm/vm.h> + +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <isa/isavar.h> + +#include <machine/dvcfg.h> + +#include <sys/device_port.h> + +#include <cam/scsi/scsi_low.h> +#include <isa/isa_common.h> +#include <cam/scsi/scsi_low_pisa.h> + +#include <dev/stg/tmc18c30reg.h> +#include <dev/stg/tmc18c30var.h> + +#define STG_HOSTID 7 + +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/select.h> + +static int stgprobe(device_t devi); +static int stgattach(device_t devi); + +static void stg_isa_unload __P((device_t)); + +static void +stg_isa_intr(void * arg) +{ + stgintr(arg); +} + +static void +stg_release_resource(device_t dev) +{ + struct stg_softc *sc = device_get_softc(dev); + + if (sc->stg_intrhand) { + bus_teardown_intr(dev, sc->irq_res, sc->stg_intrhand); + } + + if (sc->port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->port_rid, sc->port_res); + } + + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid, sc->irq_res); + } + + if (sc->mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->mem_rid, sc->mem_res); + } +} + +static int +stg_alloc_resource(device_t dev) +{ + struct stg_softc *sc = device_get_softc(dev); + u_long maddr, msize; + int error; + + sc->port_rid = 0; + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, + 0, ~0, STGIOSZ, RF_ACTIVE); + if (sc->port_res == NULL) { + stg_release_resource(dev); + return(ENOMEM); + } + + sc->irq_rid = 0; + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->irq_res == NULL) { + stg_release_resource(dev); + return(ENOMEM); + } + + error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &maddr, &msize); + if (error) { + return(0); /* XXX */ + } + + /* no need to allocate memory if not configured */ + if (maddr == 0 || msize == 0) { + return(0); + } + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, + 0, ~0, msize, RF_ACTIVE); + if (sc->mem_res == NULL) { + stg_release_resource(dev); + return(ENOMEM); + } + + return(0); +} + +static int +stg_isa_probe(device_t dev) +{ + struct stg_softc *sc = device_get_softc(dev); + int error; + + bzero(sc, sizeof(struct stg_softc)); + + error = stg_alloc_resource(dev); + if (error) { + return(error); + } + + if (stgprobe(dev) == 0) { + stg_release_resource(dev); + return(ENXIO); + } + + stg_release_resource(dev); + + return(0); +} + +static int +stg_isa_attach(device_t dev) +{ + struct stg_softc *sc = device_get_softc(dev); + int error; + + error = stg_alloc_resource(dev); + if (error) { + return(error); + } + + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CAM, + stg_isa_intr, (void *)sc, &sc->stg_intrhand); + if (error) { + stg_release_resource(dev); + return(error); + } + + if (stgattach(dev) == 0) { + stg_release_resource(dev); + return(ENXIO); + } + + return(0); +} + +static void +stg_isa_detach(device_t dev) +{ + stg_isa_unload(dev); + stg_release_resource(dev); +} + +static device_method_t stg_isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, stg_isa_probe), + DEVMETHOD(device_attach, stg_isa_attach), + DEVMETHOD(device_detach, stg_isa_detach), + + { 0, 0 } +}; + +static driver_t stg_isa_driver = { + "stg", + stg_isa_methods, + sizeof(struct stg_softc), +}; + +static devclass_t stg_devclass; + +DRIVER_MODULE(stg, isa, stg_isa_driver, stg_devclass, 0, 0); + +static void +stg_isa_unload(device_t devi) +{ + struct stg_softc *sc = device_get_softc(devi); + + printf("%s: unload\n",sc->sc_sclow.sl_xname); + scsi_low_deactivate((struct scsi_low_softc *)sc); + scsi_low_dettach(&sc->sc_sclow); +} + +static int +stgprobe(device_t devi) +{ + int rv; + struct stg_softc *sc = device_get_softc(devi); + + rv = stgprobesubr(rman_get_bustag(sc->port_res), + rman_get_bushandle(sc->port_res), + device_get_flags(devi)); + + return rv; +} + +static int +stgattach(device_t devi) +{ + struct stg_softc *sc; + struct scsi_low_softc *slp; + u_int32_t flags = device_get_flags(devi); + u_int iobase = bus_get_resource_start(devi, SYS_RES_IOPORT, 0); + + char dvname[16]; + + strcpy(dvname,"stg"); + + + if (iobase == 0) + { + printf("%s: no ioaddr is given\n", dvname); + return (0); + } + + sc = device_get_softc(devi); + if (sc == NULL) { + return(0); + } + + slp = &sc->sc_sclow; + slp->sl_dev = devi; + sc->sc_iot = rman_get_bustag(sc->port_res); + sc->sc_ioh = rman_get_bushandle(sc->port_res); + + slp->sl_hostid = STG_HOSTID; + slp->sl_cfgflags = flags; + + stgattachsubr(sc); + + sc->sc_ih = stgintr; + + printf("stg%d",device_get_unit(devi)); + return(STGIOSZ); +} diff --git a/sys/dev/stg/tmc18c30_pccard.c b/sys/dev/stg/tmc18c30_pccard.c new file mode 100644 index 0000000..e9ceef9 --- /dev/null +++ b/sys/dev/stg/tmc18c30_pccard.c @@ -0,0 +1,399 @@ +/* $FreeBSD$ */ +/* $NecBSD: tmc18c30_pisa.c,v 1.22 1998/11/26 01:59:21 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [Ported for FreeBSD] + * Copyright (c) 2000 + * Noriaki Mitsunaga, Mitsuru Iwasaki and Takanori Watanabe. + * All rights reserved. + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Naofumi HONDA. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. 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 <sys/param.h> +#include <sys/systm.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/errno.h> + +#include <vm/vm.h> + +#include <machine/bus.h> + +#include <machine/bus_pio.h> +#include <i386/isa/isa_device.h> + +#include <machine/dvcfg.h> + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static struct stg_softc *stg_get_softc(int); +extern struct stg_softc *stgdata[]; +#define DEVPORT_ALLOCSOFTCFUNC stg_get_softc +#define DEVPORT_SOFTCARRAY stgdata +#endif +#include <sys/device_port.h> + +#include <cam/scsi/scsi_low.h> +#include <cam/scsi/scsi_low_pisa.h> + +#include <dev/stg/tmc18c30reg.h> +#include <dev/stg/tmc18c30var.h> +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +#include "stg.h" +#endif + +#define STG_HOSTID 7 + +/* pccard support */ +#include "apm.h" +#if NAPM > 0 +#include <machine/apm_bios.h> +#endif + +#include "card.h" +#if NCARD > 0 +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/select.h> +#include <pccard/cardinfo.h> +#include <pccard/slot.h> + +static int stgprobe(DEVPORT_PDEVICE devi); +static int stgattach(DEVPORT_PDEVICE devi); + +static int stg_card_intr __P((DEVPORT_PDEVICE)); +static void stg_card_unload __P((DEVPORT_PDEVICE)); +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static int stg_card_init __P((DEVPORT_PDEVICE)); +#endif + +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 +/* + * Additional code for FreeBSD new-bus PCCard frontend + */ + +static void +stg_pccard_intr(void * arg) +{ + stgintr(arg); +} + +static void +stg_release_resource(DEVPORT_PDEVICE dev) +{ + struct stg_softc *sc = device_get_softc(dev); + + if (sc->stg_intrhand) { + bus_teardown_intr(dev, sc->irq_res, sc->stg_intrhand); + } + + if (sc->port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, + sc->port_rid, sc->port_res); + } + + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid, sc->irq_res); + } + + if (sc->mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->mem_rid, sc->mem_res); + } +} + +static int +stg_alloc_resource(DEVPORT_PDEVICE dev) +{ + struct stg_softc *sc = device_get_softc(dev); + u_long maddr, msize; + int error; + + sc->port_rid = 0; + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, + 0, ~0, STGIOSZ, RF_ACTIVE); + if (sc->port_res == NULL) { + stg_release_resource(dev); + return(ENOMEM); + } + + sc->irq_rid = 0; + sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->irq_res == NULL) { + stg_release_resource(dev); + return(ENOMEM); + } + + error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &maddr, &msize); + if (error) { + return(0); /* XXX */ + } + + /* no need to allocate memory if not configured */ + if (maddr == 0 || msize == 0) { + return(0); + } + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, + 0, ~0, msize, RF_ACTIVE); + if (sc->mem_res == NULL) { + stg_release_resource(dev); + return(ENOMEM); + } + + return(0); +} + +static int +stg_pccard_probe(DEVPORT_PDEVICE dev) +{ + struct stg_softc *sc = device_get_softc(dev); + int error; + + bzero(sc, sizeof(struct stg_softc)); + + error = stg_alloc_resource(dev); + if (error) { + return(error); + } + + if (stgprobe(dev) == 0) { + stg_release_resource(dev); + return(ENXIO); + } + + stg_release_resource(dev); + + return(0); +} + +static int +stg_pccard_attach(DEVPORT_PDEVICE dev) +{ + struct stg_softc *sc = device_get_softc(dev); + int error; + + error = stg_alloc_resource(dev); + if (error) { + return(error); + } + + error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CAM, + stg_pccard_intr, (void *)sc, &sc->stg_intrhand); + if (error) { + stg_release_resource(dev); + return(error); + } + + if (stgattach(dev) == 0) { + stg_release_resource(dev); + return(ENXIO); + } + + return(0); +} + +static void +stg_pccard_detach(DEVPORT_PDEVICE dev) +{ + stg_card_unload(dev); + stg_release_resource(dev); +} + +static device_method_t stg_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, stg_pccard_probe), + DEVMETHOD(device_attach, stg_pccard_attach), + DEVMETHOD(device_detach, stg_pccard_detach), + + { 0, 0 } +}; + +static driver_t stg_pccard_driver = { + "stg", + stg_pccard_methods, + sizeof(struct stg_softc), +}; + +static devclass_t stg_devclass; + +DRIVER_MODULE(stg, pccard, stg_pccard_driver, stg_devclass, 0, 0); + +#else + +PCCARD_MODULE(stg, stg_card_init,stg_card_unload, stg_card_intr, 0, cam_imask); + +#endif + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 +static struct stg_softc * +stg_get_softc(int unit) +{ + struct stg_softc *sc; + + if (unit >= NSTG) { + return(NULL); + } + + if (stgdata[unit] == NULL) { + sc = malloc(sizeof(struct stg_softc), M_TEMP,M_NOWAIT); + if (sc == NULL) { + printf("stg_get_softc: cannot malloc!\n"); + return(NULL); + } + stgdata[unit] = sc; + } else { + sc = stgdata[unit]; + } + + return(sc); +} + +static int +stg_card_init(DEVPORT_PDEVICE devi) +{ + int unit = DEVPORT_PDEVUNIT(devi); + + if (NSTG <= unit) + return (ENODEV); + + printf("probe stg\n"); + if (stgprobe(devi) == 0) + return (ENXIO); + + printf("attach stg\n"); + if (stgattach(devi) == 0) + return (ENXIO); + + return (0); +} +#endif + +static void +stg_card_unload(DEVPORT_PDEVICE devi) +{ + struct stg_softc *sc = DEVPORT_PDEVGET_SOFTC(devi); + + printf("%s: unload\n",sc->sc_sclow.sl_xname); + scsi_low_deactivate((struct scsi_low_softc *)sc); + scsi_low_dettach(&sc->sc_sclow); +} + +static int +stg_card_intr(DEVPORT_PDEVICE devi) +{ + stgintr(DEVPORT_PDEVGET_SOFTC(devi)); + return 1; +} + +static int +stgprobe(DEVPORT_PDEVICE devi) +{ + int rv; +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + struct stg_softc *sc = device_get_softc(devi); + + rv = stgprobesubr(rman_get_bustag(sc->port_res), + rman_get_bushandle(sc->port_res), + DEVPORT_PDEVFLAGS(devi)); +#else + rv = stgprobesubr(I386_BUS_SPACE_IO, + DEVPORT_PDEVIOBASE(devi), DEVPORT_PDEVFLAGS(devi)); +#endif + + return rv; +} + +static int +stgattach(DEVPORT_PDEVICE devi) +{ + int unit = DEVPORT_PDEVUNIT(devi); + struct stg_softc *sc; + struct scsi_low_softc *slp; + u_int32_t flags = DEVPORT_PDEVFLAGS(devi); + u_int iobase = DEVPORT_PDEVIOBASE(devi); + + char dvname[16]; + + strcpy(dvname,"stg"); + +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + if (unit >= NSTG) + { + printf("%s: unit number too high\n",dvname); + return (0); + } +#endif + + if (iobase == 0) + { + printf("%s: no ioaddr is given\n", dvname); + return (0); + } + + sc = DEVPORT_PDEVALLOC_SOFTC(devi); + if (sc == NULL) { + return(0); + } + + slp = &sc->sc_sclow; +#if defined(__FreeBSD__) && __FreeBSD_version >= 400001 + slp->sl_dev = devi; + sc->sc_iot = rman_get_bustag(sc->port_res); + sc->sc_ioh = rman_get_bushandle(sc->port_res); +#else + bzero(sc, sizeof(struct stg_softc)); + strcpy(slp->sl_dev.dv_xname, dvname); + slp->sl_dev.dv_unit = unit; + sc->sc_iot = I386_BUS_SPACE_IO; + sc->sc_ioh = iobase; +#endif + + slp->sl_hostid = STG_HOSTID; + slp->sl_cfgflags = flags; + + stgattachsubr(sc); + + sc->sc_ih = stgintr; + + printf("stg%d",DEVPORT_PDEVUNIT(devi)); + return(STGIOSZ); +} +#endif /* NCARD>0 */ diff --git a/sys/dev/stg/tmc18c30reg.h b/sys/dev/stg/tmc18c30reg.h new file mode 100644 index 0000000..4a29f60 --- /dev/null +++ b/sys/dev/stg/tmc18c30reg.h @@ -0,0 +1,144 @@ +/* $FreeBSD$ */ +/* $NecBSD: tmc18c30reg.h,v 1.4 1998/03/14 07:05:23 kmatsuda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. 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. + */ + +#ifndef _TMC18C30REG_H_ +#define _TMC18C30REG_H_ + +#define tmc_wdata 0x00 +#define tmc_rdata 0x00 + +#define tmc_bctl 0x01 +#define BCTL_BUSFREE 0x00 +#define BCTL_RST 0x01 +#define BCTL_SEL 0x02 +#define BCTL_BSY 0x04 +#define BCTL_ATN 0x08 +#define BCTL_IO 0x10 +#define BCTL_CD 0x20 +#define BCTL_MSG 0x40 +#define BCTL_BUSEN 0x80 +#define tmc_bstat 0x01 +#define BSTAT_BSY 0x01 +#define BSTAT_MSG 0x02 +#define BSTAT_IO 0x04 +#define BSTAT_CMD 0x08 +#define BSTAT_REQ 0x10 +#define BSTAT_SEL 0x20 +#define BSTAT_ACK 0x40 +#define BSTAT_PHMASK (BSTAT_MSG | BSTAT_IO | BSTAT_CMD) + +#define tmc_ictl 0x02 +#define ICTL_FIFO 0x10 +#define ICTL_ARBIT 0x20 +#define ICTL_SEL 0x40 +#define ICTL_CD 0x80 +#define ICTL_ALLINT (ICTL_ARBIT | ICTL_CD | ICTL_SEL) +#define tmc_astat 0x02 +#define ASTAT_INT 0x01 +#define ASTAT_ARBIT 0x02 +#define ASTAT_PARERR 0x04 +#define ASTAT_SCSIRST 0x08 +#define ASTAT_STATMASK 0x0f +#define ASTAT_FIFODIR 0x10 +#define ASTAT_FIFOEN 0x20 +#define ASTAT_PARENB 0x40 +#define ASTAT_BUSEN 0x80 + +#define tmc_ssctl 0x03 +#define SSCTL_FSYNCHEN 0x40 +#define SSCTL_SYNCHEN 0x80 +#define tmc_fstat 0x03 + +#define tmc_fctl 0x04 +#define FCTL_CLRFIFO 0x01 +#define FCTL_ARBIT 0x04 +#define FCTL_PARENB 0x08 +#define FCTL_INTEN 0x10 +#define FCTL_CLRINT 0x20 +#define FCTL_FIFOW 0x40 +#define FCTL_FIFOEN 0x80 +#define tmc_icnd 0x04 + +#define tmc_mctl 0x05 +#define tmc_idlsb 0x05 + +#define tmc_idmsb 0x06 + +#define tmc_wlb 0x07 +#define tmc_rlb 0x07 + +#define tmc_scsiid 0x08 +#define tmc_sdna 0x08 + +#define tmc_istat 0x09 +#define ISTAT_INTEN 0x08 +#define ISTAT_FIFO 0x10 +#define ISTAT_ARBIT 0x20 +#define ISTAT_SEL 0x40 +#define ISTAT_CD 0x80 + +#define tmc_cfg1 0x0a + +#define tmc_ioctl 0x0b +#define tmc_cfg2 0x0b + +#define tmc_wfifo 0x0c +#define tmc_rfifo 0x0c + +#define tmc_fdcnt 0x0e + +/* Information transfer phases */ +#define BUSFREE_PHASE 0x00 +#define DATA_OUT_PHASE (BSTAT_BSY) +#define DATA_IN_PHASE (BSTAT_BSY|BSTAT_IO) +#define COMMAND_PHASE (BSTAT_CMD|BSTAT_BSY) +#define STATUS_PHASE (BSTAT_CMD|BSTAT_BSY|BSTAT_IO) +#define MESSAGE_OUT_PHASE (BSTAT_CMD|BSTAT_MSG|BSTAT_BSY) +#define MESSAGE_IN_PHASE (BSTAT_CMD|BSTAT_MSG|BSTAT_BSY|BSTAT_IO) + +#define PHASE_RESELECTED (BSTAT_SEL|BSTAT_IO) + +#define PHASE_MASK 0x2f +#define RESEL_PHASE_MASK 0x2e + +/* chip type */ +#define TMCCHIP_UNK 0x00 +#define TMCCHIP_1800 0x01 +#define TMCCHIP_18C50 0x02 +#define TMCCHIP_18C30 0x03 + +#define STGIOSZ 0x10 + +#endif /* !_TMC18C30REG_H_ */ diff --git a/sys/dev/stg/tmc18c30var.h b/sys/dev/stg/tmc18c30var.h new file mode 100644 index 0000000..46cc986 --- /dev/null +++ b/sys/dev/stg/tmc18c30var.h @@ -0,0 +1,99 @@ +/* $FreeBSD$ */ +/* $NecBSD: tmc18c30var.h,v 1.12 1998/11/30 00:08:30 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Naofumi HONDA. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. 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. + */ + +#ifndef _TMC18C30VAR_H_ +#define _TMC18C30VAR_H_ + +/***************************************************************** + * Host adapter structure + *****************************************************************/ +struct stg_softc { + struct scsi_low_softc sc_sclow; /* generic data */ + + bus_space_tag_t sc_iot; + bus_space_tag_t sc_memt; + bus_space_handle_t sc_ioh; + + void *sc_ih; + + u_int sc_chip; /* chip type */ + u_int sc_fsz; /* fifo size */ + u_int sc_idbit; /* host id bit */ + u_int8_t sc_fcb; /* fifo intr thread */ + + u_int8_t sc_fcWinit; /* write flags */ + u_int8_t sc_fcRinit; /* read flags */ + + u_int8_t sc_fcsp; /* special control flags */ + u_int8_t sc_icinit; /* interrupt masks */ + u_int8_t sc_busc; /* default bus control register */ + u_int8_t sc_imsg; /* identify msg required */ + u_int8_t sc_busimg; /* bus control register image */ +#if defined (__FreeBSD__) && __FreeBSD_version >= 400001 + int port_rid; + int irq_rid; + int mem_rid; + struct resource *port_res; + struct resource *irq_res; + struct resource *mem_res; + void *stg_intrhand; +#endif +}; + +/***************************************************************** + * Lun information + *****************************************************************/ +struct stg_lun_info { + struct lun_info sli_li; /* generic data */ + + u_int8_t sli_reg_synch; /* synch register per lun */ +}; + +/***************************************************************** + * Proto + *****************************************************************/ +int stgprobesubr __P((bus_space_tag_t, bus_space_handle_t, u_int)); +void stgattachsubr __P((struct stg_softc *)); +int stgprint __P((void *, const char *)); +int stgintr __P((void *)); + +#if defined(i386) +#define SOFT_INTR_REQUIRED(slp) (softintr((slp)->sl_irq)) +#else /* !i386 */ +#define SOFT_INTR_REQUIRED(slp) +#endif /* !i386 */ +#endif /* !_TMC18C30VAR_H_ */ |