Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 661f7cb5 authored by Matt Porter's avatar Matt Porter Committed by Vinod Koul
Browse files

dma: edma: fix slave config dependency on direction



The edma_slave_config() implementation depends on the
direction field such that it will not properly configure
a slave channel when called without direction set.

This fixes the implementation so that the slave config
is copied as is and prep_slave_sg() handles the
direction dependent handling. spi-omap2-mcspi and
omap_hsmmc both expose this bug as they configure the
slave channel config from a common path with an unconfigured
direction field.

Signed-off-by: default avatarMatt Porter <mporter@ti.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent 3a95b9fb
Loading
Loading
Loading
Loading
+27 −28
Original line number Diff line number Diff line
@@ -69,9 +69,7 @@ struct edma_chan {
	int				ch_num;
	bool				alloced;
	int				slot[EDMA_MAX_SLOTS];
	dma_addr_t			addr;
	int				addr_width;
	int				maxburst;
	struct dma_slave_config		cfg;
};

struct edma_cc {
@@ -178,29 +176,14 @@ static int edma_terminate_all(struct edma_chan *echan)
	return 0;
}


static int edma_slave_config(struct edma_chan *echan,
	struct dma_slave_config *config)
	struct dma_slave_config *cfg)
{
	if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) ||
	    (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES))
	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
		return -EINVAL;

	if (config->direction == DMA_MEM_TO_DEV) {
		if (config->dst_addr)
			echan->addr = config->dst_addr;
		if (config->dst_addr_width)
			echan->addr_width = config->dst_addr_width;
		if (config->dst_maxburst)
			echan->maxburst = config->dst_maxburst;
	} else if (config->direction == DMA_DEV_TO_MEM) {
		if (config->src_addr)
			echan->addr = config->src_addr;
		if (config->src_addr_width)
			echan->addr_width = config->src_addr_width;
		if (config->src_maxburst)
			echan->maxburst = config->src_maxburst;
	}
	memcpy(&echan->cfg, cfg, sizeof(echan->cfg));

	return 0;
}
@@ -235,6 +218,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
	struct edma_chan *echan = to_edma_chan(chan);
	struct device *dev = chan->device->dev;
	struct edma_desc *edesc;
	dma_addr_t dev_addr;
	enum dma_slave_buswidth dev_width;
	u32 burst;
	struct scatterlist *sg;
	int i;
	int acnt, bcnt, ccnt, src, dst, cidx;
@@ -243,7 +229,20 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
	if (unlikely(!echan || !sgl || !sg_len))
		return NULL;

	if (echan->addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
	if (direction == DMA_DEV_TO_MEM) {
		dev_addr = echan->cfg.src_addr;
		dev_width = echan->cfg.src_addr_width;
		burst = echan->cfg.src_maxburst;
	} else if (direction == DMA_MEM_TO_DEV) {
		dev_addr = echan->cfg.dst_addr;
		dev_width = echan->cfg.dst_addr_width;
		burst = echan->cfg.dst_maxburst;
	} else {
		dev_err(dev, "%s: bad direction?\n", __func__);
		return NULL;
	}

	if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
		dev_err(dev, "Undefined slave buswidth\n");
		return NULL;
	}
@@ -275,14 +274,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
			}
		}

		acnt = echan->addr_width;
		acnt = dev_width;

		/*
		 * If the maxburst is equal to the fifo width, use
		 * A-synced transfers. This allows for large contiguous
		 * buffer transfers using only one PaRAM set.
		 */
		if (echan->maxburst == 1) {
		if (burst == 1) {
			edesc->absync = false;
			ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1);
			bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1);
@@ -302,7 +301,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
		 */
		} else {
			edesc->absync = true;
			bcnt = echan->maxburst;
			bcnt = burst;
			ccnt = sg_dma_len(sg) / (acnt * bcnt);
			if (ccnt > (SZ_64K - 1)) {
				dev_err(dev, "Exceeded max SG segment size\n");
@@ -313,13 +312,13 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(

		if (direction == DMA_MEM_TO_DEV) {
			src = sg_dma_address(sg);
			dst = echan->addr;
			dst = dev_addr;
			src_bidx = acnt;
			src_cidx = cidx;
			dst_bidx = 0;
			dst_cidx = 0;
		} else {
			src = echan->addr;
			src = dev_addr;
			dst = sg_dma_address(sg);
			src_bidx = 0;
			src_cidx = 0;