|
@@ -3,6 +3,7 @@
|
|
|
* Copyright (C) Semihalf 2009
|
|
|
* Copyright (C) Ilya Yanok, Emcraft Systems 2010
|
|
|
* Copyright (C) Alexander Popov, Promcontroller 2014
|
|
|
+ * Copyright (C) Mario Six, Guntermann & Drunck GmbH, 2016
|
|
|
*
|
|
|
* Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
|
|
|
* (defines, structures and comments) was taken from MPC5121 DMA driver
|
|
@@ -26,18 +27,19 @@
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
- * MPC512x and MPC8308 DMA driver. It supports
|
|
|
- * memory to memory data transfers (tested using dmatest module) and
|
|
|
- * data transfers between memory and peripheral I/O memory
|
|
|
- * by means of slave scatter/gather with these limitations:
|
|
|
- * - chunked transfers (described by s/g lists with more than one item)
|
|
|
- * are refused as long as proper support for scatter/gather is missing;
|
|
|
- * - transfers on MPC8308 always start from software as this SoC appears
|
|
|
- * not to have external request lines for peripheral flow control;
|
|
|
- * - only peripheral devices with 4-byte FIFO access register are supported;
|
|
|
- * - minimal memory <-> I/O memory transfer chunk is 4 bytes and consequently
|
|
|
- * source and destination addresses must be 4-byte aligned
|
|
|
- * and transfer size must be aligned on (4 * maxburst) boundary;
|
|
|
+ * MPC512x and MPC8308 DMA driver. It supports memory to memory data transfers
|
|
|
+ * (tested using dmatest module) and data transfers between memory and
|
|
|
+ * peripheral I/O memory by means of slave scatter/gather with these
|
|
|
+ * limitations:
|
|
|
+ * - chunked transfers (described by s/g lists with more than one item) are
|
|
|
+ * refused as long as proper support for scatter/gather is missing
|
|
|
+ * - transfers on MPC8308 always start from software as this SoC does not have
|
|
|
+ * external request lines for peripheral flow control
|
|
|
+ * - memory <-> I/O memory transfer chunks of sizes of 1, 2, 4, 16 (for
|
|
|
+ * MPC512x), and 32 bytes are supported, and, consequently, source
|
|
|
+ * addresses and destination addresses must be aligned accordingly;
|
|
|
+ * furthermore, for MPC512x SoCs, the transfer size must be aligned on
|
|
|
+ * (chunk size * maxburst)
|
|
|
*/
|
|
|
|
|
|
#include <linux/module.h>
|
|
@@ -213,8 +215,10 @@ struct mpc_dma_chan {
|
|
|
/* Settings for access to peripheral FIFO */
|
|
|
dma_addr_t src_per_paddr;
|
|
|
u32 src_tcd_nunits;
|
|
|
+ u8 swidth;
|
|
|
dma_addr_t dst_per_paddr;
|
|
|
u32 dst_tcd_nunits;
|
|
|
+ u8 dwidth;
|
|
|
|
|
|
/* Lock for this structure */
|
|
|
spinlock_t lock;
|
|
@@ -684,6 +688,15 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
|
|
|
return &mdesc->desc;
|
|
|
}
|
|
|
|
|
|
+inline u8 buswidth_to_dmatsize(u8 buswidth)
|
|
|
+{
|
|
|
+ u8 res;
|
|
|
+
|
|
|
+ for (res = 0; buswidth > 1; buswidth /= 2)
|
|
|
+ res++;
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
static struct dma_async_tx_descriptor *
|
|
|
mpc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
|
|
unsigned int sg_len, enum dma_transfer_direction direction,
|
|
@@ -742,27 +755,32 @@ mpc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
|
|
|
|
|
memset(tcd, 0, sizeof(struct mpc_dma_tcd));
|
|
|
|
|
|
- if (!IS_ALIGNED(sg_dma_address(sg), 4))
|
|
|
- goto err_prep;
|
|
|
-
|
|
|
if (direction == DMA_DEV_TO_MEM) {
|
|
|
tcd->saddr = per_paddr;
|
|
|
tcd->daddr = sg_dma_address(sg);
|
|
|
+
|
|
|
+ if (!IS_ALIGNED(sg_dma_address(sg), mchan->dwidth))
|
|
|
+ goto err_prep;
|
|
|
+
|
|
|
tcd->soff = 0;
|
|
|
- tcd->doff = 4;
|
|
|
+ tcd->doff = mchan->dwidth;
|
|
|
} else {
|
|
|
tcd->saddr = sg_dma_address(sg);
|
|
|
tcd->daddr = per_paddr;
|
|
|
- tcd->soff = 4;
|
|
|
+
|
|
|
+ if (!IS_ALIGNED(sg_dma_address(sg), mchan->swidth))
|
|
|
+ goto err_prep;
|
|
|
+
|
|
|
+ tcd->soff = mchan->swidth;
|
|
|
tcd->doff = 0;
|
|
|
}
|
|
|
|
|
|
- tcd->ssize = MPC_DMA_TSIZE_4;
|
|
|
- tcd->dsize = MPC_DMA_TSIZE_4;
|
|
|
+ tcd->ssize = buswidth_to_dmatsize(mchan->swidth);
|
|
|
+ tcd->dsize = buswidth_to_dmatsize(mchan->dwidth);
|
|
|
|
|
|
if (mdma->is_mpc8308) {
|
|
|
tcd->nbytes = sg_dma_len(sg);
|
|
|
- if (!IS_ALIGNED(tcd->nbytes, 4))
|
|
|
+ if (!IS_ALIGNED(tcd->nbytes, mchan->swidth))
|
|
|
goto err_prep;
|
|
|
|
|
|
/* No major loops for MPC8303 */
|
|
@@ -770,7 +788,7 @@ mpc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
|
|
tcd->citer = 1;
|
|
|
} else {
|
|
|
len = sg_dma_len(sg);
|
|
|
- tcd->nbytes = tcd_nunits * 4;
|
|
|
+ tcd->nbytes = tcd_nunits * tcd->ssize;
|
|
|
if (!IS_ALIGNED(len, tcd->nbytes))
|
|
|
goto err_prep;
|
|
|
|
|
@@ -806,40 +824,62 @@ err_prep:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+inline bool is_buswidth_valid(u8 buswidth, bool is_mpc8308)
|
|
|
+{
|
|
|
+ switch (buswidth) {
|
|
|
+ case 16:
|
|
|
+ if (is_mpc8308)
|
|
|
+ return false;
|
|
|
+ case 1:
|
|
|
+ case 2:
|
|
|
+ case 4:
|
|
|
+ case 32:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static int mpc_dma_device_config(struct dma_chan *chan,
|
|
|
struct dma_slave_config *cfg)
|
|
|
{
|
|
|
struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
|
|
|
+ struct mpc_dma *mdma = dma_chan_to_mpc_dma(&mchan->chan);
|
|
|
unsigned long flags;
|
|
|
|
|
|
/*
|
|
|
* Software constraints:
|
|
|
- * - only transfers between a peripheral device and
|
|
|
- * memory are supported;
|
|
|
- * - only peripheral devices with 4-byte FIFO access register
|
|
|
- * are supported;
|
|
|
- * - minimal transfer chunk is 4 bytes and consequently
|
|
|
- * source and destination addresses must be 4-byte aligned
|
|
|
- * and transfer size must be aligned on (4 * maxburst)
|
|
|
- * boundary;
|
|
|
- * - during the transfer RAM address is being incremented by
|
|
|
- * the size of minimal transfer chunk;
|
|
|
- * - peripheral port's address is constant during the transfer.
|
|
|
+ * - only transfers between a peripheral device and memory are
|
|
|
+ * supported
|
|
|
+ * - transfer chunk sizes of 1, 2, 4, 16 (for MPC512x), and 32 bytes
|
|
|
+ * are supported, and, consequently, source addresses and
|
|
|
+ * destination addresses; must be aligned accordingly; furthermore,
|
|
|
+ * for MPC512x SoCs, the transfer size must be aligned on (chunk
|
|
|
+ * size * maxburst)
|
|
|
+ * - during the transfer, the RAM address is incremented by the size
|
|
|
+ * of transfer chunk
|
|
|
+ * - the peripheral port's address is constant during the transfer.
|
|
|
*/
|
|
|
|
|
|
- if (cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES ||
|
|
|
- cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES ||
|
|
|
- !IS_ALIGNED(cfg->src_addr, 4) ||
|
|
|
- !IS_ALIGNED(cfg->dst_addr, 4)) {
|
|
|
+ if (!IS_ALIGNED(cfg->src_addr, cfg->src_addr_width) ||
|
|
|
+ !IS_ALIGNED(cfg->dst_addr, cfg->dst_addr_width)) {
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ if (!is_buswidth_valid(cfg->src_addr_width, mdma->is_mpc8308) ||
|
|
|
+ !is_buswidth_valid(cfg->dst_addr_width, mdma->is_mpc8308))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
spin_lock_irqsave(&mchan->lock, flags);
|
|
|
|
|
|
mchan->src_per_paddr = cfg->src_addr;
|
|
|
mchan->src_tcd_nunits = cfg->src_maxburst;
|
|
|
+ mchan->swidth = cfg->src_addr_width;
|
|
|
mchan->dst_per_paddr = cfg->dst_addr;
|
|
|
mchan->dst_tcd_nunits = cfg->dst_maxburst;
|
|
|
+ mchan->dwidth = cfg->dst_addr_width;
|
|
|
|
|
|
/* Apply defaults */
|
|
|
if (mchan->src_tcd_nunits == 0)
|