[U-Boot] [U-boot] [Patch v3 1/4] dma: ti-edma3: introduce edma3 driver

Ivan Khoronzhuk ivan.khoronzhuk at ti.com
Wed Oct 22 16:47:56 CEST 2014


The EDMA3 controller’s primary purpose is to service data transfers
that you program between two memory-mapped slave endpoints on the device.

Typical usage includes, but is not limited to the following:
- Servicing software-driven paging transfers (e.g., transfers from external
  memory, such as SDRAM to internal device memory, such as DSP L2 SRAM)
- Servicing event-driven peripherals, such as a serial port
- Performing sorting or sub-frame extraction of various data structures
- Offloading data transfers from the main device DSP(s)
- See the device-specific data manual for specific peripherals that are
  accessible via the EDMA3 controller

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk at ti.com>
---
 arch/arm/include/asm/ti-common/ti-edma3.h | 121 ++++++++++
 drivers/dma/Makefile                      |   1 +
 drivers/dma/ti-edma3.c                    | 384 ++++++++++++++++++++++++++++++
 3 files changed, 506 insertions(+)
 create mode 100644 arch/arm/include/asm/ti-common/ti-edma3.h
 create mode 100644 drivers/dma/ti-edma3.c

diff --git a/arch/arm/include/asm/ti-common/ti-edma3.h b/arch/arm/include/asm/ti-common/ti-edma3.h
new file mode 100644
index 0000000..5adc1da
--- /dev/null
+++ b/arch/arm/include/asm/ti-common/ti-edma3.h
@@ -0,0 +1,121 @@
+/*
+ * Enhanced Direct Memory Access (EDMA3) Controller
+ *
+ * (C) Copyright 2014
+ *     Texas Instruments Incorporated, <www.ti.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef _EDMA3_H_
+#define _EDMA3_H_
+
+#include <linux/stddef.h>
+
+#define EDMA3_PARSET_NULL_LINK			0xffff
+
+/*
+ * All parameter RAM set options
+ * opt field in edma3_param_set_config structure
+ */
+#define EDMA3_SLOPT_PRIV_LEVEL			BIT(31)
+#define EDMA3_SLOPT_PRIV_ID(id)			((0xf & (id)) << 24)
+#define EDMA3_SLOPT_INTERM_COMP_CHAIN_ENB	BIT(23)
+#define EDMA3_SLOPT_TRANS_COMP_CHAIN_ENB	BIT(22)
+#define EDMA3_SLOPT_INTERM_COMP_INT_ENB		BIT(21)
+#define EDMA3_SLOPT_TRANS_COMP_INT_ENB		BIT(20)
+#define EDMA3_SLOPT_COMP_CODE(code)		((0x3f & (code)) << 12)
+#define EDMA3_SLOPT_FIFO_WIDTH_8		0
+#define EDMA3_SLOPT_FIFO_WIDTH_16		(1 << 8)
+#define EDMA3_SLOPT_FIFO_WIDTH_32		(2 << 8)
+#define EDMA3_SLOPT_FIFO_WIDTH_64		(3 << 8)
+#define EDMA3_SLOPT_FIFO_WIDTH_128		(4 << 8)
+#define EDMA3_SLOPT_FIFO_WIDTH_256		(5 << 8)
+#define EDMA3_SLOPT_FIFO_WIDTH_SET(w)		((w & 0x7) << 8)
+#define EDMA3_SLOPT_STATIC			BIT(3)
+#define EDMA3_SLOPT_AB_SYNC			BIT(2)
+#define EDMA3_SLOPT_DST_ADDR_CONST_MODE		BIT(1)
+#define EDMA3_SLOPT_SRC_ADDR_CONST_MODE		BIT(0)
+
+enum edma3_address_mode {
+	INCR = 0,
+	FIFO = 1
+};
+
+enum edma3_fifo_width {
+	W8BIT = 0,
+	W16BIT = 1,
+	W32BIT = 2,
+	W64BIT = 3,
+	W128BIT = 4,
+	W256BIT = 5
+};
+
+enum edma3_sync_dimension {
+	ASYNC = 0,
+	ABSYNC = 1
+};
+
+/* PaRAM slots are laid out like this */
+struct edma3_slot_layout {
+	u32 opt;
+	u32 src;
+	u32 a_b_cnt;
+	u32 dst;
+	u32 src_dst_bidx;
+	u32 link_bcntrld;
+	u32 src_dst_cidx;
+	u32 ccnt;
+} __packed;
+
+/*
+ * Use this to assign trigger word number of edma3_slot_layout struct.
+ * trigger_word_name - is the exact name from edma3_slot_layout.
+ */
+#define EDMA3_TWORD(trigger_word_name)\
+		(offsetof(struct edma3_slot_layout, trigger_word_name) / 4)
+
+struct edma3_slot_config {
+	u32 opt;
+	u32 src;
+	u32 dst;
+	int bcnt;
+	int acnt;
+	int ccnt;
+	int src_bidx;
+	int dst_bidx;
+	int src_cidx;
+	int dst_cidx;
+	int bcntrld;
+	int link;
+};
+
+struct edma3_channel_config {
+	int slot;
+	int chnum;
+	int complete_code;	/* indicate pending complete interrupt */
+	int trigger_slot_word;	/* only used for qedma */
+};
+
+void qedma3_start(u32 base, struct edma3_channel_config *cfg);
+void qedma3_stop(u32 base, struct edma3_channel_config *cfg);
+void edma3_slot_configure(u32 base, int slot, struct edma3_slot_config *cfg);
+int edma3_check_for_transfer(u32 base, struct edma3_channel_config *cfg);
+void edma3_write_slot(u32 base, int slot, struct edma3_slot_layout *param);
+void edma3_read_slot(u32 base, int slot, struct edma3_slot_layout *param);
+
+void edma3_set_dest(u32 base, int slot, u32 dst, enum edma3_address_mode mode,
+		    enum edma3_fifo_width width);
+void edma3_set_dest_index(u32 base, unsigned slot, int bidx, int cidx);
+void edma3_set_dest_addr(u32 base, int slot, u32 dst);
+
+void edma3_set_src(u32 base, int slot, u32 src, enum edma3_address_mode mode,
+		   enum edma3_fifo_width width);
+void edma3_set_src_index(u32 base, unsigned slot, int bidx, int cidx);
+void edma3_set_src_addr(u32 base, int slot, u32 src);
+
+void edma3_set_transfer_params(u32 base, int slot, int acnt,
+			       int bcnt, int ccnt, u16 bcnt_rld,
+			       enum edma3_sync_dimension sync_mode);
+
+#endif
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 15b0780..4c8fcc2 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
 obj-$(CONFIG_APBH_DMA) += apbh_dma.o
 obj-$(CONFIG_FSL_DMA) += fsl_dma.o
 obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
+obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
diff --git a/drivers/dma/ti-edma3.c b/drivers/dma/ti-edma3.c
new file mode 100644
index 0000000..8184ded
--- /dev/null
+++ b/drivers/dma/ti-edma3.c
@@ -0,0 +1,384 @@
+/*
+ * Enhanced Direct Memory Access (EDMA3) Controller
+ *
+ * (C) Copyright 2014
+ *     Texas Instruments Incorporated, <www.ti.com>
+ *
+ * Author: Ivan Khoronzhuk <ivan.khoronzhuk at ti.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <asm/ti-common/ti-edma3.h>
+
+#define EDMA3_SL_BASE(slot)			(0x4000 + ((slot) << 5))
+#define EDMA3_SL_MAX_NUM			512
+#define EDMA3_SLOPT_FIFO_WIDTH_MASK		(0x7 << 8)
+
+#define EDMA3_QCHMAP(ch)			0x0200 + ((ch) << 2)
+#define EDMA3_CHMAP_PARSET_MASK			0x1ff
+#define EDMA3_CHMAP_PARSET_SHIFT		0x5
+#define EDMA3_CHMAP_TRIGWORD_SHIFT		0x2
+
+#define EDMA3_QEMCR				0x314
+#define EDMA3_IPR				0x1068
+#define EDMA3_IPRH				0x106c
+#define EDMA3_ICR				0x1070
+#define EDMA3_ICRH				0x1074
+#define EDMA3_QEECR				0x1088
+#define EDMA3_QEESR				0x108c
+#define EDMA3_QSECR				0x1094
+
+/**
+ * qedma3_start - start qdma on a channel
+ * @base: base address of edma
+ * @cfg: pinter to struct edma3_channel_config where you can set
+ * the slot number to associate with, the chnum, which corresponds
+ * your quick channel number 0-7, complete code - transfer complete code
+ * and trigger slot word - which has to correspond to the word number in
+ * edma3_slot_layout struct for generating event.
+ *
+ */
+void qedma3_start(u32 base, struct edma3_channel_config *cfg)
+{
+	u32 qchmap;
+
+	/* Clear the pending int bit */
+	if (cfg->complete_code < 32)
+		__raw_writel(1 << cfg->complete_code, base + EDMA3_ICR);
+	else
+		__raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH);
+
+	/* Map parameter set and trigger word 7 to quick channel */
+	qchmap = ((EDMA3_CHMAP_PARSET_MASK & cfg->slot)
+		  << EDMA3_CHMAP_PARSET_SHIFT) |
+		  (cfg->trigger_slot_word << EDMA3_CHMAP_TRIGWORD_SHIFT);
+
+	__raw_writel(qchmap, base + EDMA3_QCHMAP(cfg->chnum));
+
+	/* Clear missed event if set*/
+	__raw_writel(1 << cfg->chnum, base + EDMA3_QSECR);
+	__raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR);
+
+	/* Enable qdma channel event */
+	__raw_writel(1 << cfg->chnum, base + EDMA3_QEESR);
+}
+
+/**
+ * edma3_set_dest - set initial DMA destination address in parameter RAM slot
+ * @base: base address of edma
+ * @slot: parameter RAM slot being configured
+ * @dst: physical address of destination (memory, controller FIFO, etc)
+ * @addressMode: INCR, except in very rare cases
+ * @width: ignored unless @addressMode is FIFO, else specifies the
+ *	width to use when addressing the fifo (e.g. W8BIT, W32BIT)
+ *
+ * Note that the destination address is modified during the DMA transfer
+ * according to edma3_set_dest_index().
+ */
+void edma3_set_dest(u32 base, int slot, u32 dst, enum edma3_address_mode mode,
+		    enum edma3_fifo_width width)
+{
+	u32 opt;
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+
+	opt = __raw_readl(&rg->opt);
+	if (mode == FIFO)
+		opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) |
+		       (EDMA3_SLOPT_DST_ADDR_CONST_MODE |
+			EDMA3_SLOPT_FIFO_WIDTH_SET(width));
+	else
+		opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE;
+
+	__raw_writel(opt, &rg->opt);
+	__raw_writel(dst, &rg->dst);
+}
+
+/**
+ * edma3_set_dest_index - configure DMA destination address indexing
+ * @base: base address of edma
+ * @slot: parameter RAM slot being configured
+ * @bidx: byte offset between destination arrays in a frame
+ * @cidx: byte offset between destination frames in a block
+ *
+ * Offsets are specified to support either contiguous or discontiguous
+ * memory transfers, or repeated access to a hardware register, as needed.
+ * When accessing hardware registers, both offsets are normally zero.
+ */
+void edma3_set_dest_index(u32 base, unsigned slot, int bidx, int cidx)
+{
+	u32 src_dst_bidx;
+	u32 src_dst_cidx;
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+
+	src_dst_bidx = __raw_readl(&rg->src_dst_bidx);
+	src_dst_cidx = __raw_readl(&rg->src_dst_cidx);
+
+	__raw_writel((src_dst_bidx & 0x0000ffff) | (bidx << 16),
+		     &rg->src_dst_bidx);
+	__raw_writel((src_dst_cidx & 0x0000ffff) | (cidx << 16),
+		     &rg->src_dst_cidx);
+}
+
+/**
+ * edma3_set_dest_addr - set destination address for slot only
+ */
+void edma3_set_dest_addr(u32 base, int slot, u32 dst)
+{
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+	__raw_writel(dst, &rg->dst);
+}
+
+/**
+ * edma3_set_src - set initial DMA source address in parameter RAM slot
+ * @base: base address of edma
+ * @slot: parameter RAM slot being configured
+ * @src_port: physical address of source (memory, controller FIFO, etc)
+ * @mode: INCR, except in very rare cases
+ * @width: ignored unless @addressMode is FIFO, else specifies the
+ *	width to use when addressing the fifo (e.g. W8BIT, W32BIT)
+ *
+ * Note that the source address is modified during the DMA transfer
+ * according to edma3_set_src_index().
+ */
+void edma3_set_src(u32 base, int slot, u32 src, enum edma3_address_mode mode,
+		   enum edma3_fifo_width width)
+{
+	u32 opt;
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+
+	opt = __raw_readl(&rg->opt);
+	if (mode == FIFO)
+		opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) |
+		       (EDMA3_SLOPT_DST_ADDR_CONST_MODE |
+			EDMA3_SLOPT_FIFO_WIDTH_SET(width));
+	else
+		opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE;
+
+	__raw_writel(opt, &rg->opt);
+	__raw_writel(src, &rg->src);
+}
+
+/**
+ * edma3_set_src_index - configure DMA source address indexing
+ * @base: base address of edma
+ * @slot: parameter RAM slot being configured
+ * @bidx: byte offset between source arrays in a frame
+ * @cidx: byte offset between source frames in a block
+ *
+ * Offsets are specified to support either contiguous or discontiguous
+ * memory transfers, or repeated access to a hardware register, as needed.
+ * When accessing hardware registers, both offsets are normally zero.
+ */
+void edma3_set_src_index(u32 base, unsigned slot, int bidx, int cidx)
+{
+	u32 src_dst_bidx;
+	u32 src_dst_cidx;
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+
+	src_dst_bidx = __raw_readl(&rg->src_dst_bidx);
+	src_dst_cidx = __raw_readl(&rg->src_dst_cidx);
+
+	__raw_writel((src_dst_bidx & 0xffff0000) | bidx,
+		     &rg->src_dst_bidx);
+	__raw_writel((src_dst_cidx & 0xffff0000) | cidx,
+		     &rg->src_dst_cidx);
+}
+
+/**
+ * edma3_set_src_addr - set source address for slot only
+ */
+void edma3_set_src_addr(u32 base, int slot, u32 src)
+{
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+	__raw_writel(src, &rg->src);
+}
+
+/**
+ * edma3_set_transfer_params - configure DMA transfer parameters
+ * @base: base address of edma
+ * @slot: parameter RAM slot being configured
+ * @acnt: how many bytes per array (at least one)
+ * @bcnt: how many arrays per frame (at least one)
+ * @ccnt: how many frames per block (at least one)
+ * @bcnt_rld: used only for A-Synchronized transfers; this specifies
+ *	the value to reload into bcnt when it decrements to zero
+ * @sync_mode: ASYNC or ABSYNC
+ *
+ * See the EDMA3 documentation to understand how to configure and link
+ * transfers using the fields in PaRAM slots.  If you are not doing it
+ * all at once with edma3_write_slot(), you will use this routine
+ * plus two calls each for source and destination, setting the initial
+ * address and saying how to index that address.
+ *
+ * An example of an A-Synchronized transfer is a serial link using a
+ * single word shift register.  In that case, @acnt would be equal to
+ * that word size; the serial controller issues a DMA synchronization
+ * event to transfer each word, and memory access by the DMA transfer
+ * controller will be word-at-a-time.
+ *
+ * An example of an AB-Synchronized transfer is a device using a FIFO.
+ * In that case, @acnt equals the FIFO width and @bcnt equals its depth.
+ * The controller with the FIFO issues DMA synchronization events when
+ * the FIFO threshold is reached, and the DMA transfer controller will
+ * transfer one frame to (or from) the FIFO.  It will probably use
+ * efficient burst modes to access memory.
+ */
+void edma3_set_transfer_params(u32 base, int slot, int acnt,
+			       int bcnt, int ccnt, u16 bcnt_rld,
+			       enum edma3_sync_dimension sync_mode)
+{
+	u32 opt;
+	u32 link_bcntrld;
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+
+	link_bcntrld = __raw_readl(&rg->link_bcntrld);
+
+	__raw_writel((bcnt_rld << 16) | (0x0000ffff & link_bcntrld),
+		     &rg->link_bcntrld);
+
+	opt = __raw_readl(&rg->opt);
+	if (sync_mode == ASYNC)
+		__raw_writel(opt & ~EDMA3_SLOPT_AB_SYNC, &rg->opt);
+	else
+		__raw_writel(opt | EDMA3_SLOPT_AB_SYNC, &rg->opt);
+
+	/* Set the acount, bcount, ccount registers */
+	__raw_writel((bcnt << 16) | (acnt & 0xffff), &rg->a_b_cnt);
+	__raw_writel(0xffff & ccnt, &rg->ccnt);
+}
+
+/**
+ * edma3_write_slot - write parameter RAM data for slot
+ * @base: base address of edma
+ * @slot: number of parameter RAM slot being modified
+ * @param: data to be written into parameter RAM slot
+ *
+ * Use this to assign all parameters of a transfer at once.  This
+ * allows more efficient setup of transfers than issuing multiple
+ * calls to set up those parameters in small pieces, and provides
+ * complete control over all transfer options.
+ */
+void edma3_write_slot(u32 base, int slot, struct edma3_slot_layout *param)
+{
+	int i;
+	u32 *p = (u32 *)param;
+	u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot));
+
+	for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4)
+		__raw_writel(*p++, addr++);
+}
+
+/**
+ * edma3_read_slot - read parameter RAM data from slot
+ * @base: base address of edma
+ * @slot: number of parameter RAM slot being copied
+ * @param: where to store copy of parameter RAM data
+ *
+ * Use this to read data from a parameter RAM slot, perhaps to
+ * save them as a template for later reuse.
+ */
+void edma3_read_slot(u32 base, int slot, struct edma3_slot_layout *param)
+{
+	int i;
+	u32 *p = (u32 *)param;
+	u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot));
+
+	for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4)
+		*p++ = __raw_readl(addr++);
+}
+
+void edma3_slot_configure(u32 base, int slot, struct edma3_slot_config *cfg)
+{
+	struct edma3_slot_layout *rg;
+
+	rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot));
+
+	__raw_writel(cfg->opt, &rg->opt);
+	__raw_writel(cfg->src, &rg->src);
+	__raw_writel((cfg->bcnt << 16) | (cfg->acnt & 0xffff), &rg->a_b_cnt);
+	__raw_writel(cfg->dst, &rg->dst);
+	__raw_writel((cfg->dst_bidx << 16) |
+		     (cfg->src_bidx & 0xffff), &rg->src_dst_bidx);
+	__raw_writel((cfg->bcntrld << 16) |
+		     (cfg->link & 0xffff), &rg->link_bcntrld);
+	__raw_writel((cfg->dst_cidx << 16) |
+		     (cfg->src_cidx & 0xffff), &rg->src_dst_cidx);
+	__raw_writel(0xffff & cfg->ccnt, &rg->ccnt);
+}
+
+/**
+ * edma3_check_for_transfer - check if transfer coplete by checking
+ * interrupt pending bit. Clear interrupt pending bit if complete.
+ * @base: base address of edma
+ * @cfg: pinter to struct edma3_channel_config which was passed
+ * to qedma3_start when you started qdma channel
+ *
+ * Return 0 if complete, 1 if not.
+ */
+int edma3_check_for_transfer(u32 base, struct edma3_channel_config *cfg)
+{
+	u32 inum;
+	u32 ipr_base;
+	u32 icr_base;
+
+	if (cfg->complete_code < 32) {
+		ipr_base = base + EDMA3_IPR;
+		icr_base = base + EDMA3_ICR;
+		inum = 1 << cfg->complete_code;
+	} else {
+		ipr_base = base + EDMA3_IPRH;
+		icr_base = base + EDMA3_ICRH;
+		inum = 1 << (cfg->complete_code - 32);
+	}
+
+	/* check complete interrupt */
+	if (!(__raw_readl(ipr_base) & inum))
+		return 1;
+
+	/* clean up the pending int bit */
+	__raw_writel(inum, icr_base);
+
+	return 0;
+}
+
+/**
+ * qedma3_stop - stops dma on the channel passed
+ * @base: base address of edma
+ * @cfg: pinter to struct edma3_channel_config which was passed
+ * to qedma3_start when you started qdma channel
+ */
+void qedma3_stop(u32 base, struct edma3_channel_config *cfg)
+{
+	/* Disable qdma channel event */
+	__raw_writel(1 << cfg->chnum, base + EDMA3_QEECR);
+
+	/* clean up the interrupt indication */
+	if (cfg->complete_code < 32)
+		__raw_writel(1 << cfg->complete_code, base + EDMA3_ICR);
+	else
+		__raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH);
+
+	/* Clear missed event if set*/
+	__raw_writel(1 << cfg->chnum, base + EDMA3_QSECR);
+	__raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR);
+
+	/* Clear the channel map */
+	__raw_writel(0, base + EDMA3_QCHMAP(cfg->chnum));
+}
-- 
1.8.3.2



More information about the U-Boot mailing list