diff options
29 files changed, 10072 insertions, 0 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index fa8bac7..24f252e 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -94,6 +94,10 @@ device bt device aha 1 device aic +device ncv # NCR 53C500 +device nsp # Workbit Ninja SCSI-3 +device stg # TMC 18C30/18C50 + # RAID controllers interfaced to the SCSI subsystem device asr # DPT SmartRAID V, VI and Adaptec SCSI RAID device dpt # DPT Smartcache III, IV - See NOTES for options! diff --git a/sys/cam/scsi/scsi_dvcfg.h b/sys/cam/scsi/scsi_dvcfg.h new file mode 100644 index 0000000..b963e5d --- /dev/null +++ b/sys/cam/scsi/scsi_dvcfg.h @@ -0,0 +1,58 @@ +/* $FreeBSD$ */ +/* $NecBSD: scsi_dvcfg.h,v 1.4 1998/03/14 07:05:06 kmatsuda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1994, 1995, 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1994, 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 _SCSI_DVCFG_H_ +#define _SCSI_DVCFG_H_ + +/* common dvcfg flags defitions for bs, ncv, stg */ + +#define DVF_SCSI_SYNC 0x01 +#define DVF_SCSI_DISC 0x02 +#define DVF_SCSI_WAIT 0x04 +#define DVF_SCSI_LINK 0x08 +#define DVF_SCSI_QTAG 0x10 +#define DVF_SCSI_SP0 0x100 +#define DVF_SCSI_NOPARITY 0x200 +#define DVF_SCSI_SAVESP 0x400 +#define DVF_SCSI_SP1 0x800 +#define DVF_SCSI_PERIOD(XXX) (((XXX) >> 24) & 0xff) +#define DVF_SCSI_OFFSET(XXX) (((XXX) >> 16) & 0xff) +#define DVF_SCSI_SYNCMASK 0xffff0000 + +#define DVF_SCSI_DEFCFG (DVF_SCSI_SYNC | DVF_SCSI_NOPARITY | DVF_SCSI_SYNCMASK) + +#define DVF_SCSI_BITS "\020\13fssp\12noparity\11nosat\005qtag\004cmdlnk\003wait\002disc\001sync" + +#endif /* !_SCSI_DVCFG_H_ */ diff --git a/sys/cam/scsi/scsi_low.c b/sys/cam/scsi/scsi_low.c new file mode 100644 index 0000000..1d0a01d --- /dev/null +++ b/sys/cam/scsi/scsi_low.c @@ -0,0 +1,2590 @@ +/* $FreeBSD$ */ +/* $NecBSD: scsi_low.c,v 1.24 1999/07/26 06:27:01 honda Exp $ */ +/* $NetBSD$ */ + +#define SCSI_LOW_STATICS +#define SCSI_LOW_WARNINGS +#ifdef __NetBSD__ +#define SCSI_LOW_TARGET_OPEN +#define SCSI_LOW_INFORM +#endif +#ifdef __FreeBSD__ +#define CAM +#endif + +/* + * [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. + */ + +/* <On the nexus establishment> + * When our host is reselected, + * nexus establish processes are little complicated. + * Normal steps are followings: + * 1) Our host selected by target => target nexus (slp->sl_nexus) + * 2) Identify msgin => lun nexus (ti->ti_li) + * 3) Qtag msg => slccb nexus (ti->ti_nexus) + */ +#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> + +#include <machine/bus.h> +#ifdef __NetBSD__ +#include <machine/intr.h> +#endif +#include <machine/dvcfg.h> + +#ifdef __NetBSD__ +#include <dev/cons.h> + +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsipiconf.h> +#include <dev/scsipi/scsipi_disk.h> +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsiconf.h> + +#include <i386/Cbus/dev/scsi_low.h> +#endif +#ifdef __FreeBSD__ +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> +#include <cam/cam_periph.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +#include <cam/scsi/scsi_low.h> + +#if !defined(__FreeBSD__) || __FreeBSD_version < 400001 +#include <i386/i386/cons.h> +#else +#include <sys/cons.h> +#endif +#include <machine/clock.h> +#define delay(time) DELAY(time) + +/* from sys/dev/usb/usb_port.h + XXX Change this when FreeBSD has memset + */ +#define memset(d, v, s) \ + do{ \ + if ((v) == 0) \ + bzero((d), (s)); \ + else \ + panic("Non zero filler for memset, cannot handle!"); \ + } while (0) +#endif + +#define SCSI_LOW_DONE_COMPLETE 0 +#define SCSI_LOW_DONE_RETRY 1 + +static void scsi_low_engage __P((void *)); +static void scsi_low_info __P((struct scsi_low_softc *, struct targ_info *, u_char *)); +static void scsi_low_init_msgsys __P((struct scsi_low_softc *, struct targ_info *)); +static struct slccb *scsi_low_establish_ccb __P((struct targ_info *, struct lun_info *, scsi_low_tag_t)); +static int scsi_low_done __P((struct scsi_low_softc *, struct slccb *)); +static void scsi_low_twiddle_wait __P((void)); +static struct lun_info *scsi_low_alloc_li __P((struct targ_info *, int, int)); +static struct targ_info *scsi_low_alloc_ti __P((struct scsi_low_softc *, int)); +static void scsi_low_calcf __P((struct targ_info *, struct lun_info *)); +static struct lun_info *scsi_low_establish_lun __P((struct targ_info *, int)); +#ifndef CAM +static void scsi_low_scsi_minphys __P((struct buf *)); +#endif +#ifdef SCSI_LOW_TARGET_OPEN +static int scsi_low_target_open __P((struct scsipi_link *, struct cfdata *)); +#endif /* SCSI_LOW_TARGET_OPEN */ +#ifdef CAM +void scsi_low_scsi_action(struct cam_sim *sim, union ccb *ccb); +static void scsi_low_poll (struct cam_sim *sim); +#else +static int scsi_low_scsi_cmd __P((struct scsipi_xfer *)); +#endif +static void scsi_low_reset_nexus __P((struct scsi_low_softc *, int)); +static int scsi_low_init __P((struct scsi_low_softc *, u_int)); +static void scsi_low_start __P((struct scsi_low_softc *)); +static void scsi_low_free_ti __P((struct scsi_low_softc *)); +static void scsi_low_clear_ccb __P((struct slccb *)); + +#ifdef SCSI_LOW_STATICS +struct scsi_low_statics { + int nexus_win; + int nexus_fail; + int nexus_disconnected; + int nexus_reselected; + int nexus_conflict; +} scsi_low_statics; +#endif /* SCSI_LOW_STATICS */ +/************************************************************** + * power control + **************************************************************/ +static void +scsi_low_engage(arg) + void *arg; +{ + struct scsi_low_softc *slp = arg; + int s = splbio(); + + switch (slp->sl_rstep) + { + case 0: + slp->sl_rstep ++; + (*slp->sl_funcs->scsi_low_power) (slp, SCSI_LOW_ENGAGE); +#ifdef __FreeBSD__ + slp->engage_ch = +#endif + timeout(scsi_low_engage, slp, 1); + break; + + case 1: + slp->sl_rstep ++; + slp->sl_flags &= ~HW_RESUME; + scsi_low_start(slp); + break; + + case 2: + break; + } + splx(s); +} + +static int +scsi_low_init(slp, flags) + struct scsi_low_softc *slp; + u_int flags; +{ + + if ((slp->sl_flags & HW_POWERCTRL) != 0) + { +#ifdef __FreeBSD__ + untimeout(scsi_low_engage, slp, slp->engage_ch); +#else /* NetBSD */ + untimeout(scsi_low_engage, slp); +#endif + slp->sl_flags &= ~(HW_POWDOWN | HW_RESUME); + slp->sl_active = 1; + slp->sl_powc = SCSI_LOW_POWDOWN_TC; + } + + /* reset current nexus */ + scsi_low_reset_nexus(slp, flags); + if ((slp->sl_flags & HW_INACTIVE) != 0) + return EBUSY; + + if (flags == SCSI_LOW_RESTART_SOFT) + return 0; + + return ((*slp->sl_funcs->scsi_low_init) (slp, flags)); +} + +/************************************************************** + * allocate lun_info + **************************************************************/ +static struct lun_info * +scsi_low_alloc_li(ti, lun, alloc) + struct targ_info *ti; + int lun; + int alloc; +{ + struct scsi_low_softc *slp = ti->ti_sc; + struct lun_info *li; + + li = LIST_FIRST(&ti->ti_litab); + if (li != NULL) + { + if (li->li_lun == lun) + return li; + + while ((li = LIST_NEXT(li, lun_chain)) != NULL) + { + if (li->li_lun == lun) + { + LIST_REMOVE(li, lun_chain); + LIST_INSERT_HEAD(&ti->ti_litab, li, lun_chain); + return li; + } + } + } + + if (alloc == 0) + return li; + + li = malloc(ti->ti_lunsize, M_DEVBUF, M_NOWAIT); + if (li == NULL) + panic("no lun info mem\n"); + + memset(li, 0, ti->ti_lunsize); + li->li_lun = lun; + li->li_ti = ti; +#if defined(SDEV_NOPARITY) && defined(SDEV_NODISC) + li->li_quirks = SDEV_NOPARITY | SDEV_NODISC; +#endif /* SDEV_NOPARITY && SDEV_NODISC */ + li->li_cfgflags = 0xffff0000 | SCSI_LOW_SYNC; + + LIST_INSERT_HEAD(&ti->ti_litab, li, lun_chain); + + /* host specific structure initialization per lun */ + (void) ((*slp->sl_funcs->scsi_low_lun_init) (slp, ti, li)); + + return li; +} + +/************************************************************** + * allocate targ_info + **************************************************************/ +static struct targ_info * +scsi_low_alloc_ti(slp, targ) + struct scsi_low_softc *slp; + int targ; +{ + struct targ_info *ti; + + if (slp->sl_titab.tqh_first == NULL) + TAILQ_INIT(&slp->sl_titab); + + ti = malloc(sizeof(struct targ_info), M_DEVBUF, M_NOWAIT); + if (ti == NULL) + panic("%s short of memory\n", slp->sl_xname); + + memset(ti, 0, sizeof(struct targ_info)); + ti->ti_id = targ; + ti->ti_sc = slp; + + slp->sl_ti[targ] = ti; + TAILQ_INSERT_TAIL(&slp->sl_titab, ti, ti_chain); + TAILQ_INIT(&ti->ti_discq); + LIST_INIT(&ti->ti_litab); + + return ti; +} + +static void +scsi_low_free_ti(slp) + struct scsi_low_softc *slp; +{ + struct targ_info *ti, *tib; + struct lun_info *li, *nli; + + for (ti = slp->sl_titab.tqh_first; ti; ti = tib) + { + tib = ti->ti_chain.tqe_next; + for (li = LIST_FIRST(&ti->ti_litab); li != NULL; li = nli) + { + nli = LIST_NEXT(li, lun_chain); + free(li, M_DEVBUF); + } + free(ti, M_DEVBUF); + } +} + +/************************************************************** + * timeout + **************************************************************/ +void +scsi_low_timeout(arg) + void *arg; +{ + struct scsi_low_softc *slp = arg; + struct targ_info *ti; + struct slccb *cb = NULL; /* XXX */ + int s = splbio(); + + /* check */ + if ((ti = slp->sl_nexus) != NULL && (cb = ti->ti_nexus) != NULL) + { + cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; + if (cb->ccb_tc < 0) + goto bus_reset; + } + else if (slp->sl_disc > 0) + { + struct targ_info *ti; + + for (ti = slp->sl_titab.tqh_first; ti != NULL; + ti = ti->ti_chain.tqe_next) + { + for (cb = ti->ti_discq.tqh_first; cb != NULL; + cb = cb->ccb_chain.tqe_next) + { + cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; + if (cb->ccb_tc < 0) + goto bus_reset; + } + } + } + else + { + cb = slp->sl_start.tqh_first; + if (cb != NULL) + { + cb->ccb_tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL; + if (cb->ccb_tc < 0) + goto bus_reset; + } + else if ((slp->sl_flags & HW_POWERCTRL) != 0) + { + if ((slp->sl_flags & (HW_POWDOWN | HW_RESUME)) != 0) + goto out; + + if (slp->sl_active != 0) + { + slp->sl_powc = SCSI_LOW_POWDOWN_TC; + slp->sl_active = 0; + goto out; + } + + slp->sl_powc --; + if (slp->sl_powc < 0) + { + slp->sl_powc = SCSI_LOW_POWDOWN_TC; + slp->sl_flags |= HW_POWDOWN; + (*slp->sl_funcs->scsi_low_power) + (slp, SCSI_LOW_POWDOWN); + } + } + } + +out: +#ifdef __FreeBSD__ + slp->timeout_ch = +#endif + timeout(scsi_low_timeout, slp, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + + splx(s); + return; + +bus_reset: + cb->ccb_error |= TIMEOUTIO; + scsi_low_info(slp, NULL, "scsi bus hangup. try to recover."); + scsi_low_init(slp, SCSI_LOW_RESTART_HARD); + scsi_low_start(slp); +#ifdef __FreeBSD__ + slp->timeout_ch = +#endif + timeout(scsi_low_timeout, slp, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + + splx(s); +} + +/************************************************************** + * CCB + **************************************************************/ +GENERIC_CCB_STATIC_ALLOC(scsi_low, slccb) +GENERIC_CCB(scsi_low, slccb, ccb_chain) + +/************************************************************** + * SCSI INTERFACE (XS) + **************************************************************/ +#define SCSI_LOW_MINPHYS 0x10000 + +#ifdef __NetBSD__ +struct scsipi_device scsi_low_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; +#endif + +#ifdef CAM +static void +scsi_low_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) +{ + xpt_free_path(ccb->ccb_h.path); + free(ccb, M_DEVBUF); +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + free(periph, M_DEVBUF); +#endif +} + +static void +scsi_low_rescan_bus(struct scsi_low_softc *slp) +{ + struct cam_path *path; + union ccb *ccb = malloc(sizeof(union ccb), M_DEVBUF, M_WAITOK); +#if defined(__FreeBSD__) && __FreeBSD_version < 400001 + struct cam_periph *xpt_periph = malloc(sizeof(struct cam_periph), + M_DEVBUF, M_WAITOK); +#endif + cam_status status; + + bzero(ccb, sizeof(union ccb)); + + status = xpt_create_path(&path, xpt_periph, + cam_sim_path(slp->sim), -1, 0); + if (status != CAM_REQ_CMP) + return; + + xpt_setup_ccb(&ccb->ccb_h, path, 5); + ccb->ccb_h.func_code = XPT_SCAN_BUS; + ccb->ccb_h.cbfcnp = scsi_low_cam_rescan_callback; + ccb->crcn.flags = CAM_FLAG_NONE; + xpt_action(ccb); +} +#endif + +int +scsi_low_attach(slp, openings, ntargs, nluns, lunsize) + struct scsi_low_softc *slp; + int openings, ntargs, nluns, lunsize; +{ + struct targ_info *ti; + struct lun_info *li; +#ifdef CAM + struct cam_devq *devq; +#else + struct scsipi_adapter *sap; +#endif + int i, nccb; + +#ifdef CAM + OS_DEPEND(sprintf(slp->sl_xname, "%s%d", + DEVPORT_DEVNAME(slp->sl_dev), DEVPORT_DEVUNIT(slp->sl_dev))); +#else + OS_DEPEND(strncpy(slp->sl_xname, DEVPORT_DEVNAME(slp->sl_dev), 16)); +#endif + if (ntargs > SCSI_LOW_NTARGETS) + { + printf("scsi_low: %d targets are too large\n", ntargs); + printf("change kernel options SCSI_LOW_NTARGETS"); + } + + if (lunsize < sizeof(struct lun_info)) + lunsize = sizeof(struct lun_info); + + for (i = 0; i < ntargs; i ++) + { + ti = scsi_low_alloc_ti(slp, i); + ti->ti_lunsize = lunsize; + li = scsi_low_alloc_li(ti, 0, 1); + } + +#ifndef CAM + sap = malloc(sizeof(*sap), M_DEVBUF, M_NOWAIT); + if (sap == NULL) + return ENOMEM; + + memset(sap, 0, sizeof(*sap)); + sap->scsipi_cmd = scsi_low_scsi_cmd; + sap->scsipi_minphys = scsi_low_scsi_minphys; +#ifdef SCSI_LOW_TARGET_OPEN + sap->open_target_lu = scsi_low_target_open; +#endif /* SCSI_LOW_TARGET_OPEN */ +#endif + + if (scsi_low_init(slp, SCSI_LOW_RESTART_HARD) != 0) + return EINVAL; + + /* initialize queue */ + nccb = openings * (ntargs - 1); + if (nccb >= SCSI_LOW_NCCB || nccb <= 0) + nccb = SCSI_LOW_NCCB; + scsi_low_init_ccbque(nccb); + TAILQ_INIT(&slp->sl_start); + + slp->sl_openings = openings; + slp->sl_ntargs = ntargs; + slp->sl_nluns = nluns; + +#ifdef CAM + /* + * Prepare the scsibus_data area for the upperlevel + * scsi code. + */ + devq = cam_simq_alloc(256/*MAX_START*/); + if (devq == NULL) + return (0); + /* scbus->adapter_link = &slp->sc_link; */ + /* + * ask the adapter what subunits are present + */ + + slp->sim = cam_sim_alloc(scsi_low_scsi_action, scsi_low_poll, + DEVPORT_DEVNAME(slp->sl_dev), slp, + DEVPORT_DEVUNIT(slp->sl_dev), 1, 32/*MAX_TAGS*/, devq); + if (slp->sim == NULL) { + cam_simq_free(devq); + return 0; + } + + if (xpt_bus_register(slp->sim, 0) != CAM_SUCCESS) { + free(slp->sim, M_DEVBUF); + return 0; + } + + if (xpt_create_path(&slp->path, /*periph*/NULL, + cam_sim_path(slp->sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(slp->sim)); + cam_sim_free(slp->sim, /*free_simq*/TRUE); + free(slp->sim, M_DEVBUF); + return 0; + } +#else /* !CAM */ + slp->sl_link.adapter_softc = slp; + slp->sl_link.scsipi_scsi.adapter_target = slp->sl_hostid; + slp->sl_link.scsipi_scsi.max_target = ntargs - 1; + slp->sl_link.scsipi_scsi.max_lun = nluns - 1; + slp->sl_link.scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE; + slp->sl_link.openings = openings; + slp->sl_link.type = BUS_SCSI; + slp->sl_link.adapter_softc = slp; + slp->sl_link.adapter = sap; + slp->sl_link.device = &scsi_low_dev; +#endif + + /* start watch dog */ + slp->sl_max_retry = SCSI_LOW_MAX_RETRY; +#ifdef __FreeBSD__ + slp->timeout_ch = +#endif + timeout(scsi_low_timeout, slp, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); +#ifdef CAM + if (!cold) + scsi_low_rescan_bus(slp); +#endif + + return 0; +} + +#ifndef CAM +static void +scsi_low_scsi_minphys(bp) + struct buf *bp; +{ + + if (bp->b_bcount > SCSI_LOW_MINPHYS) + bp->b_bcount = SCSI_LOW_MINPHYS; + minphys(bp); +} +#endif + +int +scsi_low_dettach(slp) + struct scsi_low_softc *slp; +{ + + if (slp->sl_disc > 0 || slp->sl_start.tqh_first != NULL) + return EBUSY; + + /* + * scsipi does not have dettach bus fucntion. + * + scsipi_dettach_scsibus(&slp->sl_link); + */ + +#ifdef CAM + xpt_async(AC_LOST_DEVICE, slp->path, NULL); + xpt_free_path(slp->path); + xpt_bus_deregister(cam_sim_path(slp->sim)); + cam_sim_free(slp->sim, /* free_devq */ TRUE); +#endif + + scsi_low_free_ti(slp); + return 0; +} + +#ifdef CAM +static void +scsi_low_poll(struct cam_sim *sim) +{ + struct scsi_low_softc *slp = (struct scsi_low_softc *) cam_sim_softc(sim); + (*slp->sl_funcs->scsi_low_poll) (slp); +} + +void +scsi_low_scsi_action(struct cam_sim *sim, union ccb *ccb) +{ + struct scsi_low_softc *slp = (struct scsi_low_softc *) cam_sim_softc(sim); + int s, target = (u_int) (ccb->ccb_h.target_id); + struct targ_info *ti; + struct lun_info *li; + struct slccb *cb; + +#if 0 + printf("scsi_low_scsi_action() func code %d Target: %d, LUN: %d\n", + ccb->ccb_h.func_code, target, ccb->ccb_h.target_lun); +#endif + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: /* Execute the requested I/O operation */ + if (((cb = scsi_low_get_ccb()) == NULL)) { + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + } + + cb->ccb = ccb; + cb->ccb_tag = SCSI_LOW_UNKTAG; + cb->bp = (struct buf *)NULL; + cb->ti = ti = slp->sl_ti[target]; + cb->li = scsi_low_alloc_li(ti, ccb->ccb_h.target_lun, 1); + cb->ccb_flags = 0; + cb->ccb_rcnt = 0; + + s = splcam(); + + TAILQ_INSERT_TAIL(&slp->sl_start, cb, ccb_chain); + + if (slp->sl_nexus == NULL) { + scsi_low_start(slp); + } + + splx(s); + break; + case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ + case XPT_EN_LUN: /* Enable LUN as a target */ + case XPT_TARGET_IO: /* Execute target I/O request */ + case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ + case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ + case XPT_ABORT: /* Abort the specified CCB */ + /* XXX Implement */ + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + case XPT_SET_TRAN_SETTINGS: + /* XXX Implement */ + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + xpt_done(ccb); + break; + case XPT_GET_TRAN_SETTINGS: { + struct ccb_trans_settings *cts; + struct targ_info *ti; + int lun = ccb->ccb_h.target_lun; + /*int s;*/ + + cts = &ccb->cts; + ti = slp->sl_ti[ccb->ccb_h.target_id]; + li = LIST_FIRST(&ti->ti_litab); + if (li != NULL && li->li_lun != lun) + while ((li = LIST_NEXT(li, lun_chain)) != NULL) + if (li->li_lun == lun) + break; + s = splcam(); + if (li != NULL && (cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { + if (li->li_cfgflags & SCSI_LOW_DISC) + cts->flags = CCB_TRANS_DISC_ENB; + else + cts->flags = 0; + if (li->li_cfgflags & SCSI_LOW_QTAG) + cts->flags |= CCB_TRANS_TAG_ENB; + + cts->bus_width = 0;/*HN2*/ + + cts->valid = CCB_TRANS_SYNC_RATE_VALID + | CCB_TRANS_SYNC_OFFSET_VALID + | CCB_TRANS_BUS_WIDTH_VALID + | CCB_TRANS_DISC_VALID + | CCB_TRANS_TQ_VALID; + ccb->ccb_h.status = CAM_REQ_CMP; + } else + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + + splx(s); + xpt_done(ccb); + break; + } + case XPT_CALC_GEOMETRY: { /* not yet HN2 */ + struct ccb_calc_geometry *ccg; + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + int extended; + + extended = 1; + ccg = &ccb->ccg; + size_mb = ccg->volume_size + / ((1024L * 1024L) / ccg->block_size); + + if (size_mb > 1024 && extended) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_RESET_BUS: /* Reset the specified SCSI bus */ +#if 0 + scsi_low_bus_reset(slp); +#endif + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + case XPT_TERM_IO: /* Terminate the I/O process */ + /* XXX Implement */ + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + case XPT_PATH_INQ: { /* Path routing inquiry */ + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; /* XXX??? */ + cpi->hba_inquiry = PI_SDTR_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = SCSI_LOW_NTARGETS - 1; + cpi->max_lun = 7; + cpi->initiator_id = 7; /* HOST_SCSI_ID */ + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 3300; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "SCSI_LOW", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + default: + printf("scsi_low: non support func_code = %d ", ccb->ccb_h.func_code); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} +#else /* !CAM */ +static int +scsi_low_scsi_cmd(xs) + struct scsipi_xfer *xs; +{ + struct scsi_low_softc *slp = xs->sc_link->adapter_softc; + struct targ_info *ti; + struct slccb *cb; + int s, lun, timeo; + + if (slp->sl_cfgflags & CFG_NOATTEN) + { + if (xs->sc_link->scsipi_scsi.lun > 0) + { + xs->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } + } + + if ((cb = scsi_low_get_ccb(xs->flags & SCSI_NOSLEEP)) == NULL) + return TRY_AGAIN_LATER; + + lun = xs->sc_link->scsipi_scsi.lun; + cb->xs = xs; + cb->ccb_tag = SCSI_LOW_UNKTAG; + cb->ti = ti = slp->sl_ti[xs->sc_link->scsipi_scsi.target]; + cb->li = scsi_low_alloc_li(ti, lun, 1); + cb->ccb_flags = 0; + cb->ccb_rcnt = 0; + + s = splbio(); + + TAILQ_INSERT_TAIL(&slp->sl_start, cb, ccb_chain); + if (slp->sl_nexus == NULL) + scsi_low_start(slp); + + if ((xs->flags & SCSI_POLL) == 0) + { + splx(s); + return SUCCESSFULLY_QUEUED; + } + +#define SCSI_LOW_POLL_INTERVAL 1000 /* 1 ms */ + timeo = xs->timeout * (1000 / SCSI_LOW_POLL_INTERVAL); + + while ((xs->flags & ITSDONE) == 0 && timeo -- > 0) + { + delay(SCSI_LOW_POLL_INTERVAL); + (*slp->sl_funcs->scsi_low_poll) (slp); + } + + if ((xs->flags & ITSDONE) == 0) + { + cb->ccb_error |= (TIMEOUTIO | ABORTIO); + SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_NULL); + scsi_low_disconnected(slp, ti); + scsi_low_init(slp, SCSI_LOW_RESTART_HARD); + } + + scsipi_done(xs); + splx(s); + return COMPLETE; +} +#endif + +/************************************************************** + * Start & Done + **************************************************************/ +#ifdef __NetBSD__ +static struct scsipi_start_stop ss_cmd = { START_STOP, 0, {0,0,}, SSS_START, }; +static struct scsipi_test_unit_ready unit_ready_cmd; +#endif +#ifdef __FreeBSD__ +static struct scsi_start_stop_unit ss_cmd = { START_STOP, 0, {0,0,}, SSS_START, }; +static struct scsi_test_unit_ready unit_ready_cmd; +#endif +static void scsi_low_unit_ready_cmd __P((struct slccb *)); + +static void +scsi_low_unit_ready_cmd(cb) + struct slccb *cb; +{ + + cb->ccb_scp.scp_cmd = (u_int8_t *) &unit_ready_cmd; + cb->ccb_scp.scp_cmdlen = sizeof(unit_ready_cmd); + cb->ccb_scp.scp_datalen = 0; + cb->ccb_scp.scp_direction = SCSI_LOW_READ; + cb->ccb_tcmax = 15; +} + +static void +scsi_low_start(slp) + struct scsi_low_softc *slp; +{ +#ifdef CAM + union ccb *ccb; +#else + struct scsipi_xfer *xs; +#endif + struct targ_info *ti; + struct lun_info *li; + struct slccb *cb; + int rv; + + /* check hardware exists ? */ + if ((slp->sl_flags & HW_INACTIVE) != 0) + return; + + /* check hardware power up ? */ + if ((slp->sl_flags & HW_POWERCTRL) != 0) + { + slp->sl_active ++; + if (slp->sl_flags & (HW_POWDOWN | HW_RESUME)) + { + if (slp->sl_flags & HW_RESUME) + return; + slp->sl_flags &= ~HW_POWDOWN; + if (slp->sl_funcs->scsi_low_power != NULL) + { + slp->sl_flags |= HW_RESUME; + slp->sl_rstep = 0; + (*slp->sl_funcs->scsi_low_power) + (slp, SCSI_LOW_ENGAGE); +#ifdef __FreeBSD__ + slp->engage_ch = +#endif + timeout(scsi_low_engage, slp, 1); + return; + } + } + } + + /* setup nexus */ +#ifdef SCSI_LOW_DIAGNOSTIC + ti = slp->sl_nexus; + if (ti != NULL) + { + scsi_low_info(slp, NULL, "NEXUS INCOSISTENT"); + panic("%s: inconsistent(target)\n", slp->sl_xname); + } +#endif /* SCSI_LOW_DIAGNOSTIC */ + + for (cb = slp->sl_start.tqh_first; cb != NULL; + cb = cb->ccb_chain.tqe_next) + { + ti = cb->ti; + li = cb->li; + if (ti->ti_phase == PH_NULL) + goto scsi_low_cmd_start; + if (ti->ti_phase == PH_DISC && li->li_disc < li->li_maxnexus) + goto scsi_low_cmd_start; + } + return; + +scsi_low_cmd_start: +#ifdef CAM + ccb = cb->ccb; +#else + xs = cb->xs; +#endif +#ifdef SCSI_LOW_DIAGNOSTIC + if (ti->ti_nexus != NULL || ti->ti_li != NULL) + { + scsi_low_info(slp, NULL, "NEXUS INCOSISTENT"); + panic("%s: inconsistent(lun or ccb)\n", slp->sl_xname); + } +#endif /* SCSI_LOW_DIAGNOSTIC */ + + /* clear all error flag bits (for restart) */ + cb->ccb_error = 0; + + /* setup nexus pointer */ + ti->ti_nexus = cb; + ti->ti_li = li; + slp->sl_nexus = ti; + + /* initialize msgsys */ + scsi_low_init_msgsys(slp, ti); + + /* target lun state check */ +#ifdef CAM + li->li_maxstate = UNIT_OK; +#else + if ((xs->flags & SCSI_POLL) != 0) + li->li_maxstate = UNIT_NEGSTART; + else + li->li_maxstate = UNIT_OK; +#endif + + /* exec cmds */ +scsi_low_cmd_exec: + if ((cb->ccb_flags & CCB_SENSE) != 0) + { + memset(&cb->ccb_sense, 0, sizeof(cb->ccb_sense)); +#ifdef CAM +#else + cb->ccb_sense_cmd.opcode = REQUEST_SENSE; + cb->ccb_sense_cmd.byte2 = (li->li_lun << 5); + cb->ccb_sense_cmd.length = sizeof(cb->ccb_sense); +#endif + cb->ccb_scp.scp_cmd = (u_int8_t *) &cb->ccb_sense_cmd; + cb->ccb_scp.scp_cmdlen = sizeof(cb->ccb_sense_cmd); + cb->ccb_scp.scp_data = (u_int8_t *) &cb->ccb_sense; + cb->ccb_scp.scp_datalen = sizeof(cb->ccb_sense); + cb->ccb_scp.scp_direction = SCSI_LOW_READ; + cb->ccb_tcmax = 15; + } + else if (li->li_state >= li->li_maxstate) + { +#ifdef CAM + cb->ccb_scp.scp_cmd = ccb->csio.cdb_io.cdb_bytes; + cb->ccb_scp.scp_cmdlen = (int) ccb->csio.cdb_len; + cb->ccb_scp.scp_data = ccb->csio.data_ptr; + cb->ccb_scp.scp_datalen = (int) ccb->csio.dxfer_len; + if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) + cb->ccb_scp.scp_direction = SCSI_LOW_WRITE; + else /* if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) */ + cb->ccb_scp.scp_direction = SCSI_LOW_READ; + cb->ccb_tcmax = (ccb->ccb_h.timeout >> 10); +#else + cb->ccb_scp.scp_cmd = (u_int8_t *) xs->cmd; + cb->ccb_scp.scp_cmdlen = xs->cmdlen; + cb->ccb_scp.scp_data = xs->data; + cb->ccb_scp.scp_datalen = xs->datalen; + cb->ccb_scp.scp_direction = (xs->flags & SCSI_DATA_OUT) ? + SCSI_LOW_WRITE : SCSI_LOW_READ; + cb->ccb_tcmax = (xs->timeout >> 10); +#endif + + } + else switch(li->li_state) + { + case UNIT_SLEEP: + scsi_low_unit_ready_cmd(cb); + break; + + case UNIT_START: + cb->ccb_scp.scp_cmd = (u_int8_t *) &ss_cmd; + cb->ccb_scp.scp_cmdlen = sizeof(ss_cmd); + cb->ccb_scp.scp_datalen = 0; + cb->ccb_scp.scp_direction = SCSI_LOW_READ; + cb->ccb_tcmax = 30; + break; + + case UNIT_SYNCH: + if (li->li_maxsynch.offset > 0) + { + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_SYNCH, 0); + scsi_low_unit_ready_cmd(cb); + break; + } + li->li_state = UNIT_WIDE; + + case UNIT_WIDE: +#ifdef SCSI_LOW_SUPPORT_WIDE + if (li->li_width > 0) + { + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_WIDE, 0); + scsi_low_unit_ready_cmd(cb); + break; + } +#endif /* SCSI_LOW_SUPPORT_WIDE */ + li->li_state = UNIT_OK; + + case UNIT_OK: + goto scsi_low_cmd_exec; + } + + /* timeout */ + if (cb->ccb_tcmax < SCSI_LOW_MIN_TOUT) + cb->ccb_tcmax = SCSI_LOW_MIN_TOUT; + cb->ccb_tc = cb->ccb_tcmax; + + /* setup saved scsi data pointer */ + cb->ccb_sscp = cb->ccb_scp; + + /* setup current scsi pointer */ + slp->sl_scp = cb->ccb_sscp; + slp->sl_error = cb->ccb_error; + + /* selection start */ + slp->sl_selid = ti; + rv = ((*slp->sl_funcs->scsi_low_start_bus) (slp, cb)); + if (rv == SCSI_LOW_START_OK) + { +#ifdef SCSI_LOW_STATICS + scsi_low_statics.nexus_win ++; +#endif /* SCSI_LOW_STATICS */ + return; + } + +#ifdef SCSI_LOW_STATICS + scsi_low_statics.nexus_fail ++; +#endif /* SCSI_LOW_STATICS */ + SCSI_LOW_SETUP_PHASE(ti, PH_NULL); + scsi_low_clear_nexus(slp, ti); +} + +void +scsi_low_clear_nexus(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + + /* clear all nexus pointer */ + ti->ti_nexus = NULL; + ti->ti_li = NULL; + slp->sl_nexus = NULL; + + /* clear selection assert */ + slp->sl_selid = NULL; + + /* clear nexus data */ + slp->sl_nexus_call = 0; + slp->sl_scp.scp_direction = SCSI_LOW_RWUNK; +} + +static int +scsi_low_done(slp, cb) + struct scsi_low_softc *slp; + struct slccb *cb; +{ +#ifdef CAM + union ccb *ccb; +#else + struct scsipi_xfer *xs; +#endif + struct targ_info *ti; + struct lun_info *li; + + ti = cb->ti; + li = cb->li; +#ifdef CAM + ccb = cb->ccb; +#else + xs = cb->xs; +#endif + if (cb->ccb_error == 0) + { + if ((cb->ccb_flags & CCB_SENSE) != 0) + { + cb->ccb_flags &= ~CCB_SENSE; +#ifdef CAM + ccb->csio.sense_data = cb->ccb_sense; + /* ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; */ + ccb->ccb_h.status = CAM_REQ_CMP; + /* ccb->ccb_h.status = CAM_AUTOSNS_VALID|CAM_SCSI_STATUS_ERROR; */ +#else + xs->sense.scsi_sense = cb->ccb_sense; + xs->error = XS_SENSE; +#endif + } + else switch (ti->ti_status) + { + case ST_GOOD: + if (slp->sl_scp.scp_datalen == 0) + { +#ifdef CAM + ccb->ccb_h.status = CAM_REQ_CMP; +#else + xs->error = XS_NOERROR; +#endif + break; + } + +#define SCSIPI_SCSI_CD_COMPLETELY_BUGGY "YES" +#ifdef SCSIPI_SCSI_CD_COMPLETELY_BUGGY +#ifdef CAM + if (/* cb->bp == NULL && */ + slp->sl_scp.scp_datalen < cb->ccb_scp.scp_datalen) +#else + if (xs->bp == NULL && + slp->sl_scp.scp_datalen < cb->ccb_scp.scp_datalen) +#endif + { +#ifdef CAM + ccb->ccb_h.status = CAM_REQ_CMP; +#else + xs->error = XS_NOERROR; +#endif + break; + } +#endif /* SCSIPI_SCSI_CD_COMPLETELY_BUGGY */ + + cb->ccb_error |= PDMAERR; +#ifdef CAM + ccb->ccb_h.status = CAM_REQ_CMP_ERR; +#else + xs->error = XS_DRIVER_STUFFUP; +#endif + break; + + case ST_CHKCOND: + case ST_MET: + cb->ccb_flags |= CCB_SENSE; +#ifdef CAM + ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; +#else + xs->error = XS_SENSE; +#endif + goto retry; + + case ST_BUSY: + cb->ccb_error |= BUSYERR; +#ifdef CAM + ccb->ccb_h.status = CAM_BUSY; /* SCSI_STATUS_ERROR; */ +#else + xs->error = XS_BUSY; +#endif + break; + + default: + cb->ccb_error |= FATALIO; +#ifdef CAM + ccb->ccb_h.status = CAM_REQ_CMP_ERR; +#else + xs->error = XS_DRIVER_STUFFUP; +#endif + break; + } + } + else + { + cb->ccb_flags &= ~CCB_SENSE; + if (ti->ti_phase == PH_SELSTART) + { +#ifdef CAM + ccb->ccb_h.status = CAM_CMD_TIMEOUT; +#else + xs->error = XS_TIMEOUT; +#endif + slp->sl_error |= SELTIMEOUTIO; + if (li->li_state == UNIT_SLEEP) + cb->ccb_error |= ABORTIO; + } + else + { +#ifdef CAM + ccb->ccb_h.status = CAM_REQ_CMP_ERR; +#else + xs->error = XS_DRIVER_STUFFUP; +#endif + } + + if ((cb->ccb_error & ABORTIO) != 0) + { + cb->ccb_rcnt = slp->sl_max_retry; +#ifdef CAM + ccb->ccb_h.status = CAM_REQ_ABORTED; +#endif + } + } + + /* target state check */ + if (li->li_state < li->li_maxstate) + { + if (cb->ccb_rcnt < slp->sl_max_retry) + { + li->li_state ++; + cb->ccb_rcnt = 0; + goto retry; + } + } + + /* internal retry check */ +#ifdef CAM + if (ccb->ccb_h.status == CAM_REQ_CMP) + { + ccb->csio.resid = 0; + } + else + { +#if 0 + if (ccb->ccb_h.status != CAM_AUTOSENSE_FAIL && + cb->ccb_rcnt < slp->sl_max_retry) + goto retry; +#endif +#else + if (xs->error == XS_NOERROR) + { + xs->resid = 0; + } + else + { + if (xs->error != XS_SENSE && + cb->ccb_rcnt < slp->sl_max_retry) + goto retry; +#endif + +#ifndef CAM +#ifdef SCSI_LOW_WARNINGS + if (xs->bp != NULL) + { + scsi_low_print(slp, ti); + printf("%s: WARNING: File system IO abort\n", + slp->sl_xname); + } +#endif /* SCSI_LOW_WARNINGS */ +#endif + } + +#ifdef CAM + ccb->csio.scsi_status = ti->ti_status; + xpt_done(ccb); +#else + xs->flags |= ITSDONE; + if ((xs->flags & SCSI_POLL) == 0) + scsipi_done(xs); +#endif + + /* free our target */ + TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); + scsi_low_free_ccb(cb); + return SCSI_LOW_DONE_COMPLETE; + +retry: + cb->ccb_rcnt ++; + if (slp->sl_start.tqh_first != cb) + { + TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); + TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); + } + return SCSI_LOW_DONE_RETRY; +} + +/************************************************************** + * Reset + **************************************************************/ +static void +scsi_low_clear_ccb(cb) + struct slccb *cb; +{ + + cb->ccb_flags &= ~CCB_SENSE; + cb->ccb_tag = SCSI_LOW_UNKTAG; +} + +static void +scsi_low_reset_nexus(slp, fdone) + struct scsi_low_softc *slp; + int fdone; +{ + struct targ_info *ti; + struct lun_info *li; + struct slccb *cb, *ncb; + + /* current nexus */ + ti = slp->sl_nexus; + if (ti != NULL && (cb = ti->ti_nexus) != NULL) + { + scsi_low_clear_ccb(cb); + if (fdone != 0 && cb->ccb_rcnt ++ >= slp->sl_max_retry) + { + cb->ccb_error |= FATALIO; + scsi_low_done(slp, cb); + } + } + + /* disconnected nexus */ + for (ti = slp->sl_titab.tqh_first; ti != NULL; + ti = ti->ti_chain.tqe_next) + { + for (cb = ti->ti_discq.tqh_first; cb != NULL; cb = ncb) + { + ncb = cb->ccb_chain.tqe_next; + TAILQ_REMOVE(&ti->ti_discq, cb, ccb_chain); + TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); + scsi_low_clear_ccb(cb); + if (fdone != 0 && cb->ccb_rcnt ++ >= slp->sl_max_retry) + { + cb->ccb_error |= FATALIO; + scsi_low_done(slp, cb); + } + } + + for (li = LIST_FIRST(&ti->ti_litab); li != NULL; + li = LIST_NEXT(li, lun_chain)) + { + li->li_state = UNIT_SLEEP; + li->li_disc = 0; + ((*slp->sl_funcs->scsi_low_lun_init) (slp, ti, li)); + scsi_low_calcf(ti, li); + } + + scsi_low_init_msgsys(slp, ti); + scsi_low_clear_nexus(slp, ti); + SCSI_LOW_SETUP_PHASE(ti, PH_NULL); + } + + slp->sl_flags &= ~HW_PDMASTART; + slp->sl_disc = 0; +} + +/* misc */ +static int tw_pos; +static char tw_chars[] = "|/-\\"; + +static void +scsi_low_twiddle_wait(void) +{ + + cnputc('\b'); + cnputc(tw_chars[tw_pos++]); + tw_pos %= (sizeof(tw_chars) - 1); + delay(TWIDDLEWAIT); +} + +void +scsi_low_bus_reset(slp) + struct scsi_low_softc *slp; +{ + int i; + + (*slp->sl_funcs->scsi_low_bus_reset) (slp); + + printf("%s: try to reset scsi bus ", slp->sl_xname); + for (i = 0; i <= SCSI2_RESET_DELAY / TWIDDLEWAIT ; i++) + scsi_low_twiddle_wait(); + cnputc('\b'); + printf("\n"); +} + +int +scsi_low_restart(slp, flags, s) + struct scsi_low_softc *slp; + int flags; + u_char *s; +{ + int error; + + if (s != NULL) + printf("%s: scsi bus restart. reason: %s\n", slp->sl_xname, s); + + if ((error = scsi_low_init(slp, flags)) != 0) + return error; + + scsi_low_start(slp); + return 0; +} + +/************************************************************** + * disconnect and reselect + **************************************************************/ +#define MSGCMD_LUN(msg) (msg & 0x07) + +static struct lun_info * +scsi_low_establish_lun(ti, lun) + struct targ_info *ti; + int lun; +{ + struct lun_info *li; + + li = scsi_low_alloc_li(ti, lun, 0); + if (li == NULL) + return li; + + ti->ti_li = li; + return li; +} + +static struct slccb * +scsi_low_establish_ccb(ti, li, tag) + struct targ_info *ti; + struct lun_info *li; + scsi_low_tag_t tag; +{ + struct scsi_low_softc *slp = ti->ti_sc; + struct slccb *cb; + + /* + * Search ccb matching with lun and tag. + */ + cb = ti->ti_discq.tqh_first; + for ( ; cb != NULL; cb = cb->ccb_chain.tqe_next) + if (cb->li == li && cb->ccb_tag == tag) + goto found; + return cb; + + /* + * establish our ccb nexus + */ +found: + TAILQ_REMOVE(&ti->ti_discq, cb, ccb_chain); + TAILQ_INSERT_HEAD(&slp->sl_start, cb, ccb_chain); + ti->ti_nexus = cb; + + slp->sl_scp = cb->ccb_sscp; + slp->sl_error |= cb->ccb_error; + + slp->sl_disc --; + li->li_disc --; + + /* inform "ccb nexus established" to the host driver */ + slp->sl_nexus_call = 1; + (*slp->sl_funcs->scsi_low_establish_nexus) (slp, ti); + return cb; +} + +struct targ_info * +scsi_low_reselected(slp, targ) + struct scsi_low_softc *slp; + u_int targ; +{ + struct targ_info *ti; + u_char *s; + + /* + * Check select vs reselected collision. + */ + + if ((ti = slp->sl_selid) != NULL) + { + scsi_low_clear_nexus(slp, ti); + SCSI_LOW_SETUP_PHASE(ti, PH_NULL); +#ifdef SCSI_LOW_STATICS + scsi_low_statics.nexus_conflict ++; +#endif /* SCSI_LOW_STATICS */ + } + else if (slp->sl_nexus != NULL) + { + s = "host busy"; + goto world_restart; + } + + /* + * Check a valid target id asserted ? + */ + if (targ >= slp->sl_ntargs || targ == slp->sl_hostid) + { + s = "scsi id illegal"; + goto world_restart; + } + + /* + * Check the target scsi status. + */ + ti = slp->sl_ti[targ]; + if (ti->ti_phase != PH_DISC) + { + s = "phase mismatch"; + goto world_restart; + } + + /* + * Setup lun and init msgsys + */ + slp->sl_error = 0; + scsi_low_init_msgsys(slp, ti); + + /* + * Establish our target nexus + * Remark: ccb and scsi pointer not yet restored + * if lun != SCSI_LOW_UNKLUN. + */ + SCSI_LOW_SETUP_PHASE(ti, PH_RESEL); + slp->sl_nexus = ti; +#ifdef SCSI_LOW_STATICS + scsi_low_statics.nexus_reselected ++; +#endif /* SCSI_LOW_STATICS */ + return ti; + +world_restart: + printf("%s: reselect(%x:unknown) %s\n", slp->sl_xname, targ, s); + scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, + "reselect: scsi world confused"); + return NULL; +} + +int +scsi_low_disconnected(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + struct slccb *cb = ti->ti_nexus; + + /* check phase completion */ + switch (slp->sl_msgphase) + { + case MSGPH_DISC: + if (cb != NULL) + { + TAILQ_REMOVE(&slp->sl_start, cb, ccb_chain); + TAILQ_INSERT_TAIL(&ti->ti_discq, cb, ccb_chain); + cb->ccb_error |= slp->sl_error; + cb->li->li_disc ++; + slp->sl_disc ++; + } + SCSI_LOW_SETUP_PHASE(ti, PH_DISC); +#ifdef SCSI_LOW_STATICS + scsi_low_statics.nexus_disconnected ++; +#endif /* SCSI_LOW_STATICS */ + break; + + case MSGPH_NULL: + slp->sl_error |= FATALIO; + + case MSGPH_CMDC: + if (cb != NULL) + { + cb->ccb_error |= slp->sl_error; + scsi_low_done(slp, cb); + } + SCSI_LOW_SETUP_PHASE(ti, PH_NULL); + break; + } + + scsi_low_clear_nexus(slp, ti); + scsi_low_start(slp); + return 1; +} + +/************************************************************** + * cmd out pointer setup + **************************************************************/ +int +scsi_low_cmd(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + struct slccb *cb = ti->ti_nexus; + + if (cb == NULL) + { + /* + * no slccb, abort! + */ + slp->sl_scp.scp_cmd = (u_int8_t *) &unit_ready_cmd; + slp->sl_scp.scp_cmdlen = sizeof(unit_ready_cmd); + slp->sl_scp.scp_datalen = 0; + slp->sl_scp.scp_direction = SCSI_LOW_READ; + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); + scsi_low_info(slp, ti, "CMDOUT: slccb nexus not found"); + } + else if (slp->sl_nexus_call == 0) + { + slp->sl_nexus_call = 1; + (*slp->sl_funcs->scsi_low_establish_nexus) (slp, ti); + } + return 0; +} + +/************************************************************** + * data out pointer setup + **************************************************************/ +int +scsi_low_data(slp, ti, bp, direction) + struct scsi_low_softc *slp; + struct targ_info *ti; + struct buf **bp; + int direction; +{ + struct slccb *cb = ti->ti_nexus; + + if (cb == NULL) + { + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1); + scsi_low_info(slp, ti, "DATA PHASE: slccb nexus not found"); + return EINVAL; + } + + if (direction != cb->ccb_scp.scp_direction) + { + scsi_low_info(slp, ti, "DATA PHASE: xfer direction mismatch"); + return EINVAL; + } + +#ifdef CAM + *bp = NULL; /* (cb->ccb == NULL) ? NULL : cb->bp; */ +#else + *bp = (cb->xs == NULL) ? NULL : cb->xs->bp; +#endif + return 0; +} + +/************************************************************** + * MSG_SYS + **************************************************************/ +#define MSGINPTR_CLR(ti) {(ti)->ti_msginptr = 0; (ti)->ti_msginlen = 0;} +#define MSGIN_PERIOD(ti) ((ti)->ti_msgin[3]) +#define MSGIN_OFFSET(ti) ((ti)->ti_msgin[4]) +#define MSGIN_DATA_LAST 0x30 + +static int scsi_low_errfunc_synch __P((struct targ_info *, u_int)); +static int scsi_low_errfunc_wide __P((struct targ_info *, u_int)); +static int scsi_low_errfunc_identify __P((struct targ_info *, u_int)); + +static int scsi_low_msgfunc_synch __P((struct targ_info *)); +static int scsi_low_msgfunc_wide __P((struct targ_info *)); +static int scsi_low_msgfunc_identify __P((struct targ_info *)); +static int scsi_low_msgfunc_user __P((struct targ_info *)); +static int scsi_low_msgfunc_abort __P((struct targ_info *)); + +struct scsi_low_msgout_data { + u_int md_flags; + u_int8_t md_msg; + int (*md_msgfunc) __P((struct targ_info *)); + int (*md_errfunc) __P((struct targ_info *, u_int)); +}; + +struct scsi_low_msgout_data scsi_low_msgout_data[] = { +/* 0 */ {SCSI_LOW_MSG_RESET, MSG_RESET, scsi_low_msgfunc_abort, NULL}, +/* 1 */ {SCSI_LOW_MSG_ABORT, MSG_ABORT, scsi_low_msgfunc_abort, NULL}, +/* 2 */ {SCSI_LOW_MSG_REJECT, MSG_REJECT, NULL, NULL}, +/* 3 */ {SCSI_LOW_MSG_PARITY, MSG_PARITY, NULL, NULL}, +/* 4 */ {SCSI_LOW_MSG_ERROR, MSG_I_ERROR, NULL, NULL}, +/* 5 */ {SCSI_LOW_MSG_IDENTIFY, 0, scsi_low_msgfunc_identify, scsi_low_errfunc_identify}, +/* 6 */ {SCSI_LOW_MSG_SYNCH, 0, scsi_low_msgfunc_synch, scsi_low_errfunc_synch}, +/* 7 */ {SCSI_LOW_MSG_WIDE, 0, scsi_low_msgfunc_wide, scsi_low_errfunc_wide}, +/* 8 */ {SCSI_LOW_MSG_USER, 0, scsi_low_msgfunc_user, NULL}, +/* 9 */ {SCSI_LOW_MSG_NOOP, MSG_NOOP, NULL, NULL}, +/* 10 */ {SCSI_LOW_MSG_ALL, 0}, +}; + +static int scsi_low_msginfunc_ext __P((struct targ_info *)); +static int scsi_low_synch __P((struct targ_info *)); +static int scsi_low_msginfunc_msg_reject __P((struct targ_info *)); +static int scsi_low_msginfunc_rejop __P((struct targ_info *)); +static int scsi_low_msginfunc_rdp __P((struct targ_info *)); +static int scsi_low_msginfunc_sdp __P((struct targ_info *)); +static int scsi_low_msginfunc_disc __P((struct targ_info *)); +static int scsi_low_msginfunc_cc __P((struct targ_info *)); +static int scsi_low_msginfunc_parity __P((struct targ_info *)); +static int scsi_low_msginfunc_noop __P((struct targ_info *)); +static void scsi_low_retry_phase __P((struct targ_info *)); + +struct scsi_low_msgin_data { + u_int md_len; + int (*md_msgfunc) __P((struct targ_info *)); +}; + +struct scsi_low_msgin_data scsi_low_msgin_data[] = { +/* 0 */ {1, scsi_low_msginfunc_cc}, +/* 1 */ {2, scsi_low_msginfunc_ext}, +/* 2 */ {1, scsi_low_msginfunc_sdp}, +/* 3 */ {1, scsi_low_msginfunc_rdp}, +/* 4 */ {1, scsi_low_msginfunc_disc}, +/* 5 */ {1, scsi_low_msginfunc_rejop}, +/* 6 */ {1, scsi_low_msginfunc_rejop}, +/* 7 */ {1, scsi_low_msginfunc_msg_reject}, +/* 8 */ {1, scsi_low_msginfunc_noop}, +/* 9 */ {1, scsi_low_msginfunc_parity}, +/* a */ {1, scsi_low_msginfunc_rejop}, +/* b */ {1, scsi_low_msginfunc_rejop}, +/* c */ {1, scsi_low_msginfunc_rejop}, +/* d */ {2, scsi_low_msginfunc_rejop}, +/* e */ {1, scsi_low_msginfunc_rejop}, +/* f */ {1, scsi_low_msginfunc_rejop}, +/* 0x10 */ {1, scsi_low_msginfunc_rejop}, +/* 0x11 */ {1, scsi_low_msginfunc_rejop}, +/* 0x12 */ {1, scsi_low_msginfunc_rejop}, +/* 0x13 */ {1, scsi_low_msginfunc_rejop}, +/* 0x14 */ {1, scsi_low_msginfunc_rejop}, +/* 0x15 */ {1, scsi_low_msginfunc_rejop}, +/* 0x16 */ {1, scsi_low_msginfunc_rejop}, +/* 0x17 */ {1, scsi_low_msginfunc_rejop}, +/* 0x18 */ {1, scsi_low_msginfunc_rejop}, +/* 0x19 */ {1, scsi_low_msginfunc_rejop}, +/* 0x1a */ {1, scsi_low_msginfunc_rejop}, +/* 0x1b */ {1, scsi_low_msginfunc_rejop}, +/* 0x1c */ {1, scsi_low_msginfunc_rejop}, +/* 0x1d */ {1, scsi_low_msginfunc_rejop}, +/* 0x1e */ {1, scsi_low_msginfunc_rejop}, +/* 0x1f */ {1, scsi_low_msginfunc_rejop}, +/* 0x20 */ {2, scsi_low_msginfunc_rejop}, +/* 0x21 */ {2, scsi_low_msginfunc_rejop}, +/* 0x22 */ {2, scsi_low_msginfunc_rejop}, +/* 0x23 */ {2, scsi_low_msginfunc_rejop}, +/* 0x24 */ {2, scsi_low_msginfunc_rejop}, +/* 0x25 */ {2, scsi_low_msginfunc_rejop}, +/* 0x26 */ {2, scsi_low_msginfunc_rejop}, +/* 0x27 */ {2, scsi_low_msginfunc_rejop}, +/* 0x28 */ {2, scsi_low_msginfunc_rejop}, +/* 0x29 */ {2, scsi_low_msginfunc_rejop}, +/* 0x2a */ {2, scsi_low_msginfunc_rejop}, +/* 0x2b */ {2, scsi_low_msginfunc_rejop}, +/* 0x2c */ {2, scsi_low_msginfunc_rejop}, +/* 0x2d */ {2, scsi_low_msginfunc_rejop}, +/* 0x2e */ {2, scsi_low_msginfunc_rejop}, +/* 0x2f */ {2, scsi_low_msginfunc_rejop}, +/* 0x30 */ {1, scsi_low_msginfunc_rejop} /* default rej op */ +}; + +static void +scsi_low_init_msgsys(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + + ti->ti_msginptr = 0; + ti->ti_emsgflags = ti->ti_msgflags = ti->ti_omsgflags = 0; + ti->ti_tflags &= ~TARG_ASSERT_ATN; + SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_NULL); +} + +/************************************************************** + * msgout + **************************************************************/ +static int +scsi_low_msgfunc_synch(ti) + struct targ_info *ti; +{ + struct lun_info *li = ti->ti_li; + int ptr = ti->ti_msgoutlen; + + if (li == NULL) + { + scsi_low_assert_msg(ti->ti_sc, ti, SCSI_LOW_MSG_ABORT, 0); + return EINVAL; + } + + ti->ti_msgoutstr[ptr + 0] = MSG_EXTEND; + ti->ti_msgoutstr[ptr + 1] = MSG_EXTEND_SYNCHLEN; + ti->ti_msgoutstr[ptr + 2] = MSG_EXTEND_SYNCHCODE; + ti->ti_msgoutstr[ptr + 3] = li->li_maxsynch.period; + ti->ti_msgoutstr[ptr + 4] = li->li_maxsynch.offset; + return MSG_EXTEND_SYNCHLEN + 2; +} + +static int +scsi_low_msgfunc_wide(ti) + struct targ_info *ti; +{ + struct lun_info *li = ti->ti_li; + int ptr = ti->ti_msgoutlen; + + if (li == NULL) + { + scsi_low_assert_msg(ti->ti_sc, ti, SCSI_LOW_MSG_ABORT, 0); + return EINVAL; + } + + ti->ti_msgoutstr[ptr + 0] = MSG_EXTEND; + ti->ti_msgoutstr[ptr + 1] = MSG_EXTEND_WIDELEN; + ti->ti_msgoutstr[ptr + 2] = MSG_EXTEND_WIDECODE; + ti->ti_msgoutstr[ptr + 3] = li->li_width; + return MSG_EXTEND_WIDELEN + 2; +} + +static int +scsi_low_msgfunc_identify(ti) + struct targ_info *ti; +{ + int ptr = ti->ti_msgoutlen;; + + if (ti->ti_li == NULL) + { + ti->ti_msgoutstr[ptr + 0] = 0x80; + scsi_low_info(ti->ti_sc, ti, "MSGOUT: lun unknown"); + scsi_low_assert_msg(ti->ti_sc, ti, SCSI_LOW_MSG_ABORT, 0); + } + else + { + ti->ti_msgoutstr[ptr + 0] = ID_MSG_SETUP(ti); + } + return 1; +} + +static int +scsi_low_msgfunc_user(ti) + struct targ_info *ti; +{ +#ifdef SCSI_LOW_SUPPORT_USER_MSGOUT + struct slccb *cb = ti->ti_nexus; + int ptr = ti->ti_msgoutlen;; + + if (ti->ti_nexus == NULL) + { + ti->ti_msgoutstr[ptr + 0] = MSG_NOOP; + return 1; + } + else + { + bcopy(cb->msgout, ti->ti_msgoutstr + ptr, SCSI_LOW_MAX_MSGLEN); + return cb->msgoutlen; + } +#else /* !SCSI_LOW_SUPPORT_USER_MSGOUT */ + return 0; +#endif /* !SCSI_LOW_SUPPORT_USER_MSGOUT */ +} + +static int +scsi_low_msgfunc_abort(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + + /* The target should releases bus */ + SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_CMDC); + slp->sl_error |= /* ABORTIO */ FATALIO; + return 1; +} + +/* + * The following functions are called when targets give unexpected + * responces in msgin (after msgout). + */ +static int +scsi_low_errfunc_identify(ti, msgflags) + struct targ_info *ti; + u_int msgflags; +{ + struct lun_info *li = ti->ti_li; + + li->li_flags &= ~SCSI_LOW_DISC; + return 0; +} + +static int +scsi_low_errfunc_synch(ti, msgflags) + struct targ_info *ti; + u_int msgflags; +{ + + /* XXX: + * illegal behavior, however + * there are buggy devices! + */ + MSGIN_PERIOD(ti) = 0; + MSGIN_OFFSET(ti) = 0; + scsi_low_synch(ti); + return 0; +} + +static int +scsi_low_errfunc_wide(ti, msgflags) + struct targ_info *ti; + u_int msgflags; +{ + struct lun_info *li = ti->ti_li; + + li->li_width = 0; + return 0; +} + +int +scsi_low_msgout(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + struct scsi_low_msgout_data *mdp; + int len = 0; + + /* STEP I. + * Scsi phase changes. + * Previously msgs asserted are accepted by our target or + * processed by scsi_low_msgin. + * Thus clear all saved informations. + */ + if (ti->ti_ophase != ti->ti_phase) + { + ti->ti_omsgflags = 0; + ti->ti_emsgflags = 0; + } + + /* STEP II. + * We did not assert attention, however still our target required + * msgs. Resend previous msgs. + */ + if (ti->ti_ophase == PH_MSGOUT && !(ti->ti_tflags & TARG_ASSERT_ATN)) + { + ti->ti_msgflags |= ti->ti_omsgflags; +#ifdef SCSI_LOW_DIAGNOSTIC + printf("%s: scsi_low_msgout: retry msgout\n", slp->sl_xname); +#endif /* SCSI_LOW_DIAGNOSTIC */ + } + + /* + * OK. clear flags. + */ + ti->ti_tflags &= ~TARG_ASSERT_ATN; + + /* STEP III. + * We have no msgs. send MSG_LOOP (OK?) + */ + if (scsi_low_is_msgout_continue(ti) == 0) + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_NOOP, 0); + + /* STEP IV. + * Process all msgs + */ + ti->ti_msgoutlen = 0; + mdp = &scsi_low_msgout_data[0]; + for ( ; mdp->md_flags != SCSI_LOW_MSG_ALL; mdp ++) + { + if ((ti->ti_msgflags & mdp->md_flags) != 0) + { + ti->ti_omsgflags |= mdp->md_flags; + ti->ti_msgflags &= ~mdp->md_flags; + ti->ti_emsgflags = mdp->md_flags; + + ti->ti_msgoutstr[ti->ti_msgoutlen] = mdp->md_msg; + if (mdp->md_msgfunc != NULL) + len = (*mdp->md_msgfunc) (ti); + else + len = 1; + + ti->ti_msgoutlen += len; + if ((slp->sl_cfgflags & CFG_MSGUNIFY) == 0 || + ti->ti_msgflags == 0) + break; + if (ti->ti_msgoutlen >= SCSI_LOW_MAX_MSGLEN - 5) + break; + } + } + + if (scsi_low_is_msgout_continue(ti) != 0) + { +#ifdef SCSI_LOW_DIAGNOSTIC + printf("SCSI_LOW_ATTENTION(msgout): 0x%x\n", ti->ti_msgflags); +#endif /* SCSI_LOW_DIAGNOSTIC */ + scsi_low_attention(slp, ti); + } + + /* + * OK. advance old phase. + */ + ti->ti_ophase = ti->ti_phase; + return ti->ti_msgoutlen; +} + +/************************************************************** + * msgin + **************************************************************/ +static int +scsi_low_msginfunc_noop(ti) + struct targ_info *ti; +{ + + return 0; +} + +static int +scsi_low_msginfunc_rejop(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + u_int8_t msg = ti->ti_msgin[0]; + + printf("%s: MSGIN: msg 0x%x reject\n", slp->sl_xname, (u_int) msg); + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); + return 0; +} + +static int +scsi_low_msginfunc_cc(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + + SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_CMDC); + return 0; +} + +static int +scsi_low_msginfunc_disc(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + + SCSI_LOW_SETUP_MSGPHASE(slp, MSGPH_DISC); + return 0; +} + +static int +scsi_low_msginfunc_sdp(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + + if (ti->ti_nexus != NULL) + ti->ti_nexus->ccb_sscp = slp->sl_scp; + else + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); + return 0; +} + +static int +scsi_low_msginfunc_rdp(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + + if (ti->ti_nexus != NULL) + slp->sl_scp = ti->ti_nexus->ccb_sscp; + else + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); + return 0; +} + +static int +scsi_low_synch(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + struct lun_info *li = ti->ti_li; + u_int period = 0, offset = 0, speed; + u_char *s; + int error; + + if (MSGIN_PERIOD(ti) >= li->li_maxsynch.period && + MSGIN_OFFSET(ti) <= li->li_maxsynch.offset) + { + if ((offset = MSGIN_OFFSET(ti)) != 0) + period = MSGIN_PERIOD(ti); + s = offset ? "synchronous" : "async"; + } + else + { + /* XXX: + * Target seems to be brain damaged. + * Force async transfer. + */ + li->li_maxsynch.period = 0; + li->li_maxsynch.offset = 0; + printf("%s: target brain damaged. async transfer\n", + slp->sl_xname); + return EINVAL; + } + + li->li_maxsynch.period = period; + li->li_maxsynch.offset = offset; + + error = (*slp->sl_funcs->scsi_low_msg) (slp, ti, SCSI_LOW_MSG_SYNCH); + if (error != 0) + { + /* XXX: + * Current period and offset are not acceptable + * for our adapter. + * The adapter changes max synch and max offset. + */ + printf("%s: synch neg failed. retry synch msg neg ...\n", + slp->sl_xname); + return error; + } + +#ifdef SCSI_LOW_INFORM + /* inform data */ + printf("%s(%d:%d): <%s> offset %d period %dns ", + slp->sl_xname, ti->ti_id, li->li_lun, s, offset, period * 4); + if (period != 0) + { + speed = 1000 * 10 / (period * 4); + printf("%d.%d M/s", speed / 10, speed % 10); + } + printf("\n"); +#endif + + return 0; +} + +static int +scsi_low_msginfunc_ext(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + struct slccb *cb = ti->ti_nexus; + struct lun_info *li = ti->ti_li; + int count, retry; + u_int32_t *ptr; + + if (ti->ti_msginptr == 2) + { + ti->ti_msginlen = ti->ti_msgin[1] + 2; + return 0; + } + + switch (MKMSG_EXTEND(ti->ti_msgin[1], ti->ti_msgin[2])) + { + case MKMSG_EXTEND(MSG_EXTEND_MDPLEN, MSG_EXTEND_MDPCODE): + if (cb == NULL) + break; + + ptr = (u_int32_t *)(&ti->ti_msgin[3]); + count = (int) htonl((long) (*ptr)); + if(slp->sl_scp.scp_datalen - count < 0 || + slp->sl_scp.scp_datalen - count > cb->ccb_scp.scp_datalen) + break; + + slp->sl_scp.scp_datalen -= count; + slp->sl_scp.scp_data += count; + return 0; + + case MKMSG_EXTEND(MSG_EXTEND_SYNCHLEN, MSG_EXTEND_SYNCHCODE): + if (li == NULL) + break; + + retry = scsi_low_synch(ti); + if (retry != 0 || (ti->ti_emsgflags & SCSI_LOW_MSG_SYNCH) == 0) + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_SYNCH, 0); + return 0; + + case MKMSG_EXTEND(MSG_EXTEND_WIDELEN, MSG_EXTEND_WIDECODE): + if (li == NULL) + break; + + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_WIDE, 0); + return 0; + + default: + break; + } + + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); + return EINVAL; +} + +static void +scsi_low_retry_phase(ti) + struct targ_info *ti; +{ + + switch (ti->ti_sphase) + { + case PH_MSGOUT: + ti->ti_msgflags |= ti->ti_omsgflags; + break; + + default: + break; + } +} + +static int +scsi_low_msginfunc_parity(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + + if (ti->ti_sphase != PH_MSGOUT) + slp->sl_error |= PARITYERR; + scsi_low_retry_phase(ti); + return 0; +} + +static int +scsi_low_msginfunc_msg_reject(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *slp = ti->ti_sc; + struct lun_info *li = ti->ti_li; + struct scsi_low_msgout_data *mdp; + u_int msgflags; + + if (li == NULL) + { + /* not yet lun nexus established! */ + goto out; + } + + switch (ti->ti_sphase) + { + case PH_CMD: + slp->sl_error |= CMDREJECT; + break; + + case PH_MSGOUT: + if (ti->ti_emsgflags == 0) + break; + + msgflags = SCSI_LOW_MSG_REJECT; + mdp = &scsi_low_msgout_data[0]; + for ( ; mdp->md_flags != SCSI_LOW_MSG_ALL; mdp ++) + { + if ((ti->ti_emsgflags & mdp->md_flags) != 0) + { + ti->ti_emsgflags &= ~mdp->md_flags; + if (mdp->md_errfunc != NULL) + (*mdp->md_errfunc) (ti, msgflags); + break; + } + } + break; + + default: + break; + } + +out: + scsi_low_info(slp, ti, "msg rejected"); + slp->sl_error |= MSGERR; + return 0; +} + +void +scsi_low_msgin(slp, ti, c) + struct scsi_low_softc *slp; + struct targ_info *ti; + u_int8_t c; +{ + struct scsi_low_msgin_data *sdp; + struct lun_info *li; + u_int8_t msg; + + /* + * Phase changes, clear the pointer. + */ + if (ti->ti_ophase != ti->ti_phase) + { + ti->ti_sphase = ti->ti_ophase; + ti->ti_ophase = ti->ti_phase; + MSGINPTR_CLR(ti); +#ifdef SCSI_LOW_DIAGNOSTIC + ti->ti_msgin_hist_pointer = 0; +#endif /* SCSI_LOW_DIAGNOSTIC */ + } + + /* + * Store a current messages byte into buffer and + * wait for the completion of the current msg. + */ + ti->ti_msgin[ti->ti_msginptr ++] = c; + if (ti->ti_msginptr >= SCSI_LOW_MAX_MSGLEN) + { + ti->ti_msginptr = SCSI_LOW_MAX_MSGLEN - 1; + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_REJECT, 0); + } + + /* + * Calculate messages length. + */ + msg = ti->ti_msgin[0]; + if (msg < MSGIN_DATA_LAST) + sdp = &scsi_low_msgin_data[msg]; + else + sdp = &scsi_low_msgin_data[MSGIN_DATA_LAST]; + + if (ti->ti_msginlen == 0) + { + ti->ti_msginlen = sdp->md_len; +#ifdef SCSI_LOW_DIAGNOSTIC + if (ti->ti_msgin_hist_pointer < MSGIN_HISTORY_LEN) + { + ti->ti_msgin_history[ti->ti_msgin_hist_pointer] = msg; + ti->ti_msgin_hist_pointer ++; + } +#endif /* SCSI_LOW_DIAGNOSTIC */ + } + + /* + * Check comletion. + */ + if (ti->ti_msginptr < ti->ti_msginlen) + return; + + /* + * Do process. + */ + if ((msg & MSG_IDENTIFY) == 0) + { + (void) ((*sdp->md_msgfunc) (ti)); + } + else + { + li = ti->ti_li; + if (li == NULL) + { + li = scsi_low_establish_lun(ti, MSGCMD_LUN(msg)); + if (li == NULL) + goto badlun; + } + + if (ti->ti_nexus == NULL) + { + /* XXX: + * move the following functions to + * tag queue msg process in the future. + */ + if (!scsi_low_establish_ccb(ti, li, SCSI_LOW_UNKTAG)) + goto badlun; + } + + if (MSGCMD_LUN(msg) != li->li_lun) + goto badlun; + } + + /* + * Msg process completed, reset msin pointer and assert ATN if desired. + */ + if (ti->ti_msginptr >= ti->ti_msginlen) + { + ti->ti_sphase = ti->ti_phase; + MSGINPTR_CLR(ti); + + if (scsi_low_is_msgout_continue(ti) != 0) + { +#ifdef SCSI_LOW_DIAGNOSTIC + printf("SCSI_LOW_ATTETION(msgin): 0x%x\n", + ti->ti_msgflags); +#endif /* SCSI_LOW_DIAGNOSTIC */ + scsi_low_attention(slp, ti); + } + } + return; + +badlun: + scsi_low_info(slp, ti, "MSGIN: identify lun mismatch"); + scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 0); +} + +/************************************************************** + * Qurik setup + **************************************************************/ +#define MAXOFFSET 0x10 + +static void +scsi_low_calcf(ti, li) + struct targ_info *ti; + struct lun_info *li; +{ + u_int period; + u_int8_t offset; + struct scsi_low_softc *slp = ti->ti_sc; + + li->li_flags &= ~SCSI_LOW_DISC; + if ((slp->sl_cfgflags & CFG_NODISC) == 0 && +#ifdef SDEV_NODISC + (li->li_quirks & SDEV_NODISC) == 0 && +#endif /* SDEV_NODISC */ + (li->li_cfgflags & SCSI_LOW_DISC) != 0) + li->li_flags |= SCSI_LOW_DISC; + + li->li_flags |= SCSI_LOW_NOPARITY; + if ((slp->sl_cfgflags & CFG_NOPARITY) == 0 && +#ifdef SDEV_NOPARITY + (li->li_quirks & SDEV_NOPARITY) == 0 && +#endif /* SDEV_NOPARITY */ + (li->li_cfgflags & SCSI_LOW_NOPARITY) == 0) + li->li_flags &= ~SCSI_LOW_NOPARITY; + + li->li_flags &= ~SCSI_LOW_SYNC; + if ((li->li_cfgflags & SCSI_LOW_SYNC) && + (slp->sl_cfgflags & CFG_ASYNC) == 0) + { + offset = SCSI_LOW_OFFSET(li->li_cfgflags); + if (offset > li->li_maxsynch.offset) + offset = li->li_maxsynch.offset; + li->li_flags |= SCSI_LOW_SYNC; + } + else + offset = 0; + + if (offset > 0) + { + period = SCSI_LOW_PERIOD(li->li_cfgflags); + if (period > SCSI_LOW_MAX_SYNCH_SPEED) + period = SCSI_LOW_MAX_SYNCH_SPEED; + if (period != 0) + period = 1000 * 10 / (period * 4); + if (period < li->li_maxsynch.period) + period = li->li_maxsynch.period; + } + else + period = 0; + + li->li_maxsynch.offset = offset; + li->li_maxsynch.period = period; +} + +#ifdef SCSI_LOW_TARGET_OPEN +static int +scsi_low_target_open(link, cf) + struct scsipi_link *link; + struct cfdata *cf; +{ + u_int target = link->scsipi_scsi.target; + u_int lun = link->scsipi_scsi.lun; + struct scsi_low_softc *slp; + struct targ_info *ti; + struct lun_info *li; + + slp = (struct scsi_low_softc *) link->adapter_softc; + ti = slp->sl_ti[target]; + li = scsi_low_alloc_li(ti, lun, 0); + if (li == NULL) + return 0; + + li->li_quirks = (u_int) link->quirks; + li->li_cfgflags = cf->cf_flags; + if (li->li_state > UNIT_SYNCH) + li->li_state = UNIT_SYNCH; + + scsi_low_calcf(ti, li); + + printf("%s(%d:%d): max period(%dns) max offset(%d) flags 0x%b\n", + slp->sl_xname, target, lun, + li->li_maxsynch.period * 4, + li->li_maxsynch.offset, + li->li_flags, SCSI_LOW_BITS); + return 0; +} +#endif /* SCSI_LOW_TARGET_OPEN */ + +/********************************************************** + * DEBUG SECTION + **********************************************************/ +static void +scsi_low_info(slp, ti, s) + struct scsi_low_softc *slp; + struct targ_info *ti; + u_char *s; +{ + + printf("%s: SCSI_LOW: %s\n", slp->sl_xname, s); + if (ti == NULL) + { + for (ti = slp->sl_titab.tqh_first; ti != NULL; + ti = ti->ti_chain.tqe_next) + scsi_low_print(slp, ti); + } + else + scsi_low_print(slp, ti); + +} + +static u_char *phase[] = +{ + "FREE", "ARBSTART", "SELSTART", "SELECTED", + "CMDOUT", "DATA", "MSGIN", "MSGOUT", "STATIN", "DISC", "RESEL" +}; + +void +scsi_low_print(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + struct slccb *cb = NULL; + + if (ti == NULL) + ti = slp->sl_nexus; + if (ti != NULL) + cb = ti->ti_nexus; + + printf("%s: TARGET(0x%lx) T_NEXUS(0x%lx) C_NEXUS(0x%lx) NDISCS(%d)\n", + slp->sl_xname, (u_long) ti, (u_long) slp->sl_nexus, + (u_long) cb, slp->sl_disc); + + /* target stat */ + if (ti != NULL) + { + struct sc_p *sp = &slp->sl_scp; + struct lun_info *li = ti->ti_li; + u_int flags = 0; + int lun = -1; + + if (li != NULL) + { + lun = li->li_lun; + flags = li->li_flags; + } + + printf("%s(%d:%d) ph<%s> => ph<%s>\n", slp->sl_xname, + ti->ti_id, lun, phase[(int) ti->ti_ophase], + phase[(int) ti->ti_phase]); + +printf("MSGIN: ptr(%x) [%x][%x][%x][%x][%x] STATUSIN: 0x%x T_FLAGS: 0x%x\n", + (u_int) (ti->ti_msginptr), + (u_int) (ti->ti_msgin[0]), + (u_int) (ti->ti_msgin[1]), + (u_int) (ti->ti_msgin[2]), + (u_int) (ti->ti_msgin[3]), + (u_int) (ti->ti_msgin[4]), + ti->ti_status, ti->ti_tflags); +#ifdef SCSI_LOW_DIAGNOSTIC +printf("MSGIN HISTORY: (%d) [0x%x] => [0x%x] => [0x%x] => [0x%x] => [0x%x]\n", + ti->ti_msgin_hist_pointer, + (u_int) (ti->ti_msgin_history[0]), + (u_int) (ti->ti_msgin_history[1]), + (u_int) (ti->ti_msgin_history[2]), + (u_int) (ti->ti_msgin_history[3]), + (u_int) (ti->ti_msgin_history[4])); +#endif /* SCSI_LOW_DIAGNOSTIC */ + +printf("MSGOUT: msgflags 0x%x [%x][%x][%x][%x][%x] msgoutlen %d C_FLAGS: %b\n", + (u_int) ti->ti_msgflags, + (u_int) (ti->ti_msgoutstr[0]), + (u_int) (ti->ti_msgoutstr[1]), + (u_int) (ti->ti_msgoutstr[2]), + (u_int) (ti->ti_msgoutstr[3]), + (u_int) (ti->ti_msgoutstr[4]), + ti->ti_msgoutlen, + flags, SCSI_LOW_BITS); + +printf("SCP: datalen 0x%x dataaddr 0x%lx ", + sp->scp_datalen, + (u_long) sp->scp_data); + + if (cb != NULL) + { +printf("CCB: cmdlen %x cmdaddr %lx cmd[0] %x datalen %x", + cb->ccb_scp.scp_cmdlen, + (u_long) cb->ccb_scp.scp_cmd, + (u_int) cb->ccb_scp.scp_cmd[0], + cb->ccb_scp.scp_datalen); + } + printf("\n"); + } + printf("error flags %b\n", slp->sl_error, SCSI_LOW_ERRORBITS); +} diff --git a/sys/cam/scsi/scsi_low.h b/sys/cam/scsi/scsi_low.h new file mode 100644 index 0000000..0d8f996 --- /dev/null +++ b/sys/cam/scsi/scsi_low.h @@ -0,0 +1,615 @@ +/* $FreeBSD$ */ +/* $NecBSD: scsi_low.h,v 1.24 1999/07/23 21:00:05 honda Exp $ */ +/* $NetBSD$ */ + +#define SCSI_LOW_DIAGNOSTIC + +/* + * [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 _SCSI_LOW_H_ +#define _SCSI_LOW_H_ + +#ifdef __NetBSD__ +#include <i386/Cbus/dev/scsi_dvcfg.h> +#endif +#ifdef __FreeBSD__ +#include <sys/device_port.h> +#define CAM +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> + +#include <cam/scsi/scsi_dvcfg.h> +#endif + +/* user configuration flags defs */ +#define SCSI_LOW_SYNC DVF_SCSI_SYNC +#define SCSI_LOW_DISC DVF_SCSI_DISC +#define SCSI_LOW_WAIT DVF_SCSI_WAIT +#define SCSI_LOW_LINK DVF_SCSI_LINK +#define SCSI_LOW_QTAG DVF_SCSI_QTAG +#define SCSI_LOW_NOPARITY DVF_SCSI_NOPARITY +#define SCSI_LOW_SAVESP DVF_SCSI_SAVESP +#define SCSI_LOW_DEFCFG DVF_SCSI_DEFCFG +#define SCSI_LOW_BITS DVF_SCSI_BITS + +#define SCSI_LOW_PERIOD(n) DVF_SCSI_PERIOD(n) +#define SCSI_LOW_OFFSET(n) DVF_SCSI_OFFSET(n) + +/* host scsi id and targets macro */ +#ifndef SCSI_LOW_NTARGETS +#define SCSI_LOW_NTARGETS 8 +#endif /* SCSI_LOW_NTARGETS */ +#define SCSI_LOW_NCCB 32 + +#define SCSI_LOW_MAX_MSGLEN 16 +#define SCSI_LOW_MAX_RETRY 3 + +/* timeout control macro */ +#define SCSI_LOW_MIN_TOUT 24 +#define SCSI_LOW_TIMEOUT_CHECK_INTERVAL 4 +#define SCSI_LOW_POWDOWN_TC 15 +#define SCSI_LOW_MAX_PHCHANGES 256 + +/* max synch period */ +#ifndef SCSI_LOW_MAX_SYNCH_SPEED +#define SCSI_LOW_MAX_SYNCH_SPEED (100) /* 10.0M */ +#endif /* !SCSI_LOW_MAX_SYNCH_SPEED */ + +/************************************************* + * Scsi Data Pointer + *************************************************/ +/* scsi pointer */ +struct sc_p { + u_int8_t *scp_data; + int scp_datalen; + + u_int8_t *scp_cmd; + int scp_cmdlen; + + u_int scp_direction; +#define SCSI_LOW_RWUNK (-1) +#define SCSI_LOW_WRITE 0 +#define SCSI_LOW_READ 1 +}; + +#define SCSI_LOW_SETUP_PHASE(ti, phase) \ +{ \ + if ((ti)->ti_phase != (phase)) \ + { \ + (ti)->ti_ophase = ti->ti_phase; \ + (ti)->ti_phase = (phase); \ + } \ +} + +#define SCSI_LOW_SETUP_MSGPHASE(slp, PHASE) \ +{ \ + (slp)->sl_msgphase = (PHASE); \ +} + +#define SCSI_LOW_TARGET_ASSERT_ATN(slp) \ +{ \ + (ti)->ti_tflags |= TARG_ASSERT_ATN; \ +} + +/************************************************* + * Command Control Block Structure + *************************************************/ +typedef int scsi_low_tag_t; +struct targ_info; + +#define SCSI_LOW_UNKLUN ((u_int) -1) +#define SCSI_LOW_UNKTAG ((scsi_low_tag_t) -1) + +struct slccb { + TAILQ_ENTRY(slccb) ccb_chain; + +#ifdef CAM + union ccb *ccb; + struct buf *bp; +#else + struct scsipi_xfer *xs; /* scsi upper */ +#endif + struct targ_info *ti; /* targ_info */ + struct lun_info *li; /* lun info */ + scsi_low_tag_t ccb_tag; /* tag */ + + /***************************************** + * Scsi data pointers (original and saved) + *****************************************/ + struct sc_p ccb_scp; /* given */ + struct sc_p ccb_sscp; /* saved scsi data pointer */ + +#ifdef SCSI_LOW_SUPPORT_USER_MSGOUT + u_int8_t msgout[SCSI_LOW_MAX_MSGLEN]; /* scsi msgout */ + u_int msgoutlen; +#endif /* SCSI_LOW_SUPPORT_USER_MSGOUT */ + + /***************************************** + * Error or Timeout counters + *****************************************/ + u_int ccb_flags; +#define CCB_SENSE 0x01 + u_int ccb_error; + + int ccb_rcnt; /* retry counter */ + int ccb_tc; /* timer counter */ + int ccb_tcmax; /* max timeout */ + + /***************************************** + * Sense data buffer + *****************************************/ +#ifdef __NetBSD__ + struct scsipi_sense ccb_sense_cmd; + struct scsipi_sense_data ccb_sense; +#endif +#ifdef __FreeBSD__ + struct scsi_sense ccb_sense_cmd; + struct scsi_sense_data ccb_sense; +#endif +}; + +/* ccb assert */ +#ifdef __NetBSD__ +#include <dev/isa/ccbque.h> +#endif +#ifdef __FreeBSD__ +#include <i386/isa/ccbque.h> +#endif +GENERIC_CCB_ASSERT(scsi_low, slccb) + +/************************************************* + * Target structures + *************************************************/ +struct scsi_low_softc; +TAILQ_HEAD(targ_info_tab, targ_info); +LIST_HEAD(lun_info_tab, lun_info); + +struct lun_info { + int li_lun; + struct targ_info *li_ti; /* my target */ + + LIST_ENTRY(lun_info) lun_chain; /* targ_info link */ + + int li_disc; /* num disconnects */ + int li_maxnexus; + + /* + * lun state + */ +#define UNIT_SLEEP 0x00 +#define UNIT_START 0x01 +#define UNIT_SYNCH 0x02 +#define UNIT_WIDE 0x03 +#define UNIT_OK 0x04 +#define UNIT_NEGSTART UNIT_SYNCH + u_int li_state; /* target lun state */ + u_int li_maxstate; /* max state */ + + /* + * lun control flags + */ + u_int li_flags; /* real control flags */ + u_int li_cfgflags; /* given target cfgflags */ + u_int li_quirks; /* given target quirk */ + + /* + * lun synch and wide data + */ + struct synch { + u_int8_t offset; + u_int8_t period; + } li_maxsynch; /* synch data */ + + u_int li_width; +}; + +struct targ_info { + TAILQ_ENTRY(targ_info) ti_chain; /* targ_info link */ + + struct scsi_low_softc *ti_sc; /* our softc */ + u_int ti_id; /* scsi id */ + + /* + * Lun chain + */ + struct lun_info_tab ti_litab; /* lun chain */ + + /* + * Nexus + */ + struct slccb *ti_nexus; /* current nexus */ + struct lun_info *ti_li; /* current nexus lun_info */ + + /* + * Target status + */ +#define TARG_ASSERT_ATN 0x01 + u_int ti_tflags; /* target state I */ + + /* + * Scsi phase control + */ + struct slccbtab ti_discq; /* disconnect queue */ + +#define PH_NULL 0x00 +#define PH_ARBSTART 0x01 +#define PH_SELSTART 0x02 +#define PH_SELECTED 0x03 +#define PH_CMD 0x04 +#define PH_DATA 0x05 +#define PH_MSGIN 0x06 +#define PH_MSGOUT 0x07 +#define PH_STAT 0x08 +#define PH_DISC 0x09 +#define PH_RESEL 0x0a + u_int ti_phase; /* scsi phase */ + u_int ti_ophase; /* old scsi phase */ + + /* + * Status in + */ + u_int8_t ti_status; /* status in */ + + /* + * Msg in + */ + u_int ti_msginptr; /* msgin ptr */ + u_int ti_msginlen; /* expected msg length */ + u_int8_t ti_msgin[SCSI_LOW_MAX_MSGLEN]; /* msgin buffer */ + u_int ti_sphase; + +#ifdef SCSI_LOW_DIAGNOSTIC +#define MSGIN_HISTORY_LEN 5 + u_int8_t ti_msgin_history[MSGIN_HISTORY_LEN]; + int ti_msgin_hist_pointer; +#endif /* SCSI_LOW_DIAGNOSTIC */ + + /* + * Msg out + */ + u_int ti_msgflags; /* msgs to be asserted */ + u_int ti_omsgflags; /* msgs asserted */ + u_int ti_emsgflags; /* a msg currently asserted */ +#define SCSI_LOW_MSG_RESET 0x00000001 +#define SCSI_LOW_MSG_ABORT 0x00000002 +#define SCSI_LOW_MSG_REJECT 0x00000004 +#define SCSI_LOW_MSG_PARITY 0x00000008 +#define SCSI_LOW_MSG_ERROR 0x00000010 +#define SCSI_LOW_MSG_IDENTIFY 0x00000020 +#define SCSI_LOW_MSG_SYNCH 0x00000040 +#define SCSI_LOW_MSG_WIDE 0x00000080 +#define SCSI_LOW_MSG_USER 0x00000100 +#define SCSI_LOW_MSG_NOOP 0x00000200 +#define SCSI_LOW_MSG_ALL 0xffffffff + + /* msgout buffer */ + u_int8_t ti_msgoutstr[SCSI_LOW_MAX_MSGLEN]; /* scsi msgout */ + u_int ti_msgoutlen; /* msgout strlen */ + + /* + * lun info size. + */ + int ti_lunsize; +}; + +/************************************************* + * COMMON HEADER STRUCTURE + *************************************************/ +struct scsi_low_softc; +typedef struct scsi_low_softc *sc_low_t; + +#define SCSI_LOW_START_OK 0 +#define SCSI_LOW_START_FAIL 1 + +#define SC_LOW_INIT_T (int (*) __P((sc_low_t, int))) +#define SC_LOW_BUSRST_T (void (*) __P((sc_low_t))) +#define SC_LOW_LUN_INIT_T (int (*) __P((sc_low_t, struct targ_info *, struct lun_info *))) +#define SC_LOW_SELECT_T (int (*) __P((sc_low_t, struct slccb *))) +#define SC_LOW_ATTEN_T (void (*) __P((sc_low_t))) +#define SC_LOW_NEXUS_T (int (*) __P((sc_low_t, struct targ_info *))) +#define SC_LOW_MSG_T (int (*) __P((sc_low_t, struct targ_info *, u_int))) +#define SC_LOW_POLL_T (int (*) __P((void *))) +#define SC_LOW_POWER_T (int (*) __P((sc_low_t, u_int))) + +struct scsi_low_funcs { + int (*scsi_low_init) __P((sc_low_t, int)); + void (*scsi_low_bus_reset) __P((sc_low_t)); + int (*scsi_low_lun_init) __P((sc_low_t, struct targ_info *, struct lun_info *)); + + int (*scsi_low_start_bus) __P((sc_low_t, struct slccb *)); + int (*scsi_low_establish_nexus) __P((sc_low_t, struct targ_info *)); + + void (*scsi_low_attention) __P((sc_low_t)); + int (*scsi_low_msg) __P((sc_low_t, struct targ_info *, u_int)); + + int (*scsi_low_poll) __P((void *)); + +#define SCSI_LOW_POWDOWN 1 +#define SCSI_LOW_ENGAGE 2 + int (*scsi_low_power) __P((sc_low_t, u_int)); +}; + +/************************************************* + * SCSI LOW SOFTC + *************************************************/ +struct scsi_low_softc { + DEVPORT_DEVICE sl_dev; + u_char sl_xname[16]; + + /* upper interface */ +#ifdef CAM + struct cam_sim *sim; + struct cam_path *path; +#else + struct scsipi_link sl_link; +#endif + + /* my targets */ + struct targ_info *sl_ti[SCSI_LOW_NTARGETS]; + struct targ_info_tab sl_titab; + + /* current active nexus */ + int sl_nexus_call; + struct targ_info *sl_nexus; + + /* ccb start queue */ + struct slccbtab sl_start; + + /* retry limit and phase change counter */ + int sl_max_retry; + int sl_ph_count; + + /* selection & total num disconnect targets */ + int sl_disc; + struct targ_info *sl_selid; + + /* scsi phased suggested by scsi msg */ + u_int sl_msgphase; +#define MSGPH_NULL 0x00 /* no msg */ +#define MSGPH_DISC 0x01 /* disconnect msg */ +#define MSGPH_CMDC 0x02 /* cmd complete msg */ + + /* error */ +#define FATALIO 0x01 /* generic io error & retry io */ +#define ABORTIO 0x02 /* generic io error & terminate io */ +#define TIMEOUTIO 0x04 /* watch dog timeout */ +#define SELTIMEOUTIO 0x08 /* selection timeout */ +#define PDMAERR 0x10 /* dma xfer error */ +#define MSGERR 0x20 /* msgsys error */ +#define PARITYERR 0x40 /* parity error */ +#define BUSYERR 0x80 /* target busy error */ +#define CMDREJECT 0x100 /* cmd reject error */ +#define SCSI_LOW_ERRORBITS "\020\009cmdrej\008busy\007parity\006msgerr\005pdmaerr\004seltimeout\003timeout\002abort\001fatal" + u_int sl_error; /* error flags */ + + /* current scsi data pointer */ + struct sc_p sl_scp; + + /* power control */ + u_int sl_active; /* host is busy state */ + int sl_powc; /* power down timer counter */ + u_int sl_rstep; /* resume step */ + + /* configuration flags */ + u_int sl_flags; +#define HW_POWDOWN 0x01 +#define HW_RESUME 0x02 +#define HW_PDMASTART 0x04 +#define HW_INACTIVE 0x08 +#define HW_POWERCTRL 0x10 + + u_int sl_cfgflags; +#define CFG_NODISC 0x01 +#define CFG_NOPARITY 0x02 +#define CFG_NOATTEN 0x04 +#define CFG_ASYNC 0x08 +#define CFG_MSGUNIFY 0x10 + + /* host informations */ + u_int sl_hostid; + int sl_nluns; + int sl_ntargs; + int sl_openings; + + /* interface functions */ + struct scsi_low_funcs *sl_funcs; + +#if defined(i386) + u_int sl_irq; /* XXX */ +#endif /* i386 */ +#ifdef __FreeBSD__ + struct callout_handle engage_ch; + struct callout_handle timeout_ch; +#ifdef SCSI_LOW_POWFUNC + struct callout_handle recover_ch; +#endif +#endif /* __FreeBSD__ */ +}; + +/************************************************* + * SCSI LOW service functions + *************************************************/ +/* + * Scsi low attachment function. + */ +int scsi_low_attach __P((struct scsi_low_softc *, int, int, int, int)); +int scsi_low_dettach __P((struct scsi_low_softc *)); + +/* + * Scsi phase "bus service" functions. + * These functions are corresponding to each scsi bus phaeses. + */ +/* nexus abort (selection failed) */ +void scsi_low_clear_nexus __P((struct scsi_low_softc *, struct targ_info *)); +/* msgout phase */ +int scsi_low_msgout __P((struct scsi_low_softc *, struct targ_info *)); +/* msgin phase */ +void scsi_low_msgin __P((struct scsi_low_softc *, struct targ_info *, u_int8_t)); +/* data phase */ +int scsi_low_data __P((struct scsi_low_softc *, struct targ_info *, struct buf **, int)); +/* cmd phase */ +int scsi_low_cmd __P((struct scsi_low_softc *, struct targ_info *)); + +/* reselection phase */ +struct targ_info *scsi_low_reselected __P((struct scsi_low_softc *, u_int)); +/* disconnection phase */ +int scsi_low_disconnected __P((struct scsi_low_softc *, struct targ_info *)); + +/* + * Scsi bus restart function. + * Canncel all established nexuses => scsi system initialized => restart jobs. + */ +#define SCSI_LOW_RESTART_HARD 1 +#define SCSI_LOW_RESTART_SOFT 0 +int scsi_low_restart __P((struct scsi_low_softc *, int, u_char *)); + +/* + * Scsi utility fucntions + */ +/* print current status */ +void scsi_low_print __P((struct scsi_low_softc *, struct targ_info *)); +/* timeout utility (only in used scsi_low_pisa) */ +void scsi_low_timeout __P((void *)); +#define SCSI2_RESET_DELAY 5000000 +#define TWIDDLEWAIT 10000 +/* bus reset utility */ +void scsi_low_bus_reset __P((struct scsi_low_softc *)); + +/************************************************* + * Inline utility + *************************************************/ +static __inline u_int8_t scsi_low_identify __P((struct targ_info *ti)); +static __inline void scsi_low_attention __P((struct scsi_low_softc *, struct targ_info *)); +static __inline int scsi_low_is_msgout_continue __P((struct targ_info *)); +static __inline int scsi_low_assert_msg __P((struct scsi_low_softc *, struct targ_info *, u_int, int)); +static __inline void scsi_low_arbit_win __P((struct scsi_low_softc *, struct targ_info *)); + +static __inline int +scsi_low_is_msgout_continue(ti) + struct targ_info *ti; +{ + + return (ti->ti_msgflags != 0); +} + +static __inline u_int8_t +scsi_low_identify(ti) + struct targ_info *ti; +{ + u_int8_t msg; + struct lun_info *li = ti->ti_li; + + msg = (li->li_flags & SCSI_LOW_DISC) ? 0xc0 : 0x80; + msg |= li->li_lun; + return msg; +} + +#define ID_MSG_SETUP(ti) (scsi_low_identify(ti)) + +static __inline void +scsi_low_attention(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + + (*slp->sl_funcs->scsi_low_attention) (slp); + SCSI_LOW_TARGET_ASSERT_ATN(slp); +} + +static __inline int +scsi_low_assert_msg(slp, ti, msg, now) + struct scsi_low_softc *slp; + struct targ_info *ti; + u_int msg; + int now; +{ + + ti->ti_msgflags |= msg; + if (now != 0) + scsi_low_attention(slp, ti); + return 0; +} + +static __inline void +scsi_low_arbit_win(slp, ti) + struct scsi_low_softc *slp; + struct targ_info *ti; +{ + + slp->sl_selid = NULL; +} + +/************************************************* + * Message out defs + *************************************************/ +/* XXX: use scsi_message.h */ +#define ST_GOOD 0x00 +#define ST_CHKCOND 0x02 +#define ST_MET 0x04 +#define ST_BUSY 0x08 +#define ST_INTERGOOD 0x10 +#define ST_INTERMET 0x14 +#define ST_CONFLICT 0x18 +#define ST_QUEFULL 0x28 + +#define MSG_COMP 0x00 +#define MSG_EXTEND 0x01 + +#define MKMSG_EXTEND(XLEN, XCODE) ((((u_int)(XLEN)) << NBBY) | ((u_int)(XCODE))) +#define MSG_EXTEND_MDPCODE 0x00 +#define MSG_EXTEND_MDPLEN 0x05 +#define MSG_EXTEND_SYNCHCODE 0x01 +#define MSG_EXTEND_SYNCHLEN 0x03 +#define MSG_EXTEND_WIDECODE 0x03 +#define MSG_EXTEND_WIDELEN 0x02 + +#define MSG_SAVESP 0x02 +#define MSG_RESTORESP 0x03 +#define MSG_DISCON 0x04 +#define MSG_I_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOOP 0x08 +#define MSG_PARITY 0x09 +#define MSG_LCOMP 0x0a +#define MSG_LCOMP_F 0x0b +#define MSG_RESET 0x0c +#ifdef __FreeBSD__ +#undef MSG_IDENTIFY +#endif +#define MSG_IDENTIFY 0x80 + +#define OS_DEPEND(s) (s) +#endif /* !_SCSI_LOW_H_ */ diff --git a/sys/cam/scsi/scsi_low_pisa.c b/sys/cam/scsi/scsi_low_pisa.c new file mode 100644 index 0000000..3c530cb --- /dev/null +++ b/sys/cam/scsi/scsi_low_pisa.c @@ -0,0 +1,181 @@ +/* $FreeBSD$ */ +/* $NecBSD: scsi_low_pisa.c,v 1.13 1998/11/26 14:26:11 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. + */ + +#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> + +#include <machine/bus.h> +#ifdef __NetBSD__ +#include <machine/intr.h> +#endif + +#ifdef __NetBSD__ +#include <dev/isa/isareg.h> +#include <dev/isa/isavar.h> + +#include <dev/isa/pisaif.h> +#endif + +#include <machine/dvcfg.h> + +#ifdef __NetBSD__ +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> +#include <dev/scsipi/scsi_disk.h> + +#include <i386/Cbus/dev/scsi_low.h> +#include <i386/Cbus/dev/scsi_low_pisa.h> + +#define SCSIBUS_RESCAN +#else +#ifdef __FreeBSD__ +#include <i386/isa/isa_device.h> +#include <i386/isa/icu.h> + +#include <cam/scsi/scsi_low.h> +#include <cam/scsi/scsi_low_pisa.h> +#endif +#endif + +#ifdef __FreeBSD__ +int +scsi_low_deactivate(struct scsi_low_softc *sc) +{ +#else +#ifdef __NetBSD__ +int +scsi_low_deactivate(dh) + pisa_device_handle_t dh; +{ + struct scsi_low_softc *sc = PISA_DEV_SOFTC(dh); +#endif +#endif + sc->sl_flags |= HW_INACTIVE; + +#ifdef __NetBSD__ +#ifdef SCSI_LOW_POWFUNC + untimeout(scsi_low_recover, sc); +#endif /* SCSI_LOW_POWFUNC */ + untimeout(scsi_low_timeout, sc); +#else +#ifdef __FreeBSD__ +#ifdef SCSI_LOW_POWFUNC + untimeout(scsi_low_recover, sc, sc->recover_ch); +#endif /* SCSI_LOW_POWFUNC */ + untimeout(scsi_low_timeout, sc, sc->timeout_ch); +#endif +#endif + + return 0; +} + +#ifdef __FreeBSD__ +int +scsi_low_activate(struct scsi_low_softc *sc, struct isa_device *dev) +{ +#else +#ifdef __NetBSD__ +int +scsi_low_activate(dh) + pisa_device_handle_t dh; +{ + struct scsi_low_softc *sc = PISA_DEV_SOFTC(dh); + slot_device_res_t dr = PISA_RES_DR(dh); +#endif +#endif + int error; + + sc->sl_flags &= ~HW_INACTIVE; +#ifdef __FreeBSD__ + sc->sl_cfgflags = ((sc->sl_cfgflags << 16) | ((dev->id_flags) & 0xffff)); +#else /* __NetBSD__ */ + sc->sl_cfgflags = DVCFG_MKCFG(DVCFG_MAJOR(sc->sl_cfgflags), \ + DVCFG_MINOR(PISA_DR_DVCFG(dr))); + sc->sl_irq = PISA_DR_IRQ(dr); +#endif + + if ((error = scsi_low_restart(sc, SCSI_LOW_RESTART_HARD, NULL)) != 0) + { + sc->sl_flags |= HW_INACTIVE; + return error; + } + +#ifdef __FreeBSD__ + sc->timeout_ch = +#endif + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + /* rescan the scsi bus */ +#ifdef SCSIBUS_RESCAN + if (PISA_RES_EVENT(dh) == PISA_EVENT_INSERT && + sc->sl_start.tqh_first == NULL) + scsi_probe_busses((int) sc->sl_link.scsipi_scsi.scsibus, -1, -1); +#endif + return 0; +} + +#ifdef __NetBSD__ +int +scsi_low_notify(dh, ev) + pisa_device_handle_t dh; + pisa_event_t ev; +{ + struct scsi_low_softc *sc = PISA_DEV_SOFTC(dh); + + switch(ev) + { + case PISA_EVENT_QUERY_SUSPEND: + if (sc->sl_start.tqh_first != NULL) + return SD_EVENT_STATUS_BUSY; + break; + + default: + break; + } + return 0; +} +#endif diff --git a/sys/cam/scsi/scsi_low_pisa.h b/sys/cam/scsi/scsi_low_pisa.h new file mode 100644 index 0000000..551465a --- /dev/null +++ b/sys/cam/scsi/scsi_low_pisa.h @@ -0,0 +1,46 @@ +/* $FreeBSD$ */ +/* $NecBSD: scsi_low_pisa.h,v 1.3 1999/04/15 01:35:57 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 _SCSI_LOW_PISA_H_ +#define _SCSI_LOW_PISA_H_ + +#ifdef __NetBSD__ +int scsi_low_activate __P((pisa_device_handle_t)); +int scsi_low_deactivate __P((pisa_device_handle_t)); +int scsi_low_notify __P((pisa_device_handle_t, pisa_event_t)); +#endif +#ifdef __FreeBSD__ +int scsi_low_activate __P((struct scsi_low_softc *, struct isa_device *)); +int scsi_low_deactivate __P((struct scsi_low_softc *)); +#endif +#endif /* !_SCSI_LOW_PISA_H_ */ diff --git a/sys/compat/netbsd/physio_proc.h b/sys/compat/netbsd/physio_proc.h new file mode 100644 index 0000000..f31507e --- /dev/null +++ b/sys/compat/netbsd/physio_proc.h @@ -0,0 +1,89 @@ +/* $FreeBSD$ */ +/* $NecBSD: physio_proc.h,v 3.4 1999/07/23 20:47:03 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 _I386_PHYSIO_PROC_H_ +#define _I386_PHYSIO_PROC_H_ +#include <sys/buf.h> +#include <sys/queue.h> + +struct physio_proc; +TAILQ_HEAD(physio_proc_head, physio_proc); +struct physio_proc_head physio_proc_freet, physio_proc_busyt; + +struct physio_proc { + TAILQ_ENTRY(physio_proc) pp_chain; + struct proc *pp_proc; +}; + +static __inline struct physio_proc *physio_proc_enter __P((struct buf *)); +static __inline void physio_proc_leave __P((struct physio_proc *)); + +static __inline struct physio_proc * +physio_proc_enter(bp) + struct buf *bp; +{ + struct physio_proc *pp; + int s; + + if (bp == NULL || (bp->b_flags & B_PHYS) == 0) + return NULL; + if ((pp = physio_proc_freet.tqh_first) == NULL) + return NULL; + + s = splstatclock(); + TAILQ_REMOVE(&physio_proc_freet, pp, pp_chain); +#if !defined(__FreeBSD__) || __FreeBSD_version < 400001 + pp->pp_proc = bp->b_proc; +#endif + TAILQ_INSERT_TAIL(&physio_proc_busyt, pp, pp_chain); + splx(s); + return pp; +} + +static __inline void +physio_proc_leave(pp) + struct physio_proc *pp; +{ + int s; + + if (pp == NULL) + return; + + s = splstatclock(); + TAILQ_REMOVE(&physio_proc_busyt, pp, pp_chain); + TAILQ_INSERT_TAIL(&physio_proc_freet, pp, pp_chain); + pp->pp_proc = NULL; + splx(s); +} + +void physio_proc_init __P((void)); +#endif /* _I386_PHYSIO_PROC_H_ */ diff --git a/sys/conf/NOTES b/sys/conf/NOTES index e937b82..51e0dc6 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1263,10 +1263,13 @@ options AML_DEBUG # ISP 12160 Ultra3 SCSI, # Qlogic ISP 2100 and ISP 2200 Fibre Channel host adapters. # ncr: NCR 53C810, 53C825 self-contained SCSI host adapters. +# ncv: NCR 53C500 based SCSI host adapters. +# nsp: Workbit Ninja SCSI-3 based PC Card SCSI host adapters. # sym: Symbios/Logic 53C8XX family of PCI-SCSI I/O processors: # 53C810, 53C810A, 53C815, 53C825, 53C825A, 53C860, 53C875, # 53C876, 53C885, 53C895, 53C895A, 53C896, 53C897, 53C1510D, # 53C1010-33, 53C1010-66. +# stg: TMC 18C30, 18C50 based SCSI host adapters. # # Note that the order is important in order for Buslogic ISA/EISA cards to be @@ -1287,7 +1290,10 @@ device amd device isp device ispfw device ncr +device ncv +device nsp device sym +device stg # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, diff --git a/sys/conf/files b/sys/conf/files index a88e292..22c75c0 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -27,6 +27,12 @@ cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/scsi/scsi_da.c optional da +cam/scsi/scsi_low.c optional ncv +cam/scsi/scsi_low.c optional nsp +cam/scsi/scsi_low.c optional stg +cam/scsi/scsi_low_pisa.c optional ncv +cam/scsi/scsi_low_pisa.c optional nsp +cam/scsi/scsi_low_pisa.c optional stg cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa @@ -216,6 +222,10 @@ dev/lnc/if_lnc.c count lnc dev/lnc/if_lnc_isa.c optional lnc isa dev/lnc/if_lnc_pc98.c optional lnc isa dev/lnc/if_lnc_pci.c optional lnc pci +dev/ncv/ncr53c500.c optional ncv +dev/ncv/ncr53c500_pccard.c optional ncv card +dev/nsp/nsp.c optional nsp +dev/nsp/nsp_pccard.c optional nsp card dev/mca/mca_bus.c optional mca dev/md/md.c optional md dev/mii/amphy.c optional miibus @@ -339,6 +349,9 @@ dev/sound/pcm/mixer.c optional pcm dev/sound/pcm/sound.c optional pcm #dev/sound/usb/upcm.c optional pcm usb dev/streams/streams.c optional streams +dev/stg/tmc18c30.c optional stg +dev/stg/tmc18c30_pccard.c optional stg card +dev/stg/tmc18c30_isa.c optional stg isa dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" dev/tdfx/tdfx_pci.c optional tdfx 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_ */ diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index fa8bac7..24f252e 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -94,6 +94,10 @@ device bt device aha 1 device aic +device ncv # NCR 53C500 +device nsp # Workbit Ninja SCSI-3 +device stg # TMC 18C30/18C50 + # RAID controllers interfaced to the SCSI subsystem device asr # DPT SmartRAID V, VI and Adaptec SCSI RAID device dpt # DPT Smartcache III, IV - See NOTES for options! diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index e937b82..51e0dc6 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -1263,10 +1263,13 @@ options AML_DEBUG # ISP 12160 Ultra3 SCSI, # Qlogic ISP 2100 and ISP 2200 Fibre Channel host adapters. # ncr: NCR 53C810, 53C825 self-contained SCSI host adapters. +# ncv: NCR 53C500 based SCSI host adapters. +# nsp: Workbit Ninja SCSI-3 based PC Card SCSI host adapters. # sym: Symbios/Logic 53C8XX family of PCI-SCSI I/O processors: # 53C810, 53C810A, 53C815, 53C825, 53C825A, 53C860, 53C875, # 53C876, 53C885, 53C895, 53C895A, 53C896, 53C897, 53C1510D, # 53C1010-33, 53C1010-66. +# stg: TMC 18C30, 18C50 based SCSI host adapters. # # Note that the order is important in order for Buslogic ISA/EISA cards to be @@ -1287,7 +1290,10 @@ device amd device isp device ispfw device ncr +device ncv +device nsp device sym +device stg # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, diff --git a/sys/i386/i386/userconfig.c b/sys/i386/i386/userconfig.c index 2e49e0d..35e2480 100644 --- a/sys/i386/i386/userconfig.c +++ b/sys/i386/i386/userconfig.c @@ -338,6 +338,7 @@ static DEV_INFO device_info[] = { {"aic", "Adaptec 152x SCSI and compatible SCSI cards", 0, CLS_STORAGE}, {"nca", "ProAudio Spectrum SCSI and compatibles", 0, CLS_STORAGE}, {"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE}, +{"stg", "TMC 18C30/18C50 based SCSI cards", 0, CLS_STORAGE}, {"ata", "ATA/ATAPI compatible disk controller", 0, CLS_STORAGE}, {"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE}, {"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE}, diff --git a/sys/i386/include/physio_proc.h b/sys/i386/include/physio_proc.h new file mode 100644 index 0000000..f31507e --- /dev/null +++ b/sys/i386/include/physio_proc.h @@ -0,0 +1,89 @@ +/* $FreeBSD$ */ +/* $NecBSD: physio_proc.h,v 3.4 1999/07/23 20:47:03 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 _I386_PHYSIO_PROC_H_ +#define _I386_PHYSIO_PROC_H_ +#include <sys/buf.h> +#include <sys/queue.h> + +struct physio_proc; +TAILQ_HEAD(physio_proc_head, physio_proc); +struct physio_proc_head physio_proc_freet, physio_proc_busyt; + +struct physio_proc { + TAILQ_ENTRY(physio_proc) pp_chain; + struct proc *pp_proc; +}; + +static __inline struct physio_proc *physio_proc_enter __P((struct buf *)); +static __inline void physio_proc_leave __P((struct physio_proc *)); + +static __inline struct physio_proc * +physio_proc_enter(bp) + struct buf *bp; +{ + struct physio_proc *pp; + int s; + + if (bp == NULL || (bp->b_flags & B_PHYS) == 0) + return NULL; + if ((pp = physio_proc_freet.tqh_first) == NULL) + return NULL; + + s = splstatclock(); + TAILQ_REMOVE(&physio_proc_freet, pp, pp_chain); +#if !defined(__FreeBSD__) || __FreeBSD_version < 400001 + pp->pp_proc = bp->b_proc; +#endif + TAILQ_INSERT_TAIL(&physio_proc_busyt, pp, pp_chain); + splx(s); + return pp; +} + +static __inline void +physio_proc_leave(pp) + struct physio_proc *pp; +{ + int s; + + if (pp == NULL) + return; + + s = splstatclock(); + TAILQ_REMOVE(&physio_proc_busyt, pp, pp_chain); + TAILQ_INSERT_TAIL(&physio_proc_freet, pp, pp_chain); + pp->pp_proc = NULL; + splx(s); +} + +void physio_proc_init __P((void)); +#endif /* _I386_PHYSIO_PROC_H_ */ diff --git a/sys/sys/device_port.h b/sys/sys/device_port.h new file mode 100644 index 0000000..222b21d --- /dev/null +++ b/sys/sys/device_port.h @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org> + * Copyright (c) 1999 Takanori Watanabe <takawata@jp.FreeBSD.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#if defined(__NetBSD__) +# include <sys/device.h> +#elif defined(__FreeBSD__) +# if __FreeBSD_version >= 400001 +# include <sys/module.h> +# include <sys/bus.h> +# else +# include <sys/device.h> +# endif +#endif + +/* + * Macro's to cope with the differences between operating systems and versions. + */ + +#if defined(__NetBSD__) +# define DEVPORT_DEVICE struct device +# define DEVPORT_DEVNAME(dev) (dev).dv_xname +# define DEVPORT_DEVUNIT(dev) (dev).dv_unit + +#elif defined(__FreeBSD__) +/* + * FreeBSD (compatibility for struct device) + */ +#if __FreeBSD_version >= 400001 +# define DEVPORT_DEVICE device_t +# define DEVPORT_DEVNAME(dev) device_get_name(dev) +# define DEVPORT_DEVUNIT(dev) device_get_unit(dev) +# define DEVPORT_ALLOC_SOFTC(dev) device_get_softc(dev) +# define DEVPORT_GET_SOFTC(dev) device_get_softc(dev) + +# define UNCONF 1 /* print " not configured\n" */ + +#else + +# define DEVPORT_DEVICE struct device +# define DEVPORT_DEVNAME(dev) (dev).dv_xname +# define DEVPORT_DEVUNIT(dev) (dev).dv_unit +# ifdef DEVPORT_ALLOCSOFTCFUNC +# define DEVPORT_ALLOC_SOFTC(dev) (DEVPORT_ALLOCSOFTCFUNC)((dev).dv_unit) +# else +# define DEVPORT_ALLOC_SOFTC(dev) DEVPORT_ALLOCSOFTCFUNC_is_not_defined_prior_than_device_port_h +# endif +# ifdef DEVPORT_SOFTCARRAY +# define DEVPORT_GET_SOFTC(dev) (DEVPORT_SOFTCARRAY)[(dev).dv_unit] +# else +# define DEVPORT_GET_SOFTC(dev) DEVPORT_SOFTCARRAY_is_not_defined_prior_than_device_port_h +# endif + +#endif + +/* + * PC-Card device driver (compatibility for struct pccard_devinfo *) + */ +#if __FreeBSD_version >= 400001 +# define DEVPORT_PDEVICE device_t +# define DEVPORT_PDEVUNIT(pdev) device_get_unit(pdev) +# define DEVPORT_PDEVFLAGS(pdev) device_get_flags(pdev) +# define DEVPORT_PDEVIOBASE(pdev) bus_get_resource_start(pdev, SYS_RES_IOPORT, 0) +# define DEVPORT_PDEVIRQ(pdev) bus_get_resource_start(pdev, SYS_RES_IRQ, 0) +# define DEVPORT_PDEVMADDR(pdev) bus_get_resource_start(pdev, SYS_RES_MEMORY, 0) +# define DEVPORT_PDEVALLOC_SOFTC(pdev) device_get_softc(pdev) +# define DEVPORT_PDEVGET_SOFTC(pdev) device_get_softc(pdev) + +#else + +# define DEVPORT_PDEVICE struct pccard_devinfo * +# define DEVPORT_PDEVUNIT(pdev) (pdev)->pd_unit +# define DEVPORT_PDEVFLAGS(pdev) (pdev)->pd_flags +# define DEVPORT_PDEVIOBASE(pdev) (pdev)->pd_iobase +# define DEVPORT_PDEVIRQ(pdev) (pdev)->pd_irq +# define DEVPORT_PDEVMADDR(pdev) (pdev)->pd_maddr +# ifdef DEVPORT_ALLOCSOFTCFUNC +# define DEVPORT_PDEVALLOC_SOFTC(pdev) (DEVPORT_ALLOCSOFTCFUNC)((pdev)->pd_unit) +# else +# define DEVPORT_PDEVALLOC_SOFTC(pdev) DEVPORT_ALLOCSOFTCFUNC_is_not_defined_prior_than_device_port_h +# endif +# ifdef DEVPORT_SOFTCARRAY +# define DEVPORT_PDEVGET_SOFTC(pdev) (DEVPORT_SOFTCARRAY)[(pdev)->pd_unit] +# else +# define DEVPORT_PDEVGET_SOFTC(pdev) DEVPORT_SOFTCARRAY_is_not_defined_prior_than_device_port_h +# endif +#endif + +#endif /* __FreeBSD__ */ + |