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(&param, 0, sizeof(param));
+	param.ep_bcnt = 1;
+	for (idx = 0; idx < NUM_PARAM_SETS; idx++) {
+		edma_write_param(sc, idx, &param);
+	}
+
+	/* 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 */

Reply via email to