2019-05-24 12:04:09 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-10-13 18:10:29 +02:00
/*
* Copyright 2014 - 2015 Analog Devices Inc .
* Author : Lars - Peter Clausen < lars @ metafoo . de >
*/
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/dmaengine.h>
# include <linux/dma-mapping.h>
# include <linux/spinlock.h>
# include <linux/err.h>
2019-12-11 12:41:47 +02:00
# include <linux/module.h>
2015-10-13 18:10:29 +02:00
# include <linux/iio/iio.h>
2019-12-11 13:56:15 +02:00
# include <linux/iio/sysfs.h>
2015-10-13 18:10:29 +02:00
# include <linux/iio/buffer.h>
2017-06-13 13:12:35 +08:00
# include <linux/iio/buffer_impl.h>
2015-10-13 18:10:29 +02:00
# include <linux/iio/buffer-dma.h>
# include <linux/iio/buffer-dmaengine.h>
/*
* The IIO DMAengine buffer combines the generic IIO DMA buffer infrastructure
* with the DMAengine framework . The generic IIO DMA buffer infrastructure is
* used to manage the buffer memory and implement the IIO buffer operations
* while the DMAengine framework is used to perform the DMA transfers . Combined
* this results in a device independent fully functional DMA buffer
* implementation that can be used by device drivers for peripherals which are
* connected to a DMA controller which has a DMAengine driver implementation .
*/
struct dmaengine_buffer {
struct iio_dma_buffer_queue queue ;
struct dma_chan * chan ;
struct list_head active ;
size_t align ;
size_t max_size ;
} ;
static struct dmaengine_buffer * iio_buffer_to_dmaengine_buffer (
struct iio_buffer * buffer )
{
return container_of ( buffer , struct dmaengine_buffer , queue . buffer ) ;
}
2020-08-26 07:20:11 +02:00
static void iio_dmaengine_buffer_block_done ( void * data ,
const struct dmaengine_result * result )
2015-10-13 18:10:29 +02:00
{
struct iio_dma_buffer_block * block = data ;
unsigned long flags ;
spin_lock_irqsave ( & block - > queue - > list_lock , flags ) ;
list_del ( & block - > head ) ;
spin_unlock_irqrestore ( & block - > queue - > list_lock , flags ) ;
2020-08-26 07:20:11 +02:00
block - > bytes_used - = result - > residue ;
2015-10-13 18:10:29 +02:00
iio_dma_buffer_block_done ( block ) ;
}
static int iio_dmaengine_buffer_submit_block ( struct iio_dma_buffer_queue * queue ,
struct iio_dma_buffer_block * block )
{
struct dmaengine_buffer * dmaengine_buffer =
iio_buffer_to_dmaengine_buffer ( & queue - > buffer ) ;
struct dma_async_tx_descriptor * desc ;
dma_cookie_t cookie ;
block - > bytes_used = min ( block - > size , dmaengine_buffer - > max_size ) ;
2021-11-15 14:19:13 +00:00
block - > bytes_used = round_down ( block - > bytes_used ,
2015-10-13 18:10:29 +02:00
dmaengine_buffer - > align ) ;
desc = dmaengine_prep_slave_single ( dmaengine_buffer - > chan ,
block - > phys_addr , block - > bytes_used , DMA_DEV_TO_MEM ,
DMA_PREP_INTERRUPT ) ;
if ( ! desc )
return - ENOMEM ;
2020-08-26 07:20:11 +02:00
desc - > callback_result = iio_dmaengine_buffer_block_done ;
2015-10-13 18:10:29 +02:00
desc - > callback_param = block ;
cookie = dmaengine_submit ( desc ) ;
if ( dma_submit_error ( cookie ) )
return dma_submit_error ( cookie ) ;
spin_lock_irq ( & dmaengine_buffer - > queue . list_lock ) ;
list_add_tail ( & block - > head , & dmaengine_buffer - > active ) ;
spin_unlock_irq ( & dmaengine_buffer - > queue . list_lock ) ;
dma_async_issue_pending ( dmaengine_buffer - > chan ) ;
return 0 ;
}
static void iio_dmaengine_buffer_abort ( struct iio_dma_buffer_queue * queue )
{
struct dmaengine_buffer * dmaengine_buffer =
iio_buffer_to_dmaengine_buffer ( & queue - > buffer ) ;
2016-02-08 18:51:58 +01:00
dmaengine_terminate_sync ( dmaengine_buffer - > chan ) ;
2015-10-13 18:10:29 +02:00
iio_dma_buffer_block_list_abort ( queue , & dmaengine_buffer - > active ) ;
}
static void iio_dmaengine_buffer_release ( struct iio_buffer * buf )
{
struct dmaengine_buffer * dmaengine_buffer =
iio_buffer_to_dmaengine_buffer ( buf ) ;
iio_dma_buffer_release ( & dmaengine_buffer - > queue ) ;
kfree ( dmaengine_buffer ) ;
}
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
2019-12-11 12:43:00 +02:00
. read = iio_dma_buffer_read ,
2015-10-13 18:10:29 +02:00
. set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum ,
. set_length = iio_dma_buffer_set_length ,
. request_update = iio_dma_buffer_request_update ,
. enable = iio_dma_buffer_enable ,
. disable = iio_dma_buffer_disable ,
. data_available = iio_dma_buffer_data_available ,
. release = iio_dmaengine_buffer_release ,
. modes = INDIO_BUFFER_HARDWARE ,
. flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK ,
} ;
static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
. submit = iio_dmaengine_buffer_submit_block ,
. abort = iio_dmaengine_buffer_abort ,
} ;
2019-12-11 13:56:15 +02:00
static ssize_t iio_dmaengine_buffer_get_length_align ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2021-02-15 12:40:34 +02:00
struct iio_buffer * buffer = to_iio_dev_attr ( attr ) - > buffer ;
2019-12-11 13:56:15 +02:00
struct dmaengine_buffer * dmaengine_buffer =
2021-02-15 12:40:34 +02:00
iio_buffer_to_dmaengine_buffer ( buffer ) ;
2019-12-11 13:56:15 +02:00
2021-12-16 19:52:06 +01:00
return sysfs_emit ( buf , " %zu \n " , dmaengine_buffer - > align ) ;
2019-12-11 13:56:15 +02:00
}
static IIO_DEVICE_ATTR ( length_align_bytes , 0444 ,
iio_dmaengine_buffer_get_length_align , NULL , 0 ) ;
2022-10-03 11:13:53 +03:00
static const struct iio_dev_attr * iio_dmaengine_buffer_attrs [ ] = {
& iio_dev_attr_length_align_bytes ,
2019-12-11 13:56:15 +02:00
NULL ,
} ;
2015-10-13 18:10:29 +02:00
/**
* iio_dmaengine_buffer_alloc ( ) - Allocate new buffer which uses DMAengine
* @ dev : Parent device for the buffer
* @ channel : DMA channel name , typically " rx " .
*
* This allocates a new IIO buffer which internally uses the DMAengine framework
* to perform its transfers . The parent device will be used to request the DMA
* channel .
*
* Once done using the buffer iio_dmaengine_buffer_free ( ) should be used to
* release it .
*/
2020-09-23 15:18:10 +03:00
static struct iio_buffer * iio_dmaengine_buffer_alloc ( struct device * dev ,
2015-10-13 18:10:29 +02:00
const char * channel )
{
struct dmaengine_buffer * dmaengine_buffer ;
unsigned int width , src_width , dest_width ;
struct dma_slave_caps caps ;
struct dma_chan * chan ;
int ret ;
dmaengine_buffer = kzalloc ( sizeof ( * dmaengine_buffer ) , GFP_KERNEL ) ;
if ( ! dmaengine_buffer )
return ERR_PTR ( - ENOMEM ) ;
2019-11-13 11:24:53 +02:00
chan = dma_request_chan ( dev , channel ) ;
2015-10-13 18:10:29 +02:00
if ( IS_ERR ( chan ) ) {
ret = PTR_ERR ( chan ) ;
goto err_free ;
}
ret = dma_get_slave_caps ( chan , & caps ) ;
if ( ret < 0 )
goto err_free ;
/* Needs to be aligned to the maximum of the minimums */
if ( caps . src_addr_widths )
src_width = __ffs ( caps . src_addr_widths ) ;
else
src_width = 1 ;
if ( caps . dst_addr_widths )
dest_width = __ffs ( caps . dst_addr_widths ) ;
else
dest_width = 1 ;
width = max ( src_width , dest_width ) ;
INIT_LIST_HEAD ( & dmaengine_buffer - > active ) ;
dmaengine_buffer - > chan = chan ;
dmaengine_buffer - > align = width ;
dmaengine_buffer - > max_size = dma_get_max_seg_size ( chan - > device - > dev ) ;
iio_dma_buffer_init ( & dmaengine_buffer - > queue , chan - > device - > dev ,
& iio_dmaengine_default_ops ) ;
2020-09-29 15:59:41 +03:00
dmaengine_buffer - > queue . buffer . attrs = iio_dmaengine_buffer_attrs ;
2015-10-13 18:10:29 +02:00
dmaengine_buffer - > queue . buffer . access = & iio_dmaengine_buffer_ops ;
return & dmaengine_buffer - > queue . buffer ;
err_free :
kfree ( dmaengine_buffer ) ;
return ERR_PTR ( ret ) ;
}
/**
* iio_dmaengine_buffer_free ( ) - Free dmaengine buffer
* @ buffer : Buffer to free
*
* Frees a buffer previously allocated with iio_dmaengine_buffer_alloc ( ) .
*/
2020-09-23 15:18:10 +03:00
static void iio_dmaengine_buffer_free ( struct iio_buffer * buffer )
2015-10-13 18:10:29 +02:00
{
struct dmaengine_buffer * dmaengine_buffer =
iio_buffer_to_dmaengine_buffer ( buffer ) ;
iio_dma_buffer_exit ( & dmaengine_buffer - > queue ) ;
dma_release_channel ( dmaengine_buffer - > chan ) ;
iio_buffer_put ( buffer ) ;
}
2019-12-11 12:41:47 +02:00
2021-04-08 19:38:11 +08:00
static void __devm_iio_dmaengine_buffer_free ( void * buffer )
2020-03-24 15:46:32 +02:00
{
2021-04-08 19:38:11 +08:00
iio_dmaengine_buffer_free ( buffer ) ;
2020-03-24 15:46:32 +02:00
}
/**
* devm_iio_dmaengine_buffer_alloc ( ) - Resource - managed iio_dmaengine_buffer_alloc ( )
* @ dev : Parent device for the buffer
* @ channel : DMA channel name , typically " rx " .
*
* This allocates a new IIO buffer which internally uses the DMAengine framework
* to perform its transfers . The parent device will be used to request the DMA
* channel .
*
* The buffer will be automatically de - allocated once the device gets destroyed .
*/
2021-02-15 12:40:25 +02:00
static struct iio_buffer * devm_iio_dmaengine_buffer_alloc ( struct device * dev ,
2020-03-24 15:46:32 +02:00
const char * channel )
{
2021-04-08 19:38:11 +08:00
struct iio_buffer * buffer ;
int ret ;
2020-03-24 15:46:32 +02:00
buffer = iio_dmaengine_buffer_alloc ( dev , channel ) ;
2021-04-08 19:38:11 +08:00
if ( IS_ERR ( buffer ) )
2020-03-24 15:46:32 +02:00
return buffer ;
2021-04-08 19:38:11 +08:00
ret = devm_add_action_or_reset ( dev , __devm_iio_dmaengine_buffer_free ,
buffer ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2020-03-24 15:46:32 +02:00
return buffer ;
}
2021-02-15 12:40:25 +02:00
/**
* devm_iio_dmaengine_buffer_setup ( ) - Setup a DMA buffer for an IIO device
* @ dev : Parent device for the buffer
* @ indio_dev : IIO device to which to attach this buffer .
* @ channel : DMA channel name , typically " rx " .
*
* This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc ( )
* and attaches it to an IIO device with iio_device_attach_buffer ( ) .
* It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the
* IIO device .
*/
int devm_iio_dmaengine_buffer_setup ( struct device * dev ,
struct iio_dev * indio_dev ,
const char * channel )
{
struct iio_buffer * buffer ;
buffer = devm_iio_dmaengine_buffer_alloc ( indio_dev - > dev . parent ,
channel ) ;
if ( IS_ERR ( buffer ) )
return PTR_ERR ( buffer ) ;
indio_dev - > modes | = INDIO_BUFFER_HARDWARE ;
2021-02-15 12:40:38 +02:00
return iio_device_attach_buffer ( indio_dev , buffer ) ;
2021-02-15 12:40:25 +02:00
}
EXPORT_SYMBOL_GPL ( devm_iio_dmaengine_buffer_setup ) ;
2020-03-24 15:46:32 +02:00
2019-12-11 12:41:47 +02:00
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_DESCRIPTION ( " DMA buffer for the IIO framework " ) ;
MODULE_LICENSE ( " GPL " ) ;