aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIra Snyder <iws@ovro.caltech.edu>2010-09-30 07:46:46 -0400
committerDan Williams <dan.j.williams@intel.com>2010-10-07 17:41:41 -0400
commit968f19ae802fdc6b6b6b5af6fe79cf23d281be0f (patch)
tree122f3912ed717627b0e5bac8c72f42ef2eb0cb6e
parentc14330417ef2050f4bf38ac20e125785fea14351 (diff)
fsldma: improved DMA_SLAVE support
Now that the generic DMAEngine API has support for scatterlist to scatterlist copying, the device_prep_slave_sg() portion of the DMA_SLAVE API is no longer necessary and has been removed. However, the device_control() portion of the DMA_SLAVE API is still useful to control device specific parameters, such as externally controlled DMA transfers and maximum burst length. A special dma_ctrl_cmd has been added to enable externally controlled DMA transfers. This is currently specific to the Freescale DMA controller, but can easily be made generic when another user is found. Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--arch/powerpc/include/asm/fsldma.h137
-rw-r--r--drivers/dma/fsldma.c226
-rw-r--r--include/linux/dmaengine.h3
3 files changed, 47 insertions, 319 deletions
diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
deleted file mode 100644
index debc5ed96d6e..000000000000
--- a/arch/powerpc/include/asm/fsldma.h
+++ /dev/null
@@ -1,137 +0,0 @@
1/*
2 * Freescale MPC83XX / MPC85XX DMA Controller
3 *
4 * Copyright (c) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
5 *
6 * This file is licensed under the terms of the GNU General Public License
7 * version 2. This program is licensed "as is" without any warranty of any
8 * kind, whether express or implied.
9 */
10
11#ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
12#define __ARCH_POWERPC_ASM_FSLDMA_H__
13
14#include <linux/slab.h>
15#include <linux/dmaengine.h>
16
17/*
18 * Definitions for the Freescale DMA controller's DMA_SLAVE implemention
19 *
20 * The Freescale DMA_SLAVE implementation was designed to handle many-to-many
21 * transfers. An example usage would be an accelerated copy between two
22 * scatterlists. Another example use would be an accelerated copy from
23 * multiple non-contiguous device buffers into a single scatterlist.
24 *
25 * A DMA_SLAVE transaction is defined by a struct fsl_dma_slave. This
26 * structure contains a list of hardware addresses that should be copied
27 * to/from the scatterlist passed into device_prep_slave_sg(). The structure
28 * also has some fields to enable hardware-specific features.
29 */
30
31/**
32 * struct fsl_dma_hw_addr
33 * @entry: linked list entry
34 * @address: the hardware address
35 * @length: length to transfer
36 *
37 * Holds a single physical hardware address / length pair for use
38 * with the DMAEngine DMA_SLAVE API.
39 */
40struct fsl_dma_hw_addr {
41 struct list_head entry;
42
43 dma_addr_t address;
44 size_t length;
45};
46
47/**
48 * struct fsl_dma_slave
49 * @addresses: a linked list of struct fsl_dma_hw_addr structures
50 * @request_count: value for DMA request count
51 * @src_loop_size: setup and enable constant source-address DMA transfers
52 * @dst_loop_size: setup and enable constant destination address DMA transfers
53 * @external_start: enable externally started DMA transfers
54 * @external_pause: enable externally paused DMA transfers
55 *
56 * Holds a list of address / length pairs for use with the DMAEngine
57 * DMA_SLAVE API implementation for the Freescale DMA controller.
58 */
59struct fsl_dma_slave {
60
61 /* List of hardware address/length pairs */
62 struct list_head addresses;
63
64 /* Support for extra controller features */
65 unsigned int request_count;
66 unsigned int src_loop_size;
67 unsigned int dst_loop_size;
68 bool external_start;
69 bool external_pause;
70};
71
72/**
73 * fsl_dma_slave_append - add an address/length pair to a struct fsl_dma_slave
74 * @slave: the &struct fsl_dma_slave to add to
75 * @address: the hardware address to add
76 * @length: the length of bytes to transfer from @address
77 *
78 * Add a hardware address/length pair to a struct fsl_dma_slave. Returns 0 on
79 * success, -ERRNO otherwise.
80 */
81static inline int fsl_dma_slave_append(struct fsl_dma_slave *slave,
82 dma_addr_t address, size_t length)
83{
84 struct fsl_dma_hw_addr *addr;
85
86 addr = kzalloc(sizeof(*addr), GFP_ATOMIC);
87 if (!addr)
88 return -ENOMEM;
89
90 INIT_LIST_HEAD(&addr->entry);
91 addr->address = address;
92 addr->length = length;
93
94 list_add_tail(&addr->entry, &slave->addresses);
95 return 0;
96}
97
98/**
99 * fsl_dma_slave_free - free a struct fsl_dma_slave
100 * @slave: the struct fsl_dma_slave to free
101 *
102 * Free a struct fsl_dma_slave and all associated address/length pairs
103 */
104static inline void fsl_dma_slave_free(struct fsl_dma_slave *slave)
105{
106 struct fsl_dma_hw_addr *addr, *tmp;
107
108 if (slave) {
109 list_for_each_entry_safe(addr, tmp, &slave->addresses, entry) {
110 list_del(&addr->entry);
111 kfree(addr);
112 }
113
114 kfree(slave);
115 }
116}
117
118/**
119 * fsl_dma_slave_alloc - allocate a struct fsl_dma_slave
120 * @gfp: the flags to pass to kmalloc when allocating this structure
121 *
122 * Allocate a struct fsl_dma_slave for use by the DMA_SLAVE API. Returns a new
123 * struct fsl_dma_slave on success, or NULL on failure.
124 */
125static inline struct fsl_dma_slave *fsl_dma_slave_alloc(gfp_t gfp)
126{
127 struct fsl_dma_slave *slave;
128
129 slave = kzalloc(sizeof(*slave), gfp);
130 if (!slave)
131 return NULL;
132
133 INIT_LIST_HEAD(&slave->addresses);
134 return slave;
135}
136
137#endif /* __ARCH_POWERPC_ASM_FSLDMA_H__ */
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 1ed29d10a5fa..286c3ac6bdcc 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -35,7 +35,6 @@
35#include <linux/dmapool.h> 35#include <linux/dmapool.h>
36#include <linux/of_platform.h> 36#include <linux/of_platform.h>
37 37
38#include <asm/fsldma.h>
39#include "fsldma.h" 38#include "fsldma.h"
40 39
41static const char msg_ld_oom[] = "No free memory for link descriptor\n"; 40static const char msg_ld_oom[] = "No free memory for link descriptor\n";
@@ -719,207 +718,70 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
719 struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len, 718 struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len,
720 enum dma_data_direction direction, unsigned long flags) 719 enum dma_data_direction direction, unsigned long flags)
721{ 720{
722 struct fsldma_chan *chan;
723 struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
724 struct fsl_dma_slave *slave;
725 size_t copy;
726
727 int i;
728 struct scatterlist *sg;
729 size_t sg_used;
730 size_t hw_used;
731 struct fsl_dma_hw_addr *hw;
732 dma_addr_t dma_dst, dma_src;
733
734 if (!dchan)
735 return NULL;
736
737 if (!dchan->private)
738 return NULL;
739
740 chan = to_fsl_chan(dchan);
741 slave = dchan->private;
742
743 if (list_empty(&slave->addresses))
744 return NULL;
745
746 hw = list_first_entry(&slave->addresses, struct fsl_dma_hw_addr, entry);
747 hw_used = 0;
748
749 /* 721 /*
750 * Build the hardware transaction to copy from the scatterlist to 722 * This operation is not supported on the Freescale DMA controller
751 * the hardware, or from the hardware to the scatterlist
752 * 723 *
753 * If you are copying from the hardware to the scatterlist and it 724 * However, we need to provide the function pointer to allow the
754 * takes two hardware entries to fill an entire page, then both 725 * device_control() method to work.
755 * hardware entries will be coalesced into the same page
756 *
757 * If you are copying from the scatterlist to the hardware and a
758 * single page can fill two hardware entries, then the data will
759 * be read out of the page into the first hardware entry, and so on
760 */ 726 */
761 for_each_sg(sgl, sg, sg_len, i) {
762 sg_used = 0;
763
764 /* Loop until the entire scatterlist entry is used */
765 while (sg_used < sg_dma_len(sg)) {
766
767 /*
768 * If we've used up the current hardware address/length
769 * pair, we need to load a new one
770 *
771 * This is done in a while loop so that descriptors with
772 * length == 0 will be skipped
773 */
774 while (hw_used >= hw->length) {
775
776 /*
777 * If the current hardware entry is the last
778 * entry in the list, we're finished
779 */
780 if (list_is_last(&hw->entry, &slave->addresses))
781 goto finished;
782
783 /* Get the next hardware address/length pair */
784 hw = list_entry(hw->entry.next,
785 struct fsl_dma_hw_addr, entry);
786 hw_used = 0;
787 }
788
789 /* Allocate the link descriptor from DMA pool */
790 new = fsl_dma_alloc_descriptor(chan);
791 if (!new) {
792 dev_err(chan->dev, "No free memory for "
793 "link descriptor\n");
794 goto fail;
795 }
796#ifdef FSL_DMA_LD_DEBUG
797 dev_dbg(chan->dev, "new link desc alloc %p\n", new);
798#endif
799
800 /*
801 * Calculate the maximum number of bytes to transfer,
802 * making sure it is less than the DMA controller limit
803 */
804 copy = min_t(size_t, sg_dma_len(sg) - sg_used,
805 hw->length - hw_used);
806 copy = min_t(size_t, copy, FSL_DMA_BCR_MAX_CNT);
807
808 /*
809 * DMA_FROM_DEVICE
810 * from the hardware to the scatterlist
811 *
812 * DMA_TO_DEVICE
813 * from the scatterlist to the hardware
814 */
815 if (direction == DMA_FROM_DEVICE) {
816 dma_src = hw->address + hw_used;
817 dma_dst = sg_dma_address(sg) + sg_used;
818 } else {
819 dma_src = sg_dma_address(sg) + sg_used;
820 dma_dst = hw->address + hw_used;
821 }
822
823 /* Fill in the descriptor */
824 set_desc_cnt(chan, &new->hw, copy);
825 set_desc_src(chan, &new->hw, dma_src);
826 set_desc_dst(chan, &new->hw, dma_dst);
827
828 /*
829 * If this is not the first descriptor, chain the
830 * current descriptor after the previous descriptor
831 */
832 if (!first) {
833 first = new;
834 } else {
835 set_desc_next(chan, &prev->hw,
836 new->async_tx.phys);
837 }
838
839 new->async_tx.cookie = 0;
840 async_tx_ack(&new->async_tx);
841
842 prev = new;
843 sg_used += copy;
844 hw_used += copy;
845
846 /* Insert the link descriptor into the LD ring */
847 list_add_tail(&new->node, &first->tx_list);
848 }
849 }
850
851finished:
852
853 /* All of the hardware address/length pairs had length == 0 */
854 if (!first || !new)
855 return NULL;
856
857 new->async_tx.flags = flags;
858 new->async_tx.cookie = -EBUSY;
859
860 /* Set End-of-link to the last link descriptor of new list */
861 set_ld_eol(chan, new);
862
863 /* Enable extra controller features */
864 if (chan->set_src_loop_size)
865 chan->set_src_loop_size(chan, slave->src_loop_size);
866
867 if (chan->set_dst_loop_size)
868 chan->set_dst_loop_size(chan, slave->dst_loop_size);
869
870 if (chan->toggle_ext_start)
871 chan->toggle_ext_start(chan, slave->external_start);
872
873 if (chan->toggle_ext_pause)
874 chan->toggle_ext_pause(chan, slave->external_pause);
875
876 if (chan->set_request_count)
877 chan->set_request_count(chan, slave->request_count);
878
879 return &first->async_tx;
880
881fail:
882 /* If first was not set, then we failed to allocate the very first
883 * descriptor, and we're done */
884 if (!first)
885 return NULL;
886
887 /*
888 * First is set, so all of the descriptors we allocated have been added
889 * to first->tx_list, INCLUDING "first" itself. Therefore we
890 * must traverse the list backwards freeing each descriptor in turn
891 *
892 * We're re-using variables for the loop, oh well
893 */
894 fsldma_free_desc_list_reverse(chan, &first->tx_list);
895 return NULL; 727 return NULL;
896} 728}
897 729
898static int fsl_dma_device_control(struct dma_chan *dchan, 730static int fsl_dma_device_control(struct dma_chan *dchan,
899 enum dma_ctrl_cmd cmd, unsigned long arg) 731 enum dma_ctrl_cmd cmd, unsigned long arg)
900{ 732{
733 struct dma_slave_config *config;
901 struct fsldma_chan *chan; 734 struct fsldma_chan *chan;
902 unsigned long flags; 735 unsigned long flags;
903 736 int size;
904 /* Only supports DMA_TERMINATE_ALL */
905 if (cmd != DMA_TERMINATE_ALL)
906 return -ENXIO;
907 737
908 if (!dchan) 738 if (!dchan)
909 return -EINVAL; 739 return -EINVAL;
910 740
911 chan = to_fsl_chan(dchan); 741 chan = to_fsl_chan(dchan);
912 742
913 /* Halt the DMA engine */ 743 switch (cmd) {
914 dma_halt(chan); 744 case DMA_TERMINATE_ALL:
745 /* Halt the DMA engine */
746 dma_halt(chan);
915 747
916 spin_lock_irqsave(&chan->desc_lock, flags); 748 spin_lock_irqsave(&chan->desc_lock, flags);
917 749
918 /* Remove and free all of the descriptors in the LD queue */ 750 /* Remove and free all of the descriptors in the LD queue */
919 fsldma_free_desc_list(chan, &chan->ld_pending); 751 fsldma_free_desc_list(chan, &chan->ld_pending);
920 fsldma_free_desc_list(chan, &chan->ld_running); 752 fsldma_free_desc_list(chan, &chan->ld_running);
921 753
922 spin_unlock_irqrestore(&chan->desc_lock, flags); 754 spin_unlock_irqrestore(&chan->desc_lock, flags);
755 return 0;
756
757 case DMA_SLAVE_CONFIG:
758 config = (struct dma_slave_config *)arg;
759
760 /* make sure the channel supports setting burst size */
761 if (!chan->set_request_count)
762 return -ENXIO;
763
764 /* we set the controller burst size depending on direction */
765 if (config->direction == DMA_TO_DEVICE)
766 size = config->dst_addr_width * config->dst_maxburst;
767 else
768 size = config->src_addr_width * config->src_maxburst;
769
770 chan->set_request_count(chan, size);
771 return 0;
772
773 case FSLDMA_EXTERNAL_START:
774
775 /* make sure the channel supports external start */
776 if (!chan->toggle_ext_start)
777 return -ENXIO;
778
779 chan->toggle_ext_start(chan, arg);
780 return 0;
781
782 default:
783 return -ENXIO;
784 }
923 785
924 return 0; 786 return 0;
925} 787}
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 2c9ee98f6c77..885f35211675 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -120,12 +120,15 @@ enum dma_ctrl_flags {
120 * configuration data in statically from the platform). An additional 120 * configuration data in statically from the platform). An additional
121 * argument of struct dma_slave_config must be passed in with this 121 * argument of struct dma_slave_config must be passed in with this
122 * command. 122 * command.
123 * @FSLDMA_EXTERNAL_START: this command will put the Freescale DMA controller
124 * into external start mode.
123 */ 125 */
124enum dma_ctrl_cmd { 126enum dma_ctrl_cmd {
125 DMA_TERMINATE_ALL, 127 DMA_TERMINATE_ALL,
126 DMA_PAUSE, 128 DMA_PAUSE,
127 DMA_RESUME, 129 DMA_RESUME,
128 DMA_SLAVE_CONFIG, 130 DMA_SLAVE_CONFIG,
131 FSLDMA_EXTERNAL_START,
129}; 132};
130 133
131/** 134/**