diff options
author | Ira Snyder <iws@ovro.caltech.edu> | 2010-09-30 07:46:45 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2010-10-07 17:41:41 -0400 |
commit | c14330417ef2050f4bf38ac20e125785fea14351 (patch) | |
tree | 0b8352bd377d873feff3ec684c0c027f89aa7b14 /drivers/dma/fsldma.c | |
parent | a86ee03ce6f279ebe581a7a8c0c4393eaeb789ee (diff) |
fsldma: implement support for scatterlist to scatterlist copy
Now that the DMAEngine API has support for scatterlist to scatterlist
copy, implement support for the Freescale DMA controller.
Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/dma/fsldma.c')
-rw-r--r-- | drivers/dma/fsldma.c | 128 |
1 files changed, 125 insertions, 3 deletions
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index cea08bed9cf9..1ed29d10a5fa 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c | |||
@@ -38,6 +38,8 @@ | |||
38 | #include <asm/fsldma.h> | 38 | #include <asm/fsldma.h> |
39 | #include "fsldma.h" | 39 | #include "fsldma.h" |
40 | 40 | ||
41 | static const char msg_ld_oom[] = "No free memory for link descriptor\n"; | ||
42 | |||
41 | static void dma_init(struct fsldma_chan *chan) | 43 | static void dma_init(struct fsldma_chan *chan) |
42 | { | 44 | { |
43 | /* Reset the channel */ | 45 | /* Reset the channel */ |
@@ -499,7 +501,7 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags) | |||
499 | 501 | ||
500 | new = fsl_dma_alloc_descriptor(chan); | 502 | new = fsl_dma_alloc_descriptor(chan); |
501 | if (!new) { | 503 | if (!new) { |
502 | dev_err(chan->dev, "No free memory for link descriptor\n"); | 504 | dev_err(chan->dev, msg_ld_oom); |
503 | return NULL; | 505 | return NULL; |
504 | } | 506 | } |
505 | 507 | ||
@@ -536,8 +538,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( | |||
536 | /* Allocate the link descriptor from DMA pool */ | 538 | /* Allocate the link descriptor from DMA pool */ |
537 | new = fsl_dma_alloc_descriptor(chan); | 539 | new = fsl_dma_alloc_descriptor(chan); |
538 | if (!new) { | 540 | if (!new) { |
539 | dev_err(chan->dev, | 541 | dev_err(chan->dev, msg_ld_oom); |
540 | "No free memory for link descriptor\n"); | ||
541 | goto fail; | 542 | goto fail; |
542 | } | 543 | } |
543 | #ifdef FSL_DMA_LD_DEBUG | 544 | #ifdef FSL_DMA_LD_DEBUG |
@@ -583,6 +584,125 @@ fail: | |||
583 | return NULL; | 584 | return NULL; |
584 | } | 585 | } |
585 | 586 | ||
587 | static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan, | ||
588 | struct scatterlist *dst_sg, unsigned int dst_nents, | ||
589 | struct scatterlist *src_sg, unsigned int src_nents, | ||
590 | unsigned long flags) | ||
591 | { | ||
592 | struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL; | ||
593 | struct fsldma_chan *chan = to_fsl_chan(dchan); | ||
594 | size_t dst_avail, src_avail; | ||
595 | dma_addr_t dst, src; | ||
596 | size_t len; | ||
597 | |||
598 | /* basic sanity checks */ | ||
599 | if (dst_nents == 0 || src_nents == 0) | ||
600 | return NULL; | ||
601 | |||
602 | if (dst_sg == NULL || src_sg == NULL) | ||
603 | return NULL; | ||
604 | |||
605 | /* | ||
606 | * TODO: should we check that both scatterlists have the same | ||
607 | * TODO: number of bytes in total? Is that really an error? | ||
608 | */ | ||
609 | |||
610 | /* get prepared for the loop */ | ||
611 | dst_avail = sg_dma_len(dst_sg); | ||
612 | src_avail = sg_dma_len(src_sg); | ||
613 | |||
614 | /* run until we are out of scatterlist entries */ | ||
615 | while (true) { | ||
616 | |||
617 | /* create the largest transaction possible */ | ||
618 | len = min_t(size_t, src_avail, dst_avail); | ||
619 | len = min_t(size_t, len, FSL_DMA_BCR_MAX_CNT); | ||
620 | if (len == 0) | ||
621 | goto fetch; | ||
622 | |||
623 | dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail; | ||
624 | src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail; | ||
625 | |||
626 | /* allocate and populate the descriptor */ | ||
627 | new = fsl_dma_alloc_descriptor(chan); | ||
628 | if (!new) { | ||
629 | dev_err(chan->dev, msg_ld_oom); | ||
630 | goto fail; | ||
631 | } | ||
632 | #ifdef FSL_DMA_LD_DEBUG | ||
633 | dev_dbg(chan->dev, "new link desc alloc %p\n", new); | ||
634 | #endif | ||
635 | |||
636 | set_desc_cnt(chan, &new->hw, len); | ||
637 | set_desc_src(chan, &new->hw, src); | ||
638 | set_desc_dst(chan, &new->hw, dst); | ||
639 | |||
640 | if (!first) | ||
641 | first = new; | ||
642 | else | ||
643 | set_desc_next(chan, &prev->hw, new->async_tx.phys); | ||
644 | |||
645 | new->async_tx.cookie = 0; | ||
646 | async_tx_ack(&new->async_tx); | ||
647 | prev = new; | ||
648 | |||
649 | /* Insert the link descriptor to the LD ring */ | ||
650 | list_add_tail(&new->node, &first->tx_list); | ||
651 | |||
652 | /* update metadata */ | ||
653 | dst_avail -= len; | ||
654 | src_avail -= len; | ||
655 | |||
656 | fetch: | ||
657 | /* fetch the next dst scatterlist entry */ | ||
658 | if (dst_avail == 0) { | ||
659 | |||
660 | /* no more entries: we're done */ | ||
661 | if (dst_nents == 0) | ||
662 | break; | ||
663 | |||
664 | /* fetch the next entry: if there are no more: done */ | ||
665 | dst_sg = sg_next(dst_sg); | ||
666 | if (dst_sg == NULL) | ||
667 | break; | ||
668 | |||
669 | dst_nents--; | ||
670 | dst_avail = sg_dma_len(dst_sg); | ||
671 | } | ||
672 | |||
673 | /* fetch the next src scatterlist entry */ | ||
674 | if (src_avail == 0) { | ||
675 | |||
676 | /* no more entries: we're done */ | ||
677 | if (src_nents == 0) | ||
678 | break; | ||
679 | |||
680 | /* fetch the next entry: if there are no more: done */ | ||
681 | src_sg = sg_next(src_sg); | ||
682 | if (src_sg == NULL) | ||
683 | break; | ||
684 | |||
685 | src_nents--; | ||
686 | src_avail = sg_dma_len(src_sg); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | new->async_tx.flags = flags; /* client is in control of this ack */ | ||
691 | new->async_tx.cookie = -EBUSY; | ||
692 | |||
693 | /* Set End-of-link to the last link descriptor of new list */ | ||
694 | set_ld_eol(chan, new); | ||
695 | |||
696 | return &first->async_tx; | ||
697 | |||
698 | fail: | ||
699 | if (!first) | ||
700 | return NULL; | ||
701 | |||
702 | fsldma_free_desc_list_reverse(chan, &first->tx_list); | ||
703 | return NULL; | ||
704 | } | ||
705 | |||
586 | /** | 706 | /** |
587 | * fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction | 707 | * fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction |
588 | * @chan: DMA channel | 708 | * @chan: DMA channel |
@@ -1327,11 +1447,13 @@ static int __devinit fsldma_of_probe(struct platform_device *op, | |||
1327 | 1447 | ||
1328 | dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask); | 1448 | dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask); |
1329 | dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask); | 1449 | dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask); |
1450 | dma_cap_set(DMA_SG, fdev->common.cap_mask); | ||
1330 | dma_cap_set(DMA_SLAVE, fdev->common.cap_mask); | 1451 | dma_cap_set(DMA_SLAVE, fdev->common.cap_mask); |
1331 | fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources; | 1452 | fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources; |
1332 | fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources; | 1453 | fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources; |
1333 | fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt; | 1454 | fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt; |
1334 | fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy; | 1455 | fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy; |
1456 | fdev->common.device_prep_dma_sg = fsl_dma_prep_sg; | ||
1335 | fdev->common.device_tx_status = fsl_tx_status; | 1457 | fdev->common.device_tx_status = fsl_tx_status; |
1336 | fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; | 1458 | fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending; |
1337 | fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg; | 1459 | fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg; |