@@ -36,6 +36,7 @@
#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0)
#define PL330_QUIRK_PERIPH_BURST BIT(1)
#define PL330_QUIRK_OPTIMIZE_DEV2MEM_AXSIZE BIT(2)
+#define PL330_QUIRK_PERIPH_SINGLE_DREGS BIT(3)
enum pl330_cachectrl {
CCTRL0, /* Noncacheable and nonbufferable */
@@ -524,6 +525,10 @@ static struct pl330_of_quirks {
{
.quirk = "arm,pl330-optimize-dev2mem-axsize",
.id = PL330_QUIRK_OPTIMIZE_DEV2MEM_AXSIZE,
+ },
+ {
+ .quirk = "arm,pl330-periph-single-dregs",
+ .id = PL330_QUIRK_PERIPH_SINGLE_DREGS,
}
};
@@ -1213,6 +1218,67 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330,
return off;
}
+/*
+ * Sets up transfers to peripheral using DMA Singles instead of Bursts.
+ * Data is moved between fifo and memory in bursts following which it is
+ * loaded/stored to peripheral using Loops of DMA singles based on
+ * transfer direction.
+ */
+static inline int _ldst_periph_single_dregs(struct pl330_dmac *pl330,
+ unsigned int dry_run, u8 buf[],
+ const struct _xfer_spec *pxs,
+ int src_length, int dst_length)
+{
+ int off = 0;
+ unsigned int ljmp, lpcnt;
+ struct _arg_LPEND lpend;
+ enum dma_transfer_direction direction = pxs->desc->rqtype;
+
+ if (direction == DMA_MEM_TO_DEV) {
+ off += _emit_load(dry_run, &buf[off], ALWAYS, direction,
+ pxs->desc->peri);
+ lpcnt = dst_length;
+ } else {
+ lpcnt = src_length;
+ }
+
+ /*
+ * Use Loop Cnt 0 to load/store from/to peripheral in single transactions
+ * since Burst Req might not be set as pending transfer length maybe less
+ * size of bytes to burst (AxSize * AxLen).
+ */
+ off += _emit_LP(dry_run, &buf[off], 0, lpcnt);
+ ljmp = off;
+
+ /*
+ * do FLUSHP at beginning to clear any stale dma requests before the
+ * first WFP.
+ */
+ if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
+ off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
+
+ off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri);
+
+ if (direction == DMA_MEM_TO_DEV)
+ off += _emit_store(dry_run, &buf[off], SINGLE, direction,
+ pxs->desc->peri);
+ else
+ off += _emit_load(dry_run, &buf[off], SINGLE, direction,
+ pxs->desc->peri);
+
+ lpend.cond = ALWAYS;
+ lpend.forever = false;
+ lpend.loop = 0;
+ lpend.bjump = off - ljmp;
+ off += _emit_LPEND(dry_run, &buf[off], &lpend);
+
+ if (direction == DMA_DEV_TO_MEM)
+ off += _emit_store(dry_run, &buf[off], ALWAYS, direction,
+ pxs->desc->peri);
+
+ return off;
+}
+
static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
const struct _xfer_spec *pxs, int cyc)
{
@@ -1278,8 +1344,12 @@ static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
case DMA_MEM_TO_DEV:
case DMA_DEV_TO_MEM:
off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr);
- off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, 1,
- BURST);
+ if (pl330->quirks & PL330_QUIRK_PERIPH_SINGLE_DREGS)
+ off += _ldst_periph_single_dregs(pl330, dry_run, &buf[off],
+ pxs, src_length, dst_length);
+ else
+ off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, 1,
+ BURST);
break;
case DMA_MEM_TO_MEM: