diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 7686292bc1db..f1f0176c6c05 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1228,8 +1228,9 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], } /* - * transfer dregs with single transfers to peripheral, or a reduced size burst - * for mem-to-mem. + * only the unaligned burst transfers have the dregs. + * so, still transfer dregs with a reduced size burst + * for mem-to-mem, mem-to-dev or dev-to-mem. */ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], const struct _xfer_spec *pxs, int transfer_length) @@ -1240,22 +1241,31 @@ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], if (transfer_length == 0) return off; + /* + * dregs_len = (total bytes - BURST_TO_BYTE(bursts, ccr)) / + * BRST_SIZE(ccr) + * the dregs len must be smaller than burst len, + * so, for higher efficiency, we can modify CCR + * to use a reduced size burst len for the dregs. + */ + dregs_ccr = pxs->ccr; + dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) | + (0xf << CC_DSTBRSTLEN_SHFT)); + dregs_ccr |= (((transfer_length - 1) & 0xf) << + CC_SRCBRSTLEN_SHFT); + dregs_ccr |= (((transfer_length - 1) & 0xf) << + CC_DSTBRSTLEN_SHFT); + switch (pxs->desc->rqtype) { case DMA_MEM_TO_DEV: /* fall through */ case DMA_DEV_TO_MEM: - off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, - transfer_length, SINGLE); + off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); + off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, 1, + BURST); break; case DMA_MEM_TO_MEM: - dregs_ccr = pxs->ccr; - dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) | - (0xf << CC_DSTBRSTLEN_SHFT)); - dregs_ccr |= (((transfer_length - 1) & 0xf) << - CC_SRCBRSTLEN_SHFT); - dregs_ccr |= (((transfer_length - 1) & 0xf) << - CC_DSTBRSTLEN_SHFT); off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); off += _ldst_memtomem(dry_run, &buf[off], pxs, 1); break;