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 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 | */ | ||
| 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 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 | ||
| 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 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 | */ |
| 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 | /** |
