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