From 848e9776fee424b9368c72377de5d3509b17937c Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 22 Jan 2016 19:06:44 +0800 Subject: dmaengine: pl330: support burst mode for dev-to-mem and mem-to-dev transmit This patch adds to support burst mode for dev-to-mem and mem-to-dev transmit. Signed-off-by: Boojin Kim Signed-off-by: Addy Ke Signed-off-by: Shawn Lin cc: Heiko Stuebner cc: Doug Anderson cc: Olof Johansson Reviewed-by: Sonny Rao Signed-off-by: Caesar Wang Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 17ee758b419f..7cdf8d4c7947 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1141,10 +1141,13 @@ static inline int _ldst_devtomem(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; + enum pl330_cond cond; + + cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri); - off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->desc->peri); + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); + off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri); off += _emit_ST(dry_run, &buf[off], ALWAYS); off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); } @@ -1156,11 +1159,14 @@ static inline int _ldst_memtodev(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; + enum pl330_cond cond; + + cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->desc->peri); + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); off += _emit_LD(dry_run, &buf[off], ALWAYS); - off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->desc->peri); + off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri); off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); } @@ -1199,7 +1205,7 @@ static inline int _loop(unsigned dry_run, u8 buf[], struct _arg_LPEND lpend; if (*bursts == 1) - return _bursts(dry_run, buf, pxs, 1); + return _bursts(pl330, dry_run, buf, pxs, 1); /* Max iterations possible in DMALP is 256 */ if (*bursts >= 256*256) { @@ -2560,7 +2566,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->rqtype = direction; desc->rqcfg.brst_size = pch->burst_sz; - desc->rqcfg.brst_len = 1; + desc->rqcfg.brst_len = pch->burst_len; desc->bytes_requested = period_len; fill_px(&desc->px, dst, src, period_len); @@ -2705,7 +2711,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } desc->rqcfg.brst_size = pch->burst_sz; - desc->rqcfg.brst_len = 1; + desc->rqcfg.brst_len = pch->burst_len; desc->rqtype = direction; desc->bytes_requested = sg_dma_len(sg); } -- cgit v1.2.3 From 271e1b86e69140fe65718ae8a264284c46d3129d Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Fri, 22 Jan 2016 19:06:46 +0800 Subject: dmaengine: pl330: add quirk for broken no flushp This patch add "arm,pl330-broken-no-flushp" quirk to avoid execute DMAFLUSHP if Soc doesn't support it. Signed-off-by: Addy Ke Signed-off-by: Shawn Lin cc: Doug Anderson cc: Heiko Stuebner cc: Olof Johansson Reviewed-by: Sonny Rao Signed-off-by: Caesar Wang Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 87 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 25 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 7cdf8d4c7947..008408db6ae0 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -34,6 +34,8 @@ #define PL330_MAX_IRQS 32 #define PL330_MAX_PERI 32 +#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0) + enum pl330_cachectrl { CCTRL0, /* Noncacheable and nonbufferable */ CCTRL1, /* Bufferable only */ @@ -488,6 +490,17 @@ struct pl330_dmac { /* Peripheral channels connected to this DMAC */ unsigned int num_peripherals; struct dma_pl330_chan *peripherals; /* keep at end */ + int quirks; +}; + +static struct pl330_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "arm,pl330-broken-no-flushp", + .id = PL330_QUIRK_BROKEN_NO_FLUSHP, + } }; struct dma_pl330_desc { @@ -1137,53 +1150,68 @@ static inline int _ldst_memtomem(unsigned dry_run, u8 buf[], return off; } -static inline int _ldst_devtomem(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) +static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run, + u8 buf[], const struct _xfer_spec *pxs, + int cyc) { int off = 0; enum pl330_cond cond; - cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; + if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) + cond = BURST; + else + cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; while (cyc--) { off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri); off += _emit_ST(dry_run, &buf[off], ALWAYS); - off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); + + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], + pxs->desc->peri); } return off; } -static inline int _ldst_memtodev(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) +static inline int _ldst_memtodev(struct pl330_dmac *pl330, + unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs, int cyc) { int off = 0; enum pl330_cond cond; - cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; + if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) + cond = BURST; + else + cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; + while (cyc--) { off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); off += _emit_LD(dry_run, &buf[off], ALWAYS); off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); + + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], + pxs->desc->peri); } return off; } -static int _bursts(unsigned dry_run, u8 buf[], +static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; switch (pxs->desc->rqtype) { case DMA_MEM_TO_DEV: - off += _ldst_memtodev(dry_run, &buf[off], pxs, cyc); + off += _ldst_memtodev(pl330, dry_run, &buf[off], pxs, cyc); break; case DMA_DEV_TO_MEM: - off += _ldst_devtomem(dry_run, &buf[off], pxs, cyc); + off += _ldst_devtomem(pl330, dry_run, &buf[off], pxs, cyc); break; case DMA_MEM_TO_MEM: off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc); @@ -1197,7 +1225,7 @@ static int _bursts(unsigned dry_run, u8 buf[], } /* Returns bytes consumed and updates bursts */ -static inline int _loop(unsigned dry_run, u8 buf[], +static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], unsigned long *bursts, const struct _xfer_spec *pxs) { int cyc, cycmax, szlp, szlpend, szbrst, off; @@ -1223,7 +1251,7 @@ static inline int _loop(unsigned dry_run, u8 buf[], } szlp = _emit_LP(1, buf, 0, 0); - szbrst = _bursts(1, buf, pxs, 1); + szbrst = _bursts(pl330, 1, buf, pxs, 1); lpend.cond = ALWAYS; lpend.forever = false; @@ -1255,7 +1283,7 @@ static inline int _loop(unsigned dry_run, u8 buf[], off += _emit_LP(dry_run, &buf[off], 1, lcnt1); ljmp1 = off; - off += _bursts(dry_run, &buf[off], pxs, cyc); + off += _bursts(pl330, dry_run, &buf[off], pxs, cyc); lpend.cond = ALWAYS; lpend.forever = false; @@ -1278,8 +1306,9 @@ static inline int _loop(unsigned dry_run, u8 buf[], return off; } -static inline int _setup_loops(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs) +static inline int _setup_loops(struct pl330_dmac *pl330, + unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) { struct pl330_xfer *x = &pxs->desc->px; u32 ccr = pxs->ccr; @@ -1288,15 +1317,16 @@ static inline int _setup_loops(unsigned dry_run, u8 buf[], while (bursts) { c = bursts; - off += _loop(dry_run, &buf[off], &c, pxs); + off += _loop(pl330, dry_run, &buf[off], &c, pxs); bursts -= c; } return off; } -static inline int _setup_xfer(unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs) +static inline int _setup_xfer(struct pl330_dmac *pl330, + unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) { struct pl330_xfer *x = &pxs->desc->px; int off = 0; @@ -1307,7 +1337,7 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[], off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); /* Setup Loop(s) */ - off += _setup_loops(dry_run, &buf[off], pxs); + off += _setup_loops(pl330, dry_run, &buf[off], pxs); return off; } @@ -1316,8 +1346,9 @@ static inline int _setup_xfer(unsigned dry_run, u8 buf[], * A req is a sequence of one or more xfer units. * Returns the number of bytes taken to setup the MC for the req. */ -static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, - unsigned index, struct _xfer_spec *pxs) +static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + struct pl330_thread *thrd, unsigned index, + struct _xfer_spec *pxs) { struct _pl330_req *req = &thrd->req[index]; struct pl330_xfer *x; @@ -1334,7 +1365,7 @@ static int _setup_req(unsigned dry_run, struct pl330_thread *thrd, if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) return -EINVAL; - off += _setup_xfer(dry_run, &buf[off], pxs); + off += _setup_xfer(pl330, dry_run, &buf[off], pxs); /* DMASEV peripheral/event */ off += _emit_SEV(dry_run, &buf[off], thrd->ev); @@ -1428,7 +1459,7 @@ static int pl330_submit_req(struct pl330_thread *thrd, xs.desc = desc; /* First dry run to check if req is acceptable */ - ret = _setup_req(1, thrd, idx, &xs); + ret = _setup_req(pl330, 1, thrd, idx, &xs); if (ret < 0) goto xfer_exit; @@ -1442,7 +1473,7 @@ static int pl330_submit_req(struct pl330_thread *thrd, /* Hook the request */ thrd->lstenq = idx; thrd->req[idx].desc = desc; - _setup_req(0, thrd, idx, &xs); + _setup_req(pl330, 0, thrd, idx, &xs); ret = 0; @@ -2787,6 +2818,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) struct resource *res; int i, ret, irq; int num_chan; + struct device_node *np = adev->dev.of_node; pdat = dev_get_platdata(&adev->dev); @@ -2806,6 +2838,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0; + /* get quirk */ + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) + if (of_property_read_bool(np, of_quirks[i].quirk)) + pl330->quirks |= of_quirks[i].id; + res = &adev->res; pl330->base = devm_ioremap_resource(&adev->dev, res); if (IS_ERR(pl330->base)) -- cgit v1.2.3 From 6d5bbed30f89acd2ae0d23b3fff5b13b307525d9 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 22 Jan 2016 19:06:50 +0800 Subject: dmaengine: core: expose max burst capability to clients This patch add max_burst to dma_get_slave_caps for clients to get the burst capability of slave dma controller. Signed-off-by: Shawn Lin Signed-off-by: Caesar Wang Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 1 + include/linux/dmaengine.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index c50a247be2e0..0cb259c59916 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -496,6 +496,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) caps->src_addr_widths = device->src_addr_widths; caps->dst_addr_widths = device->dst_addr_widths; caps->directions = device->directions; + caps->max_burst = device->max_burst; caps->residue_granularity = device->residue_granularity; caps->descriptor_reuse = device->descriptor_reuse; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 16a1cad30c33..0a9a0ba1998b 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -401,6 +401,7 @@ enum dma_residue_granularity { * since the enum dma_transfer_direction is not defined as bits for each * type of direction, the dma controller should fill (1 << ) and same * should be checked by controller as well + * @max_burst: max burst capability per-transfer * @cmd_pause: true, if pause and thereby resume is supported * @cmd_terminate: true, if terminate cmd is supported * @residue_granularity: granularity of the reported transfer residue @@ -411,6 +412,7 @@ struct dma_slave_caps { u32 src_addr_widths; u32 dst_addr_widths; u32 directions; + u32 max_burst; bool cmd_pause; bool cmd_terminate; enum dma_residue_granularity residue_granularity; @@ -654,6 +656,7 @@ struct dma_filter { * the enum dma_transfer_direction is not defined as bits for * each type of direction, the dma controller should fill (1 << * ) and same should be checked by controller as well + * @max_burst: max burst capability per-transfer * @residue_granularity: granularity of the transfer residue reported * by tx_status * @device_alloc_chan_resources: allocate resources and return the @@ -712,6 +715,7 @@ struct dma_device { u32 src_addr_widths; u32 dst_addr_widths; u32 directions; + u32 max_burst; bool descriptor_reuse; enum dma_residue_granularity residue_granularity; -- cgit v1.2.3 From 86a8ce7d4103f6680236af6916abf38e12477df8 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 22 Jan 2016 19:06:51 +0800 Subject: dmaengine: pl330: add max burst for dmaengine This patch add max burst capability for dmaengine and limit burst capability to one for PL330_QUIRK_BROKEN_NO_FLUSHP Signed-off-by: Shawn Lin Signed-off-by: Caesar Wang Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/dma') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 008408db6ae0..1b0453b9e32d 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -33,6 +33,7 @@ #define PL330_MAX_CHAN 8 #define PL330_MAX_IRQS 32 #define PL330_MAX_PERI 32 +#define PL330_MAX_BURST 16 #define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0) @@ -2938,6 +2939,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->dst_addr_widths = PL330_DMA_BUSWIDTHS; pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; + pd->max_burst = ((pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ? + 1 : PL330_MAX_BURST); ret = dma_async_device_register(pd); if (ret) { -- cgit v1.2.3 From 0a18f9b268dd66c84c806a74b3bc0be6438e7ba7 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Thu, 25 Feb 2016 09:00:53 +0800 Subject: dmaengine: pl330: fix to support the burst mode This patch fixes the burst mode that will break DMA uart on SoCFPGA. In some cases, some SoCS didn't support the multi-burst even if the devices who use the pl330 claim support the maxburst. Fixes: commit 848e977 "dmaengine: pl330: support burst mode for dev-to-mem and mem-to-dev transmit" Reported-by: Dinh Nguyen Signed-off-by: Caesar Wang Tested-by: Bartlomiej Zolnierkiewicz Tested-by: Dinh Nguyen Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 1b0453b9e32d..372b4359da97 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1161,7 +1161,7 @@ static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run, if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) cond = BURST; else - cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; + cond = SINGLE; while (cyc--) { off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); @@ -1186,8 +1186,7 @@ static inline int _ldst_memtodev(struct pl330_dmac *pl330, if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) cond = BURST; else - cond = (pxs->desc->rqcfg.brst_len == 1) ? SINGLE : BURST; - + cond = SINGLE; while (cyc--) { off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); @@ -2598,7 +2597,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( desc->rqtype = direction; desc->rqcfg.brst_size = pch->burst_sz; - desc->rqcfg.brst_len = pch->burst_len; + desc->rqcfg.brst_len = 1; desc->bytes_requested = period_len; fill_px(&desc->px, dst, src, period_len); @@ -2743,7 +2742,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } desc->rqcfg.brst_size = pch->burst_sz; - desc->rqcfg.brst_len = pch->burst_len; + desc->rqcfg.brst_len = 1; desc->rqtype = direction; desc->bytes_requested = sg_dma_len(sg); } -- cgit v1.2.3