diff options
author | Linus Walleij <linus.walleij@stericsson.com> | 2010-03-04 08:40:30 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2010-03-24 17:24:44 -0400 |
commit | 84c8447c544bc7579097649273bc3f4e1b5de6af (patch) | |
tree | 7b91a03a61044612d511283e03f545893ba8c464 /drivers/dma | |
parent | cecd87da83869ad4157295b87a2e51e38c3e03bf (diff) |
DMAENGINE: COH 901 318 fix bytesleft
This makes the function to get the number of bytes left in the
ongoing DMA transaction actually work: the old code did not take
neither lli:s nor queued jobs into account. Also fix a missing
spinlock while we're at it.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/coh901318.c | 96 |
1 files changed, 88 insertions, 8 deletions
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 20889c98e9b0..f636c4a87c7f 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c | |||
@@ -408,25 +408,100 @@ coh901318_first_queued(struct coh901318_chan *cohc) | |||
408 | return d; | 408 | return d; |
409 | } | 409 | } |
410 | 410 | ||
411 | static inline u32 coh901318_get_bytes_in_lli(struct coh901318_lli *in_lli) | ||
412 | { | ||
413 | struct coh901318_lli *lli = in_lli; | ||
414 | u32 bytes = 0; | ||
415 | |||
416 | while (lli) { | ||
417 | bytes += lli->control & COH901318_CX_CTRL_TC_VALUE_MASK; | ||
418 | lli = lli->virt_link_addr; | ||
419 | } | ||
420 | return bytes; | ||
421 | } | ||
422 | |||
411 | /* | 423 | /* |
412 | * DMA start/stop controls | 424 | * Get the number of bytes left to transfer on this channel, |
425 | * it is unwise to call this before stopping the channel for | ||
426 | * absolute measures, but for a rough guess you can still call | ||
427 | * it. | ||
413 | */ | 428 | */ |
414 | u32 coh901318_get_bytes_left(struct dma_chan *chan) | 429 | u32 coh901318_get_bytes_left(struct dma_chan *chan) |
415 | { | 430 | { |
416 | unsigned long flags; | ||
417 | u32 ret; | ||
418 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | 431 | struct coh901318_chan *cohc = to_coh901318_chan(chan); |
432 | struct coh901318_desc *cohd; | ||
433 | struct list_head *pos; | ||
434 | unsigned long flags; | ||
435 | u32 left = 0; | ||
436 | int i = 0; | ||
419 | 437 | ||
420 | spin_lock_irqsave(&cohc->lock, flags); | 438 | spin_lock_irqsave(&cohc->lock, flags); |
421 | 439 | ||
422 | /* Read transfer count value */ | 440 | /* |
423 | ret = readl(cohc->base->virtbase + | 441 | * If there are many queued jobs, we iterate and add the |
424 | COH901318_CX_CTRL+COH901318_CX_CTRL_SPACING * | 442 | * size of them all. We take a special look on the first |
425 | cohc->id) & COH901318_CX_CTRL_TC_VALUE_MASK; | 443 | * job though, since it is probably active. |
444 | */ | ||
445 | list_for_each(pos, &cohc->active) { | ||
446 | /* | ||
447 | * The first job in the list will be working on the | ||
448 | * hardware. The job can be stopped but still active, | ||
449 | * so that the transfer counter is somewhere inside | ||
450 | * the buffer. | ||
451 | */ | ||
452 | cohd = list_entry(pos, struct coh901318_desc, node); | ||
453 | |||
454 | if (i == 0) { | ||
455 | struct coh901318_lli *lli; | ||
456 | dma_addr_t ladd; | ||
457 | |||
458 | /* Read current transfer count value */ | ||
459 | left = readl(cohc->base->virtbase + | ||
460 | COH901318_CX_CTRL + | ||
461 | COH901318_CX_CTRL_SPACING * cohc->id) & | ||
462 | COH901318_CX_CTRL_TC_VALUE_MASK; | ||
463 | |||
464 | /* See if the transfer is linked... */ | ||
465 | ladd = readl(cohc->base->virtbase + | ||
466 | COH901318_CX_LNK_ADDR + | ||
467 | COH901318_CX_LNK_ADDR_SPACING * | ||
468 | cohc->id) & | ||
469 | ~COH901318_CX_LNK_LINK_IMMEDIATE; | ||
470 | /* Single transaction */ | ||
471 | if (!ladd) | ||
472 | continue; | ||
473 | |||
474 | /* | ||
475 | * Linked transaction, follow the lli, find the | ||
476 | * currently processing lli, and proceed to the next | ||
477 | */ | ||
478 | lli = cohd->lli; | ||
479 | while (lli && lli->link_addr != ladd) | ||
480 | lli = lli->virt_link_addr; | ||
481 | |||
482 | if (lli) | ||
483 | lli = lli->virt_link_addr; | ||
484 | |||
485 | /* | ||
486 | * Follow remaining lli links around to count the total | ||
487 | * number of bytes left | ||
488 | */ | ||
489 | left += coh901318_get_bytes_in_lli(lli); | ||
490 | } else { | ||
491 | left += coh901318_get_bytes_in_lli(cohd->lli); | ||
492 | } | ||
493 | i++; | ||
494 | } | ||
495 | |||
496 | /* Also count bytes in the queued jobs */ | ||
497 | list_for_each(pos, &cohc->queue) { | ||
498 | cohd = list_entry(pos, struct coh901318_desc, node); | ||
499 | left += coh901318_get_bytes_in_lli(cohd->lli); | ||
500 | } | ||
426 | 501 | ||
427 | spin_unlock_irqrestore(&cohc->lock, flags); | 502 | spin_unlock_irqrestore(&cohc->lock, flags); |
428 | 503 | ||
429 | return ret; | 504 | return left; |
430 | } | 505 | } |
431 | EXPORT_SYMBOL(coh901318_get_bytes_left); | 506 | EXPORT_SYMBOL(coh901318_get_bytes_left); |
432 | 507 | ||
@@ -831,6 +906,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id) | |||
831 | static int coh901318_alloc_chan_resources(struct dma_chan *chan) | 906 | static int coh901318_alloc_chan_resources(struct dma_chan *chan) |
832 | { | 907 | { |
833 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | 908 | struct coh901318_chan *cohc = to_coh901318_chan(chan); |
909 | unsigned long flags; | ||
834 | 910 | ||
835 | dev_vdbg(COHC_2_DEV(cohc), "[%s] DMA channel %d\n", | 911 | dev_vdbg(COHC_2_DEV(cohc), "[%s] DMA channel %d\n", |
836 | __func__, cohc->id); | 912 | __func__, cohc->id); |
@@ -838,11 +914,15 @@ static int coh901318_alloc_chan_resources(struct dma_chan *chan) | |||
838 | if (chan->client_count > 1) | 914 | if (chan->client_count > 1) |
839 | return -EBUSY; | 915 | return -EBUSY; |
840 | 916 | ||
917 | spin_lock_irqsave(&cohc->lock, flags); | ||
918 | |||
841 | coh901318_config(cohc, NULL); | 919 | coh901318_config(cohc, NULL); |
842 | 920 | ||
843 | cohc->allocated = 1; | 921 | cohc->allocated = 1; |
844 | cohc->completed = chan->cookie = 1; | 922 | cohc->completed = chan->cookie = 1; |
845 | 923 | ||
924 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
925 | |||
846 | return 1; | 926 | return 1; |
847 | } | 927 | } |
848 | 928 | ||