void pcscp_dma_go(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, 0, dmap->dm_mapsize, datain ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); /* sync MDL */ bus_dmamap_sync(esc->sc_dmat, mdldmap, 0, sizeof(u_int32_t) * dmap->dm_nsegs, 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; }
static void esp_pci_dma_go(struct ncr53c9x_softc *sc) { struct esp_pci_softc *esc = (struct esp_pci_softc *)sc; int datain; datain = esc->sc_datain; /* No DMA transfer for a "Transfer Pad" operation */ if (esc->sc_dmasize == 0) return; /* Sync the transfer buffer. */ bus_dmamap_sync(esc->sc_xferdmat, esc->sc_xferdmam, datain != 0 ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); /* Set the DMA engine to the IDLE state. */ /* XXX DMA Transfer Interrupt Enable bit is broken? */ WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | /* DMACMD_INTE | */ (datain != 0 ? DMACMD_DIR : 0)); /* Issue a DMA start command. */ WRITE_DMAREG(esc, DMA_CMD, DMACMD_START | /* DMACMD_INTE | */ (datain != 0 ? DMACMD_DIR : 0)); esc->sc_active = 1; }
static int esp_pci_dma_setup(struct ncr53c9x_softc *sc, void **addr, size_t *len, int datain, size_t *dmasize) { struct esp_pci_softc *esc = (struct esp_pci_softc *)sc; int error; WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain != 0 ? DMACMD_DIR : 0)); *dmasize = esc->sc_dmasize = ulmin(*dmasize, MDL_SEG_SIZE); esc->sc_dmaaddr = addr; esc->sc_dmalen = len; esc->sc_datain = datain; /* * There's no need to set up DMA for a "Transfer Pad" operation. */ if (*dmasize == 0) return (0); /* Set the transfer length. */ WRITE_DMAREG(esc, DMA_STC, *dmasize); /* * Load the transfer buffer and program the DMA address. * Note that the NCR53C9x core can't handle EINPROGRESS so we set * BUS_DMA_NOWAIT. */ error = bus_dmamap_load(esc->sc_xferdmat, esc->sc_xferdmam, *esc->sc_dmaaddr, *dmasize, esp_pci_xfermap, sc, BUS_DMA_NOWAIT); return (error); }
void pcscp_dma_reset(struct ncr53c9x_softc *sc) { struct pcscp_softc *esc = (struct pcscp_softc *)sc; WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE); esc->sc_active = 0; }
static void esp_pci_dma_reset(struct ncr53c9x_softc *sc) { struct esp_pci_softc *esc = (struct esp_pci_softc *)sc; WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE); esc->sc_active = 0; }
void pcscp_dma_stop(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)); bus_dmamap_unload(esc->sc_dmat, esc->sc_xfermap); esc->sc_active = 0; }
static void esp_pci_dma_stop(struct ncr53c9x_softc *sc) { struct esp_pci_softc *esc = (struct esp_pci_softc *)sc; /* DMA stop */ /* XXX what should we do here ? */ WRITE_DMAREG(esc, DMA_CMD, DMACMD_ABORT | (esc->sc_datain != 0 ? DMACMD_DIR : 0)); bus_dmamap_unload(esc->sc_xferdmat, esc->sc_xferdmam); esc->sc_active = 0; }
static void esp_pci_xfermap(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct esp_pci_softc *esc = (struct esp_pci_softc *)arg; if (error != 0) return; KASSERT(nsegs == 1, ("%s: bad transfer segment count %d", __func__, nsegs)); KASSERT(segs[0].ds_len <= MDL_SEG_SIZE, ("%s: bad transfer segment length %ld", __func__, (long)segs[0].ds_len)); /* Program the DMA Starting Physical Address. */ WRITE_DMAREG(esc, DMA_SPA, segs[0].ds_addr); }
int pcscp_dma_setup(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; 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) | BUS_DMA_STREAMING | ((sc->sc_nexus->xs->flags & SCSI_DATA_IN) ? BUS_DMA_READ : BUS_DMA_WRITE)); 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; /* the first segment is possibly not aligned with 4k MDL boundary */ s_addr = dmap->dm_segs[0].ds_addr; s_offset = s_addr & MDL_SEG_OFFSET; s_addr -= s_offset; /* set the first MDL and offset */ WRITE_DMAREG(esc, DMA_SPA, s_offset); *mdl++ = htole32(s_addr); /* the rest dmamap segments are aligned with 4k boundary */ for (seg = 1; seg < nseg; seg++) *mdl++ = htole32(dmap->dm_segs[seg].ds_addr); return 0; }
int pcscp_dma_intr(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; } #ifdef DIAGNOSTIC /* This is an "assertion" :) */ if (esc->sc_active == 0) panic("pcscp dmaintr: DMA wasn't active"); #endif /* 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", PCSCP_READ_REG(esc, NCR_TCL) | (PCSCP_READ_REG(esc, NCR_TCM) << 8), PCSCP_READ_REG(esc, NCR_TCL), PCSCP_READ_REG(esc, 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 = (PCSCP_READ_REG(esc, 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 = PCSCP_READ_REG(esc, NCR_FFLAG) & NCRFIFO_FF; while (resid > 1) resid = PCSCP_READ_REG(esc, 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 += PCSCP_READ_REG(esc, NCR_TCL) | (PCSCP_READ_REG(esc, NCR_TCM) << 8) | (PCSCP_READ_REG(esc, NCR_TCH) << 16); } else { while ((dmastat & DMASTAT_DONE) == 0) dmastat = READ_DMAREG(esc, DMA_STAT); } WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain ? DMACMD_DIR : 0)); /* sync MDL */ bus_dmamap_sync(esc->sc_dmat, esc->sc_mdldmap, 0, sizeof(u_int32_t) * dmap->dm_nsegs, BUS_DMASYNC_POSTWRITE); /* sync transfer buffer */ bus_dmamap_sync(esc->sc_dmat, dmap, 0, dmap->dm_mapsize, 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 = PCSCP_READ_REG(esc, 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", PCSCP_READ_REG(esc, NCR_TCL), PCSCP_READ_REG(esc, NCR_TCM), PCSCP_READ_REG(esc, NCR_TCH), trans, resid)); *esc->sc_dmalen -= trans; *esc->sc_dmaaddr += trans; return 0; }
static int esp_pci_dma_intr(struct ncr53c9x_softc *sc) { struct esp_pci_softc *esc = (struct esp_pci_softc *)sc; bus_dma_tag_t xferdmat; bus_dmamap_t xferdmam; size_t dmasize; int datain, i, resid, trans; uint32_t dmastat; char *p = NULL; xferdmat = esc->sc_xferdmat; xferdmam = esc->sc_xferdmam; datain = esc->sc_datain; dmastat = READ_DMAREG(esc, DMA_STAT); if ((dmastat & DMASTAT_ERR) != 0) { /* XXX not tested... */ WRITE_DMAREG(esc, DMA_CMD, DMACMD_ABORT | (datain != 0 ? DMACMD_DIR : 0)); device_printf(esc->sc_dev, "DMA error detected; Aborting.\n"); bus_dmamap_sync(xferdmat, xferdmam, datain != 0 ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(xferdmat, xferdmam); return (-1); } if ((dmastat & DMASTAT_ABT) != 0) { /* XXX what should be done? */ device_printf(esc->sc_dev, "DMA aborted.\n"); WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain != 0 ? DMACMD_DIR : 0)); esc->sc_active = 0; return (0); } KASSERT(esc->sc_active != 0, ("%s: DMA wasn't active", __func__)); /* DMA has stopped. */ esc->sc_active = 0; dmasize = esc->sc_dmasize; if (dmasize == 0) { /* A "Transfer Pad" operation completed. */ NCR_DMA(("%s: discarded %d bytes (tcl=%d, tcm=%d)\n", __func__, READ_ESPREG(esc, NCR_TCL) | (READ_ESPREG(esc, NCR_TCM) << 8), READ_ESPREG(esc, NCR_TCL), READ_ESPREG(esc, 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 == 0 && (resid = (READ_ESPREG(esc, NCR_FFLAG) & NCRFIFO_FF)) != 0) NCR_DMA(("%s: empty esp FIFO of %d ", __func__, resid)); if ((sc->sc_espstat & NCRSTAT_TC) == 0) { /* * "Terminal count" is off, so read the residue * out of the ESP counter registers. */ if (datain != 0) { resid = READ_ESPREG(esc, NCR_FFLAG) & NCRFIFO_FF; while (resid > 1) resid = READ_ESPREG(esc, NCR_FFLAG) & NCRFIFO_FF; WRITE_DMAREG(esc, DMA_CMD, DMACMD_BLAST | DMACMD_DIR); for (i = 0; i < 0x8000; i++) /* XXX 0x8000 ? */ if ((READ_DMAREG(esc, DMA_STAT) & DMASTAT_BCMP) != 0) break; /* See the below comments... */ if (resid != 0) p = *esc->sc_dmaaddr; } resid += READ_ESPREG(esc, NCR_TCL) | (READ_ESPREG(esc, NCR_TCM) << 8) | (READ_ESPREG(esc, NCR_TCH) << 16); } else while ((dmastat & DMASTAT_DONE) == 0) dmastat = READ_DMAREG(esc, DMA_STAT); WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain != 0 ? DMACMD_DIR : 0)); /* Sync the transfer buffer. */ bus_dmamap_sync(xferdmat, xferdmam, datain != 0 ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(xferdmat, xferdmam); trans = 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 != NULL) { p += trans; *p = READ_ESPREG(esc, 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. */ device_printf(dev, "xfer (%d) > req (%d)\n", trans, dmasize); #endif trans = dmasize; } NCR_DMA(("%s: tcl=%d, tcm=%d, tch=%d; trans=%d, resid=%d\n", __func__, READ_ESPREG(esc, NCR_TCL), READ_ESPREG(esc, NCR_TCM), READ_ESPREG(esc, NCR_TCH), trans, resid)); *esc->sc_dmalen -= trans; *esc->sc_dmaaddr = (char *)*esc->sc_dmaaddr + trans; return (0); }