diff options
-rw-r--r-- | arch/powerpc/include/asm/fsldma.h | 137 | ||||
-rw-r--r-- | drivers/dma/fsldma.c | 226 | ||||
-rw-r--r-- | include/linux/dmaengine.h | 3 |
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 debc5ed96d6..00000000000 --- 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 | */ | ||
40 | struct 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 | */ | ||
59 | struct 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 | */ | ||
81 | static 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 | */ | ||
104 | static 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 | */ | ||
125 | static 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 1ed29d10a5f..286c3ac6bdc 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 | ||
41 | static const char msg_ld_oom[] = "No free memory for link descriptor\n"; | 40 | static 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 | |||
851 | finished: | ||
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 | |||
881 | fail: | ||
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 | ||
898 | static int fsl_dma_device_control(struct dma_chan *dchan, | 730 | static 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 2c9ee98f6c7..885f3521167 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 | */ |
124 | enum dma_ctrl_cmd { | 126 | enum 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 | /** |