public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] dma: pl330: improve status checking
@ 2015-02-11 12:23 Robert Baldyga
  2015-02-11 12:23 ` [PATCH v4 1/2] dma: pl330: improve pl330_tx_status() function Robert Baldyga
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Robert Baldyga @ 2015-02-11 12:23 UTC (permalink / raw)
  To: vinod.koul
  Cc: dan.j.williams, lars, dmaengine, linux-kernel, m.szyprowski,
	k.kozlowski, Robert Baldyga

Hello,

This series allows to check DMA transfer residue (number of bytes left
to send/receive) by modifying pl330_tx_status() function, when struct
dma_tx_state is filled with needed data. It also introduces DMA_PAUSE
feature, which allows to halt DMA transfer before termination and read
residue without risk of data loss.

This features are needed for proper implementation of DMA transfers,
particulary for serial drivers when transfer sizes are unknown and
requests on DMA channels are terminated before transfer completion
very ofter (it's becouse we terminate then in timeout interrupt to
avoid latency which is usually undesirable).

Best regards,
Robert Baldyga

Changelog:

v4:
- fixed comments from Vinod Koul
- rebased to latest linux-next

v3: https://lkml.org/lkml/2014/12/10/156
- remove double pm_runtime_put()

v2: https://lkml.org/lkml/2014/12/5/94
- add pm_runtime support
- make it working for multi-segment transfers
- some cleanups

v1: https://lkml.org/lkml/2014/11/25/554

Robert Baldyga (2):
  dma: pl330: improve pl330_tx_status() function
  dma: pl330: add DMA_PAUSE feature

 drivers/dma/pl330.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 100 insertions(+), 2 deletions(-)

-- 
1.9.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 1/2] dma: pl330: improve pl330_tx_status() function
  2015-02-11 12:23 [PATCH v4 0/2] dma: pl330: improve status checking Robert Baldyga
@ 2015-02-11 12:23 ` Robert Baldyga
  2015-02-11 12:23 ` [PATCH v4 2/2] dma: pl330: add DMA_PAUSE feature Robert Baldyga
  2015-02-13 16:14 ` [PATCH v4 0/2] dma: pl330: improve status checking Vinod Koul
  2 siblings, 0 replies; 4+ messages in thread
From: Robert Baldyga @ 2015-02-11 12:23 UTC (permalink / raw)
  To: vinod.koul
  Cc: dan.j.williams, lars, dmaengine, linux-kernel, m.szyprowski,
	k.kozlowski, Robert Baldyga, Lukasz Czerwinski

This patch adds possibility to read residue of DMA transfer. It's useful
when we want to know how many bytes have been transferred before we
terminate channel. It can take place, for example, on timeout interrupt.

Signed-off-by: Lukasz Czerwinski <l.czerwinski@samsung.com>
Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/dma/pl330.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 72 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 027f1d7..517abd6 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -504,6 +504,9 @@ struct dma_pl330_desc {
 
 	enum desc_status status;
 
+	int bytes_requested;
+	bool last;
+
 	/* The channel which currently holds this desc */
 	struct dma_pl330_chan *pchan;
 
@@ -2167,11 +2170,74 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
 	pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
 }
 
+int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
+		struct dma_pl330_desc *desc)
+{
+	struct pl330_thread *thrd = pch->thread;
+	struct pl330_dmac *pl330 = pch->dmac;
+	void __iomem *regs = thrd->dmac->base;
+	u32 val, addr;
+
+	pm_runtime_get_sync(pl330->ddma.dev);
+	val = addr = 0;
+	if (desc->rqcfg.src_inc) {
+		val = readl(regs + SA(thrd->id));
+		addr = desc->px.src_addr;
+	} else {
+		val = readl(regs + DA(thrd->id));
+		addr = desc->px.dst_addr;
+	}
+	pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
+	pm_runtime_put_autosuspend(pl330->ddma.dev);
+	return val - addr;
+}
+
 static enum dma_status
 pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
 		 struct dma_tx_state *txstate)
 {
-	return dma_cookie_status(chan, cookie, txstate);
+	enum dma_status ret;
+	unsigned long flags;
+	struct dma_pl330_desc *desc, *running = NULL;
+	struct dma_pl330_chan *pch = to_pchan(chan);
+	unsigned int transferred, residual = 0;
+
+	ret = dma_cookie_status(chan, cookie, txstate);
+
+	if (!txstate)
+		return ret;
+
+	if (ret == DMA_COMPLETE)
+		goto out;
+
+	spin_lock_irqsave(&pch->lock, flags);
+
+	if (pch->thread->req_running != -1)
+		running = pch->thread->req[pch->thread->req_running].desc;
+
+	/* Check in pending list */
+	list_for_each_entry(desc, &pch->work_list, node) {
+		if (desc->status == DONE)
+			transferred = desc->bytes_requested;
+		else if (running && desc == running)
+			transferred =
+				pl330_get_current_xferred_count(pch, desc);
+		else
+			transferred = 0;
+		residual += desc->bytes_requested - transferred;
+		if (desc->txd.cookie == cookie) {
+			ret = desc->status;
+			break;
+		}
+		if (desc->last)
+			residual = 0;
+	}
+	spin_unlock_irqrestore(&pch->lock, flags);
+
+out:
+	dma_set_residue(txstate, residual);
+
+	return ret;
 }
 
 static void pl330_issue_pending(struct dma_chan *chan)
@@ -2216,12 +2282,14 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
 			desc->txd.callback = last->txd.callback;
 			desc->txd.callback_param = last->txd.callback_param;
 		}
+		last->last = false;
 
 		dma_cookie_assign(&desc->txd);
 
 		list_move_tail(&desc->node, &pch->submitted_list);
 	}
 
+	last->last = true;
 	cookie = dma_cookie_assign(&last->txd);
 	list_add_tail(&last->node, &pch->submitted_list);
 	spin_unlock_irqrestore(&pch->lock, flags);
@@ -2444,6 +2512,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->bytes_requested = period_len;
 		fill_px(&desc->px, dst, src, period_len);
 
 		if (!first)
@@ -2586,6 +2655,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->rqtype = direction;
+		desc->bytes_requested = sg_dma_len(sg);
 	}
 
 	/* Return the last desc in the chain */
@@ -2771,7 +2841,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 	pd->src_addr_widths = PL330_DMA_BUSWIDTHS;
 	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_DESCRIPTOR;
+	pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
 
 	ret = dma_async_device_register(pd);
 	if (ret) {
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v4 2/2] dma: pl330: add DMA_PAUSE feature
  2015-02-11 12:23 [PATCH v4 0/2] dma: pl330: improve status checking Robert Baldyga
  2015-02-11 12:23 ` [PATCH v4 1/2] dma: pl330: improve pl330_tx_status() function Robert Baldyga
@ 2015-02-11 12:23 ` Robert Baldyga
  2015-02-13 16:14 ` [PATCH v4 0/2] dma: pl330: improve status checking Vinod Koul
  2 siblings, 0 replies; 4+ messages in thread
From: Robert Baldyga @ 2015-02-11 12:23 UTC (permalink / raw)
  To: vinod.koul
  Cc: dan.j.williams, lars, dmaengine, linux-kernel, m.szyprowski,
	k.kozlowski, Robert Baldyga

DMA_PAUSE command is used for halting DMA transfer on chosen channel.
It can be useful when we want to safely read residue before terminating
all requests on channel. Otherwise there can be situation when some data
is transferred before channel termination but after reading residue,
which obviously results with data loss. To avoid this situation we can
pause channel, read residue and then terminate all requests.
This scenario is common, for example, in serial port drivers.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---
 drivers/dma/pl330.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 517abd6..3c2fcd6 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2149,6 +2149,33 @@ static int pl330_terminate_all(struct dma_chan *chan)
 	return 0;
 }
 
+/*
+ * We don't support DMA_RESUME command because of hardware
+ * limitations, so after pausing the channel we cannot restore
+ * it to active state. We have to terminate channel and setup
+ * DMA transfer again. This pause feature was implemented to
+ * allow safely read residue before channel termination.
+ */
+int pl330_pause(struct dma_chan *chan)
+{
+	struct dma_pl330_chan *pch = to_pchan(chan);
+	struct pl330_dmac *pl330 = pch->dmac;
+	unsigned long flags;
+
+	pm_runtime_get_sync(pl330->ddma.dev);
+	spin_lock_irqsave(&pch->lock, flags);
+
+	spin_lock(&pl330->lock);
+	_stop(pch->thread);
+	spin_unlock(&pl330->lock);
+
+	spin_unlock_irqrestore(&pch->lock, flags);
+	pm_runtime_mark_last_busy(pl330->ddma.dev);
+	pm_runtime_put_autosuspend(pl330->ddma.dev);
+
+	return 0;
+}
+
 static void pl330_free_chan_resources(struct dma_chan *chan)
 {
 	struct dma_pl330_chan *pch = to_pchan(chan);
@@ -2836,6 +2863,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 	pd->device_tx_status = pl330_tx_status;
 	pd->device_prep_slave_sg = pl330_prep_slave_sg;
 	pd->device_config = pl330_config;
+	pd->device_pause = pl330_pause;
 	pd->device_terminate_all = pl330_terminate_all;
 	pd->device_issue_pending = pl330_issue_pending;
 	pd->src_addr_widths = PL330_DMA_BUSWIDTHS;
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v4 0/2] dma: pl330: improve status checking
  2015-02-11 12:23 [PATCH v4 0/2] dma: pl330: improve status checking Robert Baldyga
  2015-02-11 12:23 ` [PATCH v4 1/2] dma: pl330: improve pl330_tx_status() function Robert Baldyga
  2015-02-11 12:23 ` [PATCH v4 2/2] dma: pl330: add DMA_PAUSE feature Robert Baldyga
@ 2015-02-13 16:14 ` Vinod Koul
  2 siblings, 0 replies; 4+ messages in thread
From: Vinod Koul @ 2015-02-13 16:14 UTC (permalink / raw)
  To: Robert Baldyga
  Cc: dan.j.williams, lars, dmaengine, linux-kernel, m.szyprowski,
	k.kozlowski

On Wed, Feb 11, 2015 at 01:23:16PM +0100, Robert Baldyga wrote:
> Hello,
> 
> This series allows to check DMA transfer residue (number of bytes left
> to send/receive) by modifying pl330_tx_status() function, when struct
> dma_tx_state is filled with needed data. It also introduces DMA_PAUSE
> feature, which allows to halt DMA transfer before termination and read
> residue without risk of data loss.
> 
> This features are needed for proper implementation of DMA transfers,
> particulary for serial drivers when transfer sizes are unknown and
> requests on DMA channels are terminated before transfer completion
> very ofter (it's becouse we terminate then in timeout interrupt to
> avoid latency which is usually undesirable).
Applied, thanks

-- 
~Vinod


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2015-02-13 16:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-11 12:23 [PATCH v4 0/2] dma: pl330: improve status checking Robert Baldyga
2015-02-11 12:23 ` [PATCH v4 1/2] dma: pl330: improve pl330_tx_status() function Robert Baldyga
2015-02-11 12:23 ` [PATCH v4 2/2] dma: pl330: add DMA_PAUSE feature Robert Baldyga
2015-02-13 16:14 ` [PATCH v4 0/2] dma: pl330: improve status checking Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox