diff options
author | 1999-11-30 07:55:55 +0000 | |
---|---|---|
committer | 1999-11-30 07:55:55 +0000 | |
commit | 30a6e20a9d805b4bc711933dcacbed1b095d9526 (patch) | |
tree | 8c5fa9131c1a58b83b6ae49e04b798e17099bebc | |
parent | UVM support (how could I miss this?). From jason@ (diff) | |
download | wireguard-openbsd-30a6e20a9d805b4bc711933dcacbed1b095d9526.tar.xz wireguard-openbsd-30a6e20a9d805b4bc711933dcacbed1b095d9526.zip |
Added port of the NetBSD (-current) DPT SmartCache III/IV PCI/EISA host adapter
driver to OpenBSD.
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 5 | ||||
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/dev/eisa/dpt_eisa.c | 214 | ||||
-rw-r--r-- | sys/dev/eisa/files.eisa | 7 | ||||
-rw-r--r-- | sys/dev/ic/dpt.c | 1441 | ||||
-rw-r--r-- | sys/dev/ic/dptreg.h | 300 | ||||
-rw-r--r-- | sys/dev/ic/dptvar.h | 128 | ||||
-rw-r--r-- | sys/dev/pci/dpt_pci.c | 163 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 6 |
9 files changed, 2266 insertions, 4 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index a5162bed1c9..8f073217c72 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.142 1999/11/28 12:17:04 downsj Exp $ +# $OpenBSD: GENERIC,v 1.143 1999/11/30 07:55:55 cmetz Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -144,6 +144,9 @@ ahc0 at isa? port ? irq ? # Adaptec 284x SCSI controllers ahc* at eisa? slot ? # Adaptec 274x, aic7770 SCSI controllers ahc* at pci? dev ? function ? # Adaptec 2940 SCSI controllers scsibus* at ahc? +dpt* at pci? dev ? function ? # DPT SmartCache/SmartRAID PCI +dpt* at eisa? slot ? # DPT SmartCache/SmartRAID EISA +scsibus* at dpt? isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel scsibus* at isp? aic0 at isa? port 0x340 irq 11 # Adaptec 152[02] SCSI controllers diff --git a/sys/conf/files b/sys/conf/files index 46ffaad50ca..cb167e90925 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.136 1999/11/28 11:50:36 downsj Exp $ +# $OpenBSD: files,v 1.137 1999/11/30 07:55:55 cmetz Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -66,6 +66,10 @@ file dev/ic/aic7xxx.c ahc device aic: scsi file dev/ic/aic6360.c aic +# DPT EATA SCSI controllers +device dpt: scsi +file dev/ic/dpt.c dpt + # AdvanSys 1200A, 1200B and ULTRA SCSI controllers device adv: scsi file dev/ic/adv.c adv diff --git a/sys/dev/eisa/dpt_eisa.c b/sys/dev/eisa/dpt_eisa.c new file mode 100644 index 00000000000..fca43e730f9 --- /dev/null +++ b/sys/dev/eisa/dpt_eisa.c @@ -0,0 +1,214 @@ +/* $OpenBSD: dpt_eisa.c,v 1.1 1999/11/30 07:55:56 cmetz Exp $ */ +/* $NetBSD: dpt_eisa.c,v 1.2 1999/10/18 21:59:19 ad Exp $ */ + +/* + * Copyright (c) 1999 Andy Doran <ad@NetBSD.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. + * + */ + +/* + * EISA front-end for DPT EATA SCSI driver. + */ + +#include <sys/cdefs.h> +#ifdef __NetBSD__ +__KERNEL_RCSID(0, "$NetBSD: dpt_eisa.c,v 1.2 1999/10/18 21:59:19 ad Exp $"); +#endif /* __NetBSD__ */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#ifdef __NetBSD__ +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#endif /* __OpenBSD__ */ + +#include <dev/eisa/eisavar.h> +#include <dev/eisa/eisadevs.h> + +#include <dev/ic/dptreg.h> +#include <dev/ic/dptvar.h> + +#define DPT_EISA_SLOT_OFFSET 0x0c00 +#define DPT_EISA_IOSIZE 0x0100 +#define DPT_EISA_IOCONF 0x90 +#define DPT_EISA_EATA_REG_OFFSET 0x88 + +int dpt_eisa_irq __P((bus_space_tag_t, bus_space_handle_t, int *)); +#ifdef __NetBSD__ +int dpt_eisa_match __P((struct device *, struct cfdata *, void *)); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +int dpt_eisa_match __P((struct device *, void *, void *)); +#endif /* __OpenBSD__ */ +void dpt_eisa_attach __P((struct device *, struct device *, void *)); + +struct cfattach dpt_eisa_ca = { + sizeof(struct dpt_softc), dpt_eisa_match, dpt_eisa_attach +}; + +const char *dpt_eisa_boards[] = { + "DPT2402", + "DPTA401", + "DPTA402", + "DPTA410", + "DPTA411", + "DPTA412", + "DPTA420", + "DPTA501", + "DPTA502", + "DPTA701", + "DPTBC01", + "NEC8200", /* OEM */ + "ATT2408", /* OEM */ + NULL +}; + +int +dpt_eisa_irq(iot, ioh, irq) + bus_space_tag_t iot; + bus_space_handle_t ioh; + int *irq; +{ + + switch (bus_space_read_1(iot, ioh, DPT_EISA_IOCONF) & 0x38) { + case 0x08: + *irq = 11; + break; + case 0x10: + *irq = 15; + break; + case 0x20: + *irq = 14; + break; + default: + return (-1); + } + + return (0); +} + +int +dpt_eisa_match(parent, match, aux) + struct device *parent; +#ifdef __NetBSD__ + struct cfdata *match; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + void *match; +#endif /* __OpenBSD__ */ + void *aux; +{ + struct eisa_attach_args *ea; + int i; + + ea = aux; + + for (i = 0; dpt_eisa_boards[i] != NULL; i++) + if (strcmp(ea->ea_idstring, dpt_eisa_boards[i]) == 0) + break; + + return (dpt_eisa_boards[i] != NULL); +} + +void +dpt_eisa_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct eisa_attach_args *ea; + bus_space_handle_t ioh; + eisa_chipset_tag_t ec; + eisa_intr_handle_t ih; + struct dpt_softc *sc; + bus_space_tag_t iot; + const char *intrstr; + int irq; + + ea = aux; + sc = (struct dpt_softc *)self; + iot = ea->ea_iot; + ec = ea->ea_ec; + + printf(": "); + + if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) + + DPT_EISA_SLOT_OFFSET, DPT_EISA_IOSIZE, 0, &ioh)) { + printf("can't map i/o space\n"); + return; + } + + sc->sc_iot = iot; + sc->sc_ioh = ioh; + sc->sc_dmat = ea->ea_dmat; + + /* Map and establish the interrupt. */ + if (dpt_eisa_irq(iot, ioh, &irq)) { + printf("HBA on invalid IRQ (%d)\n", irq); + return; + } + + if (eisa_intr_map(ec, irq, &ih)) { + printf("can't map interrupt (%d)\n", irq); + return; + } + + intrstr = eisa_intr_string(ec, ih); +#ifdef __NetBSD__ + sc->sc_ih = eisa_intr_establish(ec, ih, IST_LEVEL, IPL_BIO, + dpt_intr, sc); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + sc->sc_ih = eisa_intr_establish(ec, ih, IST_LEVEL, IPL_BIO, + dpt_intr, sc, sc->sc_dv.dv_xname); +#endif /* __OpenBSD__ */ + if (sc->sc_ih == NULL) { + printf("can't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + + /* Read the EATA configuration */ + if (dpt_readcfg(sc)) { + printf("%s: readcfg failed - see dpt(4)\n", + sc->sc_dv.dv_xname); + return; + } + + /* Now attach to the bus-independant code */ + dpt_init(sc, intrstr); +} diff --git a/sys/dev/eisa/files.eisa b/sys/dev/eisa/files.eisa index 8e26747fb2c..22ff6417699 100644 --- a/sys/dev/eisa/files.eisa +++ b/sys/dev/eisa/files.eisa @@ -1,4 +1,4 @@ -# $OpenBSD: files.eisa,v 1.8 1996/11/28 23:27:40 niklas Exp $ +# $OpenBSD: files.eisa,v 1.9 1999/11/30 07:55:55 cmetz Exp $ # $NetBSD: files.eisa,v 1.12 1996/09/01 00:10:55 mycroft Exp $ # # Config.new file and device description for machine-independent EISA code. @@ -24,6 +24,11 @@ file dev/eisa/ahc_eisa.c ahc_eisa attach bha at eisa with bha_eisa file dev/eisa/bha_eisa.c bha_eisa +# DPT EATA SCSI controllers +# device declaration in sys/conf/files +attach dpt at eisa with dpt_eisa +file dev/eisa/dpt_eisa.c dpt_eisa + # UltraStor UHA-24f boards # device declaration in sys/conf/files attach uha at eisa with uha_eisa diff --git a/sys/dev/ic/dpt.c b/sys/dev/ic/dpt.c new file mode 100644 index 00000000000..6b11da58777 --- /dev/null +++ b/sys/dev/ic/dpt.c @@ -0,0 +1,1441 @@ +/* $OpenBSD: dpt.c,v 1.1 1999/11/30 07:55:56 cmetz Exp $ */ +/* $NetBSD: dpt.c,v 1.12 1999/10/23 16:26:33 ad Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andy Doran, Charles M. Hannum and by Jason R. Thorpe of the Numerical + * Aerospace Simulation Facility, NASA Ames Research Center. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Portions of this code fall under the following copyright: + * + * Originally written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + */ + +/* + * Driver for DPT EATA SCSI adapters. + * + * TODO: + * + * o Need a front-end for (newer) ISA boards. + * o Handle older firmware better. + * o Find a bunch of different firmware EEPROMs and try them out. + * o Test with a bunch of different boards. + * o dpt_readcfg() should not be using CP_PIO_GETCFG. + * o An interface to userland applications. + * o Some sysctls or a utility (eg dptctl(8)) to control parameters. + */ + +#include <sys/cdefs.h> +#ifdef __NetBSD__ +__KERNEL_RCSID(0, "$NetBSD: dpt.c,v 1.12 1999/10/23 16:26:33 ad Exp $"); +#endif /* __NetBSD__ */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/queue.h> +#include <sys/proc.h> +#include <sys/buf.h> + +#include <machine/endian.h> +#ifdef __NetBSD__ +#include <machine/bswap.h> +#endif /* __NetBSD__ */ +#include <machine/bus.h> + +#ifdef __NetBSD__ +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#endif /* __OpenBSD__ */ + +#include <dev/ic/dptreg.h> +#include <dev/ic/dptvar.h> + +#ifdef __OpenBSD__ +static void dpt_enqueue __P((struct dpt_softc *, struct scsi_xfer *, int)); +static struct scsi_xfer *dpt_dequeue __P((struct dpt_softc *)); + +struct cfdriver dpt_cd = { + NULL, "dpt", DV_DULL +}; +#endif /* __OpenBSD__ */ + +/* A default for our link struct */ +#ifdef __NetBSD__ +static struct scsipi_device dpt_dev = { +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +static struct scsi_device dpt_dev = { +#endif /* __OpenBSD__ */ + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +#ifndef offsetof +#define offsetof(type, member) (int)((&((type *)0)->member)) +#endif /* offsetof */ + +static char *dpt_cname[] = { + "PM3334", "SmartRAID IV", + "PM3332", "SmartRAID IV", + "PM2144", "SmartCache IV", + "PM2044", "SmartCache IV", + "PM2142", "SmartCache IV", + "PM2042", "SmartCache IV", + "PM2041", "SmartCache IV", + "PM3224", "SmartRAID III", + "PM3222", "SmartRAID III", + "PM3021", "SmartRAID III", + "PM2124", "SmartCache III", + "PM2024", "SmartCache III", + "PM2122", "SmartCache III", + "PM2022", "SmartCache III", + "PM2021", "SmartCache III", + "SK2012", "SmartCache Plus", + "SK2011", "SmartCache Plus", + NULL, "unknown adapter, please report using send-pr(1)", +}; + +/* + * Handle an interrupt from the HBA. + */ +int +dpt_intr(xxx_sc) + void *xxx_sc; +{ + struct dpt_softc *sc; + struct dpt_ccb *ccb; + struct eata_sp *sp; + static int moretimo; + int more; + + sc = xxx_sc; + sp = sc->sc_statpack; + + if (!sp) { +#ifdef DEBUG + printf("%s: premature intr (st:%02x aux:%02x)\n", + sc->sc_dv.dv_xname, dpt_inb(sc, HA_STATUS), + dpt_inb(sc, HA_AUX_STATUS)); +#else /* DEBUG */ + (void) dpt_inb(sc, HA_STATUS); +#endif /* DEBUG */ + return (0); + } + + more = 0; + +#ifdef DEBUG + if ((dpt_inb(sc, HA_AUX_STATUS) & HA_AUX_INTR) == 0) + printf("%s: spurious intr\n", sc->sc_dv.dv_xname); +#endif + + /* Don't get stalled by HA_ST_MORE */ + if (moretimo < DPT_MORE_TIMEOUT / 100) + moretimo = 0; + + for (;;) { + /* + * HBA might have interrupted while we were dealing with the + * last completed command, since we ACK before we deal; keep + * polling. If no interrupt is signalled, but the HBA has + * indicated that more data will be available soon, hang + * around. + */ + if ((dpt_inb(sc, HA_AUX_STATUS) & HA_AUX_INTR) == 0) { + if (more != 0 && moretimo++ < DPT_MORE_TIMEOUT / 100) { + DELAY(10); + continue; + } + break; + } + +#ifdef __NetBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, sc->sc_spoff, + sizeof(struct eata_sp), BUS_DMASYNC_POSTREAD); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, + BUS_DMASYNC_POSTREAD); +#endif /* __OpenBSD__ */ + + if (!sp) { + more = dpt_inb(sc, HA_STATUS) & HA_ST_MORE; + + /* Don't get stalled by HA_ST_MORE */ + if (moretimo < DPT_MORE_TIMEOUT / 100) + moretimo = 0; + continue; + } + + /* Might have looped before HBA can reset HBA_AUX_INTR */ + if (sp->sp_ccbid == -1) { + DELAY(50); +#ifdef DIAGNOSTIC + printf("%s: slow reset of HA_AUX_STATUS?", + sc->sc_dv.dv_xname); +#endif + if ((dpt_inb(sc, HA_AUX_STATUS) & HA_AUX_INTR) == 0) + return (0); +#ifdef DIAGNOSTIC + printf("%s: was a slow reset of HA_AUX_STATUS", + sc->sc_dv.dv_xname); +#endif + /* Re-sync DMA map */ +#ifdef __NetBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, + sc->sc_spoff, sizeof(struct eata_sp), + BUS_DMASYNC_POSTREAD); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, + BUS_DMASYNC_POSTREAD); +#endif /* __OpenBSD__ */ + } + + /* Make sure CCB ID from status packet is realistic */ + if (sp->sp_ccbid >= 0 && sp->sp_ccbid < sc->sc_nccbs) { + /* Sync up DMA map and cache cmd status */ + ccb = sc->sc_ccbs + sp->sp_ccbid; + +#ifdef __NetBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, + CCB_OFF(sc, ccb), sizeof(struct dpt_ccb), + BUS_DMASYNC_POSTWRITE); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, + BUS_DMASYNC_POSTWRITE); +#endif /* __OpenBSD__ */ + + ccb->ccb_hba_status = sp->sp_hba_status & 0x7F; + ccb->ccb_scsi_status = sp->sp_scsi_status; + + /* + * Ack the interrupt and process the CCB. If this + * is a private CCB it's up to dpt_poll() to notice. + */ + sp->sp_ccbid = -1; + ccb->ccb_flg |= CCB_INTR; + more = dpt_inb(sc, HA_STATUS) & HA_ST_MORE; + if ((ccb->ccb_flg & CCB_PRIVATE) == 0) + dpt_done_ccb(sc, ccb); + } else { + printf("%s: bogus status (returned CCB id %d)\n", + sc->sc_dv.dv_xname, sp->sp_ccbid); + + /* Ack the interrupt */ + sp->sp_ccbid = -1; + more = dpt_inb(sc, HA_STATUS) & HA_ST_MORE; + } + + /* Don't get stalled by HA_ST_MORE */ + if (moretimo < DPT_MORE_TIMEOUT / 100) + moretimo = 0; + } + + return (0); +} + +/* + * Initialize and attach the HBA. This is the entry point from bus + * specific probe-and-attach code. + */ +void +dpt_init(sc, intrstr) + struct dpt_softc *sc; + const char *intrstr; +{ + struct eata_inquiry_data *ei; + int i, j, error, rseg, mapsize; + bus_dma_segment_t seg; + struct eata_cfg *ec; + char model[16]; + + ec = &sc->sc_ec; + + /* Allocate the CCB/status packet/scratch DMA map and load */ + sc->sc_nccbs = min(betoh16(*(int16_t *)ec->ec_queuedepth), + DPT_MAX_CCBS); + sc->sc_spoff = sc->sc_nccbs * sizeof(struct dpt_ccb); + sc->sc_scroff = sc->sc_spoff + sizeof(struct eata_sp); + sc->sc_scrlen = 256; /* XXX */ + mapsize = sc->sc_nccbs * sizeof(struct dpt_ccb) + sc->sc_scrlen + + sizeof(struct eata_sp); + + if ((error = bus_dmamem_alloc(sc->sc_dmat, mapsize, NBPG, 0, + &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to allocate CCBs, error = %d\n", + sc->sc_dv.dv_xname, error); + return; + } + + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, mapsize, + (caddr_t *)&sc->sc_ccbs, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map CCBs, error = %d\n", + sc->sc_dv.dv_xname, error); + return; + } + + if ((error = bus_dmamap_create(sc->sc_dmat, mapsize, mapsize, 1, 0, + BUS_DMA_NOWAIT, &sc->sc_dmamap_ccb)) != 0) { + printf("%s: unable to create CCB DMA map, error = %d\n", + sc->sc_dv.dv_xname, error); + return; + } + + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_ccb, + sc->sc_ccbs, mapsize, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load CCB DMA map, error = %d\n", + sc->sc_dv.dv_xname, error); + return; + } + + sc->sc_statpack = (struct eata_sp *)((caddr_t)sc->sc_ccbs + + sc->sc_spoff); + sc->sc_sppa = sc->sc_dmamap_ccb->dm_segs[0].ds_addr + sc->sc_spoff; + sc->sc_scr = (caddr_t)sc->sc_ccbs + sc->sc_scroff; + sc->sc_scrpa = sc->sc_dmamap_ccb->dm_segs[0].ds_addr + sc->sc_scroff; + sc->sc_statpack->sp_ccbid = -1; + + /* Initialize the CCBs */ + TAILQ_INIT(&sc->sc_free_ccb); + i = dpt_create_ccbs(sc, sc->sc_ccbs, sc->sc_nccbs); + + if (i == 0) { + printf("%s: unable to create CCBs\n", sc->sc_dv.dv_xname); + return; + } else if (i != sc->sc_nccbs) { + printf("%s: %d/%d CCBs created!\n", sc->sc_dv.dv_xname, i, + sc->sc_nccbs); + sc->sc_nccbs = i; + } + + /* Set shutdownhook before we start any device activity */ + sc->sc_sdh = shutdownhook_establish(dpt_shutdown, sc); + + /* Get the page 0 inquiry data from the HBA */ + dpt_hba_inquire(sc, &ei); + + /* + * dpt0 at pci0 dev 12 function 0: DPT SmartRAID III (PM3224A/9X-R) + * dpt0: interrupting at irq 10 + * dpt0: 64 queued commands, 1 channel(s), adapter on ID(s) 7 + */ + for (i = 0; ei->ei_vendor[i] != ' ' && i < 8; i++) + ; + ei->ei_vendor[i] = '\0'; + + for (i = 0; ei->ei_model[i] != ' ' && i < 7; i++) + model[i] = ei->ei_model[i]; + for (j = 0; ei->ei_suffix[j] != ' ' && j < 7; j++) + model[i++] = ei->ei_model[i]; + model[i] = '\0'; + + /* Find the cannonical name for the board */ + for (i = 0; dpt_cname[i] != NULL; i += 2) + if (memcmp(ei->ei_model, dpt_cname[i], 6) == 0) + break; + + printf("%s %s (%s)\n", ei->ei_vendor, dpt_cname[i + 1], model); + + if (intrstr != NULL) + printf("%s: interrupting at %s\n", sc->sc_dv.dv_xname, intrstr); + + printf("%s: %d queued commands, %d channel(s), adapter on ID(s)", + sc->sc_dv.dv_xname, sc->sc_nccbs, ec->ec_maxchannel + 1); + + for (i = 0; i <= ec->ec_maxchannel; i++) + printf(" %d", ec->ec_hba[3 - i]); + printf("\n"); + + /* Reset the SCSI bus */ + if (dpt_cmd(sc, NULL, 0, CP_IMMEDIATE, CPI_BUS_RESET)) + panic("%s: dpt_cmd failed", sc->sc_dv.dv_xname); + DELAY(20000); + + /* Fill in the adapter, each link and attach in turn */ +#ifdef __NetBSD__ + sc->sc_adapter.scsipi_cmd = dpt_scsi_cmd; + sc->sc_adapter.scsipi_minphys = dpt_minphys; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + sc->sc_adapter.scsi_cmd = dpt_scsi_cmd; + sc->sc_adapter.scsi_minphys = dpt_minphys; +#endif /* __OpenBSD__ */ + + for (i = 0; i <= ec->ec_maxchannel; i++) { +#ifdef __NetBSD__ + struct scsipi_link *link; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_link *link; +#endif /* __OpenBSD__ */ + sc->sc_hbaid[i] = ec->ec_hba[3 - i]; + link = &sc->sc_link[i]; +#ifdef __NetBSD__ + link->scsipi_scsi.scsibus = i; + link->scsipi_scsi.adapter_target = sc->sc_hbaid[i]; + link->scsipi_scsi.max_lun = ec->ec_maxlun; + link->scsipi_scsi.max_target = ec->ec_maxtarget; + link->type = BUS_SCSI; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + link->scsibus = i; + link->adapter_target = sc->sc_hbaid[i]; +#endif /* __OpenBSD__ */ + link->device = &dpt_dev; + link->adapter = &sc->sc_adapter; + link->adapter_softc = sc; + link->openings = sc->sc_nccbs; + config_found(&sc->sc_dv, link, scsiprint); + } +} + +/* + * Our 'shutdownhook' to cleanly shut down the HBA. The HBA must flush + * all data from it's cache and mark array groups as clean. + */ +void +dpt_shutdown(xxx_sc) + void *xxx_sc; +{ + struct dpt_softc *sc; + + sc = xxx_sc; + printf("shutting down %s...", sc->sc_dv.dv_xname); + dpt_cmd(sc, NULL, 0, CP_IMMEDIATE, CPI_POWEROFF_WARN); + DELAY(5000*1000); + printf(" done\n"); +} + +/* + * Send an EATA command to the HBA. + */ +int +dpt_cmd(sc, cp, addr, eatacmd, icmd) + struct dpt_softc *sc; + struct eata_cp *cp; + u_int32_t addr; + int eatacmd, icmd; +{ + int i; + + for (i = 20000; i; i--) { + if ((dpt_inb(sc, HA_AUX_STATUS) & HA_AUX_BUSY) == 0) + break; + DELAY(50); + } + + /* Not the most graceful way to handle this */ + if (i == 0) { + printf("%s: HBA timeout on EATA command issue; aborting\n", + sc->sc_dv.dv_xname); + return (-1); + } + + if (cp == NULL) + addr = 0; + + dpt_outb(sc, HA_DMA_BASE + 0, (u_int32_t)addr); + dpt_outb(sc, HA_DMA_BASE + 1, (u_int32_t)addr >> 8); + dpt_outb(sc, HA_DMA_BASE + 2, (u_int32_t)addr >> 16); + dpt_outb(sc, HA_DMA_BASE + 3, (u_int32_t)addr >> 24); + + if (eatacmd == CP_IMMEDIATE) { + if (cp == NULL) { + /* XXX should really pass meaningful values */ + dpt_outb(sc, HA_ICMD_CODE2, 0); + dpt_outb(sc, HA_ICMD_CODE1, 0); + } + dpt_outb(sc, HA_ICMD, icmd); + } + + dpt_outb(sc, HA_COMMAND, eatacmd); + return (0); +} + +/* + * Wait for the HBA to reach an arbitrary state. + */ +int +dpt_wait(sc, mask, state, ms) + struct dpt_softc *sc; + u_int8_t mask, state; + int ms; +{ + + for (ms *= 10; ms; ms--) { + if ((dpt_inb(sc, HA_STATUS) & mask) == state) + return (0); + DELAY(100); + } + return (-1); +} + +/* + * Wait for the specified CCB to finish. This is used when we may not be + * able to sleep and/or interrupts are disabled (eg autoconfiguration). + * The timeout value from the CCB is used. This should only be used for + * CCB_PRIVATE requests; otherwise the CCB will get recycled before we get + * a look at it. + */ +int +dpt_poll(sc, ccb) + struct dpt_softc *sc; + struct dpt_ccb *ccb; +{ + int i; + +#ifdef DEBUG + if ((ccb->ccb_flg & CCB_PRIVATE) == 0) + panic("dpt_poll: called for non-CCB_PRIVATE request\n"); +#endif + + if ((ccb->ccb_flg & CCB_INTR) != 0) + return (0); + + for (i = ccb->ccb_timeout * 20; i; i--) { + if ((dpt_inb(sc, HA_AUX_STATUS) & HA_AUX_INTR) != 0) + dpt_intr(sc); + if ((ccb->ccb_flg & CCB_INTR) != 0) + return (0); + DELAY(50); + } + return (-1); +} + +/* + * Read the EATA configuration from the HBA and perform some sanity checks. + */ +int +dpt_readcfg(sc) + struct dpt_softc *sc; +{ + struct eata_cfg *ec; + int i, j, stat; + u_int16_t *p; + + ec = &sc->sc_ec; + + /* Older firmware may puke if we talk to it too soon after reset */ + dpt_outb(sc, HA_COMMAND, CP_RESET); + DELAY(750000); + + for (i = 1000; i; i--) { + if ((dpt_inb(sc, HA_STATUS) & HA_ST_READY) != 0) + break; + DELAY(2000); + } + + if (i == 0) { + printf("%s: HBA not ready after reset: %02x\n", + sc->sc_dv.dv_xname, dpt_inb(sc, HA_STATUS)); + return (-1); + } + + while((((stat = dpt_inb(sc, HA_STATUS)) + != (HA_ST_READY|HA_ST_SEEK_COMPLETE)) + && (stat != (HA_ST_READY|HA_ST_SEEK_COMPLETE|HA_ST_ERROR)) + && (stat != (HA_ST_READY|HA_ST_SEEK_COMPLETE|HA_ST_ERROR|HA_ST_DRQ))) + || (dpt_wait(sc, HA_ST_BUSY, 0, 2000))) { + /* RAID drives still spinning up? */ + if((dpt_inb(sc, HA_ERROR) != 'D') + || (dpt_inb(sc, HA_ERROR + 1) != 'P') + || (dpt_inb(sc, HA_ERROR + 2) != 'T')) { + printf("%s: HBA not ready\n", sc->sc_dv.dv_xname); + return (-1); + } + } + + /* + * Issue the read-config command and wait for the data to appear. + * XXX we shouldn't be doing this with PIO, but it makes it a lot + * easier as no DMA setup is required. + */ + dpt_outb(sc, HA_COMMAND, CP_PIO_GETCFG); + memset(ec, 0, sizeof(*ec)); + i = ((int)&((struct eata_cfg *)0)->ec_cfglen + + sizeof(ec->ec_cfglen)) >> 1; + p = (u_int16_t *)ec; + + if (dpt_wait(sc, 0xFF, HA_ST_DATA_RDY, 2000)) { + printf("%s: cfg data didn't appear (status:%02x)\n", + sc->sc_dv.dv_xname, dpt_inb(sc, HA_STATUS)); + return (-1); + } + + /* Begin reading */ + while (i--) + *p++ = dpt_inw(sc, HA_DATA); + + if ((i = ec->ec_cfglen) > (sizeof(struct eata_cfg) + - (int)(&(((struct eata_cfg *)0L)->ec_cfglen)) + - sizeof(ec->ec_cfglen))) + i = sizeof(struct eata_cfg) + - (int)(&(((struct eata_cfg *)0L)->ec_cfglen)) + - sizeof(ec->ec_cfglen); + + j = i + (int)(&(((struct eata_cfg *)0L)->ec_cfglen)) + + sizeof(ec->ec_cfglen); + i >>= 1; + + while (i--) + *p++ = dpt_inw(sc, HA_DATA); + + /* Flush until we have read 512 bytes. */ + i = (512 - j + 1) >> 1; + while (i--) + dpt_inw(sc, HA_DATA); + + /* Defaults for older Firmware */ + if (p <= (u_short *)&ec->ec_hba[DPT_MAX_CHANNELS - 1]) + ec->ec_hba[DPT_MAX_CHANNELS - 1] = 7; + + if ((dpt_inb(sc, HA_STATUS) & HA_ST_ERROR) != 0) { + printf("%s: HBA error\n", sc->sc_dv.dv_xname); + return (-1); + } + + if (!ec->ec_hbavalid) { + printf("%s: ec_hba field invalid\n", sc->sc_dv.dv_xname); + return (-1); + } + + if (memcmp(ec->ec_eatasig, "EATA", 4) != 0) { + printf("%s: EATA signature mismatch\n", sc->sc_dv.dv_xname); + return (-1); + } + + if (!ec->ec_dmasupported) { + printf("%s: DMA not supported\n", sc->sc_dv.dv_xname); + return (-1); + } + + return (0); +} + +/* + * Adjust the size of each I/O before it passes to the SCSI layer. + */ +void +dpt_minphys(bp) + struct buf *bp; +{ + + if (bp->b_bcount > DPT_MAX_XFER) + bp->b_bcount = DPT_MAX_XFER; + minphys(bp); +} + +/* + * Put a CCB onto the freelist. + */ +void +dpt_free_ccb(sc, ccb) + struct dpt_softc *sc; + struct dpt_ccb *ccb; +{ + int s; + + s = splbio(); + ccb->ccb_flg = 0; + TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, ccb_chain); + + /* Wake anybody waiting for a free ccb */ + if (ccb->ccb_chain.tqe_next == 0) + wakeup(&sc->sc_free_ccb); + splx(s); +} + +/* + * Initialize the specified CCB. + */ +int +dpt_init_ccb(sc, ccb) + struct dpt_softc *sc; + struct dpt_ccb *ccb; +{ + int error; + + /* Create the DMA map for this CCB's data */ + error = bus_dmamap_create(sc->sc_dmat, DPT_MAX_XFER, DPT_SG_SIZE, + DPT_MAX_XFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &ccb->ccb_dmamap_xfer); + + if (error) { + printf("%s: can't create ccb dmamap (%d)\n", + sc->sc_dv.dv_xname, error); + return (error); + } + + ccb->ccb_flg = 0; + ccb->ccb_ccbpa = sc->sc_dmamap_ccb->dm_segs[0].ds_addr + + CCB_OFF(sc, ccb); + return (0); +} + +/* + * Create a set of CCBs and add them to the free list. + */ +int +dpt_create_ccbs(sc, ccbstore, count) + struct dpt_softc *sc; + struct dpt_ccb *ccbstore; + int count; +{ + struct dpt_ccb *ccb; + int i, error; + + memset(ccbstore, 0, sizeof(struct dpt_ccb) * count); + + for (i = 0, ccb = ccbstore; i < count; i++, ccb++) { + if ((error = dpt_init_ccb(sc, ccb)) != 0) { + printf("%s: unable to init ccb, error = %d\n", + sc->sc_dv.dv_xname, error); + break; + } + ccb->ccb_id = i; + TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_chain); + } + + return (i); +} + +/* + * Get a free ccb. If there are none, see if we can allocate a new one. If + * none are available right now and we are permitted to sleep, then wait + * until one becomes free, otherwise return an error. + */ +struct dpt_ccb * +dpt_alloc_ccb(sc, flg) + struct dpt_softc *sc; + int flg; +{ + struct dpt_ccb *ccb; + int s; + + s = splbio(); + + for (;;) { + ccb = TAILQ_FIRST(&sc->sc_free_ccb); + if (ccb) { + TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ccb_chain); + break; + } +#ifdef __NetBSD__ + if ((flg & XS_CTL_NOSLEEP) != 0) { +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if ((flg & SCSI_NOSLEEP) != 0) { +#endif /* __OpenBSD__ */ + splx(s); + return (NULL); + } + tsleep(&sc->sc_free_ccb, PRIBIO, "dptccb", 0); + } + + ccb->ccb_flg |= CCB_ALLOC; + splx(s); + return (ccb); +} + +/* + * We have a CCB which has been processed by the HBA, now we look to see how + * the operation went. CCBs marked with CCB_PRIVATE are not automatically + * passed here by dpt_intr(). + */ +void +dpt_done_ccb(sc, ccb) + struct dpt_softc *sc; + struct dpt_ccb *ccb; +{ +#ifdef __NetBSD__ + struct scsipi_sense_data *s1, *s2; + struct scsipi_xfer *xs; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_sense_data *s1, *s2; + struct scsi_xfer *xs; +#endif /* __OpenBSD__ */ + bus_dma_tag_t dmat; + + dmat = sc->sc_dmat; + xs = ccb->ccb_xs; + + SC_DEBUG(xs->sc_link, SDEV_DB2, ("dpt_done_ccb\n")); + + /* + * If we were a data transfer, unload the map that described the + * data buffer. + */ + if (xs->datalen) { +#ifdef __NetBSD__ + bus_dmamap_sync(dmat, ccb->ccb_dmamap_xfer, 0, + ccb->ccb_dmamap_xfer->dm_mapsize, + (xs->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(dmat, ccb->ccb_dmamap_xfer, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); +#endif /* __OpenBSD__ */ + bus_dmamap_unload(dmat, ccb->ccb_dmamap_xfer); + } + + /* + * Otherwise, put the results of the operation into the xfer and + * call whoever started it. + */ +#ifdef DIAGNOSTIC + if ((ccb->ccb_flg & CCB_ALLOC) == 0) { + panic("%s: done ccb not allocated!\n", sc->sc_dv.dv_xname); + return; + } +#endif + + if (xs->error == XS_NOERROR) { + if (ccb->ccb_hba_status != HA_NO_ERROR) { + switch (ccb->ccb_hba_status) { + case HA_ERROR_SEL_TO: + xs->error = XS_SELTIMEOUT; + break; + case HA_ERROR_RESET: + xs->error = XS_RESET; + break; + default: /* Other scsi protocol messes */ + printf("%s: HBA status %x\n", + sc->sc_dv.dv_xname, ccb->ccb_hba_status); + xs->error = XS_DRIVER_STUFFUP; + } + } else if (ccb->ccb_scsi_status != SCSI_OK) { + switch (ccb->ccb_scsi_status) { + case SCSI_CHECK: + s1 = &ccb->ccb_sense; +#ifdef __NetBSD__ + s2 = &xs->sense.scsi_sense; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + s2 = &xs->sense; +#endif /* __OpenBSD__ */ + *s2 = *s1; + xs->error = XS_SENSE; + break; + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + default: + printf("%s: SCSI status %x\n", + sc->sc_dv.dv_xname, ccb->ccb_scsi_status); + xs->error = XS_DRIVER_STUFFUP; + } + } else + xs->resid = 0; + + xs->status = ccb->ccb_scsi_status; + } + + /* Free up the CCB and mark the command as done */ + dpt_free_ccb(sc, ccb); +#ifdef __NetBSD__ + xs->xs_status |= XS_STS_DONE; + scsipi_done(xs); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + xs->flags |= ITSDONE; + scsi_done(xs); +#endif /* __OpenBSD__ */ + + /* + * If there are entries in the software queue, try to run the first + * one. We should be more or less guaranteed to succeed, since we + * just freed an CCB. NOTE: dpt_scsi_cmd() relies on our calling it + * with the first entry in the queue. + */ +#ifdef __NetBSD__ + if ((xs = TAILQ_FIRST(&sc->sc_queue)) != NULL) +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if ((xs = sc->sc_queue.lh_first) != NULL) +#endif /* __OpenBSD__ */ + dpt_scsi_cmd(xs); +} + +#ifdef __OpenBSD__ +/* + * Insert a scsi_xfer into the software queue. We overload xs->free_list + * to avoid having to allocate additional resources (since we're used + * only during resource shortages anyhow. + */ +static void +dpt_enqueue(sc, xs, infront) + struct dpt_softc *sc; + struct scsi_xfer *xs; + int infront; +{ + + if (infront || sc->sc_queue.lh_first == NULL) { + if (sc->sc_queue.lh_first == NULL) + sc->sc_queuelast = xs; + LIST_INSERT_HEAD(&sc->sc_queue, xs, free_list); + return; + } + LIST_INSERT_AFTER(sc->sc_queuelast, xs, free_list); + sc->sc_queuelast = xs; +} + +/* + * Pull a scsi_xfer off the front of the software queue. + */ +static struct scsi_xfer * +dpt_dequeue(sc) + struct dpt_softc *sc; +{ + struct scsi_xfer *xs; + + xs = sc->sc_queue.lh_first; + LIST_REMOVE(xs, free_list); + + if (sc->sc_queue.lh_first == NULL) + sc->sc_queuelast = NULL; + + return (xs); +} +#endif /* __OpenBSD__ */ + +/* + * Start a SCSI command. + */ +int +dpt_scsi_cmd(xs) +#ifdef __NetBSD__ + struct scsipi_xfer *xs; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_xfer *xs; +#endif /* __OpenBSD__ */ +{ + int error, i, flags, s, fromqueue, dontqueue; +#ifdef __NetBSD__ + struct scsipi_link *sc_link; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_link *sc_link; +#endif /* __OpenBSD__ */ + struct dpt_softc *sc; + struct dpt_ccb *ccb; + struct eata_sg *sg; + struct eata_cp *cp; + bus_dma_tag_t dmat; + bus_dmamap_t xfer; + + sc_link = xs->sc_link; +#ifdef __NetBSD__ + flags = xs->xs_control; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + flags = xs->flags; +#endif /* __OpenBSD__ */ + sc = sc_link->adapter_softc; + dmat = sc->sc_dmat; + fromqueue = 0; + dontqueue = 0; + + SC_DEBUG(sc_link, SDEV_DB2, ("dpt_scsi_cmd\n")); + + /* Protect the queue */ + s = splbio(); + + /* + * If we're running the queue from dpt_done_ccb(), we've been called + * with the first queue entry as our argument. + */ +#ifdef __NetBSD__ + if (xs == TAILQ_FIRST(&sc->sc_queue)) { + TAILQ_REMOVE(&sc->sc_queue, xs, adapter_q); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if (xs == sc->sc_queue.lh_first) { + xs = dpt_dequeue(sc); +#endif /* __OpenBSD__ */ + fromqueue = 1; + } else { + /* Cmds must be no more than 12 bytes for us */ + if (xs->cmdlen > 12) { + splx(s); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + /* XXX we can't reset devices just yet */ +#ifdef __NetBSD__ + if ((flags & XS_CTL_RESET) != 0) { +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if ((xs->flags & SCSI_RESET) != 0) { +#endif /* __OpenBSD__ */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + /* Polled requests can't be queued for later */ +#ifdef __NetBSD__ + dontqueue = flags & XS_CTL_POLL; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + dontqueue = xs->flags & SCSI_POLL; +#endif /* __OpenBSD__ */ + + /* If there are jobs in the queue, run them first */ +#ifdef __NetBSD__ + if (TAILQ_FIRST(&sc->sc_queue) != NULL) { +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if (sc->sc_queue.lh_first != NULL) { +#endif /* __OpenBSD__ */ + /* + * If we can't queue we abort, since we must + * preserve the queue order. + */ + if (dontqueue) { + splx(s); + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + + /* Swap with the first queue entry. */ +#ifdef __NetBSD__ + TAILQ_INSERT_TAIL(&sc->sc_queue, xs, adapter_q); + xs = TAILQ_FIRST(&sc->sc_queue); + TAILQ_REMOVE(&sc->sc_queue, xs, adapter_q); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + dpt_enqueue(sc, xs, 0); + xs = dpt_dequeue(sc); +#endif /* __OpenBSD__ */ + fromqueue = 1; + } + } + + /* Get a CCB */ +#ifdef __NetBSD__ + if ((ccb = dpt_alloc_ccb(sc, flags)) == NULL) { +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if ((ccb = dpt_alloc_ccb(sc, xs->flags)) == NULL) { +#endif /* __OpenBSD__ */ + /* If we can't queue, we lose */ + if (dontqueue) { + splx(s); + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + + /* + * Stuff request into the queue, in front if we came off + * it in the first place. + */ +#ifdef __NetBSD__ + if (fromqueue) + TAILQ_INSERT_HEAD(&sc->sc_queue, xs, adapter_q); + else + TAILQ_INSERT_TAIL(&sc->sc_queue, xs, adapter_q); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + dpt_enqueue(sc, xs, fromqueue); +#endif /* __OpenBSD__ */ + splx(s); + return (SUCCESSFULLY_QUEUED); + } + + splx(s); + + ccb->ccb_xs = xs; + ccb->ccb_timeout = xs->timeout; + + cp = &ccb->ccb_eata_cp; +#ifdef __NetBSD__ + memcpy(&cp->cp_scsi_cmd, xs->cmd, xs->cmdlen); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bcopy(xs->cmd, &cp->cp_scsi_cmd, xs->cmdlen); +#endif /* __OpenBSD__ */ + cp->cp_ccbid = ccb->ccb_id; +#ifdef __NetBSD__ + cp->cp_id = sc_link->scsipi_scsi.target; + cp->cp_lun = sc_link->scsipi_scsi.lun; + cp->cp_channel = sc_link->scsipi_scsi.channel; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + cp->cp_id = sc_link->target; + cp->cp_lun = sc_link->lun; + cp->cp_channel = sc_link->scsibus; +#endif /* __OpenBSD__ */ + cp->cp_senselen = sizeof(ccb->ccb_sense); + cp->cp_stataddr = htobe32(sc->sc_sppa); + cp->cp_dispri = 1; + cp->cp_identify = 1; + cp->cp_autosense = 1; +#ifdef __NetBSD__ + cp->cp_datain = ((flags & XS_CTL_DATA_IN) != 0); + cp->cp_dataout = ((flags & XS_CTL_DATA_OUT) != 0); + cp->cp_interpret = (sc->sc_hbaid[sc_link->scsipi_scsi.channel] == + sc_link->scsipi_scsi.target); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + cp->cp_datain = ((xs->flags & SCSI_DATA_IN) != 0); + cp->cp_dataout = ((xs->flags & SCSI_DATA_OUT) != 0); + cp->cp_interpret = (sc->sc_hbaid[sc_link->scsibus] == sc_link->target); +#endif /* __OpenBSD__ */ + + /* Synchronous xfers musn't write-back through the cache */ + if (xs->bp != NULL && (xs->bp->b_flags & (B_ASYNC | B_READ)) == 0) + cp->cp_nocache = 1; + else + cp->cp_nocache = 0; + + cp->cp_senseaddr = htobe32(sc->sc_dmamap_ccb->dm_segs[0].ds_addr + + CCB_OFF(sc, ccb) + offsetof(struct dpt_ccb, ccb_sense)); + + if (xs->datalen) { + xfer = ccb->ccb_dmamap_xfer; +#ifdef TFS +#ifdef __NetBSD__ + if ((flags & XS_CTL_DATA_UIO) != 0) { + error = bus_dmamap_load_uio(dmat, xfer, + (struct uio *)xs->data, (flags & XS_CTL_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + if ((xs->flags & SCSI_DATA_UIO) != 0) { + error = bus_dmamap_load_uio(dmat, xfer, + (xs->flags & SCSI_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); +#endif /* __OpenBSD__ */ + } else +#endif /*TFS */ + { +#ifdef __NetBSD__ + error = bus_dmamap_load(dmat, xfer, xs->data, + xs->datalen, NULL, (flags & XS_CTL_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + error = bus_dmamap_load(dmat, xfer, xs->data, + xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); +#endif /* __OpenBSD__ */ + } + + if (error) { + printf("%s: dpt_scsi_cmd: ", sc->sc_dv.dv_xname); + if (error == EFBIG) + printf("more than %d dma segs\n", DPT_SG_SIZE); + else + printf("error %d loading dma map\n", error); + + xs->error = XS_DRIVER_STUFFUP; + dpt_free_ccb(sc, ccb); + return (COMPLETE); + } + +#ifdef __NetBSD__ + bus_dmamap_sync(dmat, xfer, 0, xfer->dm_mapsize, + (flags & XS_CTL_DATA_IN) ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(dmat, xfer, (xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); +#endif /* __OpenBSD__ */ + + /* Don't bother using scatter/gather for just 1 segment */ + if (xfer->dm_nsegs == 1) { + cp->cp_dataaddr = htobe32(xfer->dm_segs[0].ds_addr); + cp->cp_datalen = htobe32(xfer->dm_segs[0].ds_len); + cp->cp_scatter = 0; + } else { + /* + * Load the hardware scatter/gather map with the + * contents of the DMA map. + */ + sg = ccb->ccb_sg; + for (i = 0; i < xfer->dm_nsegs; i++, sg++) { + sg->sg_addr = + htobe32(xfer->dm_segs[i].ds_addr); + sg->sg_len = + htobe32(xfer->dm_segs[i].ds_len); + } + cp->cp_dataaddr = htobe32(CCB_OFF(sc, ccb) + + sc->sc_dmamap_ccb->dm_segs[0].ds_addr + + offsetof(struct dpt_ccb, ccb_sg)); + cp->cp_datalen = htobe32(i * sizeof(struct eata_sg)); + cp->cp_scatter = 1; + } + } else { + cp->cp_dataaddr = 0; + cp->cp_datalen = 0; + cp->cp_scatter = 0; + } + + /* Sync up CCB and status packet */ +#ifdef __NetBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, CCB_OFF(sc, ccb), + sizeof(struct dpt_ccb), BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, sc->sc_spoff, + sizeof(struct eata_sp), BUS_DMASYNC_PREREAD); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, BUS_DMASYNC_PREREAD); +#endif /* __OpenBSD__ */ + + /* + * Start the command. If we are polling on completion, mark it + * private so that dpt_intr/dpt_done_ccb don't recycle the CCB + * without us noticing. + */ + if (dontqueue != 0) + ccb->ccb_flg |= CCB_PRIVATE; + + if (dpt_cmd(sc, &ccb->ccb_eata_cp, ccb->ccb_ccbpa, CP_DMA_CMD, 0)) { + printf("%s: dpt_cmd failed\n", sc->sc_dv.dv_xname); + xs->error = XS_DRIVER_STUFFUP; + dpt_free_ccb(sc, ccb); + return (TRY_AGAIN_LATER); + } + + if (dontqueue == 0) + return (SUCCESSFULLY_QUEUED); + + /* Don't wait longer than this single command wants to wait */ + if (dpt_poll(sc, ccb)) { + dpt_timeout(ccb); + /* Wait for abort to complete */ + if (dpt_poll(sc, ccb)) + dpt_timeout(ccb); + } + + dpt_done_ccb(sc, ccb); + return (COMPLETE); +} + +/* + * Specified CCB has timed out, abort it. + */ +void +dpt_timeout(arg) + void *arg; +{ +#ifdef __NetBSD__ + struct scsipi_link *sc_link; + struct scsipi_xfer *xs; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_link *sc_link; + struct scsi_xfer *xs; +#endif /* __OpenBSD__ */ + struct dpt_softc *sc; + struct dpt_ccb *ccb; + int s; + + ccb = arg; + xs = ccb->ccb_xs; + sc_link = xs->sc_link; + sc = sc_link->adapter_softc; + +#ifdef __NetBSD__ + scsi_print_addr(sc_link); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + sc_print_addr(sc_link); +#endif /* __OpenBSD__ */ + printf("timed out (status:%02x aux status:%02x)", + dpt_inb(sc, HA_STATUS), dpt_inb(sc, HA_AUX_STATUS)); + + s = splbio(); + + if ((ccb->ccb_flg & CCB_ABORT) != 0) { + /* Abort timed out, reset the HBA */ + printf(" AGAIN, resetting HBA\n"); + dpt_outb(sc, HA_COMMAND, CP_RESET); + DELAY(750000); + } else { + /* Abort the operation that has timed out */ + printf("\n"); + ccb->ccb_xs->error = XS_TIMEOUT; + ccb->ccb_timeout = DPT_ABORT_TIMEOUT; + ccb->ccb_flg |= CCB_ABORT; + /* Start the abort */ + if (dpt_cmd(sc, &ccb->ccb_eata_cp, ccb->ccb_ccbpa, + CP_IMMEDIATE, CPI_SPEC_ABORT)) + printf("%s: dpt_cmd failed\n", sc->sc_dv.dv_xname); + } + + splx(s); +} + +#ifdef DEBUG +/* + * Dump the contents of an EATA status packet. + */ +void +dpt_dump_sp(sp) + struct eata_sp *sp; +{ + int i; + + printf("\thba_status\t%02x\n", sp->sp_hba_status); + printf("\tscsi_status\t%02x\n", sp->sp_scsi_status); + printf("\tinv_residue\t%d\n", sp->sp_inv_residue); + printf("\tccbid\t\t%d\n", sp->sp_ccbid); + printf("\tid_message\t%d\n", sp->sp_id_message); + printf("\tque_message\t%d\n", sp->sp_que_message); + printf("\ttag_message\t%d\n", sp->sp_tag_message); + printf("\tmessages\t"); + + for (i = 0; i < 9; i++) + printf("%d ", sp->sp_messages[i]); + + printf("\n"); +} +#endif /* DEBUG */ + +/* + * Get inquiry data from the adapter. + */ +void +dpt_hba_inquire(sc, ei) + struct dpt_softc *sc; + struct eata_inquiry_data **ei; +{ + struct dpt_ccb *ccb; + struct eata_cp *cp; + bus_dma_tag_t dmat; + + *ei = (struct eata_inquiry_data *)sc->sc_scr; + dmat = sc->sc_dmat; + + /* Get a CCB and mark as private */ + if ((ccb = dpt_alloc_ccb(sc, 0)) == NULL) + panic("%s: no CCB for inquiry", sc->sc_dv.dv_xname); + + ccb->ccb_flg |= CCB_PRIVATE; + ccb->ccb_timeout = 200; + + /* Put all the arguments into the CCB */ + cp = &ccb->ccb_eata_cp; + cp->cp_ccbid = ccb->ccb_id; + cp->cp_id = sc->sc_hbaid[0]; + cp->cp_lun = 0; + cp->cp_channel = 0; + cp->cp_senselen = sizeof(ccb->ccb_sense); + cp->cp_stataddr = htobe32(sc->sc_sppa); + cp->cp_dispri = 1; + cp->cp_identify = 1; + cp->cp_autosense = 0; + cp->cp_interpret = 1; + cp->cp_nocache = 0; + cp->cp_datain = 1; + cp->cp_dataout = 0; + cp->cp_senseaddr = 0; + cp->cp_dataaddr = htobe32(sc->sc_scrpa); + cp->cp_datalen = htobe32(sizeof(struct eata_inquiry_data)); + cp->cp_scatter = 0; + + /* Put together the SCSI inquiry command */ + memset(&cp->cp_scsi_cmd, 0, 12); /* XXX */ + cp->cp_scsi_cmd = INQUIRY; + cp->cp_len = sizeof(struct eata_inquiry_data); + + /* Sync up CCB, status packet and scratch area */ +#ifdef __NetBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, CCB_OFF(sc, ccb), + sizeof(struct dpt_ccb), BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, sc->sc_spoff, + sizeof(struct eata_sp), BUS_DMASYNC_PREREAD); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, sc->sc_scroff, + sizeof(struct eata_inquiry_data), BUS_DMASYNC_PREREAD); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, BUS_DMASYNC_PREREAD); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, BUS_DMASYNC_PREREAD); +#endif /* __OpenBSD__ */ + + /* Start the command and poll on completion */ + if (dpt_cmd(sc, &ccb->ccb_eata_cp, ccb->ccb_ccbpa, CP_DMA_CMD, 0)) + panic("%s: dpt_cmd failed", sc->sc_dv.dv_xname); + + if (dpt_poll(sc, ccb)) + panic("%s: inquiry timed out", sc->sc_dv.dv_xname); + + if (ccb->ccb_hba_status != HA_NO_ERROR || + ccb->ccb_scsi_status != SCSI_OK) + panic("%s: inquiry failed (hba:%02x scsi:%02x", + sc->sc_dv.dv_xname, ccb->ccb_hba_status, + ccb->ccb_scsi_status); + + /* Sync up the DMA map and free CCB, returning */ +#ifdef __NetBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, sc->sc_scroff, + sizeof(struct eata_inquiry_data), BUS_DMASYNC_POSTREAD); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_ccb, BUS_DMASYNC_POSTREAD); +#endif /* __OpenBSD__ */ + dpt_free_ccb(sc, ccb); +} diff --git a/sys/dev/ic/dptreg.h b/sys/dev/ic/dptreg.h new file mode 100644 index 00000000000..080f5b25f5b --- /dev/null +++ b/sys/dev/ic/dptreg.h @@ -0,0 +1,300 @@ +/* $OpenBSD: dptreg.h,v 1.1 1999/11/30 07:55:56 cmetz Exp $ */ +/* $NetBSD: dptreg.h,v 1.4 1999/10/19 20:16:48 ad Exp $ */ + +/* + * Copyright (c) 1999 Andy Doran <ad@NetBSD.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. + * + */ + +#ifndef _IC_DPTREG_H_ +#define _IC_DPTREG_H_ 1 + +/* Hardware limits */ +#define DPT_MAX_TARGETS 16 +#define DPT_MAX_LUNS 8 +#define DPT_MAX_CHANNELS 3 + +/* Software parameters */ +#define DPT_MAX_XFER ((DPT_SG_SIZE - 1) << PGSHIFT) +#define DPT_MAX_CCBS 256 +#define DPT_SG_SIZE 64 +#define DPT_ABORT_TIMEOUT 2000 /* milliseconds */ +#define DPT_MORE_TIMEOUT 1000 /* microseconds */ + +#ifdef _KERNEL + +#if BYTE_ORDER == LITTLE_ENDIAN +#define SWAP32(x) bswap32((x)) +#define SWAP16(x) bswap16((x)) +#define RSWAP32(x) (x) +#define RSWAP16(x) (x) +#else +#define SWAP32(x) (x) +#define SWAP16(x) (x) +#define RSWAP32(x) bswap32((x)) +#define RSWAP16(x) bswap16((x)) +#endif + +#define dpt_inb(x, o) \ + bus_space_read_1((x)->sc_iot, (x)->sc_ioh, (o)) + +#define dpt_inw(x, o) \ + RSWAP16(bus_space_read_2((x)->sc_iot, (x)->sc_ioh, (o))) + +#define dpt_inl(x, o) \ + RSWAP32(bus_space_read_4((x)->sc_iot, (x)->sc_ioh, (o))) + +#define dpt_outb(x, o, d) \ + bus_space_write_1((x)->sc_iot, (x)->sc_ioh, (o), (d)) + +#define dpt_outw(x, o, d) \ + bus_space_write_2((x)->sc_iot, (x)->sc_ioh, (o), RSWAP16(d)) + +#define dpt_outl(x, o, d) \ + bus_space_write_4((x)->sc_iot, (x)->sc_ioh, (o), RSWAP32(d)) + +#endif /* _KERNEL */ + +/* + * HBA registers + */ +#define HA_BASE 0x10 +#define HA_DATA (HA_BASE + 0) +#define HA_ERROR (HA_BASE + 1) +#define HA_DMA_BASE (HA_BASE + 2) +#define HA_ICMD_CODE2 (HA_BASE + 4) +#define HA_ICMD_CODE1 (HA_BASE + 5) +#define HA_ICMD (HA_BASE + 6) + +/* EATA commands. There are many more the we don't define or use. */ +#define HA_COMMAND (HA_BASE + 7) +#define CP_PIO_GETCFG 0xf0 /* Read configuration data, PIO */ +#define CP_PIO_CMD 0xf2 /* Execute command, PIO */ +#define CP_DMA_GETCFG 0xfd /* Read configuration data, DMA */ +#define CP_DMA_CMD 0xff /* Execute command, DMA */ +#define CP_PIO_TRUNCATE 0xf4 /* Truncate transfer command, PIO */ +#define CP_RESET 0xf9 /* Reset controller and SCSI bus */ +#define CP_REBOOT 0x06 /* Reboot controller (last resort) */ +#define CP_IMMEDIATE 0xfa /* EATA immediate command */ +#define CPI_GEN_ABORT 0x00 /* Generic abort */ +#define CPI_SPEC_RESET 0x01 /* Specific reset */ +#define CPI_BUS_RESET 0x02 /* Bus reset */ +#define CPI_SPEC_ABORT 0x03 /* Specific abort */ +#define CPI_QUIET_INTR 0x04 /* ?? */ +#define CPI_ROM_DL_EN 0x05 /* ?? */ +#define CPI_COLD_BOOT 0x06 /* Cold boot HBA */ +#define CPI_FORCE_IO 0x07 /* ?? */ +#define CPI_BUS_OFFLINE 0x08 /* Set SCSI bus offline */ +#define CPI_RESET_MSKD_BUS 0x09 /* Reset masked bus */ +#define CPI_POWEROFF_WARN 0x0a /* Power about to fail */ + +#define HA_STATUS (HA_BASE + 7) +#define HA_ST_ERROR 0x01 +#define HA_ST_MORE 0x02 +#define HA_ST_CORRECTD 0x04 +#define HA_ST_DRQ 0x08 +#define HA_ST_SEEK_COMPLETE 0x10 +#define HA_ST_WRT_FLT 0x20 +#define HA_ST_READY 0x40 +#define HA_ST_BUSY 0x80 +#define HA_ST_DATA_RDY (HA_ST_SEEK_COMPLETE|HA_ST_READY|HA_ST_DRQ) + +#define HA_AUX_STATUS (HA_BASE + 8) +#define HA_AUX_BUSY 0x01 +#define HA_AUX_INTR 0x02 + +/* + * Structure of an EATA command packet. + */ +struct eata_cp { + u_int8_t cp_scsireset :1; /* cause a bus reset */ + u_int8_t cp_hbainit :1; /* cause HBA to reinitialize */ + u_int8_t cp_autosense :1; /* auto request sense on err */ + u_int8_t cp_scatter :1; /* doing SG I/O */ + u_int8_t cp_quick :1; /* return no status packet */ + u_int8_t cp_interpret :1; /* HBA interprets SCSI CDB */ + u_int8_t cp_dataout :1; /* data out phase */ + u_int8_t cp_datain :1; /* data in phase */ + u_int8_t cp_senselen; /* request sense length */ + u_int8_t cp_unused0[3]; /* unused */ + u_int8_t cp_tophys :1; /* send to RAID component */ + u_int8_t cp_unused1 :7; /* unused */ + u_int8_t cp_physunit :1; /* phys unit on mirrored pair */ + u_int8_t cp_noat :1; /* no address translation */ + u_int8_t cp_nocache :1; /* no HBA caching */ + u_int8_t cp_unused2 :5; /* unused */ + u_int8_t cp_id :5; /* SCSI device id of target */ + u_int8_t cp_channel :3; /* SCSI channel id */ + u_int8_t cp_lun :3; /* SCSI LUN id */ + u_int8_t cp_unused3 :2; /* unused */ + u_int8_t cp_luntar :1; /* CP is for target ROUTINE */ + u_int8_t cp_dispri :1; /* give disconnect privilege */ + u_int8_t cp_identify :1; /* always true */ + u_int8_t cp_msg[3]; /* message bytes 0-3 */ + + /* Partial SCSI CDB ref */ + u_int8_t cp_scsi_cmd; + u_int8_t cp_extent :1; + u_int8_t cp_bytchk :1; + u_int8_t cp_reladr :1; + u_int8_t cp_cmplst :1; + u_int8_t cp_fmtdata :1; + u_int8_t cp_cdblun :3; + u_int8_t cp_page; + u_int8_t cp_unused4; + u_int8_t cp_len; + u_int8_t cp_link :1; + u_int8_t cp_flag :1; + u_int8_t cp_unused5 :4; + u_int8_t cp_vendor :2; + u_int8_t cp_cdbmore[6]; + + u_int32_t cp_datalen; /* length in bytes of data/SG list */ + u_int32_t cp_ccbid; /* ID of software CCB */ + u_int32_t cp_dataaddr; /* address of data/SG list */ + u_int32_t cp_stataddr; /* addr for status packet */ + u_int32_t cp_senseaddr; /* addr of req. sense (err only) */ +}; + +/* + * EATA status packet as returned by controller upon command completion. It + * contains status, message info and a handle on the initiating CCB. + */ +struct eata_sp { + u_int8_t sp_hba_status; /* host adapter status */ + u_int8_t sp_scsi_status; /* SCSI bus status */ + u_int8_t sp_reserved[2]; /* reserved */ + u_int32_t sp_inv_residue; /* bytes not transfered */ + u_int32_t sp_ccbid; /* ID of software CCB */ + u_int8_t sp_id_message; + u_int8_t sp_que_message; + u_int8_t sp_tag_message; + u_int8_t sp_messages[9]; +}; + +/* + * HBA status as returned by status packet. Bit 7 signals end of command. + */ +#define HA_NO_ERROR 0x00 /* No error on command */ +#define HA_ERROR_SEL_TO 0x01 /* Device selection timeout */ +#define HA_ERROR_CMD_TO 0x02 /* Device command timeout */ +#define HA_ERROR_RESET 0x03 /* SCSI bus was reset */ +#define HA_INIT_POWERUP 0x04 /* Initial controller power up */ +#define HA_UNX_BUSPHASE 0x05 /* Unexpected bus phase */ +#define HA_UNX_BUS_FREE 0x06 /* Unexpected bus free */ +#define HA_BUS_PARITY 0x07 /* SCSI bus parity error */ +#define HA_SCSI_HUNG 0x08 /* SCSI bus hung */ +#define HA_UNX_MSGRJCT 0x09 /* Unexpected message reject */ +#define HA_RESET_STUCK 0x0A /* SCSI bus reset stuck */ +#define HA_RSENSE_FAIL 0x0B /* Auto-request sense failed */ +#define HA_PARITY 0x0C /* HBA memory parity error */ +#define HA_ABORT_NA 0x0D /* CP aborted - not on bus */ +#define HA_ABORTED 0x0E /* CP aborted - was on bus */ +#define HA_RESET_NA 0x0F /* CP reset - not on bus */ +#define HA_RESET 0x10 /* CP reset - was on bus */ +#define HA_ECC 0x11 /* HBA memory ECC error */ +#define HA_PCI_PARITY 0x12 /* PCI parity error */ +#define HA_PCI_MASTER 0x13 /* PCI master abort */ +#define HA_PCI_TARGET 0x14 /* PCI target abort */ +#define HA_PCI_SIGNAL_TARGET 0x15 /* PCI signalled target abort */ +#define HA_ABORT 0x20 /* Software abort (too many retries) */ + +/* + * Scatter-gather list element. + */ +struct eata_sg { + u_int32_t sg_addr; + u_int32_t sg_len; +}; + +/* + * EATA configuration data as returned by HBA. XXX this is bogus, some fields + * don't *seem* to be filled on my SmartCache III. Also, it doesn't sync up + * with the structure FreeBSD uses. [ad] + */ +struct eata_cfg { + u_int8_t ec_devtype; + u_int8_t ec_pagecode; + u_int8_t ec_reserved0; + u_int8_t ec_cfglen; /* Length in bytes after this field */ + u_int8_t ec_eatasig[4]; /* EATA signature */ + u_int8_t ec_eataversion; /* EATA version number */ + u_int8_t ec_overlapcmds : 1; /* Overlapped cmds supported */ + u_int8_t ec_targetmode : 1; /* Target mode supported */ + u_int8_t ec_trunnotrec : 1; /* Truncate cmd not supported */ + u_int8_t ec_moresupported:1; /* More cmd supported */ + u_int8_t ec_dmasupported : 1; /* DMA mode supported */ + u_int8_t ec_dmanumvalid : 1; /* DMA channel field is valid */ + u_int8_t ec_atadev : 1; /* This is an ATA device */ + u_int8_t ec_hbavalid : 1; /* HBA field is valid */ + u_int8_t ec_padlength[2]; /* Pad bytes for PIO cmds */ + u_int8_t ec_hba[4]; /* Host adapter SCSI IDs */ + u_int8_t ec_cplen[4]; /* Command packet length */ + u_int8_t ec_splen[4]; /* Status packet length */ + u_int8_t ec_queuedepth[2]; /* Controller queue depth */ + u_int8_t ec_reserved1[2]; + u_int8_t ec_sglen[2]; /* Maximum scatter gather list size */ + u_int8_t ec_irqnum : 4; /* IRQ number */ + u_int8_t ec_irqtrigger : 1; /* IRQ trigger: 0 = edge, 1 = level */ + u_int8_t ec_secondary : 1; /* Controller not at address 0x170 */ + u_int8_t ec_dmanum : 2; /* DMA channel index for ISA */ + u_int8_t ec_irq; /* IRQ address */ + u_int8_t ec_iodisable : 1; /* ISA I/O address disabled */ + u_int8_t ec_forceaddr : 1; /* PCI forced to an EISA/ISA addr */ + u_int8_t ec_sg64k : 1; /* 64K of SG space */ + u_int8_t ec_sgunaligned : 1; /* Can do unaligned SG, otherwise 4 */ + u_int8_t ec_reserved2 : 4; /* Reserved */ + u_int8_t ec_maxtarget : 5; /* Maximun SCSI target ID supported */ + u_int8_t ec_maxchannel : 3; /* Maximun channel number supported */ + u_int8_t ec_maxlun; /* Maximum LUN supported */ + u_int8_t ec_reserved3 : 3; /* Reserved field */ + u_int8_t ec_autoterm : 1; /* Support auto term (low byte) */ + u_int8_t ec_pcim1 : 1; /* PCI M1 chipset */ + u_int8_t ec_bogusraidid : 1; /* Raid ID may be questionable */ + u_int8_t ec_pci : 1; /* PCI adapter */ + u_int8_t ec_eisa : 1; /* EISA adapter */ + u_int8_t ec_raidnum; /* RAID host adapter humber */ +}; + +/* + * How SCSI inquiry data breaks down for EATA boards. + */ +struct eata_inquiry_data { + u_int8_t ei_device; + u_int8_t ei_dev_qual2; + u_int8_t ei_version; + u_int8_t ei_response_format; + u_int8_t ei_additional_length; + u_int8_t ei_unused[2]; + u_int8_t ei_flags; + char ei_vendor[8]; /* Vendor, e.g: DPT, NEC */ + char ei_model[7]; /* Model number */ + char ei_suffix[9]; /* Model number suffix */ + char ei_fw[3]; /* Firmware */ + char ei_fwrev[1]; /* Firmware revision */ + u_int8_t ei_extra[8]; +}; + +#endif /* !defined _IC_DPTREG_H_ */ diff --git a/sys/dev/ic/dptvar.h b/sys/dev/ic/dptvar.h new file mode 100644 index 00000000000..bb2e9c122a1 --- /dev/null +++ b/sys/dev/ic/dptvar.h @@ -0,0 +1,128 @@ +/* $OpenBSD: dptvar.h,v 1.1 1999/11/30 07:55:56 cmetz Exp $ */ +/* $NetBSD: dptvar.h,v 1.5 1999/10/23 16:26:32 ad Exp $ */ + +/* + * Copyright (c) 1999 Andy Doran <ad@NetBSD.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. + * + */ + +#ifndef _IC_DPTVAR_H_ +#define _IC_DPTVAR_H_ 1 +#ifdef _KERNEL + +#define CCB_OFF(sc,m) ((u_long)(m) - (u_long)((sc)->sc_ccbs)) + +#define CCB_ALLOC 0x01 /* CCB allocated */ +#define CCB_ABORT 0x02 /* abort has been issued on this CCB */ +#define CCB_INTR 0x04 /* HBA interrupted for this CCB */ +#define CCB_PRIVATE 0x08 /* ours; don't talk to scsipi when done */ + +struct dpt_ccb { + struct eata_cp ccb_eata_cp; /* EATA command packet */ + struct eata_sg ccb_sg[DPT_SG_SIZE]; /* SG element list */ + volatile int ccb_flg; /* CCB flags */ + int ccb_timeout; /* timeout in ms */ + u_int32_t ccb_ccbpa; /* physical addr of this CCB */ + bus_dmamap_t ccb_dmamap_xfer; /* dmamap for data xfers */ + int ccb_hba_status; /* from status packet */ + int ccb_scsi_status; /* from status packet */ + int ccb_id; /* unique ID of this CCB */ + TAILQ_ENTRY(dpt_ccb) ccb_chain; /* link to next CCB */ +#ifdef __NetBSD__ + struct scsipi_sense_data ccb_sense; /* SCSI sense data on error */ + struct scsipi_xfer *ccb_xs; /* initiating SCSI command */ +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_sense_data ccb_sense; + struct scsi_xfer *ccb_xs; +#endif /* __OpenBSD__ */ +}; + +struct dpt_softc { + struct device sc_dv; /* generic device data */ + bus_space_handle_t sc_ioh; /* bus space handle */ +#ifdef __NetBSD__ + struct scsipi_adapter sc_adapter;/* scsipi adapter */ + struct scsipi_link sc_link[3]; /* prototype link for each channel */ +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + struct scsi_adapter sc_adapter;/* scsipi adapter */ + struct scsi_link sc_link[3]; /* prototype link for each channel */ +#endif /* __OpenBSD__ */ + struct eata_cfg sc_ec; /* EATA configuration data */ + bus_space_tag_t sc_iot; /* bus space tag */ + bus_dma_tag_t sc_dmat; /* bus DMA tag */ + bus_dmamap_t sc_dmamap_ccb; /* maps the CCBs */ + void *sc_ih; /* interrupt handler cookie */ + void *sc_sdh; /* shutdown hook */ + struct dpt_ccb *sc_ccbs; /* all our CCBs */ + struct eata_sp *sc_statpack; /* EATA status packet */ + int sc_spoff; /* status packet offset in dmamap */ + u_int32_t sc_sppa; /* status packet physical address */ + caddr_t sc_scr; /* scratch area */ + int sc_scrlen; /* scratch area length */ + int sc_scroff; /* scratch area offset in dmamap */ + u_int32_t sc_scrpa; /* scratch area physical address */ + int sc_hbaid[3]; /* ID of HBA on each channel */ + int sc_nccbs; /* number of CCBs available */ + int sc_open; /* device is open */ + TAILQ_HEAD(, dpt_ccb) sc_free_ccb;/* free ccb list */ +#ifdef __NetBSD__ + TAILQ_HEAD(, scsipi_xfer) sc_queue;/* pending commands */ +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + LIST_HEAD(, scsi_xfer) sc_queue;/* pending commands */ + struct scsi_xfer *sc_queuelast; +#endif /* __NetBSD__ */ +}; + +int dpt_intr __P((void *)); +int dpt_readcfg __P((struct dpt_softc *)); +void dpt_init __P((struct dpt_softc *, const char *)); +void dpt_shutdown __P((void *)); +void dpt_timeout __P((void *)); +void dpt_minphys __P((struct buf *)); +#ifdef __NetBSD__ +int dpt_scsi_cmd __P((struct scsipi_xfer *)); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +int dpt_scsi_cmd __P((struct scsi_xfer *)); +#endif /* __OpenBSD__ */ +int dpt_wait __P((struct dpt_softc *, u_int8_t, u_int8_t, int)); +int dpt_poll __P((struct dpt_softc *, struct dpt_ccb *)); +int dpt_cmd __P((struct dpt_softc *, struct eata_cp *, u_int32_t, int, int)); +void dpt_hba_inquire __P((struct dpt_softc *, struct eata_inquiry_data **)); +void dpt_reset_ccb __P((struct dpt_softc *, struct dpt_ccb *)); +void dpt_free_ccb __P((struct dpt_softc *, struct dpt_ccb *)); +void dpt_done_ccb __P((struct dpt_softc *, struct dpt_ccb *)); +int dpt_init_ccb __P((struct dpt_softc *, struct dpt_ccb *)); +int dpt_create_ccbs __P((struct dpt_softc *, struct dpt_ccb *, int)); +struct dpt_ccb *dpt_alloc_ccb __P((struct dpt_softc *, int)); +#ifdef DEBUG +void dpt_dump_sp __P((struct eata_sp *)); +#endif + +#endif /* _KERNEL */ +#endif /* !defined _IC_DPTVAR_H_ */ diff --git a/sys/dev/pci/dpt_pci.c b/sys/dev/pci/dpt_pci.c new file mode 100644 index 00000000000..82182436a9d --- /dev/null +++ b/sys/dev/pci/dpt_pci.c @@ -0,0 +1,163 @@ +/* $OpenBSD: dpt_pci.c,v 1.1 1999/11/30 07:55:56 cmetz Exp $ */ +/* $NetBSD: dpt_pci.c,v 1.2 1999/09/29 17:33:02 ad Exp $ */ + +/* + * Copyright (c) 1999 Andy Doran <ad@NetBSD.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. + * + */ + +/* + * PCI front-end for DPT EATA SCSI driver. + */ + +#include <sys/cdefs.h> +#ifdef __NetBSD__ +__KERNEL_RCSID(0, "$NetBSD: dpt_pci.c,v 1.2 1999/09/29 17:33:02 ad Exp $"); +#endif /* __NetBSD__ */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/queue.h> +#include <sys/proc.h> + +#include <machine/endian.h> +#include <machine/bus.h> + +#ifdef __NetBSD__ +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#endif /* __OpenBSD__ */ + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> + +#include <dev/ic/dptreg.h> +#include <dev/ic/dptvar.h> + +#define PCI_CBMA 0x14 /* Configuration base memory address */ +#define PCI_CBIO 0x10 /* Configuration base I/O address */ + +#ifdef __NetBSD__ +int dpt_pci_match __P((struct device *, struct cfdata *, void *)); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ +int dpt_pci_match __P((struct device *, void *, void *)); +#endif /* __OpenBSD__ */ +void dpt_pci_attach __P((struct device *, struct device *, void *)); + +struct cfattach dpt_pci_ca = { + sizeof(struct dpt_softc), dpt_pci_match, dpt_pci_attach +}; + +int +dpt_pci_match(parent, match, aux) + struct device *parent; +#ifdef __NetBSD__ + struct cfdata *match; +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + void *match; +#endif /* __OpenBSD__ */ + void *aux; +{ + struct pci_attach_args *pa = (struct pci_attach_args *) aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_DPT && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_DPT_SC_RAID) + return (1); + + return (0); +} + +void +dpt_pci_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct pci_attach_args *pa; + struct dpt_softc *sc; + pci_chipset_tag_t pc; + pci_intr_handle_t ih; + const char *intrstr; + pcireg_t csr; + + sc = (struct dpt_softc *)self; + pa = (struct pci_attach_args *)aux; + pc = pa->pa_pc; + printf(": "); + + if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot, + &sc->sc_ioh, NULL, NULL)) { + printf("can't map i/o space\n"); + return; + } + + sc->sc_dmat = pa->pa_dmat; + + /* Enable the device. */ + csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + csr | PCI_COMMAND_MASTER_ENABLE); + + /* Map and establish the interrupt. */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("can't map interrupt\n"); + return; + } + intrstr = pci_intr_string(pc, ih); +#ifdef __NetBSD__ + sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, dpt_intr, sc); +#endif /* __NetBSD__ */ +#ifdef __OpenBSD__ + sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, dpt_intr, sc, + sc->sc_dv.dv_xname); +#endif /* __OpenBSD__ */ + if (sc->sc_ih == NULL) { + printf("can't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + + /* Read the EATA configuration */ + if (dpt_readcfg(sc)) { + printf("%s: readcfg failed - see dpt(4)\n", + sc->sc_dv.dv_xname); + return; + } + + /* Now attach to the bus-independant code */ + dpt_init(sc, intrstr); +} diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 685b98cf632..db0807dcf9e 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.55 1999/11/28 16:43:47 aaron Exp $ +# $OpenBSD: files.pci,v 1.56 1999/11/30 07:55:56 cmetz Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -25,6 +25,10 @@ attach ahc at pci with ahc_pci file dev/pci/ahc_pci.c ahc_pci file dev/ic/smc93cx6.c ahc_pci +# DPT EATA SCSI controllers +attach dpt at pci with dpt_pci +file dev/pci/dpt_pci.c dpt_pci + # AdvanSys 1200A, 1200B, and ULTRA SCSI controllers # device declaration in sys/conf/files attach adv at pci with adv_pci |