Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Linus Walleij and committed by
Dan Williams
84c8447c cecd87da

+88 -8
+88 -8
drivers/dma/coh901318.c
··· 408 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 429 u32 coh901318_get_bytes_left(struct dma_chan *chan) 415 430 { 416 - unsigned long flags; 417 - u32 ret; 418 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 438 spin_lock_irqsave(&cohc->lock, flags); 421 439 422 - /* Read transfer count value */ 423 - ret = readl(cohc->base->virtbase + 424 - COH901318_CX_CTRL+COH901318_CX_CTRL_SPACING * 425 - cohc->id) & COH901318_CX_CTRL_TC_VALUE_MASK; 440 + /* 441 + * If there are many queued jobs, we iterate and add the 442 + * size of them all. We take a special look on the first 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 502 spin_unlock_irqrestore(&cohc->lock, flags); 428 503 429 - return ret; 504 + return left; 430 505 } 431 506 EXPORT_SYMBOL(coh901318_get_bytes_left); 432 507 ··· 906 831 static int coh901318_alloc_chan_resources(struct dma_chan *chan) 907 832 { 908 833 struct coh901318_chan *cohc = to_coh901318_chan(chan); 834 + unsigned long flags; 909 835 910 836 dev_vdbg(COHC_2_DEV(cohc), "[%s] DMA channel %d\n", 911 837 __func__, cohc->id); ··· 914 838 if (chan->client_count > 1) 915 839 return -EBUSY; 916 840 841 + spin_lock_irqsave(&cohc->lock, flags); 842 + 917 843 coh901318_config(cohc, NULL); 918 844 919 845 cohc->allocated = 1; 920 846 cohc->completed = chan->cookie = 1; 847 + 848 + spin_unlock_irqrestore(&cohc->lock, flags); 921 849 922 850 return 1; 923 851 }