dmaengine: mpc512x: Implement additional chunk sizes for DMA transfers

This patch extends the capabilities of the driver to handle DMA
transfers to and from devices of 1, 2, 4, 16 (for MPC512x), and 32 byte
widths.

Signed-off-by: Mario Six <mario.six@gdsys.cc>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
This commit is contained in:
Mario Six 2016-03-18 14:57:20 +01:00 committed by Vinod Koul
parent 237ec70903
commit 899ed9dd4f

View File

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