dmaengine: idma64: improve residue estimation

The residue calculation may provide a wrong estimation when the transfer is
started. There are possible scenarios we have to separate:

	1) the transfer is not started yet; residue is equal to the total
	   length;

	2) the transfer is just started (first chunk is ongoing); residue is
	   equal to the total length without already transfered bytes;

	3) the transfer is ongoing and we already sent few chunks of data;
	   residue is equal to the total length without fully transfered chunks
	   and already sent bytes.

Mistakenly the calculation in cases 2) and 3) was done in the similar way and
the result is equal to -bytes that have been transfered, i.e. quite big since
size_t type can't keep negative values.

Rewrite the calculation algorithm to be one pass and have a correct result.

Besides above in case user asks for a status of the active DMA descriptor
without pausing an ongoing transfer the residue will be estimated based on the
register value, though it's still racy. Since the transfer is active the value
is continuously being changed. Here we have to read two registers at a time. To
minimize an error make those reads close to each other.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
This commit is contained in:
Andy Shevchenko 2015-09-14 11:55:36 +03:00 committed by Vinod Koul
parent c1492b4c54
commit 0b23a1ece9
1 changed files with 8 additions and 8 deletions

View File

@ -355,23 +355,23 @@ static size_t idma64_active_desc_size(struct idma64_chan *idma64c)
struct idma64_desc *desc = idma64c->desc;
struct idma64_hw_desc *hw;
size_t bytes = desc->length;
u64 llp;
u32 ctlhi;
u64 llp = channel_readq(idma64c, LLP);
u32 ctlhi = channel_readl(idma64c, CTL_HI);
unsigned int i = 0;
llp = channel_readq(idma64c, LLP);
do {
hw = &desc->hw[i];
} while ((hw->llp != llp) && (++i < desc->ndesc));
if (hw->llp == llp)
break;
bytes -= hw->len;
} while (++i < desc->ndesc);
if (!i)
return bytes;
do {
bytes -= desc->hw[--i].len;
} while (i);
/* The current chunk is not fully transfered yet */
bytes += desc->hw[--i].len;
ctlhi = channel_readl(idma64c, CTL_HI);
return bytes - IDMA64C_CTLH_BLOCK_TS(ctlhi);
}