18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2014-2015 Analog Devices Inc.
48c2ecf20Sopenharmony_ci *  Author: Lars-Peter Clausen <lars@metafoo.de>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
168c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
178c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h>
188c2ecf20Sopenharmony_ci#include <linux/iio/buffer_impl.h>
198c2ecf20Sopenharmony_ci#include <linux/iio/buffer-dma.h>
208c2ecf20Sopenharmony_ci#include <linux/iio/buffer-dmaengine.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * The IIO DMAengine buffer combines the generic IIO DMA buffer infrastructure
248c2ecf20Sopenharmony_ci * with the DMAengine framework. The generic IIO DMA buffer infrastructure is
258c2ecf20Sopenharmony_ci * used to manage the buffer memory and implement the IIO buffer operations
268c2ecf20Sopenharmony_ci * while the DMAengine framework is used to perform the DMA transfers. Combined
278c2ecf20Sopenharmony_ci * this results in a device independent fully functional DMA buffer
288c2ecf20Sopenharmony_ci * implementation that can be used by device drivers for peripherals which are
298c2ecf20Sopenharmony_ci * connected to a DMA controller which has a DMAengine driver implementation.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct dmaengine_buffer {
338c2ecf20Sopenharmony_ci	struct iio_dma_buffer_queue queue;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	struct dma_chan *chan;
368c2ecf20Sopenharmony_ci	struct list_head active;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	size_t align;
398c2ecf20Sopenharmony_ci	size_t max_size;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(
438c2ecf20Sopenharmony_ci		struct iio_buffer *buffer)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return container_of(buffer, struct dmaengine_buffer, queue.buffer);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void iio_dmaengine_buffer_block_done(void *data,
498c2ecf20Sopenharmony_ci		const struct dmaengine_result *result)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct iio_dma_buffer_block *block = data;
528c2ecf20Sopenharmony_ci	unsigned long flags;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&block->queue->list_lock, flags);
558c2ecf20Sopenharmony_ci	list_del(&block->head);
568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&block->queue->list_lock, flags);
578c2ecf20Sopenharmony_ci	block->bytes_used -= result->residue;
588c2ecf20Sopenharmony_ci	iio_dma_buffer_block_done(block);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
628c2ecf20Sopenharmony_ci	struct iio_dma_buffer_block *block)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct dmaengine_buffer *dmaengine_buffer =
658c2ecf20Sopenharmony_ci		iio_buffer_to_dmaengine_buffer(&queue->buffer);
668c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
678c2ecf20Sopenharmony_ci	dma_cookie_t cookie;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	block->bytes_used = min(block->size, dmaengine_buffer->max_size);
708c2ecf20Sopenharmony_ci	block->bytes_used = rounddown(block->bytes_used,
718c2ecf20Sopenharmony_ci			dmaengine_buffer->align);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
748c2ecf20Sopenharmony_ci		block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
758c2ecf20Sopenharmony_ci		DMA_PREP_INTERRUPT);
768c2ecf20Sopenharmony_ci	if (!desc)
778c2ecf20Sopenharmony_ci		return -ENOMEM;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	desc->callback_result = iio_dmaengine_buffer_block_done;
808c2ecf20Sopenharmony_ci	desc->callback_param = block;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	cookie = dmaengine_submit(desc);
838c2ecf20Sopenharmony_ci	if (dma_submit_error(cookie))
848c2ecf20Sopenharmony_ci		return dma_submit_error(cookie);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	spin_lock_irq(&dmaengine_buffer->queue.list_lock);
878c2ecf20Sopenharmony_ci	list_add_tail(&block->head, &dmaengine_buffer->active);
888c2ecf20Sopenharmony_ci	spin_unlock_irq(&dmaengine_buffer->queue.list_lock);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	dma_async_issue_pending(dmaengine_buffer->chan);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void iio_dmaengine_buffer_abort(struct iio_dma_buffer_queue *queue)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct dmaengine_buffer *dmaengine_buffer =
988c2ecf20Sopenharmony_ci		iio_buffer_to_dmaengine_buffer(&queue->buffer);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	dmaengine_terminate_sync(dmaengine_buffer->chan);
1018c2ecf20Sopenharmony_ci	iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void iio_dmaengine_buffer_release(struct iio_buffer *buf)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct dmaengine_buffer *dmaengine_buffer =
1078c2ecf20Sopenharmony_ci		iio_buffer_to_dmaengine_buffer(buf);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	iio_dma_buffer_release(&dmaengine_buffer->queue);
1108c2ecf20Sopenharmony_ci	kfree(dmaengine_buffer);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
1148c2ecf20Sopenharmony_ci	.read = iio_dma_buffer_read,
1158c2ecf20Sopenharmony_ci	.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
1168c2ecf20Sopenharmony_ci	.set_length = iio_dma_buffer_set_length,
1178c2ecf20Sopenharmony_ci	.request_update = iio_dma_buffer_request_update,
1188c2ecf20Sopenharmony_ci	.enable = iio_dma_buffer_enable,
1198c2ecf20Sopenharmony_ci	.disable = iio_dma_buffer_disable,
1208c2ecf20Sopenharmony_ci	.data_available = iio_dma_buffer_data_available,
1218c2ecf20Sopenharmony_ci	.release = iio_dmaengine_buffer_release,
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	.modes = INDIO_BUFFER_HARDWARE,
1248c2ecf20Sopenharmony_ci	.flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
1288c2ecf20Sopenharmony_ci	.submit = iio_dmaengine_buffer_submit_block,
1298c2ecf20Sopenharmony_ci	.abort = iio_dmaengine_buffer_abort,
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
1338c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
1368c2ecf20Sopenharmony_ci	struct dmaengine_buffer *dmaengine_buffer =
1378c2ecf20Sopenharmony_ci		iio_buffer_to_dmaengine_buffer(indio_dev->buffer);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return sprintf(buf, "%zu\n", dmaengine_buffer->align);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(length_align_bytes, 0444,
1438c2ecf20Sopenharmony_ci		       iio_dmaengine_buffer_get_length_align, NULL, 0);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic const struct attribute *iio_dmaengine_buffer_attrs[] = {
1468c2ecf20Sopenharmony_ci	&iio_dev_attr_length_align_bytes.dev_attr.attr,
1478c2ecf20Sopenharmony_ci	NULL,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/**
1518c2ecf20Sopenharmony_ci * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine
1528c2ecf20Sopenharmony_ci * @dev: Parent device for the buffer
1538c2ecf20Sopenharmony_ci * @channel: DMA channel name, typically "rx".
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * This allocates a new IIO buffer which internally uses the DMAengine framework
1568c2ecf20Sopenharmony_ci * to perform its transfers. The parent device will be used to request the DMA
1578c2ecf20Sopenharmony_ci * channel.
1588c2ecf20Sopenharmony_ci *
1598c2ecf20Sopenharmony_ci * Once done using the buffer iio_dmaengine_buffer_free() should be used to
1608c2ecf20Sopenharmony_ci * release it.
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_cistatic struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
1638c2ecf20Sopenharmony_ci	const char *channel)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct dmaengine_buffer *dmaengine_buffer;
1668c2ecf20Sopenharmony_ci	unsigned int width, src_width, dest_width;
1678c2ecf20Sopenharmony_ci	struct dma_slave_caps caps;
1688c2ecf20Sopenharmony_ci	struct dma_chan *chan;
1698c2ecf20Sopenharmony_ci	int ret;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	dmaengine_buffer = kzalloc(sizeof(*dmaengine_buffer), GFP_KERNEL);
1728c2ecf20Sopenharmony_ci	if (!dmaengine_buffer)
1738c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	chan = dma_request_chan(dev, channel);
1768c2ecf20Sopenharmony_ci	if (IS_ERR(chan)) {
1778c2ecf20Sopenharmony_ci		ret = PTR_ERR(chan);
1788c2ecf20Sopenharmony_ci		goto err_free;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	ret = dma_get_slave_caps(chan, &caps);
1828c2ecf20Sopenharmony_ci	if (ret < 0)
1838c2ecf20Sopenharmony_ci		goto err_free;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* Needs to be aligned to the maximum of the minimums */
1868c2ecf20Sopenharmony_ci	if (caps.src_addr_widths)
1878c2ecf20Sopenharmony_ci		src_width = __ffs(caps.src_addr_widths);
1888c2ecf20Sopenharmony_ci	else
1898c2ecf20Sopenharmony_ci		src_width = 1;
1908c2ecf20Sopenharmony_ci	if (caps.dst_addr_widths)
1918c2ecf20Sopenharmony_ci		dest_width = __ffs(caps.dst_addr_widths);
1928c2ecf20Sopenharmony_ci	else
1938c2ecf20Sopenharmony_ci		dest_width = 1;
1948c2ecf20Sopenharmony_ci	width = max(src_width, dest_width);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dmaengine_buffer->active);
1978c2ecf20Sopenharmony_ci	dmaengine_buffer->chan = chan;
1988c2ecf20Sopenharmony_ci	dmaengine_buffer->align = width;
1998c2ecf20Sopenharmony_ci	dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
2028c2ecf20Sopenharmony_ci		&iio_dmaengine_default_ops);
2038c2ecf20Sopenharmony_ci	iio_buffer_set_attrs(&dmaengine_buffer->queue.buffer,
2048c2ecf20Sopenharmony_ci		iio_dmaengine_buffer_attrs);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return &dmaengine_buffer->queue.buffer;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cierr_free:
2118c2ecf20Sopenharmony_ci	kfree(dmaengine_buffer);
2128c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/**
2168c2ecf20Sopenharmony_ci * iio_dmaengine_buffer_free() - Free dmaengine buffer
2178c2ecf20Sopenharmony_ci * @buffer: Buffer to free
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc().
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct dmaengine_buffer *dmaengine_buffer =
2248c2ecf20Sopenharmony_ci		iio_buffer_to_dmaengine_buffer(buffer);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	iio_dma_buffer_exit(&dmaengine_buffer->queue);
2278c2ecf20Sopenharmony_ci	dma_release_channel(dmaengine_buffer->chan);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	iio_buffer_put(buffer);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	iio_dmaengine_buffer_free(*(struct iio_buffer **)res);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/**
2388c2ecf20Sopenharmony_ci * devm_iio_dmaengine_buffer_alloc() - Resource-managed iio_dmaengine_buffer_alloc()
2398c2ecf20Sopenharmony_ci * @dev: Parent device for the buffer
2408c2ecf20Sopenharmony_ci * @channel: DMA channel name, typically "rx".
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * This allocates a new IIO buffer which internally uses the DMAengine framework
2438c2ecf20Sopenharmony_ci * to perform its transfers. The parent device will be used to request the DMA
2448c2ecf20Sopenharmony_ci * channel.
2458c2ecf20Sopenharmony_ci *
2468c2ecf20Sopenharmony_ci * The buffer will be automatically de-allocated once the device gets destroyed.
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_cistruct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
2498c2ecf20Sopenharmony_ci	const char *channel)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct iio_buffer **bufferp, *buffer;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	bufferp = devres_alloc(__devm_iio_dmaengine_buffer_free,
2548c2ecf20Sopenharmony_ci			       sizeof(*bufferp), GFP_KERNEL);
2558c2ecf20Sopenharmony_ci	if (!bufferp)
2568c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	buffer = iio_dmaengine_buffer_alloc(dev, channel);
2598c2ecf20Sopenharmony_ci	if (IS_ERR(buffer)) {
2608c2ecf20Sopenharmony_ci		devres_free(bufferp);
2618c2ecf20Sopenharmony_ci		return buffer;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	*bufferp = buffer;
2658c2ecf20Sopenharmony_ci	devres_add(dev, bufferp);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return buffer;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_alloc);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
2728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DMA buffer for the IIO framework");
2738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
274