From 0a855a73716d4fe9756848fbe8c78eb51571bb38 Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Wed, 23 Oct 2013 10:07:15 +0000 Subject: Checkpoint of work-in-progress DMA support for oaic(4). Not working yet, probably due to incorrect programming of the system board ``Diagnostic Control Register'', for which I do not have documentation yet; commited so as not to lose this work, since the machine I was testing on has apparently commited suicide and will no longer POST. --- sys/arch/aviion/aviion/av400_machdep.c | 6 +- sys/arch/aviion/aviion/av530_machdep.c | 4 +- sys/arch/aviion/dev/dma.c | 217 +++++++++++++++++++++++++++++++++ sys/arch/aviion/dev/dmareg.h | 40 ++++++ sys/arch/aviion/dev/dmavar.h | 23 ++++ sys/arch/aviion/dev/oaic_syscon.c | 89 +++++++++++++- sys/arch/aviion/include/board.h | 5 +- sys/dev/ic/aic6250.c | 43 ++++++- sys/dev/ic/aic6250var.h | 6 +- 9 files changed, 424 insertions(+), 9 deletions(-) create mode 100644 sys/arch/aviion/dev/dma.c create mode 100644 sys/arch/aviion/dev/dmareg.h create mode 100644 sys/arch/aviion/dev/dmavar.h (limited to 'sys') diff --git a/sys/arch/aviion/aviion/av400_machdep.c b/sys/arch/aviion/aviion/av400_machdep.c index ad06858f2d5..65c495d170b 100644 --- a/sys/arch/aviion/aviion/av400_machdep.c +++ b/sys/arch/aviion/aviion/av400_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: av400_machdep.c,v 1.25 2013/10/10 21:24:58 miod Exp $ */ +/* $OpenBSD: av400_machdep.c,v 1.26 2013/10/23 10:07:14 miod Exp $ */ /* * Copyright (c) 2006, 2007, Miodrag Vallat. * @@ -522,7 +522,7 @@ av400_intsrc(int i) AV400_IRQ_ECI, 0, AV400_IRQ_SCI, - 0, + AV400_IRQ_DTC, AV400_IRQ_VME1, AV400_IRQ_VME2, AV400_IRQ_VME3, @@ -558,7 +558,7 @@ static const u_int av400_obio_vec[32] = { INTSRC_VME(3), /* VME3 */ 0, /* DWP */ INTSRC_VME(4), /* VME4 */ - 0, /* DTC */ + INTSRC_DMA, /* DTC */ INTSRC_VME(5), /* VME5 */ INTSRC_ETHERNET1, /* ECI */ INTSRC_DUART2, /* DI2 */ diff --git a/sys/arch/aviion/aviion/av530_machdep.c b/sys/arch/aviion/aviion/av530_machdep.c index c0603e9b740..503d4ab9ecf 100644 --- a/sys/arch/aviion/aviion/av530_machdep.c +++ b/sys/arch/aviion/aviion/av530_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: av530_machdep.c,v 1.11 2013/10/10 21:24:58 miod Exp $ */ +/* $OpenBSD: av530_machdep.c,v 1.12 2013/10/23 10:07:14 miod Exp $ */ /* * Copyright (c) 2006, 2007, 2010 Miodrag Vallat. * @@ -428,6 +428,7 @@ av530_intsrc(int i) 0, 0, 0, + 0, AV530_IRQ_VME1, AV530_IRQ_VME2, AV530_IRQ_VME3, @@ -461,6 +462,7 @@ av530_exintsrc(int i) 0, 0, 0, + 0, 0 }; diff --git a/sys/arch/aviion/dev/dma.c b/sys/arch/aviion/dev/dma.c new file mode 100644 index 00000000000..eedb46875f1 --- /dev/null +++ b/sys/arch/aviion/dev/dma.c @@ -0,0 +1,217 @@ +/* $OpenBSD: dma.c,v 1.1 2013/10/23 10:07:14 miod Exp $ */ + +/* + * Copyright (c) 2013 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +/* + * DMA request information structure. + */ +struct dmareq { + TAILQ_ENTRY(dmareq) chain; + + vaddr_t mem; + size_t len; + uint dir; + + size_t lastcnt; + + void *cbarg; + void (*cbstart)(void *); + void (*cbdone)(void *); +}; + +struct dma_softc { + struct device sc_dev; + + struct intrhand sc_ih; + + TAILQ_HEAD(, dmareq) sc_req; +}; + +int dma_match(struct device *, void *, void *); +void dma_attach(struct device *, struct device *, void *); + +const struct cfattach dma_ca = { + sizeof(struct dma_softc), dma_match, dma_attach +}; + +struct cfdriver dma_cd = { + NULL, "dma", DV_DULL +}; + +int dma_intr(void *); +void dma_start(struct dma_softc *); + +int +dma_match(struct device *parent, void *match, void *aux) +{ + struct confargs *ca = aux; + + switch (cpuid) { +#ifdef AV400 + case AVIION_300_310: + case AVIION_400_4000: + case AVIION_410_4100: + case AVIION_300C_310C: + case AVIION_300CD_310CD: + case AVIION_300D_310D: + case AVIION_4300_25: + case AVIION_4300_20: + case AVIION_4300_16: + if (ca->ca_paddr == DMA_MAP) + return 1; + /* FALLTHROUGH */ +#endif + default: + return 0; + } +} + +void +dma_attach(struct device *parent, struct device *self, void *aux) +{ + struct dma_softc *sc = (struct dma_softc *)self; + + printf("\n"); + + *(volatile uint32_t *)DMA_COUNT = 0; /* clear possibly pending int */ + + TAILQ_INIT(&sc->sc_req); + + sc->sc_ih.ih_fn = dma_intr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_flags = 0; + sc->sc_ih.ih_ipl = IPL_BIO; + sysconintr_establish(INTSRC_DMA, &sc->sc_ih, self->dv_xname); +} + +int +dma_intr(void *v) +{ + struct dma_softc *sc = v; + struct dmareq *req; + size_t tc; + + req = TAILQ_FIRST(&sc->sc_req); + if (req == NULL) { + printf("%s: interrupt but no active request?\n", __func__); + *(volatile uint32_t *)DMA_STOP = DMASTOP_INHIBIT; + return -1; + } + + tc = *(volatile uint32_t *)DMA_COUNT; + if (tc != 0) + printf("%s: unexpected intr, TC = %x\n", __func__, tc); + req->len -= req->lastcnt; + if (req->len == 0) { + /* this transfer is over */ + TAILQ_REMOVE(&sc->sc_req, req, chain); + + if (req->cbdone != NULL) + (*req->cbdone)(req->cbarg); + + free(req, M_DEVBUF); + } else { + req->mem += req->lastcnt; + } + dma_start(sc); + + return 1; +} + +int +dma_req(void *vaddr, size_t sz, int dir, void (*cbstart)(void *), + void (*cbdone)(void *), void *cbarg) +{ + struct dma_softc *sc; + struct dmareq *req; + int run; + int s; + + if (dma_cd.cd_devs == NULL || + (sc = (struct dma_softc *)dma_cd.cd_devs[0]) == NULL) + return ENXIO; + req = malloc(sizeof *req, M_DEVBUF, M_NOWAIT | M_CANFAIL); + if (req == NULL) + return ENOMEM; + + req->mem = (vaddr_t)vaddr; + req->len = sz; + req->dir = dir; + req->cbstart = cbstart; + req->cbdone = cbdone; + req->cbarg = cbarg; + req->lastcnt = 0; + + s = splbio(); + run = TAILQ_EMPTY(&sc->sc_req); + TAILQ_INSERT_TAIL(&sc->sc_req, req, chain); + if (run) + dma_start(sc); + splx(s); + + return 0; +} + +void +dma_start(struct dma_softc *sc) +{ + struct dmareq *req; + paddr_t pa, paoff; + + req = TAILQ_FIRST(&sc->sc_req); + if (req == NULL) { + *(volatile uint32_t *)DMA_STOP = DMASTOP_INHIBIT; + return; + } + + if (pmap_extract(pmap_kernel(), req->mem, &pa) == FALSE) + panic("%s: pmap_extract(%p) failed", + __func__, (void *)req->mem); + + paoff = pa & ~DMAMAP_MASK; + + req->lastcnt = DMA_BOUNDARY - paoff; + if (req->lastcnt > req->len) + req->lastcnt = req->len; + + *(volatile uint32_t *)DMA_DIR = req->dir; + *(volatile uint32_t *)DMA_COUNT = req->lastcnt; + *(volatile uint32_t *)DMA_OFF = paoff; + *(volatile uint32_t *)DMA_MAP = (pa & DMAMAP_MASK) | + DMAMAP_G | DMAMAP_V; + *(volatile uint32_t *)DMA_CLR_INVALID = DMACLR; + *(volatile uint32_t *)DMA_STOP = DMASTOP_RESTART; + + if (req->cbstart != NULL) + (*req->cbstart)(req->cbarg); +} diff --git a/sys/arch/aviion/dev/dmareg.h b/sys/arch/aviion/dev/dmareg.h new file mode 100644 index 00000000000..d60301c15c3 --- /dev/null +++ b/sys/arch/aviion/dev/dmareg.h @@ -0,0 +1,40 @@ +/* $OpenBSD: dmareg.h,v 1.1 2013/10/23 10:07:14 miod Exp $ */ + +/* + * Copyright (c) 2013 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * AV400 family DMA controller definitions + */ + +#define DMA_BOUNDARY 0x1000 + +#define DMA_MAP 0xfff8b000 +#define DMAMAP_MASK 0xfffff000 +#define DMAMAP_G 0x00000080 +#define DMAMAP_RO 0x00000004 +#define DMAMAP_V 0x00000001 +#define DMA_OFF 0xfff8b004 +#define DMAOFF_MASK 0x00000ffe +#define DMA_COUNT 0xfff8b008 +#define DMACOUNTC_MASK 0x00001ffe +#define DMA_DIR 0xfff8b00c +#define DMA_CLR_INVALID 0xfff8b010 +#define DMA_CLR_WP 0xfff8b014 +#define DMACLR 0x00000000 +#define DMA_STOP 0xfff8b018 +#define DMASTOP_RESTART 0x00000000 /* restart DMA */ +#define DMASTOP_INHIBIT 0x00000001 /* do not restart DMA */ diff --git a/sys/arch/aviion/dev/dmavar.h b/sys/arch/aviion/dev/dmavar.h new file mode 100644 index 00000000000..23527e9821d --- /dev/null +++ b/sys/arch/aviion/dev/dmavar.h @@ -0,0 +1,23 @@ +/* $OpenBSD: dmavar.h,v 1.1 2013/10/23 10:07:14 miod Exp $ */ + +/* + * Copyright (c) 2013 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define DMADIR_TO_DEVICE 0x00000001 /* DMA from memory to device */ +#define DMADIR_FROM_DEVICE 0x00000000 /* DMA from device to memory */ + +int dma_req(void *, size_t, int, + void (*)(void *), void (*)(void *), void *); diff --git a/sys/arch/aviion/dev/oaic_syscon.c b/sys/arch/aviion/dev/oaic_syscon.c index 83294ae47ce..3d3402ec051 100644 --- a/sys/arch/aviion/dev/oaic_syscon.c +++ b/sys/arch/aviion/dev/oaic_syscon.c @@ -1,4 +1,4 @@ -/* $OpenBSD: oaic_syscon.c,v 1.1 2013/10/15 01:41:44 miod Exp $ */ +/* $OpenBSD: oaic_syscon.c,v 1.2 2013/10/23 10:07:14 miod Exp $ */ /* * Copyright (c) 2013 Miodrag Vallat. @@ -33,6 +33,9 @@ #include #include +#if 0 +#include +#endif int oaic_syscon_match(struct device *, void *, void *); void oaic_syscon_attach(struct device *, struct device *, void *); @@ -42,6 +45,9 @@ struct oaic_syscon_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; struct intrhand sc_ih; +#if 0 + uint8_t sc_dmac; +#endif }; const struct cfattach oaic_syscon_ca = { @@ -52,6 +58,13 @@ const struct cfattach oaic_syscon_ca = { uint8_t oaic_read(struct aic6250_softc *, uint); void oaic_write(struct aic6250_softc *, uint, uint8_t); +#if 0 +int oaic_dmastart(struct aic6250_softc *, void *, size_t, int); +int oaic_dmadone(struct aic6250_softc *); +void oaic_dmago(void *); +void oaic_dmastop(void *); +#endif + int oaic_syscon_match(struct device *parent, void *match, void *aux) { @@ -122,6 +135,11 @@ oaic_syscon_attach(struct device *parent, struct device *self, void *aux) sc->sc_read = oaic_read; sc->sc_write = oaic_write; +#if 0 + sc->sc_dma_start = oaic_dmastart; + sc->sc_dma_done = oaic_dmadone; +#endif + aic6250_attach(sc); ssc->sc_ih.ih_fn = (int(*)(void *))aic6250_intr; @@ -149,3 +167,72 @@ oaic_write(struct aic6250_softc *sc, uint reg, uint8_t val) bus_space_write_4(ssc->sc_iot, ssc->sc_ioh, reg << 2, val); } + +#if 0 +int +oaic_dmastart(struct aic6250_softc *sc, void *addr, size_t size, int datain) +{ + struct oaic_syscon_softc *osc = (struct oaic_syscon_softc *)sc; + char *vaddr = (char *)addr; + + /* + * The DMA engine can only operate on 16-bit words. + */ + if (size & 1) + return EINVAL; + + osc->sc_dmac = AIC_DC_DMA_XFER_EN | (datain ? 0 : AIC_DC_TRANSFER_DIR); + if ((vaddr_t)vaddr & 1) { + if (datain) { + /* + * The AIC_DC_ODD_XFER_START bit ought to have been + * set before changing phase to DATA IN. + */ + return EINVAL; + } + vaddr--; + osc->sc_dmac |= AIC_DC_ODD_XFER_START; + } + + return dma_req(vaddr, size, + datain ? DMADIR_FROM_DEVICE : DMADIR_TO_DEVICE, + oaic_dmago, oaic_dmastop, sc); +} + +void +oaic_dmago(void *v) +{ + struct aic6250_softc *sc = (struct aic6250_softc *)v; + struct oaic_syscon_softc *osc = (struct oaic_syscon_softc *)sc; + + sc->sc_flags |= AIC_DOINGDMA; + + oaic_write(sc, AIC_DMA_BYTE_COUNT_H, sc->sc_dleft >> 16); + oaic_write(sc, AIC_DMA_BYTE_COUNT_M, sc->sc_dleft >> 8); + oaic_write(sc, AIC_DMA_BYTE_COUNT_L, sc->sc_dleft); + oaic_write(sc, AIC_DMA_CNTRL, osc->sc_dmac); +} + +void +oaic_dmastop(void *v) +{ + struct aic6250_softc *sc = (struct aic6250_softc *)v; + + sc->sc_flags &= ~AIC_DOINGDMA; +} + +int +oaic_dmadone(struct aic6250_softc *sc) +{ + int resid; + + resid = oaic_read(sc, AIC_DMA_BYTE_COUNT_H) << 16 | + oaic_read(sc, AIC_DMA_BYTE_COUNT_M) << 8 | + oaic_read(sc, AIC_DMA_BYTE_COUNT_L); + + sc->sc_dp += sc->sc_dleft - resid; + sc->sc_dleft = resid; + + return 0; +} +#endif diff --git a/sys/arch/aviion/include/board.h b/sys/arch/aviion/include/board.h index 6946b1ad329..9102a931416 100644 --- a/sys/arch/aviion/include/board.h +++ b/sys/arch/aviion/include/board.h @@ -1,4 +1,4 @@ -/* $OpenBSD: board.h,v 1.13 2013/10/10 21:24:59 miod Exp $ */ +/* $OpenBSD: board.h,v 1.14 2013/10/23 10:07:14 miod Exp $ */ /* * Copyright (c) 2006, 2007, Miodrag Vallat * @@ -139,7 +139,8 @@ extern const struct board *platform;/* just to have people confuse both names */ #define INTSRC_ETHERNET2 8 /* second on-board Ethernet */ #define INTSRC_SCSI1 9 /* first on-board SCSI controller */ #define INTSRC_SCSI2 10 /* second on-board SCSI controller */ -#define NINTSRC_SYSCON 11 /* total number of non-VME sources */ +#define INTSRC_DMA 11 /* DMA completion interrupt */ +#define NINTSRC_SYSCON 12 /* total number of non-VME sources */ #define INTSRC_VME(lvl) (NINTSRC_SYSCON + (lvl) - 1) /* seven VME levels */ #define IS_VME_INTSRC(intsrc) ((intsrc) >= NINTSRC_SYSCON) diff --git a/sys/dev/ic/aic6250.c b/sys/dev/ic/aic6250.c index c0c4f5c88a2..67a2bd02e48 100644 --- a/sys/dev/ic/aic6250.c +++ b/sys/dev/ic/aic6250.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aic6250.c,v 1.1 2013/10/15 01:41:45 miod Exp $ */ +/* $OpenBSD: aic6250.c,v 1.2 2013/10/23 10:07:14 miod Exp $ */ /* * Copyright (c) 2010, 2013 Miodrag Vallat. @@ -100,6 +100,9 @@ #define AIC_ABORT_TIMEOUT 2000 /* time to wait for abort */ +/* threshold length for DMA transfer */ +#define AIC_MIN_DMA_LEN 32 + /* End of customizable parameters */ #include @@ -1376,11 +1379,20 @@ aic6250_intr(void *arg) loop: sr0 = (*sc->sc_read)(sc, AIC_STATUS_REG0); + /* * First check for abnormal conditions, such as reset. */ AIC_MISC(("sr0:0x%02x ", sr0)); + /* + * Check for the end of a DMA operation before doing anything else... + */ + if ((sc->sc_flags & AIC_DOINGDMA) != 0 && + (sr0 & AIC_SR1_CMD_DONE) != 0) { + (*sc->sc_dma_done)(sc); + } + if ((sr0 & AIC_SR0_SCSI_RST_OCCURED) != 0) { printf("%s: SCSI bus reset\n", sc->sc_dev.dv_xname); while (((*sc->sc_read)(sc, AIC_STATUS_REG1) & @@ -1618,6 +1630,13 @@ loop: } } + /* + * Do not change phase (yet) if we have a pending DMA operation. + */ + if ((sc->sc_flags & AIC_DOINGDMA) != 0) { + goto out; + } + dophase: if ((sr0 & AIC_SR0_SCSI_REQ_ON) == 0) { /* Wait for AIC_SR0_SCSI_REQ_ON. */ @@ -1665,6 +1684,17 @@ dophase: if (sc->sc_state != AIC_CONNECTED) break; AIC_MISC(("dataout dleft=%lu ", (u_long)sc->sc_dleft)); + if (sc->sc_dma_start != NULL && + sc->sc_dleft > AIC_MIN_DMA_LEN) { + AIC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + if ((acb->xs->flags & SCSI_POLL) == 0 && + (*sc->sc_dma_start) + (sc, sc->sc_dp, sc->sc_dleft, 0) == 0) { + sc->sc_prevphase = PH_DATAOUT; + goto out; + } + } n = aic6250_dataout_pio(sc, sc->sc_dp, sc->sc_dleft, PH_DATAOUT); sc->sc_dp += n; sc->sc_dleft -= n; @@ -1675,6 +1705,17 @@ dophase: if (sc->sc_state != AIC_CONNECTED) break; AIC_MISC(("datain %lu ", (u_long)sc->sc_dleft)); + if (sc->sc_dma_start != NULL && + sc->sc_dleft > AIC_MIN_DMA_LEN) { + AIC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + if ((acb->xs->flags & SCSI_POLL) == 0 && + (*sc->sc_dma_start) + (sc, sc->sc_dp, sc->sc_dleft, 1) == 0) { + sc->sc_prevphase = PH_DATAIN; + goto out; + } + } n = aic6250_datain_pio(sc, sc->sc_dp, sc->sc_dleft, PH_DATAIN); sc->sc_dp += n; sc->sc_dleft -= n; diff --git a/sys/dev/ic/aic6250var.h b/sys/dev/ic/aic6250var.h index 713e165ce0f..f5e49a24015 100644 --- a/sys/dev/ic/aic6250var.h +++ b/sys/dev/ic/aic6250var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aic6250var.h,v 1.1 2013/10/15 01:41:46 miod Exp $ */ +/* $OpenBSD: aic6250var.h,v 1.2 2013/10/23 10:07:14 miod Exp $ */ /* * Copyright (c) 2010, 2013 Miodrag Vallat. @@ -190,6 +190,10 @@ struct aic6250_softc { int sc_freq; /* Clock frequency in MHz */ int sc_minsync; /* Minimum sync period / 4 */ int sc_maxsync; /* Maximum sync period / 4 */ + + /* DMA function set from MD code */ + int (*sc_dma_start)(struct aic6250_softc *, void *, size_t, int); + int (*sc_dma_done)(struct aic6250_softc *); }; #if AIC_DEBUG -- cgit v1.2.3