From 77fc5151f9c0e6068f1567b73d33e75a0c35333d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 17 Feb 2024 16:42:35 +0000 Subject: device property: Move fwnode_handle_put() into property.h By having this function as static inline in the header, the compiler is able to see if can optmize the call out if (IS_ERR_OR_NULL(fwnode)) This will allow a simpler DEFINE_FREE() call in the following patch. Suggested-by: Sakari Ailus Reviewed-by: Sakari Ailus Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240217164249.921878-2-jic23@kernel.org Signed-off-by: Jonathan Cameron --- include/linux/property.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/property.h b/include/linux/property.h index 3a1045eb786c..93d992a92f59 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -180,7 +180,19 @@ struct fwnode_handle *device_get_named_child_node(const struct device *dev, const char *childname); struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode); -void fwnode_handle_put(struct fwnode_handle *fwnode); + +/** + * fwnode_handle_put - Drop reference to a device node + * @fwnode: Pointer to the device node to drop the reference to. + * + * This has to be used when terminating device_for_each_child_node() iteration + * with break or return to prevent stale device node references from being left + * behind. + */ +static inline void fwnode_handle_put(struct fwnode_handle *fwnode) +{ + fwnode_call_void_op(fwnode, put); +} int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index); int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name); -- cgit v1.2.3 From 59ed5e2d505bf5f9b4af64d0021cd0c96aec1f7c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 17 Feb 2024 16:42:36 +0000 Subject: device property: Add cleanup.h based fwnode_handle_put() scope based cleanup. Useful where the fwnode_handle was obtained from a call such as fwnode_find_reference() as it will safely do nothing if IS_ERR() is true and will automatically release the reference on the variable leaving scope. Reviewed-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20240217164249.921878-3-jic23@kernel.org Signed-off-by: Jonathan Cameron --- include/linux/property.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/property.h b/include/linux/property.h index 93d992a92f59..1322f0cce77a 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -194,6 +195,8 @@ static inline void fwnode_handle_put(struct fwnode_handle *fwnode) fwnode_call_void_op(fwnode, put); } +DEFINE_FREE(fwnode_handle, struct fwnode_handle *, fwnode_handle_put(_T)) + int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index); int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name); -- cgit v1.2.3 From 365130fd47af6d4317aa16a407874b699ab8d8cb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 17 Feb 2024 16:42:38 +0000 Subject: device property: Introduce device_for_each_child_node_scoped() Similar to recently propose for_each_child_of_node_scoped() this new version of the loop macro instantiates a new local struct fwnode_handle * that uses the __free(fwnode_handle) auto cleanup handling so that if a reference to a node is held on early exit from the loop the reference will be released. If the loop runs to completion, the child pointer will be NULL and no action will be taken. The reason this is useful is that it removes the need for fwnode_handle_put() on early loop exits. If there is a need to retain the reference, then return_ptr(child) or no_free_ptr(child) may be used to safely disable the auto cleanup. Acked-by: Greg Kroah-Hartman Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20240217164249.921878-5-jic23@kernel.org Signed-off-by: Jonathan Cameron --- include/linux/property.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/property.h b/include/linux/property.h index 1322f0cce77a..61fc20e5f81f 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -175,6 +175,11 @@ struct fwnode_handle *device_get_next_child_node(const struct device *dev, for (child = device_get_next_child_node(dev, NULL); child; \ child = device_get_next_child_node(dev, child)) +#define device_for_each_child_node_scoped(dev, child) \ + for (struct fwnode_handle *child __free(fwnode_handle) = \ + device_get_next_child_node(dev, NULL); \ + child; child = device_get_next_child_node(dev, child)) + struct fwnode_handle *fwnode_get_named_child_node(const struct fwnode_handle *fwnode, const char *childname); struct fwnode_handle *device_get_named_child_node(const struct device *dev, -- cgit v1.2.3 From 7b0c9f8fa3d25622c21eeaa2c095c8a36b8b08b7 Mon Sep 17 00:00:00 2001 From: Dumitru Ceclan Date: Wed, 28 Feb 2024 13:06:19 +0200 Subject: iio: adc: ad_sigma_delta: Add optional irq selection Add optional irq_num attribute to ad_sigma_delta_info structure for selecting the used interrupt line for ADC's conversion completion. Signed-off-by: Dumitru Ceclan Reviewed-by: Nuno Sa Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240228110622.25114-2-mitrutzceclan@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 23 ++++++++++++++--------- include/linux/iio/adc/ad_sigma_delta.h | 3 +++ 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a602429cdde4..97a05f325df7 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -222,11 +222,11 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, goto out; sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); if (timeout == 0) { sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->spi->irq); + disable_irq_nosync(sigma_delta->irq_line); ret = -EIO; } else { ret = 0; @@ -295,7 +295,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -315,7 +315,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, out: if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->spi->irq); + disable_irq_nosync(sigma_delta->irq_line); sigma_delta->irq_dis = true; } @@ -396,7 +396,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) goto err_unlock; sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); return 0; @@ -414,7 +414,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) wait_for_completion_timeout(&sigma_delta->completion, HZ); if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->spi->irq); + disable_irq_nosync(sigma_delta->irq_line); sigma_delta->irq_dis = true; } @@ -516,7 +516,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); return IRQ_HANDLED; } @@ -587,13 +587,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de sigma_delta->irq_dis = true; /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */ - irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY); + irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY); /* Allow overwriting the flags from firmware */ if (!irq_flags) irq_flags = sigma_delta->info->irq_flags; - ret = devm_request_irq(dev, sigma_delta->spi->irq, + ret = devm_request_irq(dev, sigma_delta->irq_line, ad_sd_data_rdy_trig_poll, irq_flags | IRQF_NO_AUTOEN, indio_dev->name, @@ -673,6 +673,11 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } + if (info->irq_line) + sigma_delta->irq_line = info->irq_line; + else + sigma_delta->irq_line = spi->irq; + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 719cf9cc6e1a..383614ebd760 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -48,6 +48,7 @@ struct iio_dev; * be used. * @irq_flags: flags for the interrupt used by the triggered buffer * @num_slots: Number of sequencer slots + * @irq_line: IRQ for reading conversions. If 0, spi->irq will be used */ struct ad_sigma_delta_info { int (*set_channel)(struct ad_sigma_delta *, unsigned int channel); @@ -62,6 +63,7 @@ struct ad_sigma_delta_info { unsigned int data_reg; unsigned long irq_flags; unsigned int num_slots; + int irq_line; }; /** @@ -89,6 +91,7 @@ struct ad_sigma_delta { unsigned int active_slots; unsigned int current_slot; unsigned int num_slots; + int irq_line; bool status_appended; /* map slots to channels in order to know what to expect from devices */ unsigned int *slots; -- cgit v1.2.3 From a094de22e2efc2ec7f540d10d1edb7038f863925 Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Fri, 19 Apr 2024 10:25:34 +0200 Subject: iio: buffer-dma: add iio_dmaengine_buffer_setup() This brings the DMA buffer API more in line with what we have in the triggered buffer. There's no need of having both devm_iio_dmaengine_buffer_setup() and devm_iio_dmaengine_buffer_alloc(). Hence we introduce the new iio_dmaengine_buffer_setup() that together with devm_iio_dmaengine_buffer_setup() should be all we need. Note that as part of this change iio_dmaengine_buffer_alloc() is again static and the axi-adc was updated accordingly. Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-1-5ca45b4de294@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/adi-axi-adc.c | 16 +------- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 48 +++++++++------------- include/linux/iio/buffer-dmaengine.h | 5 ++- 3 files changed, 24 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 4156639b3c8b..184b36dca6d0 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -124,26 +124,12 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, struct iio_dev *indio_dev) { struct adi_axi_adc_state *st = iio_backend_get_priv(back); - struct iio_buffer *buffer; const char *dma_name; - int ret; if (device_property_read_string(st->dev, "dma-names", &dma_name)) dma_name = "rx"; - buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name); - if (IS_ERR(buffer)) { - dev_err(st->dev, "Could not get DMA buffer, %ld\n", - PTR_ERR(buffer)); - return ERR_CAST(buffer); - } - - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - ret = iio_device_attach_buffer(indio_dev, buffer); - if (ret) - return ERR_PTR(ret); - - return buffer; + return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name); } static void axi_adc_free_buffer(struct iio_backend *back, diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index a18c1da292af..97f3116566f5 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -159,7 +159,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { * Once done using the buffer iio_dmaengine_buffer_free() should be used to * release it. */ -struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, +static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, const char *channel) { struct dmaengine_buffer *dmaengine_buffer; @@ -210,7 +210,6 @@ err_free: kfree(dmaengine_buffer); return ERR_PTR(ret); } -EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER); /** * iio_dmaengine_buffer_free() - Free dmaengine buffer @@ -230,39 +229,33 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer) } EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER); -static void __devm_iio_dmaengine_buffer_free(void *buffer) -{ - iio_dmaengine_buffer_free(buffer); -} - -/** - * 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. - */ -static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, - const char *channel) +struct iio_buffer *iio_dmaengine_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + const char *channel) { struct iio_buffer *buffer; int ret; buffer = iio_dmaengine_buffer_alloc(dev, channel); if (IS_ERR(buffer)) - return buffer; + return ERR_CAST(buffer); + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; - ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, - buffer); - if (ret) + ret = iio_device_attach_buffer(indio_dev, buffer); + if (ret) { + iio_dmaengine_buffer_free(buffer); return ERR_PTR(ret); + } return buffer; } +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); + +static void __devm_iio_dmaengine_buffer_free(void *buffer) +{ + iio_dmaengine_buffer_free(buffer); +} /** * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device @@ -281,13 +274,12 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev, { struct iio_buffer *buffer; - buffer = devm_iio_dmaengine_buffer_alloc(dev, channel); + buffer = iio_dmaengine_buffer_setup(dev, indio_dev, channel); if (IS_ERR(buffer)) return PTR_ERR(buffer); - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - - return iio_device_attach_buffer(indio_dev, buffer); + return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, + buffer); } EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h index cbb8ba957fad..acb60f9a3fff 100644 --- a/include/linux/iio/buffer-dmaengine.h +++ b/include/linux/iio/buffer-dmaengine.h @@ -10,9 +10,10 @@ struct iio_dev; struct device; -struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, - const char *channel); void iio_dmaengine_buffer_free(struct iio_buffer *buffer); +struct iio_buffer *iio_dmaengine_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + const char *channel); int devm_iio_dmaengine_buffer_setup(struct device *dev, struct iio_dev *indio_dev, const char *channel); -- cgit v1.2.3 From 04ae3b1a76b77f98a4a0c8ed2c544007334fc680 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 19 Apr 2024 10:25:35 +0200 Subject: iio: buffer-dma: Rename iio_dma_buffer_data_available() Change its name to iio_dma_buffer_usage(), as this function can be used both for the .data_available and the .space_available callbacks. Signed-off-by: Paul Cercueil Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-2-5ca45b4de294@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 11 ++++++----- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 +- include/linux/iio/buffer-dma.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 5610ba67925e..404f9867bdc5 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -547,13 +547,14 @@ out_unlock: EXPORT_SYMBOL_GPL(iio_dma_buffer_read); /** - * iio_dma_buffer_data_available() - DMA buffer data_available callback + * iio_dma_buffer_usage() - DMA buffer data_available and + * space_available callback * @buf: Buffer to check for data availability * - * Should be used as the data_available callback for iio_buffer_access_ops - * struct for DMA buffers. + * Should be used as the data_available and space_available callbacks for + * iio_buffer_access_ops struct for DMA buffers. */ -size_t iio_dma_buffer_data_available(struct iio_buffer *buf) +size_t iio_dma_buffer_usage(struct iio_buffer *buf) { struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf); struct iio_dma_buffer_block *block; @@ -586,7 +587,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf) return data_available; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available); +EXPORT_SYMBOL_GPL(iio_dma_buffer_usage); /** * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 97f3116566f5..df05d66afff9 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -117,7 +117,7 @@ static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = { .request_update = iio_dma_buffer_request_update, .enable = iio_dma_buffer_enable, .disable = iio_dma_buffer_disable, - .data_available = iio_dma_buffer_data_available, + .data_available = iio_dma_buffer_usage, .release = iio_dmaengine_buffer_release, .modes = INDIO_BUFFER_HARDWARE, diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h index 18d3702fa95d..52a838ec0e57 100644 --- a/include/linux/iio/buffer-dma.h +++ b/include/linux/iio/buffer-dma.h @@ -132,7 +132,7 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer, struct iio_dev *indio_dev); int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, char __user *user_buffer); -size_t iio_dma_buffer_data_available(struct iio_buffer *buffer); +size_t iio_dma_buffer_usage(struct iio_buffer *buffer); int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd); int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length); int iio_dma_buffer_request_update(struct iio_buffer *buffer); -- cgit v1.2.3 From fb09febafd160b7aefd9e61f710a0c50f0472403 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 19 Apr 2024 10:25:36 +0200 Subject: iio: buffer-dma: Enable buffer write support Adding write support to the buffer-dma code is easy - the write() function basically needs to do the exact same thing as the read() function: dequeue a block, read or write the data, enqueue the block when entirely processed. Therefore, the iio_buffer_dma_read() and the new iio_buffer_dma_write() now both call a function iio_buffer_dma_io(), which will perform this task. Note that we preemptively reset block->bytes_used to the buffer's size in iio_dma_buffer_request_update(), as in the future the iio_dma_buffer_enqueue() function won't reset it. Signed-off-by: Paul Cercueil Reviewed-by: Alexandru Ardelean Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-3-5ca45b4de294@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 89 +++++++++++++++++++++++----- include/linux/iio/buffer-dma.h | 2 + 2 files changed, 75 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 404f9867bdc5..13b1a858969e 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) block->state = IIO_BLOCK_STATE_DONE; } +static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue) +{ + __poll_t flags; + + if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) + flags = EPOLLIN | EPOLLRDNORM; + else + flags = EPOLLOUT | EPOLLWRNORM; + + wake_up_interruptible_poll(&queue->buffer.pollq, flags); +} + /** * iio_dma_buffer_block_done() - Indicate that a block has been completed * @block: The completed block @@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) spin_unlock_irqrestore(&queue->list_lock, flags); iio_buffer_block_put_atomic(block); - wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM); + iio_dma_buffer_queue_wake(queue); } EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done); @@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, } spin_unlock_irqrestore(&queue->list_lock, flags); - wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM); + iio_dma_buffer_queue_wake(queue); } EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort); @@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer) queue->fileio.blocks[i] = block; } - block->state = IIO_BLOCK_STATE_QUEUED; - list_add_tail(&block->head, &queue->incoming); + /* + * block->bytes_used may have been modified previously, e.g. by + * iio_dma_buffer_block_list_abort(). Reset it here to the + * block's so that iio_dma_buffer_io() will work. + */ + block->bytes_used = block->size; + + /* + * If it's an input buffer, mark the block as queued, and + * iio_dma_buffer_enable() will submit it. Otherwise mark it as + * done, which means it's ready to be dequeued. + */ + if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) { + block->state = IIO_BLOCK_STATE_QUEUED; + list_add_tail(&block->head, &queue->incoming); + } else { + block->state = IIO_BLOCK_STATE_DONE; + } } out_unlock: @@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( return block; } -/** - * iio_dma_buffer_read() - DMA buffer read callback - * @buffer: Buffer to read form - * @n: Number of bytes to read - * @user_buffer: Userspace buffer to copy the data to - * - * Should be used as the read callback for iio_buffer_access_ops - * struct for DMA buffers. - */ -int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, - char __user *user_buffer) +static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n, + char __user *user_buffer, bool is_from_user) { struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); struct iio_dma_buffer_block *block; + void *addr; int ret; if (n < buffer->bytes_per_datum) @@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, n = rounddown(n, buffer->bytes_per_datum); if (n > block->bytes_used - queue->fileio.pos) n = block->bytes_used - queue->fileio.pos; + addr = block->vaddr + queue->fileio.pos; - if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) { + if (is_from_user) + ret = copy_from_user(addr, user_buffer, n); + else + ret = copy_to_user(user_buffer, addr, n); + if (ret) { ret = -EFAULT; goto out_unlock; } @@ -544,8 +569,40 @@ out_unlock: return ret; } + +/** + * iio_dma_buffer_read() - DMA buffer read callback + * @buffer: Buffer to read form + * @n: Number of bytes to read + * @user_buffer: Userspace buffer to copy the data to + * + * Should be used as the read callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, + char __user *user_buffer) +{ + return iio_dma_buffer_io(buffer, n, user_buffer, false); +} EXPORT_SYMBOL_GPL(iio_dma_buffer_read); +/** + * iio_dma_buffer_write() - DMA buffer write callback + * @buffer: Buffer to read form + * @n: Number of bytes to read + * @user_buffer: Userspace buffer to copy the data from + * + * Should be used as the write callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n, + const char __user *user_buffer) +{ + return iio_dma_buffer_io(buffer, n, + (__force __user char *)user_buffer, true); +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_write); + /** * iio_dma_buffer_usage() - DMA buffer data_available and * space_available callback diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h index 52a838ec0e57..6e27e47077d5 100644 --- a/include/linux/iio/buffer-dma.h +++ b/include/linux/iio/buffer-dma.h @@ -132,6 +132,8 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer, struct iio_dev *indio_dev); int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, char __user *user_buffer); +int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n, + const char __user *user_buffer); size_t iio_dma_buffer_usage(struct iio_buffer *buffer); int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd); int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length); -- cgit v1.2.3 From c1b91566580c245cf1147745d174be5e059ace6b Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 19 Apr 2024 10:25:37 +0200 Subject: iio: buffer-dmaengine: Support specifying buffer direction Update the devm_iio_dmaengine_buffer_setup() function to support specifying the buffer direction. Update the iio_dmaengine_buffer_submit() function to handle input buffers as well as output buffers. Signed-off-by: Paul Cercueil Reviewed-by: Alexandru Ardelean Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-4-5ca45b4de294@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 44 +++++++++++++++------- include/linux/iio/buffer-dmaengine.h | 25 +++++++++--- 2 files changed, 49 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index df05d66afff9..951012651018 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct dmaengine_buffer *dmaengine_buffer = iio_buffer_to_dmaengine_buffer(&queue->buffer); struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction dma_dir; + size_t max_size; dma_cookie_t cookie; - block->bytes_used = min(block->size, dmaengine_buffer->max_size); - block->bytes_used = round_down(block->bytes_used, - dmaengine_buffer->align); + max_size = min(block->size, dmaengine_buffer->max_size); + max_size = round_down(max_size, dmaengine_buffer->align); + + if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) { + block->bytes_used = max_size; + dma_dir = DMA_DEV_TO_MEM; + } else { + dma_dir = DMA_MEM_TO_DEV; + } + + if (!block->bytes_used || block->bytes_used > max_size) + return -EINVAL; desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, - block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM, + block->phys_addr, block->bytes_used, dma_dir, DMA_PREP_INTERRUPT); if (!desc) return -ENOMEM; @@ -229,9 +240,10 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer) } EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER); -struct iio_buffer *iio_dmaengine_buffer_setup(struct device *dev, - struct iio_dev *indio_dev, - const char *channel) +struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir) { struct iio_buffer *buffer; int ret; @@ -242,6 +254,8 @@ struct iio_buffer *iio_dmaengine_buffer_setup(struct device *dev, indio_dev->modes |= INDIO_BUFFER_HARDWARE; + buffer->direction = dir; + ret = iio_device_attach_buffer(indio_dev, buffer); if (ret) { iio_dmaengine_buffer_free(buffer); @@ -250,7 +264,7 @@ struct iio_buffer *iio_dmaengine_buffer_setup(struct device *dev, return buffer; } -EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER); static void __devm_iio_dmaengine_buffer_free(void *buffer) { @@ -258,30 +272,32 @@ static void __devm_iio_dmaengine_buffer_free(void *buffer) } /** - * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device + * devm_iio_dmaengine_buffer_setup_ext() - 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". + * @dir: Direction of buffer (in or out) * * 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) +int devm_iio_dmaengine_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir) { struct iio_buffer *buffer; - buffer = iio_dmaengine_buffer_setup(dev, indio_dev, channel); + buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir); if (IS_ERR(buffer)) return PTR_ERR(buffer); return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, buffer); } -EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); +EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h index acb60f9a3fff..81d9a19aeb91 100644 --- a/include/linux/iio/buffer-dmaengine.h +++ b/include/linux/iio/buffer-dmaengine.h @@ -7,15 +7,28 @@ #ifndef __IIO_DMAENGINE_H__ #define __IIO_DMAENGINE_H__ +#include + struct iio_dev; struct device; void iio_dmaengine_buffer_free(struct iio_buffer *buffer); -struct iio_buffer *iio_dmaengine_buffer_setup(struct device *dev, - struct iio_dev *indio_dev, - const char *channel); -int devm_iio_dmaengine_buffer_setup(struct device *dev, - struct iio_dev *indio_dev, - const char *channel); +struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir); + +#define iio_dmaengine_buffer_setup(dev, indio_dev, channel) \ + iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \ + IIO_BUFFER_DIRECTION_IN) + +int devm_iio_dmaengine_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir); + +#define devm_iio_dmaengine_buffer_setup(dev, indio_dev, channel) \ + devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \ + IIO_BUFFER_DIRECTION_IN) #endif -- cgit v1.2.3 From 87800c4342a29d4e1c378ce72d27e3976d094ffa Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Fri, 19 Apr 2024 10:25:41 +0200 Subject: iio: backend: add new functionality This adds the needed backend ops for supporting a backend inerfacing with an high speed dac. The new ops are: * data_source_set(); * set_sampling_freq(); * extend_chan_spec(); * ext_info_set(); * ext_info_get(). Also to note the new helpers that are meant to be used by the backends when extending an IIO channel (adding extended info): * iio_backend_ext_info_set(); * iio_backend_ext_info_get(). Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-8-5ca45b4de294@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-backend.c | 179 +++++++++++++++++++++++++++++++++++++ include/linux/iio/backend.h | 49 ++++++++++ 2 files changed, 228 insertions(+) (limited to 'include/linux') diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 2fea2bbbe47f..f08ed6d70ae5 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -43,10 +43,12 @@ #include #include +#include struct iio_backend { struct list_head entry; const struct iio_backend_ops *ops; + struct device *frontend_dev; struct device *dev; struct module *owner; void *priv; @@ -186,6 +188,44 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, } EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND); +/** + * iio_backend_data_source_set - Select data source + * @back: Backend device + * @chan: Channel number + * @data: Data source + * + * A given backend may have different sources to stream/sync data. This allows + * to choose that source. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source data) +{ + if (data >= IIO_BACKEND_DATA_SOURCE_MAX) + return -EINVAL; + + return iio_backend_op_call(back, data_source_set, chan, data); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND); + +/** + * iio_backend_set_sampling_freq - Set channel sampling rate + * @back: Backend device + * @chan: Channel number + * @sample_rate_hz: Sample rate + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan, + u64 sample_rate_hz) +{ + return iio_backend_op_call(back, set_sample_rate, chan, sample_rate_hz); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND); + static void iio_backend_free_buffer(void *arg) { struct iio_backend_buffer_pair *pair = arg; @@ -231,6 +271,143 @@ int devm_iio_backend_request_buffer(struct device *dev, } EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND); +static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev) +{ + struct iio_backend *back = ERR_PTR(-ENODEV), *iter; + + /* + * We deliberately go through all backends even after finding a match. + * The reason is that we want to catch frontend devices which have more + * than one backend in which case returning the first we find is bogus. + * For those cases, frontends need to explicitly define + * get_iio_backend() in struct iio_info. + */ + guard(mutex)(&iio_back_lock); + list_for_each_entry(iter, &iio_back_list, entry) { + if (dev == iter->frontend_dev) { + if (!IS_ERR(back)) { + dev_warn(dev, + "Multiple backends! get_iio_backend() needs to be implemented"); + return ERR_PTR(-ENODEV); + } + + back = iter; + } + } + + return back; +} + +/** + * iio_backend_ext_info_get - IIO ext_info read callback + * @indio_dev: IIO device + * @private: Data private to the driver + * @chan: IIO channel + * @buf: Buffer where to place the attribute data + * + * This helper is intended to be used by backends that extend an IIO channel + * (through iio_backend_extend_chan_spec()) with extended info. In that case, + * backends are not supposed to give their own callbacks (as they would not have + * a way to get the backend from indio_dev). This is the getter. + * + * RETURNS: + * Number of bytes written to buf, negative error number on failure. + */ +ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct iio_backend *back; + + /* + * The below should work for the majority of the cases. It will not work + * when one frontend has multiple backends in which case we'll need a + * new callback in struct iio_info so we can directly request the proper + * backend from the frontend. Anyways, let's only introduce new options + * when really needed... + */ + back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent); + if (IS_ERR(back)) + return PTR_ERR(back); + + return iio_backend_op_call(back, ext_info_get, private, chan, buf); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND); + +/** + * iio_backend_ext_info_set - IIO ext_info write callback + * @indio_dev: IIO device + * @private: Data private to the driver + * @chan: IIO channel + * @buf: Buffer holding the sysfs attribute + * @len: Buffer length + * + * This helper is intended to be used by backends that extend an IIO channel + * (trough iio_backend_extend_chan_spec()) with extended info. In that case, + * backends are not supposed to give their own callbacks (as they would not have + * a way to get the backend from indio_dev). This is the setter. + * + * RETURNS: + * Buffer length on success, negative error number on failure. + */ +ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct iio_backend *back; + + back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent); + if (IS_ERR(back)) + return PTR_ERR(back); + + return iio_backend_op_call(back, ext_info_set, private, chan, buf, len); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND); + +/** + * iio_backend_extend_chan_spec - Extend an IIO channel + * @indio_dev: IIO device + * @back: Backend device + * @chan: IIO channel + * + * Some backends may have their own functionalities and hence capable of + * extending a frontend's channel. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev, + struct iio_backend *back, + struct iio_chan_spec *chan) +{ + const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info; + const struct iio_chan_spec_ext_info *back_ext_info; + int ret; + + ret = iio_backend_op_call(back, extend_chan_spec, chan); + if (ret) + return ret; + /* + * Let's keep things simple for now. Don't allow to overwrite the + * frontend's extended info. If ever needed, we can support appending + * it. + */ + if (frontend_ext_info && chan->ext_info != frontend_ext_info) + return -EOPNOTSUPP; + if (!chan->ext_info) + return 0; + + /* Don't allow backends to get creative and force their own handlers */ + for (back_ext_info = chan->ext_info; back_ext_info->name; back_ext_info++) { + if (back_ext_info->read != iio_backend_ext_info_get) + return -EINVAL; + if (back_ext_info->write != iio_backend_ext_info_set) + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND); + static void iio_backend_release(void *arg) { struct iio_backend *back = arg; @@ -263,6 +440,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) "Could not link to supplier(%s)\n", dev_name(back->dev)); + back->frontend_dev = dev; + dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev)); return 0; diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index a6d79381866e..9d144631134d 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -4,6 +4,7 @@ #include +struct iio_chan_spec; struct fwnode_handle; struct iio_backend; struct device; @@ -15,6 +16,26 @@ enum iio_backend_data_type { IIO_BACKEND_DATA_TYPE_MAX }; +enum iio_backend_data_source { + IIO_BACKEND_INTERNAL_CONTINUOS_WAVE, + IIO_BACKEND_EXTERNAL, + IIO_BACKEND_DATA_SOURCE_MAX +}; + +/** + * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute + * @_name: Attribute name + * @_shared: Whether the attribute is shared between all channels + * @_what: Data private to the driver + */ +#define IIO_BACKEND_EX_INFO(_name, _shared, _what) { \ + .name = (_name), \ + .shared = (_shared), \ + .read = iio_backend_ext_info_get, \ + .write = iio_backend_ext_info_set, \ + .private = (_what), \ +} + /** * struct iio_backend_data_fmt - Backend data format * @type: Data type. @@ -35,8 +56,13 @@ struct iio_backend_data_fmt { * @chan_enable: Enable one channel. * @chan_disable: Disable one channel. * @data_format_set: Configure the data format for a specific channel. + * @data_source_set: Configure the data source for a specific channel. + * @set_sample_rate: Configure the sampling rate for a specific channel. * @request_buffer: Request an IIO buffer. * @free_buffer: Free an IIO buffer. + * @extend_chan_spec: Extend an IIO channel. + * @ext_info_set: Extended info setter. + * @ext_info_get: Extended info getter. **/ struct iio_backend_ops { int (*enable)(struct iio_backend *back); @@ -45,10 +71,21 @@ struct iio_backend_ops { int (*chan_disable)(struct iio_backend *back, unsigned int chan); int (*data_format_set)(struct iio_backend *back, unsigned int chan, const struct iio_backend_data_fmt *data); + int (*data_source_set)(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source data); + int (*set_sample_rate)(struct iio_backend *back, unsigned int chan, + u64 sample_rate_hz); struct iio_buffer *(*request_buffer)(struct iio_backend *back, struct iio_dev *indio_dev); void (*free_buffer)(struct iio_backend *back, struct iio_buffer *buffer); + int (*extend_chan_spec)(struct iio_backend *back, + struct iio_chan_spec *chan); + int (*ext_info_set)(struct iio_backend *back, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len); + int (*ext_info_get)(struct iio_backend *back, uintptr_t private, + const struct iio_chan_spec *chan, char *buf); }; int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan); @@ -56,10 +93,22 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan); int devm_iio_backend_enable(struct device *dev, struct iio_backend *back); int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, const struct iio_backend_data_fmt *data); +int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source data); +int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan, + u64 sample_rate_hz); int devm_iio_backend_request_buffer(struct device *dev, struct iio_backend *back, struct iio_dev *indio_dev); +ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len); +ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf); +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev, + struct iio_backend *back, + struct iio_chan_spec *chan); void *iio_backend_get_priv(const struct iio_backend *conv); struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); struct iio_backend * -- cgit v1.2.3