--- sys/dev/pci/files.pci.orig Wed Oct 22 13:57:56 1997 +++ sys/dev/pci/files.pci Mon Feb 15 18:09:24 1999 @@ -18,6 +18,11 @@ file dev/pci/ahc_pci.c ahc_pci file dev/ic/smc93cx6.c ahc_pci +# AMD Am53c974 PCscsi-PCI SCSI controllers +device pcscp: scsi, ncr53c9x +attach pcscp at pci +file dev/pci/pcscp.c pcscp + # BusLogic BT-9xx PCI family # device declaration in sys/conf/files attach bha at pci with bha_pci --- sys/dev/ic/ncr53c9x.c.orig Thu Dec 31 18:02:12 1998 +++ sys/dev/ic/ncr53c9x.c Mon Feb 15 20:38:38 1999 @@ -135,6 +135,7 @@ "ESP406", "FAS408", "FAS216", + "AM53C974", }; /* @@ -242,6 +243,7 @@ case NCR_VARIANT_ESP406: case NCR_VARIANT_FAS408: NCR_SCSIREGS(sc); + case NCR_VARIANT_AM53C974: case NCR_VARIANT_FAS216: case NCR_VARIANT_NCR53C94: case NCR_VARIANT_NCR53C96: @@ -263,6 +265,9 @@ NCR_WRITE_REG(sc, NCR_SYNCOFF, 0); NCR_WRITE_REG(sc, NCR_TIMEOUT, sc->sc_timeout); } + if (sc->sc_rev == NCR_VARIANT_AM53C974) + NCR_WRITE_REG(sc, NCR_AMDCFG4, + NCRAMDCFG4_GE12NS | NCRAMDCFG4_RADE); } /* @@ -401,15 +406,26 @@ struct ncr53c9x_softc *sc; struct ncr53c9x_tinfo *ti; { + u_char syncoff, synctp, cfg3 = sc->sc_cfg3; if (ti->flags & T_SYNCMODE) { - NCR_WRITE_REG(sc, NCR_SYNCOFF, ti->offset); - NCR_WRITE_REG(sc, NCR_SYNCTP, - ncr53c9x_stp2cpb(sc, ti->period)); + syncoff = ti->offset; + synctp = ncr53c9x_stp2cpb(sc, ti->period); + if (sc->sc_rev == NCR_VARIANT_AM53C974) { + if (ti->period <= 50) + cfg3 |= NCRAMDCFG3_FSCSI; + else + synctp--; + } } else { - NCR_WRITE_REG(sc, NCR_SYNCOFF, 0); - NCR_WRITE_REG(sc, NCR_SYNCTP, 0); + syncoff = 0; + synctp = 0; } + if (sc->sc_rev == NCR_VARIANT_AM53C974) + NCR_WRITE_REG(sc, NCR_CFG3, cfg3); + + NCR_WRITE_REG(sc, NCR_SYNCOFF, syncoff); + NCR_WRITE_REG(sc, NCR_SYNCTP, synctp); } int ncr53c9x_dmaselect = 0; --- sys/dev/ic/ncr53c9xreg.h.orig Sun May 18 20:21:23 1997 +++ sys/dev/ic/ncr53c9xreg.h Mon Feb 15 18:29:49 1999 @@ -224,3 +224,25 @@ #define NCRCFG5_AINT 0x01 /* ATA Interupt Enable */ #define NCR_SIGNTR 0x0e /* RO - Signature */ + +/* Am53c974 Config #3 */ +#define NCR_AMDCFG3 0x0c /* RW - Configuration #3 */ +#define NCRAMDCFG3_IDM 0x80 /* ID Message Res Check */ +#define NCRAMDCFG3_QTE 0x40 /* Queue Tag Enable */ +#define NCRAMDCFG3_CDB 0x20 /* CDB 10-bytes OK */ +#define NCRAMDCFG3_FSCSI 0x10 /* Fast SCSI */ +#define NCRAMDCFG3_FCLK 0x08 /* Fast Clock (40MHz) */ +#define NCRAMDCFG3_RSVD 0x07 /* Reserved */ + +/* Am53c974 Config #4 */ +#define NCR_AMDCFG4 0x0d /* RW - Configuration #4 */ +#define NCRAMDCFG4_GE 0xc0 /* Glitch Eater */ +#define NCRAMDCFG4_GE12NS 0x00 /* Signal window 12ns */ +#define NCRAMDCFG4_GE25NS 0x80 /* Signal window 25ns */ +#define NCRAMDCFG4_GE35NS 0x40 /* Signal window 35ns */ +#define NCRAMDCFG4_GE0NS 0xc0 /* Signal window 0ns */ +#define NCRAMDCFG4_PWD 0x20 /* Reduced power feature */ +#define NCRAMDCFG4_RSVD 0x13 /* Reserved */ +#define NCRAMDCFG4_RAE 0x08 /* Active neg. REQ/ACK */ +#define NCRAMDCFG4_RADE 0x04 /* Active neg. REQ/ACK/DAT */ + --- sys/dev/ic/ncr53c9xvar.h.orig Mon Oct 6 20:25:43 1997 +++ sys/dev/ic/ncr53c9xvar.h Mon Feb 15 18:26:58 1999 @@ -86,7 +86,8 @@ #define NCR_VARIANT_ESP406 5 #define NCR_VARIANT_FAS408 6 #define NCR_VARIANT_FAS216 7 -#define NCR_VARIANT_MAX 8 +#define NCR_VARIANT_AM53C974 8 +#define NCR_VARIANT_MAX 9 /* * ECB. Holds additional information for each SCSI command Comments: We --- /dev/null Sat Mar 6 08:18:04 1999 +++ sys/dev/pci/pcscp.c Sat Mar 27 05:39:36 1999 @@ -0,0 +1,711 @@ +/* $NetBSD: pcscp.c,v 1.2 1999/01/08 19:55:17 thorpej 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 Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center; Izumi Tsutsui. + * + * 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. + */ + +/* + * pcscp.c: device dependent code for AMD Am53c974 (PCscsi-PCI) + * written by Izumi Tsutsui + * + * Technical manual available at + * http://www.amd.com/products/npd/techdocs/techdocs.html + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define IO_MAP_REG 0x10 +#define MEM_MAP_REG 0x14 + +struct pcscp_softc { + struct ncr53c9x_softc sc_ncr53c9x; /* glue to MI code */ + + bus_space_tag_t sc_st; /* bus space tag */ + bus_space_handle_t sc_sh; /* bus space handle */ + void *sc_ih; /* interrupt cookie */ + + bus_dma_tag_t sc_dmat; /* DMA tag */ + + bus_dmamap_t sc_xfermap; /* DMA map for transfers */ + + u_int32_t *sc_mdladdr; /* MDL array */ + bus_dmamap_t sc_mdldmap; /* MDL DMA map */ + + int sc_active; /* DMA state */ + int sc_datain; /* DMA Data Direction */ + size_t sc_dmasize; /* DMA size */ + char **sc_dmaaddr; /* DMA address */ + size_t *sc_dmalen; /* DMA length */ +}; + +#define READ_DMAREG(sc, reg) \ + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) +#define WRITE_DMAREG(sc, reg, var) \ + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (var)) + +/* don't have to use MI defines in MD code... */ +#undef NCR_READ_REG +#define NCR_READ_REG(sc, reg) pcscp_read_reg((sc), (reg)) +#undef NCR_WRITE_REG +#define NCR_WRITE_REG(sc, reg, val) pcscp_write_reg((sc), (reg), (val)) + +#ifdef __BROKEN_INDIRECT_CONFIG +int pcscp_match __P((struct device *, void *, void *)); +#else +int pcscp_match __P((struct device *, struct cfdata *, void *)); +#endif +void pcscp_attach __P((struct device *, struct device *, void *)); + +struct cfattach pcscp_ca = { + sizeof(struct pcscp_softc), pcscp_match, pcscp_attach +}; + +struct cfdriver pcscp_cd = { + NULL, "pcscp", DV_DULL +}; + +struct scsipi_adapter pcscp_switch = { + ncr53c9x_scsi_cmd, + minphys, /* no max at this level; handled by DMA code */ + NULL, + NULL, +}; + +struct scsipi_device pcscp_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +/* + * Functions and the switch for the MI code. + */ + +u_char pcscp_read_reg __P((struct ncr53c9x_softc *, int)); +void pcscp_write_reg __P((struct ncr53c9x_softc *, int, u_char)); +int pcscp_dma_isintr __P((struct ncr53c9x_softc *)); +void pcscp_dma_reset __P((struct ncr53c9x_softc *)); +int pcscp_dma_intr __P((struct ncr53c9x_softc *)); +int pcscp_dma_setup __P((struct ncr53c9x_softc *, caddr_t *, + size_t *, int, size_t *)); +void pcscp_dma_go __P((struct ncr53c9x_softc *)); +void pcscp_dma_stop __P((struct ncr53c9x_softc *)); +int pcscp_dma_isactive __P((struct ncr53c9x_softc *)); + +struct ncr53c9x_glue pcscp_glue = { + pcscp_read_reg, + pcscp_write_reg, + pcscp_dma_isintr, + pcscp_dma_reset, + pcscp_dma_intr, + pcscp_dma_setup, + pcscp_dma_go, + pcscp_dma_stop, + pcscp_dma_isactive, + NULL, /* gl_clear_latched_intr */ +}; + +int +pcscp_match(parent, match, aux) + struct device *parent; +#ifdef __BROKEN_INDIRECT_CONFIG + void *match, *aux; +#else + struct cfdata *match; + void *aux; +#endif +{ + struct pci_attach_args *pa = aux; + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_AMD) + return 0; + + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_AMD_PCSCSI_PCI: +#if 0 + case PCI_PRODUCT_AMD_PCNETS_PCI: +#endif + return 1; + } + return 0; +} + +/* + * Attach this instance, and then all the sub-devices + */ +void +pcscp_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pci_attach_args *pa = aux; + struct pcscp_softc *esc = (void *)self; + struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x; + bus_space_tag_t st, iot, memt; + bus_space_handle_t sh, ioh, memh; + int ioh_valid, memh_valid; + pci_intr_handle_t ih; + const char *intrstr; + pcireg_t csr; + bus_dma_segment_t seg; + int error, rseg; + + ioh_valid = (pci_mapreg_map(pa, IO_MAP_REG, + PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL) == 0); +#if 0 /* XXX cannot use memory map? */ + memh_valid = (pci_mapreg_map(pa, MEM_MAP_REG, + PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, + &memt, &memh, NULL, NULL) == 0); +#else + memh_valid = 0; +#endif + + if (memh_valid) { + st = memt; + sh = memh; + } else if (ioh_valid) { + st = iot; + sh = ioh; + } else { + printf(": unable to map registers\n"); + return; + } + printf("\n"); + + sc->sc_glue = &pcscp_glue; + + esc->sc_st = st; + esc->sc_sh = sh; + esc->sc_dmat = pa->pa_dmat; + + 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 | PCI_COMMAND_IO_ENABLE); + + /* + * XXX More of this should be in ncr53c9x_attach(), but + * XXX should we really poke around the chip that much in + * XXX the MI code? Think about this more... + */ + + /* + * Set up static configuration info. + */ + + /* + * XXX should read configuration from EEPROM? + * + * MI ncr53c9x driver does not support configuration + * per each target device, though... + */ + sc->sc_id = 7; + sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB; + sc->sc_cfg2 = NCRCFG2_SCSI2 | NCRCFG2_FE; + sc->sc_cfg3 = NCRAMDCFG3_IDM | NCRAMDCFG3_FCLK; + sc->sc_rev = NCR_VARIANT_AM53C974; + sc->sc_freq = 40; /* MHz */ + + /* + * XXX minsync and maxxfer _should_ be set up in MI code, + * XXX but it appears to have some dependency on what sort + * XXX of DMA we're hooked up to, etc. + */ + + /* + * This is the value used to start sync negotiations + * Note that the NCR register "SYNCTP" is programmed + * in "clocks per byte", and has a minimum value of 4. + * The SCSI period used in negotiation is one-fourth + * of the time (in nanoseconds) needed to transfer one byte. + * Since the chip's clock is given in MHz, we have the following + * formula: 4 * period = (1000 / freq) * 4 + */ + + sc->sc_minsync = 1000 / sc->sc_freq; + + /* Really no limit, but since we want to fit into the TCR... */ + sc->sc_maxxfer = 16 * 1024 * 1024; + + /* map and establish interrupt */ + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); + return; + } + + intrstr = pci_intr_string(pa->pa_pc, ih); + esc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, + (int (*)(void *))ncr53c9x_intr, esc); + if (esc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt", sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + if (intrstr != NULL) + printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, + intrstr); + + /* + * Create the DMA maps for the data transfers. + */ + +#define MDL_SEG_SIZE 0x1000 /* 4kbyte per segment */ +#define MDL_SEG_OFFSET 0x0FFF +#define MDL_SIZE (MAXPHYS / MDL_SEG_SIZE + 1) /* no hardware limit? */ + + if (bus_dmamap_create(esc->sc_dmat, MAXPHYS, MDL_SIZE, MAXPHYS, 0, + BUS_DMA_NOWAIT, &esc->sc_xfermap)) { + printf("%s: can't create dma maps\n", sc->sc_dev.dv_xname); + return; + } + + /* + * Allocate and map memory for the MDL. + */ + + if ((error = bus_dmamem_alloc(esc->sc_dmat, + sizeof(u_int32_t) * MDL_SIZE, NBPG, 0, &seg, 1, &rseg, + BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to allocate memory for the MDL, " + "error = %d\n", sc->sc_dev.dv_xname, error); + return; + } + if ((error = bus_dmamem_map(esc->sc_dmat, &seg, rseg, + sizeof(u_int32_t) * MDL_SIZE , (caddr_t *)&esc->sc_mdladdr, + BUS_DMA_NOWAIT|BUS_DMAMEM_NOSYNC)) != 0) { + printf("%s: unable to map the MDL memory, error = %d\n", + sc->sc_dev.dv_xname, error); + return; + } + if ((error = bus_dmamap_create(esc->sc_dmat, + sizeof(u_int32_t) * MDL_SIZE, 1, sizeof(u_int32_t) * MDL_SIZE, + 0, BUS_DMA_NOWAIT, &esc->sc_mdldmap)) != 0) { + printf("%s: unable to map_create for the MDL, error = %d\n", + sc->sc_dev.dv_xname, error); + return; + } + if ((error = bus_dmamap_load(esc->sc_dmat, esc->sc_mdldmap, + esc->sc_mdladdr, sizeof(u_int32_t) * MDL_SIZE, + NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load for the MDL, error = %d\n", + sc->sc_dev.dv_xname, error); + return; + } + + /* Do the common parts of attachment. */ + printf("%s", sc->sc_dev.dv_xname); + + ncr53c9x_attach(sc, &pcscp_switch, &pcscp_dev); + + /* Turn on target selection using the `dma' method */ + ncr53c9x_dmaselect = 1; +} + +/* + * Glue functions. + */ + +u_char +pcscp_read_reg(sc, reg) + struct ncr53c9x_softc *sc; + int reg; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + + return bus_space_read_1(esc->sc_st, esc->sc_sh, reg << 2); +} + +void +pcscp_write_reg(sc, reg, v) + struct ncr53c9x_softc *sc; + int reg; + u_char v; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + + bus_space_write_1(esc->sc_st, esc->sc_sh, reg << 2, v); +} + +int +pcscp_dma_isintr(sc) + struct ncr53c9x_softc *sc; +{ + + return NCR_READ_REG(sc, NCR_STAT) & NCRSTAT_INT; +} + +void +pcscp_dma_reset(sc) + struct ncr53c9x_softc *sc; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + + WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE); + + esc->sc_active = 0; +} + +int +pcscp_dma_intr(sc) + struct ncr53c9x_softc *sc; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + int trans, resid, i; + bus_dmamap_t dmap = esc->sc_xfermap; + int datain = esc->sc_datain; + u_int32_t dmastat; + char *p = NULL; + + dmastat = READ_DMAREG(esc, DMA_STAT); + + if (dmastat & DMASTAT_ERR) { + /* XXX not tested... */ + WRITE_DMAREG(esc, DMA_CMD, DMACMD_ABORT | + (datain ? DMACMD_DIR : 0)); + + printf("%s: error: DMA error detected; Aborting.\n", + sc->sc_dev.dv_xname); + bus_dmamap_unload(esc->sc_dmat, dmap); + return -1; + } + + if (dmastat & DMASTAT_ABT) { + /* XXX What should be done? */ + printf("%s: dma_intr: DMA aborted.\n", sc->sc_dev.dv_xname); + WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | + (datain ? DMACMD_DIR : 0)); + esc->sc_active = 0; + return 0; + } + + /* This is an "assertion" :) */ + if (esc->sc_active == 0) + panic("pcscp dmaintr: DMA wasn't active"); + + /* DMA has stopped */ + + esc->sc_active = 0; + + if (esc->sc_dmasize == 0) { + /* A "Transfer Pad" operation completed */ + NCR_DMA(("dmaintr: discarded %d bytes (tcl=%d, tcm=%d)\n", + NCR_READ_REG(sc, NCR_TCL) | + (NCR_READ_REG(sc, NCR_TCM) << 8), + NCR_READ_REG(sc, NCR_TCL), + NCR_READ_REG(sc, NCR_TCM))); + return 0; + } + + resid = 0; + /* + * If a transfer onto the SCSI bus gets interrupted by the device + * (e.g. for a SAVEPOINTER message), the data in the FIFO counts + * as residual since the ESP counter registers get decremented as + * bytes are clocked into the FIFO. + */ + if (!datain && + (resid = (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF)) != 0) { + NCR_DMA(("pcscp_dma_intr: empty esp FIFO of %d ", resid)); + } + + if ((sc->sc_espstat & NCRSTAT_TC) == 0) { + /* + * `Terminal count' is off, so read the residue + * out of the ESP counter registers. + */ + if (datain) { + resid = NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF; + while (resid > 1) + resid = NCR_READ_REG(sc, NCR_FFLAG) & + NCRFIFO_FF; + WRITE_DMAREG(esc, DMA_CMD, DMACMD_BLAST | DMACMD_MDL | + (datain ? DMACMD_DIR : 0)); + + for (i = 0; i < 0x8000; i++) /* XXX 0x8000 ? */ + if (READ_DMAREG(esc, DMA_STAT) & DMASTAT_BCMP) + break; + + /* See the below comments... */ + if (resid) + p = *esc->sc_dmaaddr; + } + + resid += (NCR_READ_REG(sc, NCR_TCL) | + (NCR_READ_REG(sc, NCR_TCM) << 8) | + ((sc->sc_cfg2 & NCRCFG2_FE) + ? (NCR_READ_REG(sc, NCR_TCH) << 16) : 0)); + + if (resid == 0 && esc->sc_dmasize == 65536 && + (sc->sc_cfg2 & NCRCFG2_FE) == 0) + /* A transfer of 64K is encoded as `TCL=TCM=0' */ + resid = 65536; + } else { + while((dmastat & DMASTAT_DONE) == 0) + dmastat = READ_DMAREG(esc, DMA_STAT); + } + + WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain ? DMACMD_DIR : 0)); + + bus_dmamap_sync(esc->sc_dmat, dmap, + datain ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(esc->sc_dmat, dmap); + + trans = esc->sc_dmasize - resid; + + /* + * From the technical manual notes: + * + * `In some odd byte conditions, one residual byte will be left + * in the SCSI FIFO, and the FIFO flags will never count to 0. + * When this happens, the residual byte should be retrieved + * via PIO following completion of the BLAST operation.' + */ + + if (p) { + p += trans; + *p = NCR_READ_REG(sc, NCR_FIFO); + trans++; + } + + if (trans < 0) { /* transferred < 0 ? */ +#if 0 + /* + * This situation can happen in perfectly normal operation + * if the ESP is reselected while using DMA to select + * another target. As such, don't print the warning. + */ + printf("%s: xfer (%d) > req (%d)\n", + sc->sc_dev.dv_xname, trans, esc->sc_dmasize); +#endif + trans = esc->sc_dmasize; + } + + NCR_DMA(("dmaintr: tcl=%d, tcm=%d, tch=%d; trans=%d, resid=%d\n", + NCR_READ_REG(sc, NCR_TCL), + NCR_READ_REG(sc, NCR_TCM), + (sc->sc_cfg2 & NCRCFG2_FE) + ? NCR_READ_REG(sc, NCR_TCH) : 0, + trans, resid)); + + *esc->sc_dmalen -= trans; + *esc->sc_dmaaddr += trans; + + return 0; +} + +int +pcscp_dma_setup(sc, addr, len, datain, dmasize) + struct ncr53c9x_softc *sc; + caddr_t *addr; + size_t *len; + int datain; + size_t *dmasize; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + bus_dmamap_t dmap = esc->sc_xfermap; + u_int32_t *mdl; + int error, nseg, seg; + bus_addr_t s_offset, s_addr; + long rest, count; + + WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain ? DMACMD_DIR : 0)); + + esc->sc_dmaaddr = addr; + esc->sc_dmalen = len; + esc->sc_dmasize = *dmasize; + esc->sc_datain = datain; + +#ifdef DIAGNOSTIC + if ((*dmasize / MDL_SEG_SIZE) > MDL_SIZE) + panic("pcscp: transfer size too large"); +#endif + + /* + * No need to set up DMA in `Transfer Pad' operation. + * (case of *dmasize == 0) + */ + if (*dmasize == 0) + return 0; + + error = bus_dmamap_load(esc->sc_dmat, dmap, *esc->sc_dmaaddr, + *esc->sc_dmalen, NULL, + sc->sc_nexus->xs->flags & SCSI_NOSLEEP ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + if (error) { + printf("%s: unable to load dmamap, error = %d\n", + sc->sc_dev.dv_xname, error); + return error; + } + + /* set transfer length */ + WRITE_DMAREG(esc, DMA_STC, *dmasize); + + /* set up MDL */ + mdl = esc->sc_mdladdr; + nseg = dmap->dm_nsegs; + seg = 0; + + /* the first segment is possibly not aligned with 4k MDL boundary */ + count = dmap->dm_segs[seg].ds_len; + s_offset = dmap->dm_segs[seg].ds_addr & MDL_SEG_OFFSET; + s_addr = dmap->dm_segs[seg].ds_addr - s_offset; + rest = MDL_SEG_SIZE - s_offset; + +#if BYTE_ORDER == BIG_ENDIAN +#define htopci(addr) bswap32(addr) +#else +#define htopci(addr) (addr) +#endif + + /* set the first MDL and offset */ + WRITE_DMAREG(esc, DMA_SPA, s_offset); + *mdl++ = htopci(s_addr); + count -= rest; + + /* rests of the first dmamap segment */ + while (count > 0) { + s_addr += MDL_SEG_SIZE; + *mdl++ = htopci(s_addr); + count -= MDL_SEG_SIZE; + } + + /* the rest dmamap segments are aligned with 4k boundary */ + for (seg = 1; seg < nseg; seg++) { + count = dmap->dm_segs[seg].ds_len; + s_addr = dmap->dm_segs[seg].ds_addr; + + /* first 4kbyte of each dmamap segment */ + *mdl++ = htopci(s_addr); + count -= MDL_SEG_SIZE; + + /* trailing contiguous 4k frames of each dmamap segments */ + while (count > 0) { + s_addr += MDL_SEG_SIZE; + *mdl++ = htopci(s_addr); + count -= MDL_SEG_SIZE; + } + } + +#undef htopci + + return 0; +} + +void +pcscp_dma_go(sc) + struct ncr53c9x_softc *sc; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + bus_dmamap_t dmap = esc->sc_xfermap, mdldmap = esc->sc_mdldmap; + int datain = esc->sc_datain; + + /* No DMA transfer in Transfer Pad operation */ + if (esc->sc_dmasize == 0) + return; + + /* sync transfer buffer */ + bus_dmamap_sync(esc->sc_dmat, dmap, + datain ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + /* sync MDL */ + bus_dmamap_sync(esc->sc_dmat, mdldmap, + BUS_DMASYNC_PREWRITE); + + /* set Starting MDL Address */ + WRITE_DMAREG(esc, DMA_SMDLA, mdldmap->dm_segs[0].ds_addr); + + /* set DMA command register bits */ + /* XXX DMA Transfer Interrupt Enable bit is broken? */ + WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | DMACMD_MDL | + /* DMACMD_INTE | */ + (datain ? DMACMD_DIR : 0)); + + /* issue DMA start command */ + WRITE_DMAREG(esc, DMA_CMD, DMACMD_START | DMACMD_MDL | + /* DMACMD_INTE | */ + (datain ? DMACMD_DIR : 0)); + + esc->sc_active = 1; +} + +void +pcscp_dma_stop(sc) + struct ncr53c9x_softc *sc; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + + /* dma stop */ + /* XXX What should we do here ? */ + WRITE_DMAREG(esc, DMA_CMD, DMACMD_ABORT | + ( esc->sc_datain ? DMACMD_DIR : 0)); + + esc->sc_active = 0; +} + +int +pcscp_dma_isactive(sc) + struct ncr53c9x_softc *sc; +{ + struct pcscp_softc *esc = (struct pcscp_softc *)sc; + + /* XXX should check esc->sc_active? */ + if ((READ_DMAREG(esc, DMA_CMD) & DMACMD_CMD) != DMACMD_IDLE) + return 1; + return 0; +} --- /dev/null Mon Feb 15 19:39:39 1999 +++ sys/dev/pci/pcscpreg.h Mon Feb 15 18:39:05 1999 @@ -0,0 +1,72 @@ +/* $NetBSD: pcscpreg.h,v 1.1 1999/01/06 23:23:33 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Izumi Tsutsui. + * + * 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. + */ + +/* + * Am53c974 DMA engine registers + */ + +#define DMA_CMD 0x40 /* Command */ +#define DMACMD_RSVD 0xFFFFFF28 /* reserved */ +#define DMACMD_DIR 0x00000080 /* Transfer Direction (read:1) */ +#define DMACMD_INTE 0x00000040 /* DMA Interrupt Enable */ +#define DMACMD_MDL 0x00000010 /* Map to Memory Description List */ +#define DMACMD_DIAG 0x00000004 /* Diagnostic */ +#define DMACMD_CMD 0x00000003 /* Command Code Bit */ +#define DMACMD_IDLE 0x00000000 /* Idle */ +#define DMACMD_BLAST 0x00000001 /* Blast */ +#define DMACMD_ABORT 0x00000002 /* Abort */ +#define DMACMD_START 0x00000003 /* Start */ + +#define DMA_STC 0x44 /* Start Transfer Count */ +#define DMA_SPA 0x48 /* Start Physical Address */ +#define DMA_WBC 0x4C /* Working Byte Counter */ +#define DMA_WAC 0x50 /* Working Address Counter */ + +#define DMA_STAT 0x54 /* Status Register */ +#define DMASTAT_RSVD 0xFFFFFF80 /* reserved */ +#define DMASTAT_PABT 0x00000040 /* PCI master/target Abort */ +#define DMASTAT_BCMP 0x00000020 /* BLAST Complete */ +#define DMASTAT_SINT 0x00000010 /* SCSI Interrupt */ +#define DMASTAT_DONE 0x00000008 /* DMA Transfer Terminated */ +#define DMASTAT_ABT 0x00000004 /* DMA Transfer Aborted */ +#define DMASTAT_ERR 0x00000002 /* DMA Transfer Error */ +#define DMASTAT_PWDN 0x00000001 /* Power Down Indicator */ + +#define DMA_SMDLA 0x58 /* Starting Memory Descpritor List Address */ +#define DMA_WMAC 0x5C /* Working MDL Counter */ +#define DMA_SBAC 0x70 /* SCSI Bus and Control */ --- sys/arch/i386/conf/GENERIC.orig Thu Dec 31 18:05:42 1998 +++ sys/arch/i386/conf/GENERIC Mon Feb 15 19:06:34 1999 @@ -229,6 +229,7 @@ bha* at pci? dev ? function ? # BusLogic 9xx SCSI isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel ncr* at pci? dev ? function ? # NCR 53c8xx SCSI +pcscp* at pci? dev ? function ? # AMD Am53c974 PCscsi-PCI SCSI # EISA SCSI controllers ahb* at eisa? slot ? # Adaptec 174[02] SCSI @@ -260,6 +261,7 @@ scsibus* at bha? scsibus* at isp? scsibus* at ncr? +scsibus* at pcscp? scsibus* at sea? scsibus* at uha? scsibus* at wds?