aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElen Song <elen.song@atmel.com>2013-05-09 23:01:46 -0400
committerVinod Koul <vinod.koul@intel.com>2013-07-05 02:10:31 -0400
commitd48de6f1a81b3d10de0f5765aff1b3bd788617b0 (patch)
tree6e6e83e17b5a776524793d114723b31e3135f1a4
parentd088c33b646e9f3564eea7a057a2cb697c18bcd0 (diff)
DMA: AT91: Get residual bytes in dma buffer
Add support for returning the residue for current transfer cookie by reading the transfered buffer size(BTSIZE) in CTRLA register. For a single buffer cookie, the descriptor length minus BTSIZE can get the residue. For a lli cookie, remain_desc will record remain descriptor length when last descriptor finish, the remain_desc minus BTSIZE can get the current residue. If the cookie has completed successfully, the residue will be zero. If the cookie is in progress, it will be the number of bytes yet to be transferred. If get residue error, the cookie will be turn into error status. Check dma fifo to see if data remain, let issue pending finish remain work if there is. Signed-off-by: Elen Song <elen.song@atmel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r--drivers/dma/at_hdmac.c130
-rw-r--r--drivers/dma/at_hdmac_regs.h3
2 files changed, 115 insertions, 18 deletions
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 4c101a9dd3cb..5ce89368a8db 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -54,6 +54,7 @@ MODULE_PARM_DESC(init_nr_desc_per_channel,
54 54
55/* prototypes */ 55/* prototypes */
56static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx); 56static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx);
57static void atc_issue_pending(struct dma_chan *chan);
57 58
58 59
59/*----------------------------------------------------------------------*/ 60/*----------------------------------------------------------------------*/
@@ -230,6 +231,94 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first)
230 vdbg_dump_regs(atchan); 231 vdbg_dump_regs(atchan);
231} 232}
232 233
234/*
235 * atc_get_current_descriptors -
236 * locate the descriptor which equal to physical address in DSCR
237 * @atchan: the channel we want to start
238 * @dscr_addr: physical descriptor address in DSCR
239 */
240static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan,
241 u32 dscr_addr)
242{
243 struct at_desc *desc, *_desc, *child, *desc_cur = NULL;
244
245 list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) {
246 if (desc->lli.dscr == dscr_addr) {
247 desc_cur = desc;
248 break;
249 }
250
251 list_for_each_entry(child, &desc->tx_list, desc_node) {
252 if (child->lli.dscr == dscr_addr) {
253 desc_cur = child;
254 break;
255 }
256 }
257 }
258
259 return desc_cur;
260}
261
262/*
263 * atc_get_bytes_left -
264 * Get the number of bytes residue in dma buffer,
265 * @chan: the channel we want to start
266 */
267static int atc_get_bytes_left(struct dma_chan *chan)
268{
269 struct at_dma_chan *atchan = to_at_dma_chan(chan);
270 struct at_dma *atdma = to_at_dma(chan->device);
271 int chan_id = atchan->chan_common.chan_id;
272 struct at_desc *desc_first = atc_first_active(atchan);
273 struct at_desc *desc_cur;
274 int ret = 0, count = 0;
275
276 /*
277 * Initialize necessary values in the first time.
278 * remain_desc record remain desc length.
279 */
280 if (atchan->remain_desc == 0)
281 /* First descriptor embedds the transaction length */
282 atchan->remain_desc = desc_first->len;
283
284 /*
285 * This happens when current descriptor transfer complete.
286 * The residual buffer size should reduce current descriptor length.
287 */
288 if (unlikely(test_bit(ATC_IS_BTC, &atchan->status))) {
289 clear_bit(ATC_IS_BTC, &atchan->status);
290 desc_cur = atc_get_current_descriptors(atchan,
291 channel_readl(atchan, DSCR));
292 if (!desc_cur) {
293 ret = -EINVAL;
294 goto out;
295 }
296 atchan->remain_desc -= (desc_cur->lli.ctrla & ATC_BTSIZE_MAX)
297 << (desc_first->tx_width);
298 if (atchan->remain_desc < 0) {
299 ret = -EINVAL;
300 goto out;
301 } else
302 ret = atchan->remain_desc;
303 } else {
304 /*
305 * Get residual bytes when current
306 * descriptor transfer in progress.
307 */
308 count = (channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX)
309 << (desc_first->tx_width);
310 ret = atchan->remain_desc - count;
311 }
312 /*
313 * Check fifo empty.
314 */
315 if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id)))
316 atc_issue_pending(chan);
317
318out:
319 return ret;
320}
321
233/** 322/**
234 * atc_chain_complete - finish work for one transaction chain 323 * atc_chain_complete - finish work for one transaction chain
235 * @atchan: channel we work on 324 * @atchan: channel we work on
@@ -496,6 +585,8 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id)
496 /* Give information to tasklet */ 585 /* Give information to tasklet */
497 set_bit(ATC_IS_ERROR, &atchan->status); 586 set_bit(ATC_IS_ERROR, &atchan->status);
498 } 587 }
588 if (pending & AT_DMA_BTC(i))
589 set_bit(ATC_IS_BTC, &atchan->status);
499 tasklet_schedule(&atchan->tasklet); 590 tasklet_schedule(&atchan->tasklet);
500 ret = IRQ_HANDLED; 591 ret = IRQ_HANDLED;
501 } 592 }
@@ -1035,34 +1126,35 @@ atc_tx_status(struct dma_chan *chan,
1035 struct dma_tx_state *txstate) 1126 struct dma_tx_state *txstate)
1036{ 1127{
1037 struct at_dma_chan *atchan = to_at_dma_chan(chan); 1128 struct at_dma_chan *atchan = to_at_dma_chan(chan);
1038 dma_cookie_t last_used;
1039 dma_cookie_t last_complete;
1040 unsigned long flags; 1129 unsigned long flags;
1041 enum dma_status ret; 1130 enum dma_status ret;
1042 1131 int bytes = 0;
1043 spin_lock_irqsave(&atchan->lock, flags);
1044 1132
1045 ret = dma_cookie_status(chan, cookie, txstate); 1133 ret = dma_cookie_status(chan, cookie, txstate);
1046 if (ret != DMA_SUCCESS) { 1134 if (ret == DMA_SUCCESS)
1047 atc_cleanup_descriptors(atchan); 1135 return ret;
1136 /*
1137 * There's no point calculating the residue if there's
1138 * no txstate to store the value.
1139 */
1140 if (!txstate)
1141 return DMA_ERROR;
1048 1142
1049 ret = dma_cookie_status(chan, cookie, txstate); 1143 spin_lock_irqsave(&atchan->lock, flags);
1050 }
1051 1144
1052 last_complete = chan->completed_cookie; 1145 /* Get number of bytes left in the active transactions */
1053 last_used = chan->cookie; 1146 bytes = atc_get_bytes_left(chan);
1054 1147
1055 spin_unlock_irqrestore(&atchan->lock, flags); 1148 spin_unlock_irqrestore(&atchan->lock, flags);
1056 1149
1057 if (ret != DMA_SUCCESS) 1150 if (unlikely(bytes < 0)) {
1058 dma_set_residue(txstate, atc_first_active(atchan)->len); 1151 dev_vdbg(chan2dev(chan), "get residual bytes error\n");
1059 1152 return DMA_ERROR;
1060 if (atc_chan_is_paused(atchan)) 1153 } else
1061 ret = DMA_PAUSED; 1154 dma_set_residue(txstate, bytes);
1062 1155
1063 dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n", 1156 dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d residue = %d\n",
1064 ret, cookie, last_complete ? last_complete : 0, 1157 ret, cookie, bytes);
1065 last_used ? last_used : 0);
1066 1158
1067 return ret; 1159 return ret;
1068} 1160}
@@ -1146,6 +1238,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan)
1146 1238
1147 spin_lock_irqsave(&atchan->lock, flags); 1239 spin_lock_irqsave(&atchan->lock, flags);
1148 atchan->descs_allocated = i; 1240 atchan->descs_allocated = i;
1241 atchan->remain_desc = 0;
1149 list_splice(&tmp_list, &atchan->free_list); 1242 list_splice(&tmp_list, &atchan->free_list);
1150 dma_cookie_init(chan); 1243 dma_cookie_init(chan);
1151 spin_unlock_irqrestore(&atchan->lock, flags); 1244 spin_unlock_irqrestore(&atchan->lock, flags);
@@ -1188,6 +1281,7 @@ static void atc_free_chan_resources(struct dma_chan *chan)
1188 list_splice_init(&atchan->free_list, &list); 1281 list_splice_init(&atchan->free_list, &list);
1189 atchan->descs_allocated = 0; 1282 atchan->descs_allocated = 0;
1190 atchan->status = 0; 1283 atchan->status = 0;
1284 atchan->remain_desc = 0;
1191 1285
1192 dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); 1286 dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
1193} 1287}
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h
index 3679933fb646..f31d647acdfa 100644
--- a/drivers/dma/at_hdmac_regs.h
+++ b/drivers/dma/at_hdmac_regs.h
@@ -213,6 +213,7 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd)
213enum atc_status { 213enum atc_status {
214 ATC_IS_ERROR = 0, 214 ATC_IS_ERROR = 0,
215 ATC_IS_PAUSED = 1, 215 ATC_IS_PAUSED = 1,
216 ATC_IS_BTC = 2,
216 ATC_IS_CYCLIC = 24, 217 ATC_IS_CYCLIC = 24,
217}; 218};
218 219
@@ -230,6 +231,7 @@ enum atc_status {
230 * @save_cfg: configuration register that is saved on suspend/resume cycle 231 * @save_cfg: configuration register that is saved on suspend/resume cycle
231 * @save_dscr: for cyclic operations, preserve next descriptor address in 232 * @save_dscr: for cyclic operations, preserve next descriptor address in
232 * the cyclic list on suspend/resume cycle 233 * the cyclic list on suspend/resume cycle
234 * @remain_desc: to save remain desc length
233 * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG 235 * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG
234 * @lock: serializes enqueue/dequeue operations to descriptors lists 236 * @lock: serializes enqueue/dequeue operations to descriptors lists
235 * @active_list: list of descriptors dmaengine is being running on 237 * @active_list: list of descriptors dmaengine is being running on
@@ -248,6 +250,7 @@ struct at_dma_chan {
248 struct tasklet_struct tasklet; 250 struct tasklet_struct tasklet;
249 u32 save_cfg; 251 u32 save_cfg;
250 u32 save_dscr; 252 u32 save_dscr;
253 u32 remain_desc;
251 struct dma_slave_config dma_sconfig; 254 struct dma_slave_config dma_sconfig;
252 255
253 spinlock_t lock; 256 spinlock_t lock;