aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2015-10-13 12:10:29 -0400
committerJonathan Cameron <jic23@kernel.org>2015-10-25 09:55:32 -0400
commit2d6ca60f328450ff5c7802d0857d12e3711348ce (patch)
tree1e3f39956aef3539819429946cbabf429a7fa1a1
parent670b19ae9bfdbcb4ce2c2ffb2ec1659a7f4a2074 (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/Kconfig11
-rw-r--r--drivers/iio/buffer/Makefile1
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c213
-rw-r--r--include/linux/iio/buffer-dmaengine.h18
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
21config 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
21config IIO_KFIFO_BUF 32config 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
6obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o 6obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
7obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o 7obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o
8obj-$(CONFIG_IIO_BUFFER_DMAENGINE) += industrialio-buffer-dmaengine.o
8obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o 9obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
9obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o 10obj-$(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
30struct 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
40static 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
46static 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
57static 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
91static 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
105static 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
114static 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
128static 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 */
145struct 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
191err_free:
192 kfree(dmaengine_buffer);
193 return ERR_PTR(ret);
194}
195EXPORT_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 */
203void 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}
213EXPORT_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
11struct iio_buffer;
12struct device;
13
14struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
15 const char *channel);
16void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
17
18#endif