diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2015-10-13 12:10:29 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2015-10-25 09:55:32 -0400 |
commit | 2d6ca60f328450ff5c7802d0857d12e3711348ce (patch) | |
tree | 1e3f39956aef3539819429946cbabf429a7fa1a1 | |
parent | 670b19ae9bfdbcb4ce2c2ffb2ec1659a7f4a2074 (diff) |
iio: Add a DMAengine framework based buffer
Add a generic fully device independent DMA buffer implementation that uses
the DMAegnine framework to perform the DMA transfers. This can be used by
converter drivers that whish to provide a DMA buffer for converters that
are connected to a DMA core that implements the DMAengine API.
Apart from allocating the buffer using iio_dmaengine_buffer_alloc() and
freeing it using iio_dmaengine_buffer_free() no additional converter driver
specific code is required when using this DMA buffer implementation.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/buffer/Kconfig | 11 | ||||
-rw-r--r-- | drivers/iio/buffer/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/buffer/industrialio-buffer-dmaengine.c | 213 | ||||
-rw-r--r-- | include/linux/iio/buffer-dmaengine.h | 18 |
4 files changed, 243 insertions, 0 deletions
diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig index b2fda1afc03e..4ffd3db7817f 100644 --- a/drivers/iio/buffer/Kconfig +++ b/drivers/iio/buffer/Kconfig | |||
@@ -18,6 +18,17 @@ config IIO_BUFFER_DMA | |||
18 | Should be selected by drivers that want to use the generic DMA buffer | 18 | Should be selected by drivers that want to use the generic DMA buffer |
19 | infrastructure. | 19 | infrastructure. |
20 | 20 | ||
21 | config IIO_BUFFER_DMAENGINE | ||
22 | tristate | ||
23 | select IIO_BUFFER_DMA | ||
24 | help | ||
25 | Provides a bonding of the generic IIO DMA buffer infrastructure with the | ||
26 | DMAengine framework. This can be used by converter drivers with a DMA port | ||
27 | connected to an external DMA controller which is supported by the | ||
28 | DMAengine framework. | ||
29 | |||
30 | Should be selected by drivers that want to use this functionality. | ||
31 | |||
21 | config IIO_KFIFO_BUF | 32 | config IIO_KFIFO_BUF |
22 | tristate "Industrial I/O buffering based on kfifo" | 33 | tristate "Industrial I/O buffering based on kfifo" |
23 | help | 34 | help |
diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile index bda3f1143e72..85beaae831ae 100644 --- a/drivers/iio/buffer/Makefile +++ b/drivers/iio/buffer/Makefile | |||
@@ -5,5 +5,6 @@ | |||
5 | # When adding new entries keep the list in alphabetical order | 5 | # When adding new entries keep the list in alphabetical order |
6 | obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o | 6 | obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o |
7 | obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o | 7 | obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o |
8 | obj-$(CONFIG_IIO_BUFFER_DMAENGINE) += industrialio-buffer-dmaengine.o | ||
8 | obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o | 9 | obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o |
9 | obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o | 10 | obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o |
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c new file mode 100644 index 000000000000..ebdb838d3a1c --- /dev/null +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Copyright 2014-2015 Analog Devices Inc. | ||
3 | * Author: Lars-Peter Clausen <lars@metafoo.de> | ||
4 | * | ||
5 | * Licensed under the GPL-2 or later. | ||
6 | */ | ||
7 | |||
8 | #include <linux/slab.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/dmaengine.h> | ||
11 | #include <linux/dma-mapping.h> | ||
12 | #include <linux/spinlock.h> | ||
13 | #include <linux/err.h> | ||
14 | |||
15 | #include <linux/iio/iio.h> | ||
16 | #include <linux/iio/buffer.h> | ||
17 | #include <linux/iio/buffer-dma.h> | ||
18 | #include <linux/iio/buffer-dmaengine.h> | ||
19 | |||
20 | /* | ||
21 | * The IIO DMAengine buffer combines the generic IIO DMA buffer infrastructure | ||
22 | * with the DMAengine framework. The generic IIO DMA buffer infrastructure is | ||
23 | * used to manage the buffer memory and implement the IIO buffer operations | ||
24 | * while the DMAengine framework is used to perform the DMA transfers. Combined | ||
25 | * this results in a device independent fully functional DMA buffer | ||
26 | * implementation that can be used by device drivers for peripherals which are | ||
27 | * connected to a DMA controller which has a DMAengine driver implementation. | ||
28 | */ | ||
29 | |||
30 | struct dmaengine_buffer { | ||
31 | struct iio_dma_buffer_queue queue; | ||
32 | |||
33 | struct dma_chan *chan; | ||
34 | struct list_head active; | ||
35 | |||
36 | size_t align; | ||
37 | size_t max_size; | ||
38 | }; | ||
39 | |||
40 | static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer( | ||
41 | struct iio_buffer *buffer) | ||
42 | { | ||
43 | return container_of(buffer, struct dmaengine_buffer, queue.buffer); | ||
44 | } | ||
45 | |||
46 | static void iio_dmaengine_buffer_block_done(void *data) | ||
47 | { | ||
48 | struct iio_dma_buffer_block *block = data; | ||
49 | unsigned long flags; | ||
50 | |||
51 | spin_lock_irqsave(&block->queue->list_lock, flags); | ||
52 | list_del(&block->head); | ||
53 | spin_unlock_irqrestore(&block->queue->list_lock, flags); | ||
54 | iio_dma_buffer_block_done(block); | ||
55 | } | ||
56 | |||
57 | static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, | ||
58 | struct iio_dma_buffer_block *block) | ||
59 | { | ||
60 | struct dmaengine_buffer *dmaengine_buffer = | ||
61 | iio_buffer_to_dmaengine_buffer(&queue->buffer); | ||
62 | struct dma_async_tx_descriptor *desc; | ||
63 | dma_cookie_t cookie; | ||
64 | |||
65 | block->bytes_used = min(block->size, dmaengine_buffer->max_size); | ||
66 | block->bytes_used = rounddown(block->bytes_used, | ||
67 | dmaengine_buffer->align); | ||
68 | |||
69 | desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, | ||
70 | block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM, | ||
71 | DMA_PREP_INTERRUPT); | ||
72 | if (!desc) | ||
73 | return -ENOMEM; | ||
74 | |||
75 | desc->callback = iio_dmaengine_buffer_block_done; | ||
76 | desc->callback_param = block; | ||
77 | |||
78 | cookie = dmaengine_submit(desc); | ||
79 | if (dma_submit_error(cookie)) | ||
80 | return dma_submit_error(cookie); | ||
81 | |||
82 | spin_lock_irq(&dmaengine_buffer->queue.list_lock); | ||
83 | list_add_tail(&block->head, &dmaengine_buffer->active); | ||
84 | spin_unlock_irq(&dmaengine_buffer->queue.list_lock); | ||
85 | |||
86 | dma_async_issue_pending(dmaengine_buffer->chan); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void iio_dmaengine_buffer_abort(struct iio_dma_buffer_queue *queue) | ||
92 | { | ||
93 | struct dmaengine_buffer *dmaengine_buffer = | ||
94 | iio_buffer_to_dmaengine_buffer(&queue->buffer); | ||
95 | |||
96 | dmaengine_terminate_all(dmaengine_buffer->chan); | ||
97 | /* FIXME: There is a slight chance of a race condition here. | ||
98 | * dmaengine_terminate_all() does not guarantee that all transfer | ||
99 | * callbacks have finished running. Need to introduce a | ||
100 | * dmaengine_terminate_all_sync(). | ||
101 | */ | ||
102 | iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active); | ||
103 | } | ||
104 | |||
105 | static void iio_dmaengine_buffer_release(struct iio_buffer *buf) | ||
106 | { | ||
107 | struct dmaengine_buffer *dmaengine_buffer = | ||
108 | iio_buffer_to_dmaengine_buffer(buf); | ||
109 | |||
110 | iio_dma_buffer_release(&dmaengine_buffer->queue); | ||
111 | kfree(dmaengine_buffer); | ||
112 | } | ||
113 | |||
114 | static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = { | ||
115 | .read_first_n = iio_dma_buffer_read, | ||
116 | .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum, | ||
117 | .set_length = iio_dma_buffer_set_length, | ||
118 | .request_update = iio_dma_buffer_request_update, | ||
119 | .enable = iio_dma_buffer_enable, | ||
120 | .disable = iio_dma_buffer_disable, | ||
121 | .data_available = iio_dma_buffer_data_available, | ||
122 | .release = iio_dmaengine_buffer_release, | ||
123 | |||
124 | .modes = INDIO_BUFFER_HARDWARE, | ||
125 | .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK, | ||
126 | }; | ||
127 | |||
128 | static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = { | ||
129 | .submit = iio_dmaengine_buffer_submit_block, | ||
130 | .abort = iio_dmaengine_buffer_abort, | ||
131 | }; | ||
132 | |||
133 | /** | ||
134 | * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine | ||
135 | * @dev: Parent device for the buffer | ||
136 | * @channel: DMA channel name, typically "rx". | ||
137 | * | ||
138 | * This allocates a new IIO buffer which internally uses the DMAengine framework | ||
139 | * to perform its transfers. The parent device will be used to request the DMA | ||
140 | * channel. | ||
141 | * | ||
142 | * Once done using the buffer iio_dmaengine_buffer_free() should be used to | ||
143 | * release it. | ||
144 | */ | ||
145 | struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, | ||
146 | const char *channel) | ||
147 | { | ||
148 | struct dmaengine_buffer *dmaengine_buffer; | ||
149 | unsigned int width, src_width, dest_width; | ||
150 | struct dma_slave_caps caps; | ||
151 | struct dma_chan *chan; | ||
152 | int ret; | ||
153 | |||
154 | dmaengine_buffer = kzalloc(sizeof(*dmaengine_buffer), GFP_KERNEL); | ||
155 | if (!dmaengine_buffer) | ||
156 | return ERR_PTR(-ENOMEM); | ||
157 | |||
158 | chan = dma_request_slave_channel_reason(dev, channel); | ||
159 | if (IS_ERR(chan)) { | ||
160 | ret = PTR_ERR(chan); | ||
161 | goto err_free; | ||
162 | } | ||
163 | |||
164 | ret = dma_get_slave_caps(chan, &caps); | ||
165 | if (ret < 0) | ||
166 | goto err_free; | ||
167 | |||
168 | /* Needs to be aligned to the maximum of the minimums */ | ||
169 | if (caps.src_addr_widths) | ||
170 | src_width = __ffs(caps.src_addr_widths); | ||
171 | else | ||
172 | src_width = 1; | ||
173 | if (caps.dst_addr_widths) | ||
174 | dest_width = __ffs(caps.dst_addr_widths); | ||
175 | else | ||
176 | dest_width = 1; | ||
177 | width = max(src_width, dest_width); | ||
178 | |||
179 | INIT_LIST_HEAD(&dmaengine_buffer->active); | ||
180 | dmaengine_buffer->chan = chan; | ||
181 | dmaengine_buffer->align = width; | ||
182 | dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev); | ||
183 | |||
184 | iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev, | ||
185 | &iio_dmaengine_default_ops); | ||
186 | |||
187 | dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops; | ||
188 | |||
189 | return &dmaengine_buffer->queue.buffer; | ||
190 | |||
191 | err_free: | ||
192 | kfree(dmaengine_buffer); | ||
193 | return ERR_PTR(ret); | ||
194 | } | ||
195 | EXPORT_SYMBOL(iio_dmaengine_buffer_alloc); | ||
196 | |||
197 | /** | ||
198 | * iio_dmaengine_buffer_free() - Free dmaengine buffer | ||
199 | * @buffer: Buffer to free | ||
200 | * | ||
201 | * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc(). | ||
202 | */ | ||
203 | void iio_dmaengine_buffer_free(struct iio_buffer *buffer) | ||
204 | { | ||
205 | struct dmaengine_buffer *dmaengine_buffer = | ||
206 | iio_buffer_to_dmaengine_buffer(buffer); | ||
207 | |||
208 | iio_dma_buffer_exit(&dmaengine_buffer->queue); | ||
209 | dma_release_channel(dmaengine_buffer->chan); | ||
210 | |||
211 | iio_buffer_put(buffer); | ||
212 | } | ||
213 | EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free); | ||
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h new file mode 100644 index 000000000000..5dcddf427bb0 --- /dev/null +++ b/include/linux/iio/buffer-dmaengine.h | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * Copyright 2014-2015 Analog Devices Inc. | ||
3 | * Author: Lars-Peter Clausen <lars@metafoo.de> | ||
4 | * | ||
5 | * Licensed under the GPL-2 or later. | ||
6 | */ | ||
7 | |||
8 | #ifndef __IIO_DMAENGINE_H__ | ||
9 | #define __IIO_DMAENGINE_H__ | ||
10 | |||
11 | struct iio_buffer; | ||
12 | struct device; | ||
13 | |||
14 | struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, | ||
15 | const char *channel); | ||
16 | void iio_dmaengine_buffer_free(struct iio_buffer *buffer); | ||
17 | |||
18 | #endif | ||