diff options
Diffstat (limited to 'sys/dev/nsp')
-rw-r--r-- | sys/dev/nsp/nsp.c | 1933 | ||||
-rw-r--r-- | sys/dev/nsp/nsp_pccard.c | 343 | ||||
-rw-r--r-- | sys/dev/nsp/nspreg.h | 221 | ||||
-rw-r--r-- | sys/dev/nsp/nspvar.h | 113 |
4 files changed, 2610 insertions, 0 deletions
diff --git a/sys/dev/nsp/nsp.c b/sys/dev/nsp/nsp.c new file mode 100644 index 0000000..e983092 --- /dev/null +++ b/sys/dev/nsp/nsp.c @@ -0,0 +1,1933 @@ +/* $FreeBSD$ */ +/* $NecBSD: nsp.c,v 1.21.12.6 2001/06/29 06:27:52 honda Exp $ */ +/* $NetBSD$ */ + +#define NSP_DEBUG +#define NSP_STATICS +#define NSP_IO_CONTROL_FLAGS \ + (NSP_READ_SUSPEND_IO | NSP_WRITE_SUSPEND_IO | \ + NSP_READ_FIFO_INTERRUPTS | NSP_WRITE_FIFO_INTERRUPTS | \ + NSP_USE_MEMIO | NSP_WAIT_FOR_SELECT) + +/* + * Copyright (c) 1998, 1999, 2000, 2001 + * NetBSD/pc98 porting staff. All rights reserved. + * + * Copyright (c) 1998, 1999, 2000, 2001 + * 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> +#if defined(__FreeBSD__) && __FreeBSD_version > 500001 +#include <sys/bio.h> +#endif /* __ FreeBSD__ */ +#include <sys/buf.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/errno.h> + +#ifdef __NetBSD__ +#include <sys/device.h> +#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> +#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> +#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_MAX_DATA_SIZE (64 * 1024) +#define NSP_SELTIMEOUT (200) +#define NSP_DELAY_MAX (2 * 1000 * 1000) +#define NSP_DELAY_INTERVAL (1) +#define NSP_TIMER_1MS (1000 / 51) + +/*************************************************** + * DEBUG + ***************************************************/ +#ifdef NSP_DEBUG +int nsp_debug; +#endif /* NSP_DEBUG */ + +#ifdef NSP_STATICS +struct nsp_statics { + int arbit_conflict_1; + int arbit_conflict_2; + int device_data_write; + int device_busy; + int disconnect; + int reselect; + int data_phase_bypass; +} nsp_statics; +#endif /* NSP_STATICS */ + +/*************************************************** + * IO control + ***************************************************/ +#define NSP_READ_SUSPEND_IO 0x0001 +#define NSP_WRITE_SUSPEND_IO 0x0002 +#define NSP_USE_MEMIO 0x0004 +#define NSP_READ_FIFO_INTERRUPTS 0x0010 +#define NSP_WRITE_FIFO_INTERRUPTS 0x0020 +#define NSP_WAIT_FOR_SELECT 0x0100 + +u_int nsp_io_control = NSP_IO_CONTROL_FLAGS; +int nsp_read_suspend_bytes = DEV_BSIZE; +int nsp_write_suspend_bytes = DEV_BSIZE; +int nsp_read_interrupt_bytes = 4096; +int nsp_write_interrupt_bytes = 4096; + +/*************************************************** + * DEVICE STRUCTURE + ***************************************************/ +extern struct cfdriver nsp_cd; + +/************************************************************** + * DECLARE + **************************************************************/ +#define NSP_FIFO_ON 1 +#define NSP_FIFO_OFF 0 +static void nsp_pio_read(struct nsp_softc *, int); +static void nsp_pio_write(struct nsp_softc *, int); +static int nsp_xfer(struct nsp_softc *, u_int8_t *, int, int, int); +static int nsp_msg(struct nsp_softc *, struct targ_info *, u_int); +static int nsp_reselected(struct nsp_softc *); +static int nsp_disconnected(struct nsp_softc *, struct targ_info *); +static void nsp_pdma_end(struct nsp_softc *, struct targ_info *); +static void nsphw_init(struct nsp_softc *); +static int nsp_target_nexus_establish(struct nsp_softc *); +static int nsp_lun_nexus_establish(struct nsp_softc *); +static int nsp_ccb_nexus_establish(struct nsp_softc *); +static int nsp_world_start(struct nsp_softc *, int); +static int nsphw_start_selection(struct nsp_softc *sc, struct slccb *); +static void nsphw_bus_reset(struct nsp_softc *); +static void nsphw_attention(struct nsp_softc *); +static u_int nsp_fifo_count(struct nsp_softc *); +static u_int nsp_request_count(struct nsp_softc *); +static int nsp_negate_signal(struct nsp_softc *, u_int8_t, u_char *); +static int nsp_expect_signal(struct nsp_softc *, u_int8_t, u_int8_t); +static void nsp_start_timer(struct nsp_softc *, int); +static void nsp_setup_fifo(struct nsp_softc *, int, int, int); +static int nsp_targ_init(struct nsp_softc *, struct targ_info *, int); +static void nsphw_selection_done_and_expect_msgout(struct nsp_softc *); +static void nsp_data_padding(struct nsp_softc *, int, u_int); +static int nsp_timeout(struct nsp_softc *); +static int nsp_read_fifo(struct nsp_softc *, int); +static int nsp_write_fifo(struct nsp_softc *, int); +static int nsp_phase_match(struct nsp_softc *, u_int8_t, u_int8_t); +static int nsp_wait_interrupt(struct nsp_softc *); + +struct scsi_low_funcs nspfuncs = { + SC_LOW_INIT_T nsp_world_start, + SC_LOW_BUSRST_T nsphw_bus_reset, + SC_LOW_TARG_INIT_T nsp_targ_init, + SC_LOW_LUN_INIT_T NULL, + + SC_LOW_SELECT_T nsphw_start_selection, + SC_LOW_NEXUS_T nsp_lun_nexus_establish, + SC_LOW_NEXUS_T nsp_ccb_nexus_establish, + + SC_LOW_ATTEN_T nsphw_attention, + SC_LOW_MSG_T nsp_msg, + + SC_LOW_TIMEOUT_T nsp_timeout, + SC_LOW_POLL_T nspintr, + + NULL, +}; + +/**************************************************** + * hwfuncs + ****************************************************/ +static __inline u_int8_t nsp_cr_read_1(bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t ofs); +static __inline void nsp_cr_write_1(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 wc; + u_int8_t ph, isrc; + + for (wc = 0; wc < NSP_DELAY_MAX / NSP_DELAY_INTERVAL; wc ++) + { + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (ph == (u_int8_t) -1) + return -1; + + isrc = bus_space_read_1(bst, bsh, nsp_irqsr); + if (isrc & IRQSR_SCSI) + return 0; + + if ((ph & mask) != 0 && (ph & SCBUSMON_PHMASK) == curphase) + return 1; + + SCSI_LOW_DELAY(NSP_DELAY_INTERVAL); + } + + printf("%s: nsp_expect_signal timeout\n", slp->sl_xname); + return -1; +} + +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, sc->sc_parr); + 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, sc->sc_icr); + bus_space_write_1(bst, bsh, nsp_irqcr, IRQSR_MASK); + + nsp_setup_fifo(sc, NSP_FIFO_OFF, SCSI_LOW_READ, 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); + SCSI_LOW_DELAY(10); +} + +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); + SCSI_LOW_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 void +nsphw_selection_done_and_expect_msgout(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; + + /* clear ack counter */ + sc->sc_cnt = 0; + nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_PT | PTCLRR_ACK | + PTCLRR_REQ | PTCLRR_HOST); + + /* deassert sel and assert atten */ + sc->sc_seltout = 0; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, sc->sc_busc); + SCSI_LOW_DELAY(1); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + sc->sc_busc | SCBUSCR_ADIR | SCBUSCR_ACKEN); + SCSI_LOW_ASSERT_ATN(slp); +} + +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, wc; + + wc = sc->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000; + sc->sc_dataout_timeout = 0; + + /* check bus free */ + s = splhigh(); + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (ph != SCBUSMON_FREE) + { + splx(s); +#ifdef NSP_STATICS + nsp_statics.arbit_conflict_1 ++; +#endif /* NSP_STATICS */ + return SCSI_LOW_START_FAIL; + } + + /* start arbitration */ + nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_EXEC); + splx(s); + + SCSI_LOW_SETUP_PHASE(ti, PH_ARBSTART); + do + { + /* XXX: what a stupid chip! */ + arbs = nsp_cr_read_1(bst, bsh, NSPR_ARBITS); + SCSI_LOW_DELAY(1); + } + while ((arbs & (ARBITS_WIN | ARBITS_FAIL)) == 0 && wc -- > 0); + + if ((arbs & ARBITS_WIN) == 0) + { + nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_CLR); +#ifdef NSP_STATICS + nsp_statics.arbit_conflict_2 ++; +#endif /* NSP_STATICS */ + return SCSI_LOW_START_FAIL; + } + + /* assert select line */ + SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART); + scsi_low_arbit_win(slp); + + s = splhigh(); + SCSI_LOW_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); + SCSI_LOW_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); + SCSI_LOW_DELAY(3); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + SCBUSCR_SEL | SCBUSCR_DOUT | sc->sc_busc); + SCSI_LOW_DELAY(1); + + if ((nsp_io_control & NSP_WAIT_FOR_SELECT) != 0) + { +#define NSP_FIRST_SEL_WAIT 300 +#define NSP_SEL_CHECK_INTERVAL 10 + + /* wait for a selection response */ + for (wc = 0; wc < NSP_FIRST_SEL_WAIT / NSP_SEL_CHECK_INTERVAL; + wc ++) + { + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if ((ph & SCBUSMON_BSY) == 0) + { + SCSI_LOW_DELAY(NSP_SEL_CHECK_INTERVAL); + continue; + } + + SCSI_LOW_DELAY(1); + ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if ((ph & SCBUSMON_BSY) != 0) + { + nsphw_selection_done_and_expect_msgout(sc); + splx(s); + + SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); + return SCSI_LOW_START_OK; + } + } + } + splx(s); + + /* check a selection timeout */ + nsp_start_timer(sc, NSP_TIMER_1MS); + 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; + + 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; + + if ((slp->sl_cfgflags & CFG_NOPARITY) == 0) + sc->sc_parr = PARITYR_ENABLE | PARITYR_CLEAR; + else + sc->sc_parr = 0; + + sc->sc_icr = (SCIENR_SCCHG | SCIENR_RESEL | SCIENR_RST); + + nsphw_init(sc); + scsi_low_bus_reset(slp); + + 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; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + struct ncp_synch_data *sdp; + struct nsp_targ_info *nti = (void *) ti; + u_int period, offset; + int i, error; + + if ((msg & SCSI_LOW_MSG_WIDE) != 0) + { + if (ti->ti_width != SCSI_LOW_BUS_WIDTH_8) + { + ti->ti_width = SCSI_LOW_BUS_WIDTH_8; + return EINVAL; + } + return 0; + } + + if ((msg & SCSI_LOW_MSG_SYNCH) == 0) + return 0; + + period = ti->ti_maxsynch.period; + offset = ti->ti_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. + */ + ti->ti_maxsynch.period = 0; + ti->ti_maxsynch.offset = 0; + nti->nti_reg_syncr = 0; + nti->nti_reg_ackwidth = 0; + error = EINVAL; + } + else + { + nti->nti_reg_syncr = (sdp->chip_period << SYNCR_PERS) | + (offset & SYNCR_OFFM); + nti->nti_reg_ackwidth = sdp->ack_width; + error = 0; + } + + nsp_cr_write_1(bst, bsh, NSPR_SYNCR, nti->nti_reg_syncr); + nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, nti->nti_reg_ackwidth); + return error; +} + +static int +nsp_targ_init(sc, ti, action) + struct nsp_softc *sc; + struct targ_info *ti; + int action; +{ + struct nsp_targ_info *nti = (void *) ti; + + if (action == SCSI_LOW_INFO_ALLOC || action == SCSI_LOW_INFO_REVOKE) + { + ti->ti_width = SCSI_LOW_BUS_WIDTH_8; + ti->ti_maxsynch.period = 100 / 4; + ti->ti_maxsynch.offset = 15; + nti->nti_reg_syncr = 0; + nti->nti_reg_ackwidth = 0; + } + return 0; +} + +static 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_flags |= HW_READ_PADDING; + slp->sl_funcs = &nspfuncs; + sc->sc_tmaxcnt = SCSI_LOW_MIN_TOUT * 1000 * 1000; /* default */ + + (void) scsi_low_attach(slp, 0, NSP_NTARGETS, NSP_NLUNS, + sizeof(struct nsp_targ_info), 0); +} + +/************************************************************** + * 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_RSS_ACK | 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 u_int +nsp_request_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_RSS_REQ | 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, direction, datalen) + struct nsp_softc *sc; + int on; + int direction; + int datalen; +{ + u_int8_t xfermode; + + sc->sc_suspendio = 0; + if (on == NSP_FIFO_OFF) + { + xfermode = XFERMR_IO8; + goto out; + } + + /* check if suspend io OK ? */ + if (datalen > 0) + { + if (direction == SCSI_LOW_READ) + { + if ((nsp_io_control & NSP_READ_SUSPEND_IO) != 0 && + (datalen % nsp_read_suspend_bytes) == 0) + sc->sc_suspendio = nsp_read_suspend_bytes; + } + else + { + if ((nsp_io_control & NSP_WRITE_SUSPEND_IO) != 0 && + (datalen % nsp_write_suspend_bytes) == 0) + sc->sc_suspendio = nsp_write_suspend_bytes; + } + } + + /* determine a transfer type */ + if (datalen < DEV_BSIZE || (datalen & 3) != 0) + { + if (sc->sc_memh != 0 && + (nsp_io_control & NSP_USE_MEMIO) != 0) + xfermode = XFERMR_XEN | XFERMR_MEM8; + else + xfermode = XFERMR_XEN | XFERMR_IO8; + } + else + { + if (sc->sc_memh != 0 && + (nsp_io_control & NSP_USE_MEMIO) != 0) + xfermode = XFERMR_XEN | XFERMR_MEM32; + else + xfermode = XFERMR_XEN | XFERMR_IO32; + + if (sc->sc_suspendio > 0) + xfermode |= XFERMR_FIFOEN; + } + +out: + sc->sc_xfermr = xfermode; + nsp_cr_write_1(sc->sc_iot, sc->sc_ioh, NSPR_XFERMR, sc->sc_xfermr); +} + +static 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 = slp->sl_Qnexus; + u_int len = 0, cnt; + + sc->sc_dataout_timeout = 0; + slp->sl_flags &= ~HW_PDMASTART; + nsp_setup_fifo(sc, NSP_FIFO_OFF, SCSI_LOW_READ, 0); + if ((sc->sc_icr & SCIENR_FIFO) != 0) + { + sc->sc_icr &= ~SCIENR_FIFO; + nsp_cr_write_1(sc->sc_iot, sc->sc_ioh, NSPR_SCIENR, sc->sc_icr); + } + + if (cb == NULL) + { + slp->sl_error |= PDMAERR; + return; + } + + 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 (sc->sc_cnt >= cnt && + 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 || + sc->sc_cnt > cb->ccb_scp.scp_datalen) + { + slp->sl_error |= PDMAERR; + printf("%s: data read count error %x != %x (%x)\n", + slp->sl_xname, sc->sc_cnt, cnt, + cb->ccb_scp.scp_datalen); + } + } + sc->sc_cnt = cnt; + scsi_low_data_finish(slp); + } + else + { + + printf("%s data phase miss\n", slp->sl_xname); + slp->sl_error |= PDMAERR; + } +} + +#define RFIFO_CRIT 64 +#define WFIFO_CRIT 32 + +static void +nsp_data_padding(sc, direction, count) + struct nsp_softc *sc; + int direction; + u_int count; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + + if (count > NSP_MAX_DATA_SIZE) + count = NSP_MAX_DATA_SIZE; + + nsp_cr_write_1(bst, bsh, NSPR_XFERMR, XFERMR_XEN | XFERMR_IO8); + if (direction == SCSI_LOW_READ) + { + while (count -- > 0) + (void) bus_space_read_1(bst, bsh, nsp_fifodr); + } + else + { + while (count -- > 0) + (void) bus_space_write_1(bst, bsh, nsp_fifodr, 0); + } + nsp_cr_write_1(bst, bsh, NSPR_XFERMR, sc->sc_xfermr); +} + +static int +nsp_read_fifo(sc, suspendio) + struct nsp_softc *sc; + int suspendio; +{ + 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; + + res = nsp_fifo_count(sc); + if (res == sc->sc_cnt) + return 0; + +#ifdef NSP_DEBUG + if (res < sc->sc_cnt || res == (u_int) -1) + { + printf("%s: strange fifo ack count 0x%x < 0x%x\n", + slp->sl_xname, res, sc->sc_cnt); + return 0; + } +#endif /* NSP_DEBUG */ + + res = res - sc->sc_cnt; + if (res > slp->sl_scp.scp_datalen) + { + if ((slp->sl_error & PDMAERR) == 0) + { + printf("%s: data overrun 0x%x > 0x%x\n", + slp->sl_xname, res, slp->sl_scp.scp_datalen); + } + + slp->sl_error |= PDMAERR; + slp->sl_scp.scp_datalen = 0; + + if ((slp->sl_flags & HW_READ_PADDING) == 0) + { + printf("%s: read padding required\n", slp->sl_xname); + return 0; + } + + nsp_data_padding(sc, SCSI_LOW_READ, res); + sc->sc_cnt += res; + return 1; /* padding start */ + } + + if (suspendio > 0 && slp->sl_scp.scp_datalen >= suspendio) + res = suspendio; + + if ((sc->sc_xfermr & (XFERMR_MEM32 | XFERMR_MEM8)) != 0) + { + if ((sc->sc_xfermr & XFERMR_MEM32) != 0) + { + res &= ~3; + bus_space_read_region_4(sc->sc_memt, sc->sc_memh, 0, + (u_int32_t *) slp->sl_scp.scp_data, res >> 2); + } + else + { + bus_space_read_region_1(sc->sc_memt, sc->sc_memh, 0, + (u_int8_t *) slp->sl_scp.scp_data, res); + } + } + else + { + if ((sc->sc_xfermr & XFERMR_IO32) != 0) + { + res &= ~3; + 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); + } + } + + if (nsp_cr_read_1(bst, bsh, NSPR_PARITYR) & PARITYR_PE) + { + nsp_cr_write_1(bst, bsh, NSPR_PARITYR, + PARITYR_ENABLE | PARITYR_CLEAR); + scsi_low_assert_msg(slp, slp->sl_Tnexus, SCSI_LOW_MSG_ERROR, 1); + } + + slp->sl_scp.scp_data += res; + slp->sl_scp.scp_datalen -= res; + sc->sc_cnt += res; + return 0; +} + +static int +nsp_write_fifo(sc, suspendio) + struct nsp_softc *sc; + int suspendio; +{ + 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; + register u_int8_t stat; + + if (suspendio > 0) + { +#ifdef NSP_DEBUG + if ((slp->sl_scp.scp_datalen % WFIFO_CRIT) != 0) + { + printf("%s: strange write length 0x%x\n", + slp->sl_xname, slp->sl_scp.scp_datalen); + } +#endif /* NSP_DEBUG */ + res = slp->sl_scp.scp_datalen % suspendio; + if (res == 0) + { + res = suspendio; + } + } + else + { + res = WFIFO_CRIT; + } + + if (res > slp->sl_scp.scp_datalen) + res = slp->sl_scp.scp_datalen; + + /* XXX: reconfirm! */ + stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON) & SCBUSMON_PHMASK; + if (stat != PHASE_DATAOUT) + return 0; + + if ((sc->sc_xfermr & (XFERMR_MEM32 | XFERMR_MEM8)) != 0) + { + if ((sc->sc_xfermr & XFERMR_MEM32) != 0) + { + bus_space_write_region_4(sc->sc_memt, sc->sc_memh, 0, + (u_int32_t *) slp->sl_scp.scp_data, res >> 2); + } + else + { + bus_space_write_region_1(sc->sc_memt, sc->sc_memh, 0, + (u_int8_t *) slp->sl_scp.scp_data, res); + } + } + else + { + if ((sc->sc_xfermr & XFERMR_IO32) != 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; + sc->sc_cnt += res; + return 0; +} + +static int +nsp_wait_interrupt(sc) + struct nsp_softc *sc; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int tout; + register u_int8_t isrc; + + for (tout = 0; tout < DEV_BSIZE / 10; tout ++) + { + isrc = bus_space_read_1(bst, bsh, nsp_irqsr); + if ((isrc & (IRQSR_SCSI | IRQSR_FIFO)) != 0) + { + if ((isrc & IRQSR_FIFO) != 0) + { + bus_space_write_1(bst, bsh, + nsp_irqcr, IRQCR_FIFOCL); + } + return 1; + } + SCSI_LOW_DELAY(1); + } + return 0; +} + +static void +nsp_pio_read(sc, suspendio) + struct nsp_softc *sc; + int suspendio; +{ + 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, padding, datalen; + register u_int8_t stat, fstat; + + padding = 0; + tout = sc->sc_tmaxcnt; + slp->sl_flags |= HW_PDMASTART; + datalen = slp->sl_scp.scp_datalen; + +ReadLoop: + while (1) + { + stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (stat == (u_int8_t) -1) + return; + + /* out of data phase */ + if ((stat & SCBUSMON_PHMASK) != PHASE_DATAIN) + { + nsp_read_fifo(sc, 0); + return; + } + + /* data phase */ + fstat = bus_space_read_1(bst, bsh, nsp_fifosr); + if ((fstat & FIFOSR_FULLEMP) != 0) + { + if ((sc->sc_icr & SCIENR_FIFO) != 0) + { + bus_space_write_1(bst, bsh, nsp_irqcr, + IRQCR_FIFOCL); + } + + if (suspendio > 0) + { + padding |= nsp_read_fifo(sc, suspendio); + } + else + { + padding |= nsp_read_fifo(sc, 0); + } + + if ((sc->sc_icr & SCIENR_FIFO) != 0) + break; + } + else + { + if (padding == 0 && slp->sl_scp.scp_datalen <= 0) + return; + + if ((sc->sc_icr & SCIENR_FIFO) != 0) + break; + + SCSI_LOW_DELAY(1); + } + + if ((-- tout) <= 0) + { + printf("%s: nsp_pio_read: timeout\n", slp->sl_xname); + return; + } + } + + + if (slp->sl_scp.scp_datalen > 0 && + slp->sl_scp.scp_datalen > datalen - nsp_read_interrupt_bytes) + { + if (nsp_wait_interrupt(sc) != 0) + goto ReadLoop; + } +} + +static void +nsp_pio_write(sc, suspendio) + struct nsp_softc *sc; + int suspendio; +{ + 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 rcount, acount; + int tout, datalen; + register u_int8_t stat, fstat; + + tout = sc->sc_tmaxcnt; + slp->sl_flags |= HW_PDMASTART; + datalen = slp->sl_scp.scp_datalen; + +WriteLoop: + while (1) + { + stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON) & SCBUSMON_PHMASK; + if (stat != PHASE_DATAOUT) + return; + + if (slp->sl_scp.scp_datalen <= 0) + { + if (sc->sc_dataout_timeout == 0) + sc->sc_dataout_timeout = SCSI_LOW_TIMEOUT_HZ; + return; + } + + fstat = bus_space_read_1(bst, bsh, nsp_fifosr); + if ((fstat & FIFOSR_FULLEMP) != 0) + { + if ((sc->sc_icr & SCIENR_FIFO) != 0) + { + bus_space_write_1(bst, bsh, nsp_irqcr, + IRQCR_FIFOCL); + } + + if (suspendio > 0) + { + /* XXX:IMPORTANT: + * To avoid timeout of pcmcia bus + * (not scsi bus!), we should check + * the scsi device sends us request + * signals, which means the scsi device + * is ready to recieve data without + * heavy delays. + */ + if ((slp->sl_scp.scp_datalen % suspendio) == 0) + { + /* Step I: + * fill the nsp fifo, and waiting for + * the fifo empty. + */ + nsp_write_fifo(sc, 0); + } + else + { + /* Step II: + * check the request singals. + */ + acount = nsp_fifo_count(sc); + rcount = nsp_request_count(sc); + if (rcount <= acount) + { + nsp_write_fifo(sc, 0); +#ifdef NSP_STATICS + nsp_statics.device_busy ++; +#endif /* NSP_STATICS */ + } + else + { + nsp_write_fifo(sc, suspendio); +#ifdef NSP_STATICS + nsp_statics.device_data_write ++; +#endif /* NSP_STATICS */ + } + } + } + else + { + nsp_write_fifo(sc, 0); + } + + if ((sc->sc_icr & SCIENR_FIFO) != 0) + break; + } + else + { + if ((sc->sc_icr & SCIENR_FIFO) != 0) + break; + + SCSI_LOW_DELAY(1); + } + + if ((-- tout) <= 0) + { + printf("%s: nsp_pio_write: timeout\n", slp->sl_xname); + return; + } + } + + if (slp->sl_scp.scp_datalen > 0 && + slp->sl_scp.scp_datalen > datalen - nsp_write_interrupt_bytes) + { + if (nsp_wait_interrupt(sc) != 0) + goto WriteLoop; + } +} + +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 wc; + u_int8_t regv; + + for (wc = 0; wc < NSP_DELAY_MAX / NSP_DELAY_INTERVAL; wc ++) + { + regv = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON); + if (regv == (u_int8_t) -1) + return -1; + if ((regv & mask) == 0) + return 1; + SCSI_LOW_DELAY(NSP_DELAY_INTERVAL); + } + + printf("%s: %s nsp_negate_signal timeout\n", slp->sl_xname, s); + return -1; +} + +static int +nsp_xfer(sc, buf, len, phase, clear_atn) + struct nsp_softc *sc; + u_int8_t *buf; + int len; + int phase; + int clear_atn; +{ + bus_space_tag_t bst = sc->sc_iot; + bus_space_handle_t bsh = sc->sc_ioh; + int ptr, rv; + + for (ptr = 0; len > 0; len --, ptr ++) + { + rv = nsp_expect_signal(sc, phase, SCBUSMON_REQ); + if (rv <= 0) + goto out; + + if (len == 1 && clear_atn != 0) + { + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, + SCBUSCR_ADIR | SCBUSCR_ACKEN); + SCSI_LOW_DEASSERT_ATN(&sc->sc_sclow); + } + + 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; +} + +/************************************************************** + * 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); + cr &= ~(SCBUSCR_BSY | SCBUSCR_ATN); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + cr |= SCBUSCR_ADIR | SCBUSCR_ACKEN; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + +#ifdef NSP_STATICS + nsp_statics.reselect ++; +#endif /* NSP_STATCIS */ + return EJUSTRETURN; +} + +static int +nsp_disconnected(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; + + nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_PT | PTCLRR_ACK | + PTCLRR_REQ | PTCLRR_HOST); + if ((sc->sc_icr & SCIENR_FIFO) != 0) + { + sc->sc_icr &= ~SCIENR_FIFO; + nsp_cr_write_1(bst, bsh, NSPR_SCIENR, sc->sc_icr); + } + sc->sc_cnt = 0; + sc->sc_dataout_timeout = 0; +#ifdef NSP_STATICS + nsp_statics.disconnect ++; +#endif /* NSP_STATICS */ + scsi_low_disconnected(slp, ti); + return 1; +} + +/************************************************************** + * SEQUENCER + **************************************************************/ +static void nsp_error(struct nsp_softc *, u_char *, u_int8_t, u_int8_t, u_int8_t); + +static void +nsp_error(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_target_nexus_establish(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 = slp->sl_Tnexus; + struct nsp_targ_info *nti = (void *) ti; + + /* setup synch transfer registers */ + nsp_cr_write_1(bst, bsh, NSPR_SYNCR, nti->nti_reg_syncr); + nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, nti->nti_reg_ackwidth); + + /* setup pdma fifo (minimum) */ + nsp_setup_fifo(sc, NSP_FIFO_ON, SCSI_LOW_READ, 0); + return 0; +} + +static int +nsp_lun_nexus_establish(sc) + struct nsp_softc *sc; +{ + + return 0; +} + +static int +nsp_ccb_nexus_establish(sc) + struct nsp_softc *sc; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + struct slccb *cb = slp->sl_Qnexus; + + sc->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000; + + /* setup pdma fifo */ + nsp_setup_fifo(sc, NSP_FIFO_ON, + slp->sl_scp.scp_direction, slp->sl_scp.scp_datalen); + + if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + { + if (sc->sc_suspendio > 0 && + (nsp_io_control & NSP_READ_FIFO_INTERRUPTS) != 0) + { + sc->sc_icr |= SCIENR_FIFO; + nsp_cr_write_1(sc->sc_iot, sc->sc_ioh, + NSPR_SCIENR, sc->sc_icr); + } + } + else + { + if (sc->sc_suspendio > 0 && + (nsp_io_control & NSP_WRITE_FIFO_INTERRUPTS) != 0) + { + sc->sc_icr |= SCIENR_FIFO; + nsp_cr_write_1(sc->sc_iot, sc->sc_ioh, + NSPR_SCIENR, sc->sc_icr); + } + } + return 0; +} + +static int +nsp_phase_match(sc, phase, stat) + struct nsp_softc *sc; + u_int8_t phase; + u_int8_t stat; +{ + struct scsi_low_softc *slp = &sc->sc_sclow; + + if ((stat & SCBUSMON_PHMASK) != phase) + { + printf("%s: phase mismatch 0x%x != 0x%x\n", + slp->sl_xname, (u_int) phase, (u_int) stat); + return EINVAL; + } + + if ((stat & SCBUSMON_REQ) == 0) + return EINVAL; + + 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; + u_int derror, flags; + 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 == (u_int8_t) -1 || (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; + } + + /* check a timer interrupt */ + regv = 0; + if ((isrc & IRQSR_TIMER) != 0) + { + if ((isrc & IRQSR_MASK) == IRQSR_TIMER && sc->sc_seltout == 0) + { + bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_TIMERCL); + return 1; + } + regv |= IRQCR_TIMERCL; + } + + /* check a fifo interrupt */ + if ((isrc & IRQSR_FIFO) != 0) + { + regv |= IRQCR_FIFOCL; + } + + /* OK. enable all interrupts */ + bus_space_write_1(bst, bsh, nsp_irqcr, regv); + + /******************************************* + * debug section + *******************************************/ +#ifdef NSP_DEBUG + if (nsp_debug) + { + nsp_error(sc, "current status", isrc, ph, irqphs); + scsi_low_print(slp, NULL); +#ifdef DDB + if (nsp_debug > 1) + SCSI_LOW_DEBUGGER("nsp"); +#endif /* DDB */ + } +#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_Tnexus) == NULL) + { + /* unknown scsi phase changes */ + nsp_error(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, NSP_TIMER_1MS); + return 1; + } + + SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED); + nsphw_selection_done_and_expect_msgout(sc); + return 1; + + case PH_SELECTED: + if ((isrc & IRQSR_SCSI) == 0) + return 1; + + nsp_target_nexus_establish(sc); + break; + + case PH_RESEL: + if ((isrc & IRQSR_SCSI) == 0) + return 1; + + nsp_target_nexus_establish(sc); + if ((ph & SCBUSMON_PHMASK) != PHASE_MSGIN) + { + printf("%s: unexpected phase after reselect\n", + slp->sl_xname); + slp->sl_error |= FATALIO; + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); + return 1; + } + break; + + case PH_DATA: + if ((isrc & IRQSR_SCSI) != 0) + break; + if ((isrc & IRQSR_FIFO) != 0) + { + if (NSP_IS_PHASE_DATA(ph) == 0) + return 1; + irqphs = (ph & IRQPHS_PHMASK); + break; + } + return 1; + + default: + if ((isrc & IRQSR_SCSI) == 0) + return 1; + break; + } + + /******************************************* + * data phase control + *******************************************/ + if (slp->sl_flags & HW_PDMASTART) + { + if ((isrc & IRQSR_SCSI) != 0 && + NSP_IS_IRQPHS_DATA(irqphs) == 0) + { + if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + nsp_pio_read(sc, 0); + nsp_pdma_end(sc, ti); + } + } + + /******************************************* + * scsi seq + *******************************************/ + if (slp->sl_msgphase != 0 && (irqphs & IRQPHS_LBF) != 0) + return nsp_disconnected(sc, ti); + + /* check unexpected bus free state */ + if (ph == 0) + { + nsp_error(sc, "unexpected bus free", isrc, ph, irqphs); + return nsp_disconnected(sc, ti); + } + + /* check normal scsi phase */ + switch (irqphs & IRQPHS_PHMASK) + { + case IRQPHS_CMD: + if (nsp_phase_match(sc, PHASE_CMD, ph) != 0) + return 1; + + SCSI_LOW_SETUP_PHASE(ti, PH_CMD); + if (scsi_low_cmd(slp, ti) != 0) + { + scsi_low_attention(slp); + } + + 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 IRQPHS_DATAOUT: + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0) + { + scsi_low_attention(slp); + } + + pp = physio_proc_enter(bp); + nsp_pio_write(sc, sc->sc_suspendio); + physio_proc_leave(pp); + break; + + case IRQPHS_DATAIN: + SCSI_LOW_SETUP_PHASE(ti, PH_DATA); + if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0) + { + scsi_low_attention(slp); + } + + pp = physio_proc_enter(bp); + nsp_pio_read(sc, sc->sc_suspendio); + physio_proc_leave(pp); + break; + + case IRQPHS_STATUS: + if (nsp_phase_match(sc, PHASE_STATUS, ph) != 0) + return 1; + + SCSI_LOW_SETUP_PHASE(ti, PH_STAT); + regv = nsp_cr_read_1(bst, bsh, NSPR_DATA); + if (nsp_cr_read_1(bst, bsh, NSPR_PARITYR) & PARITYR_PE) + { + nsp_cr_write_1(bst, bsh, NSPR_PARITYR, + PARITYR_ENABLE | PARITYR_CLEAR); + derror = SCSI_LOW_DATA_PE; + } + else + derror = 0; + + /* assert ACK */ + cr = SCBUSCR_ACK | nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + + if (scsi_low_statusin(slp, ti, derror | regv) != 0) + { + scsi_low_attention(slp); + } + + /* check REQ nagated */ + nsp_negate_signal(sc, SCBUSMON_REQ, "statin<REQ>"); + + /* deassert ACK */ + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) & (~SCBUSCR_ACK); + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + break; + + case IRQPHS_MSGOUT: + if (nsp_phase_match(sc, PHASE_MSGOUT, ph) != 0) + return 1; + +#ifdef NSP_MSGOUT_SERIALIZE + /* + * 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); + flags = (ti->ti_ophase != ti->ti_phase) ? + SCSI_LOW_MSGOUT_INIT : 0; + len = scsi_low_msgout(slp, ti, flags); + + if (len > 1 && slp->sl_atten == 0) + { + scsi_low_attention(slp); + } + + if (nsp_xfer(sc, ti->ti_msgoutstr, len, PHASE_MSGOUT, + slp->sl_clear_atten) != 0) + { + slp->sl_error |= FATALIO; + nsp_error(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); + +#else /* !NSP_MSGOUT_SERIALIZE */ + SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT); + flags = SCSI_LOW_MSGOUT_UNIFY; + if (ti->ti_ophase != ti->ti_phase) + flags |= SCSI_LOW_MSGOUT_INIT; + len = scsi_low_msgout(slp, ti, flags); + + if (len > 1 && slp->sl_atten == 0) + { + scsi_low_attention(slp); + } + + if (nsp_xfer(sc, ti->ti_msgoutstr, len, PHASE_MSGOUT, + slp->sl_clear_atten) != 0) + { + nsp_error(sc, "MSGOUT: xfer short", isrc, ph, irqphs); + } + +#endif /* !NSP_MSGOUT_SERIALIZE */ + break; + + case IRQPHS_MSGIN: + if (nsp_phase_match(sc, PHASE_MSGIN, ph) != 0) + return 1; + + /* + * 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 { + SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN); + + /* read a data */ + regv = nsp_cr_read_1(bst, bsh, NSPR_DATA); + if (nsp_cr_read_1(bst, bsh, NSPR_PARITYR) & PARITYR_PE) + { + nsp_cr_write_1(bst, bsh, + NSPR_PARITYR, + PARITYR_ENABLE | PARITYR_CLEAR); + derror = SCSI_LOW_DATA_PE; + } + else + { + derror = 0; + } + + /* assert ack */ + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) | SCBUSCR_ACK; + nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr); + + if (scsi_low_msgin(slp, ti, regv | derror) == 0) + { + if (scsi_low_is_msgout_continue(ti, 0) != 0) + { + scsi_low_attention(slp); + } + } + + /* check REQ nagated */ + nsp_negate_signal(sc, SCBUSMON_REQ, "msgin<REQ>"); + + /* deassert ack */ + cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) & (~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; + + default: + slp->sl_error |= FATALIO; + nsp_error(sc, "unknown scsi phase", isrc, ph, irqphs); + break; + } + + return 1; + +#if 0 +timerout: + nsp_start_timer(sc, NSP_TIMER_1MS); + return 0; +#endif +} + +static int +nsp_timeout(sc) + struct nsp_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; + int tout; + u_int8_t ph, regv; + + if (slp->sl_Tnexus == NULL) + return 0; + + ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON); + switch (ph & SCBUSMON_PHMASK) + { + case PHASE_DATAOUT: + if (sc->sc_dataout_timeout == 0) + break; + + /* check a fifo empty */ + regv = bus_space_read_1(iot, ioh, nsp_fifosr); + if ((regv & FIFOSR_FULLEMP) == 0) + break; + bus_space_write_1(iot, ioh, nsp_irqcr, IRQCR_FIFOCL); + + /* check still requested */ + ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON); + if ((ph & SCBUSMON_REQ) == 0) + break; + /* check timeout */ + if ((-- sc->sc_dataout_timeout) > 0) + break; + + slp->sl_error |= PDMAERR; + if ((slp->sl_flags & HW_WRITE_PADDING) == 0) + { + printf("%s: write padding required\n", slp->sl_xname); + break; + } + + tout = NSP_DELAY_MAX; + while (tout -- > 0) + { + ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON); + if ((ph & SCBUSMON_PHMASK) != PHASE_DATAOUT) + break; + regv = bus_space_read_1(iot, ioh, nsp_fifosr); + if ((regv & FIFOSR_FULLEMP) == 0) + { + SCSI_LOW_DELAY(1); + continue; + } + + bus_space_write_1(iot, ioh, nsp_irqcr, IRQCR_FIFOCL); + nsp_data_padding(sc, SCSI_LOW_WRITE, 32); + } + ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON); + if ((ph & SCBUSMON_PHMASK) == PHASE_DATAOUT) + sc->sc_dataout_timeout = SCSI_LOW_TIMEOUT_HZ; + break; + + default: + break; + } + return 0; +} diff --git a/sys/dev/nsp/nsp_pccard.c b/sys/dev/nsp/nsp_pccard.c new file mode 100644 index 0000000..c5b505b --- /dev/null +++ b/sys/dev/nsp/nsp_pccard.c @@ -0,0 +1,343 @@ +/* $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/malloc.h> +#include <sys/errno.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <machine/dvcfg.h> + +#include <sys/device_port.h> + +#include <dev/pccard/pccarddevs.h> +#include <dev/pccard/pccardvar.h> + +#include <cam/scsi/scsi_low.h> +#include <cam/scsi/scsi_low_pisa.h> + +#include <dev/nsp/nspreg.h> +#include <dev/nsp/nspvar.h> + +#define NSP_HOSTID 7 + +#include <sys/kernel.h> +#include <sys/module.h> +#if !defined(__FreeBSD__) || __FreeBSD_version < 500014 +#include <sys/select.h> +#endif +#include <pccard/cardinfo.h> +#include <pccard/slot.h> + +#define PIO_MODE 0x100 /* pd_flags */ + +static int nspprobe(DEVPORT_PDEVICE devi); +static int nspattach(DEVPORT_PDEVICE devi); + +static void nsp_card_unload (DEVPORT_PDEVICE); + +const struct pccard_product nsp_products[] = { + PCMCIA_CARD(IODATA3, CBSC16, 0), + PCMCIA_CARD(PANASONIC, KME, 0), + PCMCIA_CARD(WORKBIT2, NINJA_SCSI3, 0), + PCMCIA_CARD(WORKBIT, ULTRA_NINJA_16, 0), + { NULL } +}; + +/* + * 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 ioaddr, iosize, maddr, msize; + int error; + + error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &ioaddr, &iosize); + if (error || iosize < NSP_IOSIZE) + return(ENOMEM); + + 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); + } + + error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &maddr, &msize); + if (error) { + return(0); /* XXX */ + } + + /* No need to allocate memory if not configured and it's in PIO mode */ + if (maddr == 0 || msize == 0) { + if ((DEVPORT_PDEVFLAGS(dev) & PIO_MODE) == 0) { + printf("Memory window was not configured. Configure or use in PIO mode."); + nsp_release_resource(dev); + return(ENOMEM); + } + /* no need to allocate memory if PIO mode */ + return(0); + } + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->mem_res == NULL) { + nsp_release_resource(dev); + return(ENOMEM); + } + + return(0); +} + +static int nsp_pccard_match(device_t dev) +{ + const struct pccard_product *pp; + + if ((pp = pccard_product_lookup(dev, nsp_products, + sizeof(nsp_products[0]), NULL)) != NULL) { + device_set_desc(dev, pp->pp_name); + return(0); + } + return(EIO); +} + +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 | INTR_ENTROPY, + 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, pccard_compat_probe), + DEVMETHOD(device_attach, pccard_compat_attach), + DEVMETHOD(device_detach, nsp_pccard_detach), + + /* Card interface */ + DEVMETHOD(card_compat_match, nsp_pccard_match), + DEVMETHOD(card_compat_probe, nsp_pccard_probe), + DEVMETHOD(card_compat_attach, nsp_pccard_attach), + + { 0, 0 } +}; + +static driver_t nsp_pccard_driver = { + "nsp", + nsp_pccard_methods, + sizeof(struct nsp_softc), +}; + +static devclass_t nsp_devclass; + +MODULE_DEPEND(nsp, scsi_low, 1, 1, 1); +DRIVER_MODULE(nsp, pccard, nsp_pccard_driver, nsp_devclass, 0, 0); + +static void +nsp_card_unload(DEVPORT_PDEVICE devi) +{ + struct nsp_softc *sc = DEVPORT_PDEVGET_SOFTC(devi); + intrmask_t s; + + printf("%s: unload\n",sc->sc_sclow.sl_xname); + s = splcam(); + scsi_low_deactivate((struct scsi_low_softc *)sc); + scsi_low_dettach(&sc->sc_sclow); + splx(s); +} + +static int +nspprobe(DEVPORT_PDEVICE devi) +{ + int rv; + 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)); + + return rv; +} + +static int +nspattach(DEVPORT_PDEVICE devi) +{ + struct nsp_softc *sc; + struct scsi_low_softc *slp; + u_int32_t flags = DEVPORT_PDEVFLAGS(devi); + u_int iobase = DEVPORT_PDEVIOBASE(devi); + intrmask_t s; + char dvname[16]; + + strcpy(dvname,"nsp"); + + 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; + slp->sl_dev = devi; + sc->sc_iot = rman_get_bustag(sc->port_res); + sc->sc_ioh = rman_get_bushandle(sc->port_res); + + if(sc->mem_res == NULL){ + printf("WARNING: CANNOT GET Memory RESOURCE going PIO mode"); + flags |= PIO_MODE; + } + + if((flags & PIO_MODE) == 0) { + sc->sc_memt = rman_get_bustag(sc->mem_res); + sc->sc_memh = rman_get_bushandle(sc->mem_res); + } 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; + + s = splcam(); + nspattachsubr(sc); + splx(s); + + return(NSP_IOSIZE); +} diff --git a/sys/dev/nsp/nspreg.h b/sys/dev/nsp/nspreg.h new file mode 100644 index 0000000..3bfcd96 --- /dev/null +++ b/sys/dev/nsp/nspreg.h @@ -0,0 +1,221 @@ +/* $FreeBSD$ */ +/* $NecBSD: nspreg.h,v 1.4.14.3 2001/06/29 06:27:53 honda 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 IRQPHS_PHMASK (IRQPHS_LCD | IRQPHS_LMSG | IRQPHS_LIO) + +#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 PARITYR_ENABLE 0x01 +#define PARITYR_CLEAR 0x02 +#define PARITYR_PE 0x02 + +#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 PTCLRR_RSS_ACK 0x00 +#define PTCLRR_RSS_REQ 0x10 +#define PTCLRR_RSS_HOST 0x20 + +#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) + +/* Data phase */ +#define NSP_IS_PHASE_DATA(ph) \ + ((((ph) & SCBUSMON_PHMASK) & ~SCBUSMON_IO) == 0) +#define NSP_IS_IRQPHS_DATA(ph) \ + ((((ph) & IRQPHS_PHMASK) & ~SCBUSMON_IO) == 0) + +/* 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) + +#define IRQPHS_CMD (IRQPHS_LCD) +#define IRQPHS_DATAIN (IRQPHS_LIO) +#define IRQPHS_DATAOUT (0) +#define IRQPHS_STATUS (IRQPHS_LCD | IRQPHS_LIO) +#define IRQPHS_MSGIN (IRQPHS_LCD | IRQPHS_LMSG | IRQPHS_LIO) +#define IRQPHS_MSGOUT (IRQPHS_LCD | IRQPHS_LMSG) + +/* 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..5761408 --- /dev/null +++ b/sys/dev/nsp/nspvar.h @@ -0,0 +1,113 @@ +/* $FreeBSD$ */ +/* $NecBSD: nspvar.h,v 1.7.14.5 2001/06/29 06:27:54 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1998, 1999, 2000, 2001 + * NetBSD/pc98 porting staff. All rights reserved. + * + * Copyright (c) 1998, 1999, 2000, 2001 + * 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 _NSPVAR_H_ +#define _NSPVAR_H_ + +/***************************************************************** + * Host adapter structure + *****************************************************************/ +struct nsp_softc { + struct scsi_low_softc sc_sclow; /* generic data */ + +#ifdef __NetBSD__ + 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; +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_space_tag_t sc_memt; + bus_space_handle_t sc_memh; + + 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 /* __FreeBSD__ */ + + int sc_tmaxcnt; /* timeout count */ + int sc_seltout; /* selection timeout counter */ + int sc_timer; /* timer start */ + + int sc_suspendio; /* SMIT: data suspendio bytes */ + u_int8_t sc_xfermr; /* SMIT: fifo control reg */ + int sc_dataout_timeout; /* data out timeout counter */ + + u_int sc_idbit; /* host id bit pattern */ + 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_icr; /* interrupt control reg */ + + u_int8_t sc_busc; /* busc registers */ + u_int8_t sc_parr; /* parity control register */ +}; + +/***************************************************************** + * Lun information + *****************************************************************/ +struct nsp_targ_info { + struct targ_info nti_ti; /* generic lun info */ + + u_int8_t nti_reg_syncr; /* sync registers per devices */ + u_int8_t nti_reg_ackwidth; /* ackwidth per devices */ +}; + +/***************************************************************** + * Proto + *****************************************************************/ +int nspprobesubr(bus_space_tag_t, bus_space_handle_t, u_int); +void nspattachsubr(struct nsp_softc *); +int nspprint(void *, const char *); +int nspintr(void *); + +#if defined(__i386__) && 0 +#define SOFT_INTR_REQUIRED(slp) (softintr((slp)->sl_irq)) +#else /* !__i386__ */ +#define SOFT_INTR_REQUIRED(slp) +#endif /* !__i386__ */ +#endif /* !_NSPVAR_H_ */ |