Module Name: src
Committed By: bouyer
Date: Tue Apr 14 18:37:43 UTC 2015
Modified Files:
src/sys/arch/arm/omap: files.omap2 omap2_obio.c omap2_obiovar.h
omap2_reg.h
Added Files:
src/sys/arch/arm/omap: omap_edma.c omap_edma.c.orig omap_edma.h
omap_edma.h.orig
Log Message:
Add a driver for the Enhanced Direct Memory Access controller found
in the AM335x SoC. Written by Jared D. McNeill, with some final debug
by me.
Supports only DMA (not QDMA) yet, and there's no support for DMA event
matrix yet (this means that only primary DMA events can be used)
To generate a diff of this commit:
cvs rdiff -u -r1.29 -r1.30 src/sys/arch/arm/omap/files.omap2
cvs rdiff -u -r1.21 -r1.22 src/sys/arch/arm/omap/omap2_obio.c
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/omap/omap2_obiovar.h
cvs rdiff -u -r1.28 -r1.29 src/sys/arch/arm/omap/omap2_reg.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/omap/omap_edma.c \
src/sys/arch/arm/omap/omap_edma.c.orig src/sys/arch/arm/omap/omap_edma.h \
src/sys/arch/arm/omap/omap_edma.h.orig
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/arm/omap/files.omap2
diff -u src/sys/arch/arm/omap/files.omap2:1.29 src/sys/arch/arm/omap/files.omap2:1.30
--- src/sys/arch/arm/omap/files.omap2:1.29 Fri Aug 22 19:44:04 2014
+++ src/sys/arch/arm/omap/files.omap2 Tue Apr 14 18:37:43 2015
@@ -1,4 +1,4 @@
-# $NetBSD: files.omap2,v 1.29 2014/08/22 19:44:04 jakllsch Exp $
+# $NetBSD: files.omap2,v 1.30 2015/04/14 18:37:43 bouyer Exp $
#
# Configuration info for Texas Instruments OMAP2/OMAP3 CPU support
# Based on xscale/files.pxa2x0
@@ -32,7 +32,7 @@ defflag opt_omap.h TI_AM335X: OMAP3
defflag opt_omap.h TI_DM37XX: OMAP3
# OBIO just an attach point
-device obio { [addr=-1], [size=0], [intr=-1], [mult=1], [intrbase=-1], [nobyteacc=0]
+device obio { [addr=-1], [size=0], [intr=-1], [mult=1], [intrbase=-1], [nobyteacc=0], [edmabase=-1]
} : bus_space_generic
attach obio at mainbus
file arch/arm/omap/omap2_obio.c obio needs-count
@@ -170,6 +170,10 @@ device omapdma
attach omapdma at obio
file arch/arm/omap/omap3_sdma.c omapdma needs-flag
+device edma
+attach edma at obio
+file arch/arm/omap/omap_edma.c edma needs-flag
+
# these bus space methods are not bus-specific ...
#
file arch/arm/omap/omap_nobyteacc_space.c emifs | gpmc
Index: src/sys/arch/arm/omap/omap2_obio.c
diff -u src/sys/arch/arm/omap/omap2_obio.c:1.21 src/sys/arch/arm/omap/omap2_obio.c:1.22
--- src/sys/arch/arm/omap/omap2_obio.c:1.21 Sat Jun 15 21:58:20 2013
+++ src/sys/arch/arm/omap/omap2_obio.c Tue Apr 14 18:37:43 2015
@@ -1,7 +1,7 @@
-/* $Id: omap2_obio.c,v 1.21 2013/06/15 21:58:20 matt Exp $ */
+/* $Id: omap2_obio.c,v 1.22 2015/04/14 18:37:43 bouyer Exp $ */
/* adapted from: */
-/* $NetBSD: omap2_obio.c,v 1.21 2013/06/15 21:58:20 matt Exp $ */
+/* $NetBSD: omap2_obio.c,v 1.22 2015/04/14 18:37:43 bouyer Exp $ */
/*
@@ -103,7 +103,7 @@
#include "opt_omap.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: omap2_obio.c,v 1.21 2013/06/15 21:58:20 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: omap2_obio.c,v 1.22 2015/04/14 18:37:43 bouyer Exp $");
#include "locators.h"
#include "obio.h"
@@ -284,6 +284,7 @@ obio_search(device_t parent, cfdata_t cf
oa.obio_size = cf->cf_loc[OBIOCF_SIZE];
oa.obio_intr = cf->cf_loc[OBIOCF_INTR];
oa.obio_intrbase = cf->cf_loc[OBIOCF_INTRBASE];
+ oa.obio_edmabase = cf->cf_loc[OBIOCF_EDMABASE];
#if defined(OMAP2)
if ((oa.obio_addr >= sc->sc_base)
@@ -351,6 +352,7 @@ obio_find(device_t parent, cfdata_t cf,
oa->obio_size = cf->cf_loc[OBIOCF_SIZE];
oa->obio_intr = cf->cf_loc[OBIOCF_INTR];
oa->obio_intrbase = cf->cf_loc[OBIOCF_INTRBASE];
+ oa->obio_edmabase = cf->cf_loc[OBIOCF_EDMABASE];
return config_match(parent, cf, oa);
}
@@ -398,6 +400,7 @@ static const struct {
{ .name = "omapicu", .addr = 0x48200000, .required = true },
{ .name = "prcm", .addr = 0x44e00000, .required = true },
{ .name = "sitaracm", .addr = 0x44e10000, .required = true },
+ { .name = "edma", .addr = 0x49000000, .required = false },
#endif
#if defined(OMAP_3530)
{ .name = "omapdma", .addr = OMAP3530_SDMA_BASE, .required = true },
Index: src/sys/arch/arm/omap/omap2_obiovar.h
diff -u src/sys/arch/arm/omap/omap2_obiovar.h:1.2 src/sys/arch/arm/omap/omap2_obiovar.h:1.3
--- src/sys/arch/arm/omap/omap2_obiovar.h:1.2 Wed Sep 5 00:19:59 2012
+++ src/sys/arch/arm/omap/omap2_obiovar.h Tue Apr 14 18:37:43 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: omap2_obiovar.h,v 1.2 2012/09/05 00:19:59 matt Exp $ */
+/* $NetBSD: omap2_obiovar.h,v 1.3 2015/04/14 18:37:43 bouyer Exp $ */
/*
* Copyright (c) 2007 Microsoft
@@ -40,6 +40,7 @@ struct obio_attach_args {
bus_dma_tag_t obio_dmat;
unsigned int obio_mult;
unsigned int obio_intrbase;
+ int obio_edmabase;
};
struct obio_softc {
Index: src/sys/arch/arm/omap/omap2_reg.h
diff -u src/sys/arch/arm/omap/omap2_reg.h:1.28 src/sys/arch/arm/omap/omap2_reg.h:1.29
--- src/sys/arch/arm/omap/omap2_reg.h:1.28 Sun Jul 20 23:08:43 2014
+++ src/sys/arch/arm/omap/omap2_reg.h Tue Apr 14 18:37:43 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: omap2_reg.h,v 1.28 2014/07/20 23:08:43 bouyer Exp $ */
+/* $NetBSD: omap2_reg.h,v 1.29 2015/04/14 18:37:43 bouyer Exp $ */
/*
* Copyright (c) 2007 Microsoft
@@ -896,5 +896,20 @@
#define SDRAM_CONFIG_EBANK __BIT(3)
#define SDRAM_CONFIG_PAGESIZE __BITS(2,0)
#endif
-
+
+/* EDMA3 */
+#define AM335X_TPCC_BASE 0x49000000
+#define AM335X_TPCC_SIZE 0x00100000
+#define AM335X_TPTC0_BASE 0x49800000
+#define AM335X_TPTC0_SIZE 0x00100000
+#define AM335X_TPTC1_BASE 0x49900000
+#define AM335X_TPTC1_SIZE 0x00100000
+#define AM335X_TPTC2_BASE 0x49a00000
+#define AM335X_TPTC2_SIZE 0x00100000
+#define AM335X_INT_EDMACOMPINT 12
+#define AM335X_INT_EDMAMPERR 13
+#define AM335X_INT_EDMAERRINT 14
+#define AM335X_INT_TCERRINT0 112
+#define AM335X_INT_TCERRINT1 113
+#define AM335X_INT_TCERRINT2 114
#endif /* _ARM_OMAP_OMAP2_REG_H_ */
Added files:
Index: src/sys/arch/arm/omap/omap_edma.c
diff -u /dev/null src/sys/arch/arm/omap/omap_edma.c:1.1
--- /dev/null Tue Apr 14 18:37:43 2015
+++ src/sys/arch/arm/omap/omap_edma.c Tue Apr 14 18:37:43 2015
@@ -0,0 +1,578 @@
+/* $NetBSD: omap_edma.c,v 1.1 2015/04/14 18:37:43 bouyer Exp $ */
+
+/*-
+ * Copyright (c) 2014 Jared D. McNeill <[email protected]>
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: omap_edma.c,v 1.1 2015/04/14 18:37:43 bouyer Exp $");
+
+#include "opt_omap.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/intr.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/bitops.h>
+
+#include <arm/omap/am335x_prcm.h>
+#include <arm/omap/omap2_prcm.h>
+#include <arm/omap/sitara_cm.h>
+#include <arm/omap/sitara_cmreg.h>
+
+#include <arm/omap/omap2_reg.h>
+#include <arm/omap/omap2_obiovar.h>
+#include <arm/omap/omap_edma.h>
+
+#ifdef TI_AM335X
+static const struct omap_module edma3cc_module =
+ { AM335X_PRCM_CM_PER, CM_PER_TPCC_CLKCTRL };
+static const struct omap_module edma3tc0_module =
+ { AM335X_PRCM_CM_PER, CM_PER_TPTC0_CLKCTRL };
+static const struct omap_module edma3tc1_module =
+ { AM335X_PRCM_CM_PER, CM_PER_TPTC1_CLKCTRL };
+static const struct omap_module edma3tc2_module =
+ { AM335X_PRCM_CM_PER, CM_PER_TPTC2_CLKCTRL };
+#endif
+
+#define NUM_DMA_CHANNELS 64
+#define NUM_PARAM_SETS 256
+#define MAX_PARAM_PER_CHANNEL 32
+
+#ifdef EDMA_DEBUG
+int edmadebug = 1;
+#define DPRINTF(n,s) do { if ((n) <= edmadebug) device_printf s; } while (0)
+#else
+#define DPRINTF(n,s) do {} while (0)
+#endif
+
+struct edma_softc;
+
+struct edma_channel {
+ struct edma_softc *ch_sc;
+ enum edma_type ch_type;
+ uint8_t ch_index;
+ void (*ch_callback)(void *);
+ void *ch_callbackarg;
+ unsigned int ch_nparams;
+};
+
+struct edma_softc {
+ device_t sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ kmutex_t sc_lock;
+ struct edma_channel sc_dma[NUM_DMA_CHANNELS];
+
+ void *sc_ih;
+ void *sc_mperr_ih;
+ void *sc_errint_ih;
+
+ uint32_t sc_dmamask[NUM_DMA_CHANNELS / 32];
+ uint32_t sc_parammask[NUM_PARAM_SETS / 32];
+};
+
+static int edma_match(device_t, cfdata_t, void *);
+static void edma_attach(device_t, device_t, void *);
+
+static void edma_init(struct edma_softc *);
+static int edma_intr(void *);
+static int edma_mperr_intr(void *);
+static int edma_errint_intr(void *);
+static void edma_write_param(struct edma_softc *,
+ unsigned int, const struct edma_param *);
+static bool edma_bit_isset(uint32_t *, unsigned int);
+static void edma_bit_set(uint32_t *, unsigned int);
+static void edma_bit_clr(uint32_t *, unsigned int);
+
+CFATTACH_DECL_NEW(edma, sizeof(struct edma_softc),
+ edma_match, edma_attach, NULL, NULL);
+
+#define EDMA_READ(sc, reg) \
+ bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
+#define EDMA_WRITE(sc, reg, val) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+
+static int
+edma_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct obio_attach_args *obio = aux;
+
+#ifdef TI_AM335X
+ if (obio->obio_addr == AM335X_TPCC_BASE &&
+ obio->obio_size == AM335X_TPCC_SIZE &&
+ obio->obio_intrbase == AM335X_INT_EDMACOMPINT)
+ return 1;
+#endif
+
+ return 0;
+}
+
+static void
+edma_attach(device_t parent, device_t self, void *aux)
+{
+ struct edma_softc *sc = device_private(self);
+ struct obio_attach_args *obio = aux;
+ int idx;
+
+ sc->sc_dev = self;
+ sc->sc_iot = obio->obio_iot;
+ mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SCHED);
+ if (bus_space_map(obio->obio_iot, obio->obio_addr, obio->obio_size,
+ 0, &sc->sc_ioh) != 0) {
+ aprint_error(": couldn't map address spcae\n");
+ return;
+ }
+
+ aprint_normal("\n");
+ aprint_naive("\n");
+
+ for (idx = 0; idx < NUM_DMA_CHANNELS; idx++) {
+ struct edma_channel *ch = &sc->sc_dma[idx];
+ ch->ch_sc = sc;
+ ch->ch_type = EDMA_TYPE_DMA;
+ ch->ch_index = idx;
+ ch->ch_callback = NULL;
+ ch->ch_callbackarg = NULL;
+ ch->ch_nparams = 0;
+ }
+
+ edma_init(sc);
+
+ sc->sc_ih = intr_establish(obio->obio_intrbase + 0,
+ IPL_SCHED, IST_LEVEL, edma_intr, sc);
+ KASSERT(sc->sc_ih != NULL);
+
+ sc->sc_mperr_ih = intr_establish(obio->obio_intrbase + 1,
+ IPL_SCHED, IST_LEVEL, edma_mperr_intr, sc);
+ sc->sc_errint_ih = intr_establish(obio->obio_intrbase + 2,
+ IPL_SCHED, IST_LEVEL, edma_errint_intr, sc);
+}
+
+/*
+ * Hardware initialization
+ */
+static void
+edma_init(struct edma_softc *sc)
+{
+ struct edma_param param;
+ uint32_t val;
+ int idx;
+
+#ifdef TI_AM335X
+ prcm_module_enable(&edma3cc_module);
+ prcm_module_enable(&edma3tc0_module);
+ prcm_module_enable(&edma3tc1_module);
+ prcm_module_enable(&edma3tc2_module);
+#endif
+
+ val = EDMA_READ(sc, EDMA_CCCFG_REG);
+ if (val & EDMA_CCCFG_CHMAP_EXIST) {
+ for (idx = 0; idx < NUM_DMA_CHANNELS; idx++) {
+ EDMA_WRITE(sc, EDMA_DCHMAP_REG(idx),
+ __SHIFTIN(0, EDMA_DCHMAP_PAENTRY));
+ }
+ }
+
+ memset(¶m, 0, sizeof(param));
+ param.ep_bcnt = 1;
+ for (idx = 0; idx < NUM_PARAM_SETS; idx++) {
+ edma_write_param(sc, idx, ¶m);
+ }
+
+ /* reserve PaRAM entry 0 for dummy slot */
+ edma_bit_set(sc->sc_parammask, 0);
+ for (idx = 1; idx <= 32; idx++) {
+ edma_bit_set(sc->sc_parammask, idx);
+ }
+}
+
+/*
+ * Write a PaRAM entry
+ */
+static void
+edma_write_param(struct edma_softc *sc,
+ unsigned int idx, const struct edma_param *ep)
+{
+ EDMA_WRITE(sc, EDMA_PARAM_OPT_REG(idx), ep->ep_opt);
+ EDMA_WRITE(sc, EDMA_PARAM_SRC_REG(idx), ep->ep_src);
+ EDMA_WRITE(sc, EDMA_PARAM_CNT_REG(idx),
+ __SHIFTIN(ep->ep_bcnt, EDMA_PARAM_CNT_BCNT) |
+ __SHIFTIN(ep->ep_acnt, EDMA_PARAM_CNT_ACNT));
+ EDMA_WRITE(sc, EDMA_PARAM_DST_REG(idx), ep->ep_dst);
+ EDMA_WRITE(sc, EDMA_PARAM_BIDX_REG(idx),
+ __SHIFTIN(ep->ep_dstbidx, EDMA_PARAM_BIDX_DSTBIDX) |
+ __SHIFTIN(ep->ep_srcbidx, EDMA_PARAM_BIDX_SRCBIDX));
+ EDMA_WRITE(sc, EDMA_PARAM_LNK_REG(idx),
+ __SHIFTIN(ep->ep_bcntrld, EDMA_PARAM_LNK_BCNTRLD) |
+ __SHIFTIN(ep->ep_link, EDMA_PARAM_LNK_LINK));
+ EDMA_WRITE(sc, EDMA_PARAM_CIDX_REG(idx),
+ __SHIFTIN(ep->ep_dstcidx, EDMA_PARAM_CIDX_DSTCIDX) |
+ __SHIFTIN(ep->ep_srccidx, EDMA_PARAM_CIDX_SRCCIDX));
+ EDMA_WRITE(sc, EDMA_PARAM_CCNT_REG(idx),
+ __SHIFTIN(ep->ep_ccnt, EDMA_PARAM_CCNT_CCNT));
+}
+
+static bool
+edma_bit_isset(uint32_t *bits, unsigned int bit)
+{
+ return !!(bits[bit >> 5] & (1 << (bit & 0x1f)));
+}
+
+static void
+edma_bit_set(uint32_t *bits, unsigned int bit)
+{
+ bits[bit >> 5] |= (1 << (bit & 0x1f));
+}
+
+static void
+edma_bit_clr(uint32_t *bits, unsigned int bit)
+{
+ bits[bit >> 5] &= ~(1 << (bit & 0x1f));
+}
+
+static int
+edma_intr(void *priv)
+{
+ struct edma_softc *sc = priv;
+ uint64_t ipr, ier;
+ int bit, idx;
+
+ ipr = EDMA_READ(sc, EDMA_IPR_REG);
+ ipr |= (uint64_t)EDMA_READ(sc, EDMA_IPRH_REG) << 32;
+ if (ipr == 0)
+ return 0;
+
+ ier = EDMA_READ(sc, EDMA_IER_REG);
+ ier |= (uint64_t)EDMA_READ(sc, EDMA_IERH_REG) << 32;
+
+ DPRINTF(2, (sc->sc_dev, "ipr = 0x%016llx ier 0x%016llx\n", ipr, ier));
+
+ EDMA_WRITE(sc, EDMA_ICR_REG, ipr & 0xffffffff);
+ EDMA_WRITE(sc, EDMA_ICRH_REG, ipr >> 32);
+
+ while ((bit = ffs64(ipr)) != 0) {
+ idx = bit - 1;
+ ipr &= ~__BIT(idx);
+ if (!(ier & __BIT(idx)))
+ continue;
+ if (!edma_bit_isset(sc->sc_dmamask, idx))
+ continue;
+
+ sc->sc_dma[idx].ch_callback(sc->sc_dma[idx].ch_callbackarg);
+ }
+
+ EDMA_WRITE(sc, EDMA_IEVAL_REG, EDMA_IEVAL_EVAL);
+
+ return 1;
+}
+
+static int
+edma_mperr_intr(void *priv)
+{
+ printf(" ===== edma mperr!\n");
+ return 0;
+}
+
+static int
+edma_errint_intr(void *priv)
+{
+ printf(" ===== edma errint!\n");
+ return 0;
+}
+
+
+/*
+ * Allocate a DMA channel. Currently only DMA types are supported, not QDMA.
+ * Returns NULL on failure.
+ */
+struct edma_channel *
+edma_channel_alloc(enum edma_type type, unsigned int drq,
+ void (*cb)(void *), void *cbarg)
+{
+ struct edma_softc *sc;
+ device_t dev;
+ struct edma_channel *ch = NULL;
+
+ KASSERT(drq < __arraycount(sc->sc_dma));
+ KASSERT(type == EDMA_TYPE_DMA); /* QDMA not implemented */
+ KASSERT(cb != NULL);
+ KASSERT(cbarg != NULL);
+
+ dev = device_find_by_driver_unit("edma", 0);
+ if (dev == NULL)
+ return NULL;
+ sc = device_private(dev);
+
+ mutex_enter(&sc->sc_lock);
+ if (!edma_bit_isset(sc->sc_dmamask, drq)) {
+ ch = &sc->sc_dma[drq];
+ KASSERT(ch->ch_callback == NULL);
+ KASSERT(ch->ch_index == drq);
+ ch->ch_callback = cb;
+ ch->ch_callbackarg = cbarg;
+ edma_bit_set(sc->sc_dmamask, drq);
+ }
+
+ if (ch == NULL)
+ goto done;
+
+ EDMA_WRITE(sc, EDMA_DRAE_REG(0), sc->sc_dmamask[0]);
+ EDMA_WRITE(sc, EDMA_DRAEH_REG(0), sc->sc_dmamask[1]);
+
+ if (ch->ch_index < 32) {
+ EDMA_WRITE(sc, EDMA_ICR_REG, __BIT(ch->ch_index));
+ EDMA_WRITE(sc, EDMA_IESR_REG, __BIT(ch->ch_index));
+ } else {
+ EDMA_WRITE(sc, EDMA_ICRH_REG, __BIT(ch->ch_index - 32));
+ EDMA_WRITE(sc, EDMA_IESRH_REG, __BIT(ch->ch_index - 32));
+ }
+
+done:
+ mutex_exit(&sc->sc_lock);
+
+ return ch;
+}
+
+/*
+ * Free a DMA channel allocated with edma_channel_alloc
+ */
+void
+edma_channel_free(struct edma_channel *ch)
+{
+ struct edma_softc *sc = ch->ch_sc;
+
+ KASSERT(ch->ch_nparams == 0);
+
+ mutex_enter(&sc->sc_lock);
+ if (ch->ch_index < 32) {
+ EDMA_WRITE(sc, EDMA_IECR_REG, __BIT(ch->ch_index));
+ } else {
+ EDMA_WRITE(sc, EDMA_IECRH_REG, __BIT(ch->ch_index - 32));
+ }
+ ch->ch_callback = NULL;
+ ch->ch_callbackarg = NULL;
+ edma_bit_clr(sc->sc_dmamask, ch->ch_index);
+ mutex_exit(&sc->sc_lock);
+}
+
+/*
+ * Allocate a PaRAM entry. The driver artifically restricts the number
+ * of PaRAM entries available for each channel to MAX_PARAM_PER_CHANNEL.
+ * If the number of entries for the channel has been exceeded, or there
+ * are no entries available, 0xffff is returned.
+ */
+uint16_t
+edma_param_alloc(struct edma_channel *ch)
+{
+ struct edma_softc *sc = ch->ch_sc;
+ uint16_t param_entry = 0xffff;
+ int idx;
+
+ if (ch->ch_nparams == MAX_PARAM_PER_CHANNEL)
+ return param_entry;
+
+ mutex_enter(&sc->sc_lock);
+ for (idx = 0; idx < NUM_PARAM_SETS; idx++) {
+ if (!edma_bit_isset(sc->sc_parammask, idx)) {
+ param_entry = idx;
+ edma_bit_set(sc->sc_parammask, idx);
+ ch->ch_nparams++;
+ break;
+ }
+ }
+ mutex_exit(&sc->sc_lock);
+
+ return param_entry;
+}
+
+/*
+ * Free a PaRAM entry allocated with edma_param_alloc
+ */
+void
+edma_param_free(struct edma_channel *ch, uint16_t param_entry)
+{
+ struct edma_softc *sc = ch->ch_sc;
+
+ KASSERT(param_entry < NUM_PARAM_SETS);
+ KASSERT(ch->ch_nparams > 0);
+ KASSERT(edma_bit_isset(sc->sc_parammask, param_entry));
+
+ mutex_enter(&sc->sc_lock);
+ edma_bit_clr(sc->sc_parammask, param_entry);
+ ch->ch_nparams--;
+ mutex_exit(&sc->sc_lock);
+}
+
+/*
+ * Update a PaRAM entry register set with caller-provided values
+ */
+void
+edma_set_param(struct edma_channel *ch, uint16_t param_entry,
+ struct edma_param *ep)
+{
+ struct edma_softc *sc = ch->ch_sc;
+
+ KASSERT(param_entry < NUM_PARAM_SETS);
+ KASSERT(ch->ch_nparams > 0);
+ KASSERT(edma_bit_isset(sc->sc_parammask, param_entry));
+
+ DPRINTF(1, (sc->sc_dev, "write param entry ch# %d pe %d: 0x%08x -> 0x%08x (%u, %u, %u)\n", ch->ch_index, param_entry, ep->ep_src, ep->ep_dst, ep->ep_acnt, ep->ep_bcnt, ep->ep_ccnt));
+ edma_write_param(sc, param_entry, ep);
+}
+
+/*
+ * Enable a DMA channel: Point channel to the PaRam entry,
+ * clear error if any, and only set the Event Enable bit.
+ * The Even will either be generated by hardware, or with
+ * edma_transfer_start()
+ */
+int
+edma_transfer_enable(struct edma_channel *ch, uint16_t param_entry)
+{
+ struct edma_softc *sc = ch->ch_sc;
+ bus_size_t off = (ch->ch_index < 32 ? 0 : 4);
+ uint32_t bit = __BIT(ch->ch_index < 32 ?
+ ch->ch_index : ch->ch_index - 32);
+
+ DPRINTF(1, (sc->sc_dev, "enable transfer ch# %d off %d bit %x pe %d\n", ch->ch_index, (int)off, bit, param_entry));
+
+ EDMA_WRITE(sc, EDMA_DCHMAP_REG(ch->ch_index),
+ __SHIFTIN(param_entry, EDMA_DCHMAP_PAENTRY));
+
+ uint32_t ccerr = EDMA_READ(sc, EDMA_CCERR_REG);
+ if (ccerr) {
+ device_printf(sc->sc_dev, " !!! CCER %08x\n", ccerr);
+ EDMA_WRITE(sc, EDMA_CCERRCLR_REG, ccerr);
+ }
+
+ EDMA_WRITE(sc, EDMA_EESR_REG + off, bit);
+ return 0;
+}
+
+/*
+ * Software-start a DMA channel: Set the Event bit.
+ */
+int
+edma_transfer_start(struct edma_channel *ch)
+{
+ struct edma_softc *sc = ch->ch_sc;
+ bus_size_t off = (ch->ch_index < 32 ? 0 : 4);
+ uint32_t bit = __BIT(ch->ch_index < 32 ?
+ ch->ch_index : ch->ch_index - 32);
+
+ DPRINTF(1, (sc->sc_dev, "start transfer ch# %d off %d bit %x pe %d\n", ch->ch_index, (int)off, bit));
+
+ EDMA_WRITE(sc, EDMA_ESR_REG + off, bit);
+ return 0;
+}
+
+/*
+ * Halt a DMA transfer. Called after successfull transfer, or to abort
+ * a transfer.
+ */
+void
+edma_halt(struct edma_channel *ch)
+{
+ struct edma_softc *sc = ch->ch_sc;
+ bus_size_t off = (ch->ch_index < 32 ? 0 : 4);
+ uint32_t bit = __BIT(ch->ch_index < 32 ?
+ ch->ch_index : ch->ch_index - 32);
+
+ EDMA_WRITE(sc, EDMA_EECR_REG + off, bit);
+ EDMA_WRITE(sc, EDMA_ECR_REG + off, bit);
+ EDMA_WRITE(sc, EDMA_SECR_REG + off, bit);
+ EDMA_WRITE(sc, EDMA_EMCR_REG + off, bit);
+
+ EDMA_WRITE(sc, EDMA_DCHMAP_REG(ch->ch_index),
+ __SHIFTIN(0, EDMA_DCHMAP_PAENTRY));
+}
+
+uint8_t
+edma_channel_index(struct edma_channel *ch)
+{
+ return ch->ch_index;
+}
+
+void
+edma_dump(struct edma_channel *ch)
+{
+ static const struct {
+ const char *name;
+ uint16_t off;
+ } regs[] = {
+ { "ER", EDMA_ER_REG },
+ { "ERH", EDMA_ERH_REG },
+ { "EER", EDMA_EER_REG },
+ { "EERH", EDMA_EERH_REG },
+ { "SER", EDMA_SER_REG },
+ { "SERH", EDMA_SERH_REG },
+ { "IER", EDMA_IER_REG },
+ { "IERH", EDMA_IERH_REG },
+ { "IPR", EDMA_IPR_REG },
+ { "IPRH", EDMA_IPRH_REG },
+ { "CCERR", EDMA_CCERR_REG },
+ { "CCSTAT", EDMA_CCSTAT_REG },
+ { "DRAE0", EDMA_DRAE_REG(0) },
+ { "DRAEH0", EDMA_DRAEH_REG(0) },
+ { NULL, 0 }
+ };
+ struct edma_softc *sc = ch->ch_sc;
+ int i;
+
+ for (i = 0; regs[i].name; i++) {
+ device_printf(sc->sc_dev, "%s: %08x\n",
+ regs[i].name, EDMA_READ(sc, regs[i].off));
+ }
+ device_printf(sc->sc_dev, "DCHMAP%d: %08x\n", ch->ch_index,
+ EDMA_READ(sc, EDMA_DCHMAP_REG(ch->ch_index)));
+}
+
+void
+edma_dump_param(struct edma_channel *ch, uint16_t param_entry)
+{
+ struct {
+ const char *name;
+ uint16_t off;
+ } regs[] = {
+ { "OPT", EDMA_PARAM_OPT_REG(param_entry) },
+ { "CNT", EDMA_PARAM_CNT_REG(param_entry) },
+ { "DST", EDMA_PARAM_DST_REG(param_entry) },
+ { "BIDX", EDMA_PARAM_BIDX_REG(param_entry) },
+ { "LNK", EDMA_PARAM_LNK_REG(param_entry) },
+ { "CIDX", EDMA_PARAM_CIDX_REG(param_entry) },
+ { "CCNT", EDMA_PARAM_CCNT_REG(param_entry) },
+ { NULL, 0 }
+ };
+ struct edma_softc *sc = ch->ch_sc;
+ int i;
+
+ for (i = 0; regs[i].name; i++) {
+ device_printf(sc->sc_dev, "%s%d: %08x\n",
+ regs[i].name, param_entry, EDMA_READ(sc, regs[i].off));
+ }
+}
Index: src/sys/arch/arm/omap/omap_edma.h
diff -u /dev/null src/sys/arch/arm/omap/omap_edma.h:1.1
--- /dev/null Tue Apr 14 18:37:43 2015
+++ src/sys/arch/arm/omap/omap_edma.h Tue Apr 14 18:37:43 2015
@@ -0,0 +1,173 @@
+/* $NetBSD: omap_edma.h,v 1.1 2015/04/14 18:37:43 bouyer Exp $ */
+
+/*-
+ * Copyright (c) 2014 Jared D. McNeill <[email protected]>
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _OMAP_EDMA_H
+#define _OMAP_EDMA_H
+
+#define EDMA_PID_REG 0x0000
+#define EDMA_CCCFG_REG 0x0004
+#define EDMA_CCCFG_MP_EXIST __BIT(25)
+#define EDMA_CCCFG_CHMAP_EXIST __BIT(24)
+#define EDMA_CCCFG_NUM_REGN __BITS(21,20)
+#define EDMA_CCCFG_NUM_EVQUEUE __BITS(18,16)
+#define EDMA_CCCFG_NUM_PAENTRY __BITS(14,12)
+#define EDMA_CCCFG_NUM_INTCH __BITS(10,8)
+#define EDMA_CCCFG_NUM_QDMACH __BITS(6,4)
+#define EDMA_CCCFG_NUM_DMACH __BITS(2,0)
+#define EDMA_SYSCONFIG_REG 0x0010
+#define EDMA_DCHMAP_REG(n) (0x0100 + 4 * (n))
+#define EDMA_DCHMAP_PAENTRY __BITS(13,5)
+#define EDMA_QCHMAP_REG(n) (0x0200 + 4 * (n))
+#define EDMA_DMAQNUM_REG(n) (0x0240 + 4 * (n))
+#define EDMA_QDMAQNUM_REG 0x0260
+#define EDMA_QUEPRI_REG 0x0284
+#define EDMA_EMR_REG 0x0300
+#define EDMA_EMRH_REG 0x0304
+#define EDMA_EMCR_REG 0x0308
+#define EDMA_EMCRH_REG 0x030c
+#define EDMA_QEMR_REG 0x0310
+#define EDMA_QEMRC_REG 0x0314
+#define EDMA_CCERR_REG 0x0318
+#define EDMA_CCERRCLR_REG 0x031c
+#define EDMA_EEVAL_REG 0x0320
+#define EDMA_DRAE_REG(n) (0x0340 + 8 * (n))
+#define EDMA_DRAEH_REG(n) (0x0340 + 8 * (n) + 4)
+#define EDMA_QRAE_REG(n) (0x0380 + 4 * (n))
+#define EDMA_QE_REG(q, e) (0x0400 + 0x40 * (q) + 4 * (e))
+#define EDMA_QSTAT_REG(n) (0x0600 + 4 * (n))
+#define EDMA_QWMTHRA_REG 0x0620
+#define EDMA_CCSTAT_REG 0x0640
+#define EDMA_MPFAR_REG 0x0800
+#define EDMA_MPFSR_REG 0x0804
+#define EDMA_MPFCR_REG 0x0808
+#define EDMA_MPPAG_REG 0x080c
+#define EDMA_MPPA_REG(n) (0x0810 + 4 * (n))
+#define EDMA_ER_REG 0x1000
+#define EDMA_ERH_REG 0x1004
+#define EDMA_ECR_REG 0x1008
+#define EDMA_ECRH_REG 0x100c
+#define EDMA_ESR_REG 0x1010
+#define EDMA_ESRH_REG 0x1014
+#define EDMA_CER_REG 0x1018
+#define EDMA_CERH_REG 0x101c
+#define EDMA_EER_REG 0x1020
+#define EDMA_EERH_REG 0x1024
+#define EDMA_EECR_REG 0x1028
+#define EDMA_EECRH_REG 0x102c
+#define EDMA_EESR_REG 0x1030
+#define EDMA_EESRH_REG 0x1034
+#define EDMA_SER_REG 0x1038
+#define EDMA_SERH_REG 0x103c
+#define EDMA_SECR_REG 0x1040
+#define EDMA_SECRH_REG 0x1044
+#define EDMA_IER_REG 0x1050
+#define EDMA_IERH_REG 0x1054
+#define EDMA_IECR_REG 0x1058
+#define EDMA_IECRH_REG 0x105c
+#define EDMA_IESR_REG 0x1060
+#define EDMA_IESRH_REG 0x1064
+#define EDMA_IPR_REG 0x1068
+#define EDMA_IPRH_REG 0x106c
+#define EDMA_ICR_REG 0x1070
+#define EDMA_ICRH_REG 0x1074
+#define EDMA_IEVAL_REG 0x1078
+#define EDMA_IEVAL_EVAL __BIT(0)
+#define EDMA_QER_REG 0x1080
+#define EDMA_QEER_REG 0x1084
+#define EDMA_QEECR_REG 0x1088
+#define EDMA_QEESR_REG 0x108c
+#define EDMA_QSER_REG 0x1090
+#define EDMA_QSECR_REG 0x1094
+
+#define EDMA_PARAM_BASE(n) (0x4000 + 0x20 * (n))
+#define EDMA_PARAM_OPT_REG(n) (EDMA_PARAM_BASE(n) + 0x00)
+#define EDMA_PARAM_OPT_PRIV __BIT(31)
+#define EDMA_PARAM_OPT_PRIVID __BITS(27,24)
+#define EDMA_PARAM_OPT_ITCCHEN __BIT(23)
+#define EDMA_PARAM_OPT_TCCHEN __BIT(22)
+#define EDMA_PARAM_OPT_ITCINTEN __BIT(21)
+#define EDMA_PARAM_OPT_TCINTEN __BIT(20)
+#define EDMA_PARAM_OPT_TCC __BITS(17,12)
+#define EDMA_PARAM_OPT_TCCMODE __BIT(11)
+#define EDMA_PARAM_OPT_FWID __BITS(10,8)
+#define EDMA_PARAM_OPT_STATIC __BIT(3)
+#define EDMA_PARAM_OPT_SYNCDIM __BIT(2)
+#define EDMA_PARAM_OPT_DAM __BIT(1)
+#define EDMA_PARAM_OPT_SAM __BIT(0)
+#define EDMA_PARAM_SRC_REG(n) (EDMA_PARAM_BASE(n) + 0x04)
+#define EDMA_PARAM_CNT_REG(n) (EDMA_PARAM_BASE(n) + 0x08)
+#define EDMA_PARAM_CNT_BCNT __BITS(31,16)
+#define EDMA_PARAM_CNT_ACNT __BITS(15,0)
+#define EDMA_PARAM_DST_REG(n) (EDMA_PARAM_BASE(n) + 0x0c)
+#define EDMA_PARAM_BIDX_REG(n) (EDMA_PARAM_BASE(n) + 0x10)
+#define EDMA_PARAM_BIDX_DSTBIDX __BITS(31,16)
+#define EDMA_PARAM_BIDX_SRCBIDX __BITS(15,0)
+#define EDMA_PARAM_LNK_REG(n) (EDMA_PARAM_BASE(n) + 0x14)
+#define EDMA_PARAM_LNK_BCNTRLD __BITS(31,16)
+#define EDMA_PARAM_LNK_LINK __BITS(15,0)
+#define EDMA_PARAM_CIDX_REG(n) (EDMA_PARAM_BASE(n) + 0x18)
+#define EDMA_PARAM_CIDX_DSTCIDX __BITS(31,16)
+#define EDMA_PARAM_CIDX_SRCCIDX __BITS(15,0)
+#define EDMA_PARAM_CCNT_REG(n) (EDMA_PARAM_BASE(n) + 0x1c)
+#define EDMA_PARAM_CCNT_CCNT __BITS(15,0)
+
+enum edma_type {
+ EDMA_TYPE_DMA,
+ EDMA_TYPE_QDMA
+};
+
+struct edma_param {
+ uint32_t ep_opt;
+ uint32_t ep_src;
+ uint32_t ep_dst;
+ uint16_t ep_bcnt;
+ uint16_t ep_acnt;
+ uint16_t ep_dstbidx;
+ uint16_t ep_srcbidx;
+ uint16_t ep_bcntrld;
+ uint16_t ep_link;
+ uint16_t ep_dstcidx;
+ uint16_t ep_srccidx;
+ uint16_t ep_ccnt;
+};
+
+struct edma_channel;
+
+struct edma_channel *edma_channel_alloc(enum edma_type, unsigned int,
+ void (*)(void *), void *);
+void edma_channel_free(struct edma_channel *);
+uint16_t edma_param_alloc(struct edma_channel *);
+void edma_param_free(struct edma_channel *, uint16_t);
+void edma_set_param(struct edma_channel *, uint16_t, struct edma_param *);
+int edma_transfer_enable(struct edma_channel *, uint16_t);
+int edma_transfer_start(struct edma_channel *);
+void edma_halt(struct edma_channel *);
+uint8_t edma_channel_index(struct edma_channel *);
+void edma_dump(struct edma_channel *);
+void edma_dump_param(struct edma_channel *, uint16_t);
+
+#endif /* !_OMAP_EDMA_H */