* [PATCH 01/10] iio: buffer: add helper for setting direction
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Nuno Sa <nuno.sa@analog.com>
Simple helper for setting the buffer direction when it's allocated using
iio_dmaengine_buffer_alloc().
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
drivers/iio/industrialio-buffer.c | 7 +++++++
include/linux/iio/buffer.h | 3 +++
2 files changed, 10 insertions(+)
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 1d950a3e153b..4b1ca6ad86ee 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1956,6 +1956,13 @@ void iio_buffer_put(struct iio_buffer *buffer)
}
EXPORT_SYMBOL_GPL(iio_buffer_put);
+void iio_buffer_set_dir(struct iio_buffer *buffer,
+ enum iio_buffer_direction dir)
+{
+ buffer->direction = dir;
+}
+EXPORT_SYMBOL_GPL(iio_buffer_set_dir);
+
/**
* iio_device_attach_buffer - Attach a buffer to a IIO device
* @indio_dev: The device the buffer should be attached to
diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index 418b1307d3f2..7e70bb5adc01 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -55,4 +55,7 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
int iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer);
+void iio_buffer_set_dir(struct iio_buffer *buffer,
+ enum iio_buffer_direction dir);
+
#endif /* _IIO_BUFFER_GENERIC_H_ */
--
2.44.0
^ permalink raw reply related
* [PATCH 00/10] iio: dac: support IIO backends on the output direction
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa, Paul Cercueil, Alexandru Ardelean
Hi Jonathan,
This is the result of the brief discussion we had in the RFC [1].
Patches 2 to 5 from Paul's series to bring output buffer support to
DMA buffers [2].
Note that the original DMA patch 4 ("iio: buffer-dmaengine: Support
specifying buffer direction") also had changes for the axi-adc as it
used devm_iio_dmaengine_buffer_setup(). However, when that was converted
to the backend framework, we started to use iio_dmaengine_buffer_alloc()
and default is INPUT so no need for any change.
I did gave it a try on beginning to support extending IIO channels
(chan_spec) from backend's. For now, we just need to do it for the
extended info and honestly, IMHO, I don't think the result to be
horrible :). The only thing that I don't like much is the call to
iio_device_set_drvdata() in iio_backend_extend_chan_spec(). Long story
short, I know it's likely to break so maybe we should have a proper
solution right from the beginning. But see the comment I have in there.
I did thought in a solution that should work but you may have a better
idea.
Also note the main reason to come up with the iio_backend_ext_info_set()
and iio_backend_ext_info_get() helpers is not to allow starting having
these from backends. I was even thinking in just passing the callbacks
from the frontend to iio_backend_extend_chan_spec() but I kind of prefer
this way. We may still need at some point to allow frontends to have full
control and have their callbacks in the attributes. But for now, I'm not
even allowing for backends to append a channel ext_info so i would say to
worry about that when such a scenario pops up.
I would also prefer for iio_backend_ext_info_set() and
iio_backend_ext_info_get() to be static but that would make the code more
complex than it needs to be (we would have to kmemdup() the backends
ext_info and assign the callbacks) so I went this way.
[1]: https://lore.kernel.org/linux-iio/20240216-iio-backend-axi-dds-v1-0-22aed9fb07a1@analog.com/
[2]: https://lore.kernel.org/linux-iio/20230807112113.47157-1-paul@crapouillou.net/
---
Nuno Sa (6):
iio: buffer: add helper for setting direction
dt-bindings: iio: dac: add bindings doc for AXI DAC driver
dt-bindings: iio: dac: add bindings doc for AD9739A
iio: backend: add new functionality
iio: dac: add support for AXI DAC IP core
iio: dac: support the ad9739a RF DAC
Paul Cercueil (4):
iio: buffer-dma: Rename iio_dma_buffer_data_available()
iio: buffer-dma: Enable buffer write support
iio: buffer-dmaengine: Support specifying buffer direction
iio: buffer-dmaengine: Enable write support
Documentation/ABI/testing/sysfs-bus-iio-ad9739a | 17 +
.../devicetree/bindings/iio/dac/adi,ad9739a.yaml | 88 +++
.../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 62 ++
MAINTAINERS | 17 +
drivers/iio/buffer/industrialio-buffer-dma.c | 100 +++-
drivers/iio/buffer/industrialio-buffer-dmaengine.c | 28 +-
drivers/iio/dac/Kconfig | 37 ++
drivers/iio/dac/Makefile | 2 +
drivers/iio/dac/ad9739a.c | 445 ++++++++++++++
drivers/iio/dac/adi-axi-dac.c | 644 +++++++++++++++++++++
drivers/iio/industrialio-backend.c | 144 +++++
drivers/iio/industrialio-buffer.c | 7 +
include/linux/iio/backend.h | 49 ++
include/linux/iio/buffer-dma.h | 4 +-
include/linux/iio/buffer-dmaengine.h | 6 +-
include/linux/iio/buffer.h | 3 +
16 files changed, 1624 insertions(+), 29 deletions(-)
---
base-commit: a276b4da56e988157a34b9fef9c46ebfd95f7f09
change-id: 20240326-iio-backend-axi-dac-497a707a5d88
--
Thanks!
- Nuno Sá
^ permalink raw reply
* [PATCH 02/10] iio: buffer-dma: Rename iio_dma_buffer_data_available()
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Paul Cercueil, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Paul Cercueil <paul@crapouillou.net>
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 <paul@crapouillou.net>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
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(-)
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 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
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 a18c1da292af..ccf6e0b19019 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);
--
2.44.0
^ permalink raw reply related
* [PATCH 04/10] iio: buffer-dmaengine: Support specifying buffer direction
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Paul Cercueil, Alexandru Ardelean, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Paul Cercueil <paul@crapouillou.net>
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 <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
drivers/iio/buffer/industrialio-buffer-dmaengine.c | 24 +++++++++++++++++-----
include/linux/iio/buffer-dmaengine.h | 6 +++++-
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index ccf6e0b19019..d9d72f7cba60 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;
@@ -277,7 +288,8 @@ static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
*/
int devm_iio_dmaengine_buffer_setup(struct device *dev,
struct iio_dev *indio_dev,
- const char *channel)
+ const char *channel,
+ enum iio_buffer_direction dir)
{
struct iio_buffer *buffer;
@@ -287,6 +299,8 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+ buffer->direction = dir;
+
return iio_device_attach_buffer(indio_dev, 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..f0d750ce2880 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -7,6 +7,8 @@
#ifndef __IIO_DMAENGINE_H__
#define __IIO_DMAENGINE_H__
+#include <linux/iio/buffer.h>
+
struct iio_dev;
struct device;
@@ -15,6 +17,8 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
int devm_iio_dmaengine_buffer_setup(struct device *dev,
struct iio_dev *indio_dev,
- const char *channel);
+ const char *channel,
+ enum iio_buffer_direction dir);
+
#endif
--
2.44.0
^ permalink raw reply related
* [PATCH 03/10] iio: buffer-dma: Enable buffer write support
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Paul Cercueil, Alexandru Ardelean, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Paul Cercueil <paul@crapouillou.net>
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 <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
drivers/iio/buffer/industrialio-buffer-dma.c | 89 +++++++++++++++++++++++-----
include/linux/iio/buffer-dma.h | 2 +
2 files changed, 75 insertions(+), 16 deletions(-)
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 404f9867bdc5..29cc3083cb7e 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 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
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);
--
2.44.0
^ permalink raw reply related
* [PATCH 05/10] iio: buffer-dmaengine: Enable write support
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Paul Cercueil, Alexandru Ardelean, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Paul Cercueil <paul@crapouillou.net>
Use the iio_dma_buffer_write() and iio_dma_buffer_space_available()
functions provided by the buffer-dma core, to enable write support in
the buffer-dmaengine code.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index d9d72f7cba60..e151af7e8907 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -123,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
.read = iio_dma_buffer_read,
+ .write = iio_dma_buffer_write,
.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_usage,
+ .space_available = iio_dma_buffer_usage,
.release = iio_dmaengine_buffer_release,
.modes = INDIO_BUFFER_HARDWARE,
--
2.44.0
^ permalink raw reply related
* [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Nuno Sa <nuno.sa@analog.com>
This adds the bindings documentation for the AXI DAC driver.
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
.../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 62 ++++++++++++++++++++++
MAINTAINERS | 7 +++
2 files changed, 69 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
new file mode 100644
index 000000000000..1018fd274f04
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AXI DAC IP core
+
+maintainers:
+ - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+ Analog Devices Generic AXI DAC IP core for interfacing a DAC device
+ with a high speed serial (JESD204B/C) or source synchronous parallel
+ interface (LVDS/CMOS).
+ Usually, some other interface type (i.e SPI) is used as a control
+ interface for the actual DAC, while this IP core will interface
+ to the data-lines of the DAC and handle the streaming of data into
+ memory via DMA.
+
+ https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+
+properties:
+ compatible:
+ enum:
+ - adi,axi-dac-9.1.b
+
+ reg:
+ maxItems: 1
+
+ dmas:
+ maxItems: 1
+
+ dma-names:
+ items:
+ - const: tx
+
+ clocks:
+ maxItems: 1
+
+ '#io-backend-cells':
+ const: 0
+
+required:
+ - compatible
+ - dmas
+ - reg
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ dac@44a00000 {
+ compatible = "adi,axi-dac-9.1.b";
+ reg = <0x44a00000 0x10000>;
+ dmas = <&tx_dma 0>;
+ dma-names = "tx";
+ #io-backend-cells = <0>;
+ clocks = <&axi_clk>;
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index a7287cf44869..2137eb452376 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1399,6 +1399,13 @@ F: sound/soc/codecs/adav*
F: sound/soc/codecs/sigmadsp.*
F: sound/soc/codecs/ssm*
+ANALOG DEVICES INC AXI DAC DRIVER
+M: Nuno Sa <nuno.sa@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+
ANALOG DEVICES INC DMA DRIVERS
M: Lars-Peter Clausen <lars@metafoo.de>
S: Supported
--
2.44.0
^ permalink raw reply related
* [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Nuno Sa <nuno.sa@analog.com>
This adds the bindings documentation for the 14 bit
RF Digital-to-Analog converter.
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
.../devicetree/bindings/iio/dac/adi,ad9739a.yaml | 88 ++++++++++++++++++++++
MAINTAINERS | 8 ++
2 files changed, 96 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
new file mode 100644
index 000000000000..24bcec763a9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9739A RF DAC
+
+maintainers:
+ - Dragos Bogdan <dragos.bogdan@analog.com>
+ - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+ The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
+ of synthesizing wideband signals from dc up to 3 GHz.
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad9739a
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ vdd_3_3-supply:
+ description: 3.3V Digital input supply.
+
+ vdd-supply:
+ description: 1.8V Digital input supply.
+
+ vdda-supply:
+ description: 3.3V Analog input supply.
+
+ vddc-supply:
+ description: 1.8V Clock input supply.
+
+ io-backends:
+ maxItems: 1
+
+ adi,full-scale-microamp:
+ description: This property represents the DAC full scale current.
+ minimum: 8700
+ maximum: 31700
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - io-backends
+ - vdd_3_3-supply
+ - vdd-supply
+ - vdda-supply
+ - vddc-supply
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,ad9739a";
+ reg = <0>;
+
+ clocks = <&dac_clk>;
+
+ io-backends = <&iio_backend>;
+
+ vdd_3_3-supply = <&vdd_3_3>;
+ vdd-supply = <&vdd>;
+ vdda-supply = <&vdd_3_3>;
+ vddc-supply = <&vdd>;
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 2137eb452376..76e872e320d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1234,6 +1234,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
F: drivers/iio/adc/ad7780.c
+ANALOG DEVICES INC AD9739a DRIVER
+M: Nuno Sa <nuno.sa@analog.com>
+M: Dragos Bogdan <dragos.bogdan@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+
ANALOG DEVICES INC ADA4250 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
--
2.44.0
^ permalink raw reply related
* [PATCH 10/10] iio: dac: support the ad9739a RF DAC
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Nuno Sa <nuno.sa@analog.com>
The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
of synthesizing wideband signals from dc up to 3 GHz.
A dual-port, source synchronous, LVDS interface simplifies the digital
interface with existing FGPA/ASIC technology. On-chip controllers are used
to manage external and internal clock domain variations over temperature to
ensure reliable data transfer from the host to the DAC core.
Co-developed-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
Documentation/ABI/testing/sysfs-bus-iio-ad9739a | 17 +
MAINTAINERS | 1 +
drivers/iio/dac/Kconfig | 16 +
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/ad9739a.c | 445 ++++++++++++++++++++++++
5 files changed, 480 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
new file mode 100644
index 000000000000..8a8a5cd10386
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
@@ -0,0 +1,17 @@
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
+KernelVersion: 6.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ Dac operating mode. One of the following modes can be selected:
+ * normal: This is DAC normal mode.
+ * mixed-mode: In this mode the output is effectively chopped at the
+ DAC sample rate. This has the effect of reducing the
+ power of the fundamental signal while increasing the
+ power of the images centered around the DAC sample rate,
+ thus improving the output power of these images.
+
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
+KernelVersion: 6.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ Available operating modes.
diff --git a/MAINTAINERS b/MAINTAINERS
index 505f28dc6da6..8ad79cf70552 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1241,6 +1241,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+F: drivers/iio/dac/ad9739a.c
ANALOG DEVICES INC ADA4250 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 7c0a8caa9a34..ee0d9798d8b4 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,22 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config AD9739A
+ tristate "Analog Devices AD9739A RF DAC spi driver"
+ depends on SPI
+ select REGMAP_SPI
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices AD9739A Digital-to
+ Analog Converter.
+
+ The driver requires the assistance of the AXI DAC IP core to operate,
+ since SPI is used for configuration only, while data has to be
+ streamed into memory via DMA.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad9739a.
+
config ADI_AXI_DAC
tristate "Analog Devices Generic AXI DAC IP core driver"
select IIO_BUFFER
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 6bcaa65434b2..8432a81a19dc 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
obj-$(CONFIG_AD7293) += ad7293.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_AD9739A) += ad9739a.o
obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
new file mode 100644
index 000000000000..46431fa345a5
--- /dev/null
+++ b/drivers/iio/dac/ad9739a.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9739a SPI DAC driver
+ *
+ * Copyright 2015-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define AD9739A_REG_MODE 0
+#define AD9739A_RESET_MASK BIT(5)
+#define AD9739A_REG_FSC_1 0x06
+#define AD9739A_REG_FSC_2 0x07
+#define AD9739A_FSC_MSB GENMASK(1, 0)
+#define AD9739A_REG_DEC_CNT 0x8
+#define AD9739A_DAC_DEC GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT1 0x10
+#define AD9739A_RCVR_LOOP_EN_MASK GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT4 0x13
+#define AD9739A_FINE_DEL_SKW_MASK GENMASK(3, 0)
+#define AD9739A_REG_LVDS_REC_STAT9 0x21
+#define AD9739A_RCVR_TRACK_AND_LOCK (BIT(3) | BIT(0))
+#define AD9739A_REG_CROSS_CNT1 0x22
+#define AD9739A_REG_CROSS_CNT2 0x23
+#define AD9739A_REG_PHS_DET 0x24
+#define AD9739A_REG_MU_DUTY 0x25
+#define AD9739A_REG_MU_CNT1 0x26
+#define AD9739A_MU_EN_MASK BIT(0)
+#define AD9739A_REG_MU_CNT2 0x27
+#define AD9739A_REG_MU_CNT3 0x28
+#define AD9739A_REG_MU_CNT4 0x29
+#define AD9739A_MU_CNT4_DEFAULT 0xcb
+#define AD9739A_REG_MU_STAT1 0x2A
+#define AD9739A_MU_LOCK_MASK BIT(0)
+#define AD9739A_REG_ANA_CNT_1 0x32
+#define AD9739A_REG_ID 0x35
+
+#define AD9739A_ID 0x24
+#define AD9739A_REG_IS_RESERVED(reg) \
+ ((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \
+ (reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34)
+
+#define AD9739A_FSC_MIN 8700
+#define AD9739A_FSC_MAX 31700
+#define AD9739A_FSC_RANGE (AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1)
+
+#define AD9739A_MIN_DAC_CLK (1600 * MEGA)
+#define AD9739A_MAX_DAC_CLK (2500 * MEGA)
+#define AD9739A_DAC_CLK_RANGE (AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1)
+/* as recommended by the datasheet */
+#define AD9739A_LOCK_N_TRIES 3
+
+struct ad9739a_state {
+ struct iio_backend *back;
+ struct regmap *regmap;
+ unsigned long sample_rate;
+};
+
+enum {
+ AD9739A_NORMAL_MODE,
+ AD9739A_MIXED_MODE = 2,
+};
+
+static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+ u32 mode;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
+ if (ret)
+ return ret;
+
+ mode = FIELD_GET(AD9739A_DAC_DEC, mode);
+ /* sanity check we get valid values from the HW */
+ if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
+ return -EIO;
+ if (!mode)
+ return AD9739A_NORMAL_MODE;
+
+ return AD9739A_MIXED_MODE - 1;
+}
+
+static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, u32 mode)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ if (mode == AD9739A_MIXED_MODE - 1)
+ mode = AD9739A_MIXED_MODE;
+
+ return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
+ AD9739A_DAC_DEC, mode);
+}
+
+static int ad9739a_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->sample_rate;
+ *val2 = 0;
+ return IIO_VAL_INT_64;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad9739a_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
+}
+
+static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ return iio_backend_data_source_set(st->back, 0,
+ IIO_BACKEND_INTERNAL_CW);
+}
+
+static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg)
+{
+ if (AD9739A_REG_IS_RESERVED(reg))
+ return false;
+ if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1)
+ return false;
+
+ return true;
+}
+
+static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ if (gpio) {
+ /* minimum pulse width of 40ns */
+ ndelay(40);
+ gpiod_set_value_cansleep(gpio, 0);
+ return 0;
+ }
+
+ /* bring all registers to their default state */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK);
+ if (ret)
+ return ret;
+
+ ndelay(40);
+
+ return regmap_clear_bits(st->regmap, AD9739A_REG_MODE,
+ AD9739A_RESET_MASK);
+}
+
+/*
+ * Recommended values (as per datasheet) for the dac clk common mode voltage
+ * and Mu controller. Look at table 29.
+ */
+static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
+ /* DAC clk common mode voltage */
+ {AD9739A_REG_CROSS_CNT1, 0x0f},
+ {AD9739A_REG_CROSS_CNT2, 0x0f},
+ /* Mu controller configuration */
+ {AD9739A_REG_PHS_DET, 0x30},
+ {AD9739A_REG_MU_DUTY, 0x80},
+ {AD9739A_REG_MU_CNT2, 0x44},
+ {AD9739A_REG_MU_CNT3, 0x6c},
+};
+
+static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
+{
+ unsigned int i = 0, lock, fsc;
+ u32 fsc_raw;
+ int ret;
+
+ ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
+ ARRAY_SIZE(ad9739a_clk_mu_ctrl));
+ if (ret)
+ return ret;
+
+ /*
+ * Try to get the MU lock. Repeat the below steps AD9739A_LOCK_N_TRIES
+ * (as specified by the datasheet) until we get the lock.
+ */
+ do {
+ ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
+ AD9739A_MU_CNT4_DEFAULT);
+ if (ret)
+ return ret;
+
+ /* Enable the Mu controller search and track mode. */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
+ AD9739A_MU_EN_MASK);
+ if (ret)
+ return ret;
+
+ /* Ensure the DLL loop is locked */
+ ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
+ lock, lock & AD9739A_MU_LOCK_MASK,
+ 0, 1000);
+ } while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+ if (i == AD9739A_LOCK_N_TRIES)
+ return dev_err_probe(dev, ret, "Mu lock timeout\n");
+
+ /* Receiver tracking and lock. Same deal as the Mu controller */
+ i = 0;
+ do {
+ ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
+ AD9739A_FINE_DEL_SKW_MASK,
+ FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
+ if (ret)
+ return ret;
+
+ /* Disable the receiver and the loop. */
+ ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Re-enable the loop so it falls out of lock and begins the
+ * search/track routine again.
+ */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
+ AD9739A_RCVR_LOOP_EN_MASK);
+ if (ret)
+ return ret;
+
+ /* Ensure the DLL loop is locked */
+ ret = regmap_read_poll_timeout(st->regmap,
+ AD9739A_REG_LVDS_REC_STAT9, lock,
+ lock == AD9739A_RCVR_TRACK_AND_LOCK,
+ 0, 1000);
+ } while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+ if (i == AD9739A_LOCK_N_TRIES)
+ return dev_err_probe(dev, ret, "Receiver lock timeout\n");
+
+ ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
+ if (ret && ret == -EINVAL)
+ return 0;
+ if (ret)
+ return ret;
+ if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid full scale current(%u) [%u %u]\n",
+ fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
+ /*
+ * IOUTFS is given by
+ * Ioutfs = 0.0226 * FSC + 8.58
+ * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
+ * order to get rid of the fractional.
+ */
+ fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
+
+ ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD9739A_REG_FSC_1,
+ AD9739A_FSC_MSB, fsc_raw >> 8);
+}
+
+static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" };
+
+static const struct iio_enum ad9739a_modes = {
+ .items = ad9739a_modes_avail,
+ .num_items = ARRAY_SIZE(ad9739a_modes_avail),
+ .get = ad9739a_oper_mode_get,
+ .set = ad9739a_oper_mode_set,
+};
+
+static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = {
+ IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+ IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+};
+
+static struct iio_chan_spec ad9739a_channels[] = {
+ {
+ .type = IIO_ALTVOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .scan_index = -1,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .output = 1,
+ .ext_info = ad9739a_ext_info,
+ .scan_type = {
+ .sign = 's',
+ .storagebits = 16,
+ .realbits = 16,
+ },
+ }
+};
+
+static const struct iio_info ad9739a_info = {
+ .read_raw = ad9739a_read_raw,
+};
+
+static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = {
+ .preenable = &ad9739a_buffer_preenable,
+ .postdisable = &ad9739a_buffer_postdisable,
+};
+
+static const struct regmap_config ad9739a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .readable_reg = ad9739a_reg_accessible,
+ .writeable_reg = ad9739a_reg_accessible,
+ .max_register = AD9739A_REG_ID,
+};
+
+static int ad9739a_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad9739a_state *st;
+ unsigned int id;
+ struct clk *clk;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
+
+ st->sample_rate = clk_get_rate(clk);
+ if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
+ AD9739A_DAC_CLK_RANGE))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid dac clk range(%lu) [%lu %lu]\n",
+ st->sample_rate, AD9739A_MIN_DAC_CLK,
+ AD9739A_MAX_DAC_CLK);
+
+ st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
+ if (ret)
+ return ret;
+
+ if (id != AD9739A_ID)
+ return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
+ id);
+
+ ret = ad9739a_reset(dev, st);
+ if (ret)
+ return ret;
+
+ ret = ad9739a_init(dev, st);
+ if (ret)
+ return ret;
+
+ st->back = devm_iio_backend_get(dev, NULL);
+ if (IS_ERR(st->back))
+ return PTR_ERR(st->back);
+
+ ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_extend_chan_spec(indio_dev, st->back,
+ &ad9739a_channels[0]);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_backend_enable(dev, st->back);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ad9739a";
+ indio_dev->info = &ad9739a_info;
+ indio_dev->channels = ad9739a_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
+ indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad9739a_of_match[] = {
+ { .compatible = "adi,ad9739a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad9739a_of_match);
+
+static const struct spi_device_id ad9739a_id[] = {
+ {"ad9739a"},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9739a_id);
+
+static struct spi_driver ad9739a_driver = {
+ .driver = {
+ .name = "ad9739a",
+ .of_match_table = ad9739a_of_match,
+ },
+ .probe = ad9739a_probe,
+ .id_table = ad9739a_id,
+};
+module_spi_driver(ad9739a_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9739 DAC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BACKEND);
--
2.44.0
^ permalink raw reply related
* [PATCH 08/10] iio: backend: add new functionality
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Nuno Sa <nuno.sa@analog.com>
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 <nuno.sa@analog.com>
---
drivers/iio/industrialio-backend.c | 144 +++++++++++++++++++++++++++++++++++++
include/linux/iio/backend.h | 49 +++++++++++++
2 files changed, 193 insertions(+)
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 2fea2bbbe47f..0a26dd8c6343 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -43,6 +43,7 @@
#include <linux/types.h>
#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
struct iio_backend {
struct list_head entry;
@@ -186,6 +187,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: 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)
+{
+ return iio_backend_op_call(back, set_sample_rate, chan, sample_rate);
+}
+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 +270,111 @@ int devm_iio_backend_request_buffer(struct device *dev,
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
+/**
+ * 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
+ * (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
+ * a way to get te 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 = iio_device_get_drvdata(indio_dev);
+
+ 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
+ * a way to get te 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 = iio_device_get_drvdata(indio_dev);
+
+ 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 *ext_info = chan->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 (ext_info && chan->ext_info != ext_info)
+ return -EOPNOTSUPP;
+ if (!chan->ext_info)
+ return 0;
+ /*
+ * !\NOTE: this will break as soon as we have multiple backends on one
+ * frontend and all of them extend channels. In that case, the core
+ * backend code has no way to get the correct backend given the
+ * iio device.
+ *
+ * One solution for this could be introducing a new backend
+ * dedicated callback in struct iio_info so we can callback into the
+ * frontend so it can give us the right backend given a chan_spec.
+ */
+ iio_device_set_drvdata(indio_dev, back);
+
+ /* Don't allow backends to get creative and force their own handlers */
+ for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
+ if (ext_info->read != iio_backend_ext_info_get)
+ return -EINVAL;
+ if (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;
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index a6d79381866e..09ff2f8f9fd8 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
+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_CW,
+ 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);
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);
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 *
--
2.44.0
^ permalink raw reply related
* [PATCH 09/10] iio: dac: add support for AXI DAC IP core
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
To: linux-iio, devicetree
Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Nuno Sa
In-Reply-To: <20240328-iio-backend-axi-dac-v1-0-afc808b3fde3@analog.com>
From: Nuno Sa <nuno.sa@analog.com>
Support the Analog Devices Generic AXI DAC IP core. The IP core is used
for interfacing with digital-to-analog (DAC) converters that require either
a high-speed serial interface (JESD204B/C) or a source synchronous parallel
interface (LVDS/CMOS). Typically (for such devices) SPI will be used for
configuration only, while this IP core handles the streaming of data into
memory via DMA.
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
MAINTAINERS | 1 +
drivers/iio/dac/Kconfig | 21 ++
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/adi-axi-dac.c | 644 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 667 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 76e872e320d7..505f28dc6da6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1413,6 +1413,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+F: drivers/iio/dac/adi-axi-dac.c
ANALOG DEVICES INC DMA DRIVERS
M: Lars-Peter Clausen <lars@metafoo.de>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 34eb40bb9529..7c0a8caa9a34 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,27 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config ADI_AXI_DAC
+ tristate "Analog Devices Generic AXI DAC IP core driver"
+ select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
+ select REGMAP_MMIO
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices Generic
+ AXI DAC IP core. The IP core is used for interfacing with
+ digital-to-analog (DAC) converters that require either a high-speed
+ serial interface (JESD204B/C) or a source synchronous parallel
+ interface (LVDS/CMOS).
+ Typically (for such devices) SPI will be used for configuration only,
+ while this IP core handles the streaming of data into memory via DMA.
+
+ Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called adi-axi-dac.
+
config LTC2688
tristate "Analog Devices LTC2688 DAC spi driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 55bf89739d14..6bcaa65434b2 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
obj-$(CONFIG_AD7293) += ad7293.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_DS4424) += ds4424.o
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
new file mode 100644
index 000000000000..0022ecb4e4bb
--- /dev/null
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI DAC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ *
+ * Copyright 2016-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/kstrtox.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+
+/*
+ * Register definitions:
+ * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
+ */
+
+/* Base controls */
+#define AXI_DAC_REG_CONFIG 0x0c
+#define AXI_DDS_DISABLE BIT(6)
+
+ /* DAC controls */
+#define AXI_DAC_REG_RSTN 0x0040
+#define AXI_DAC_RSTN_CE_N BIT(2)
+#define AXI_DAC_RSTN_MMCM_RSTN BIT(1)
+#define AXI_DAC_RSTN_RSTN BIT(0)
+#define AXI_DAC_REG_CNTRL_1 0x0044
+#define AXI_DAC_SYNC BIT(0)
+#define AXI_DAC_REG_CNTRL_2 0x0048
+#define ADI_DAC_R1_MODE BIT(4)
+#define AXI_DAC_DRP_STATUS 0x0074
+#define AXI_DAC_DRP_LOCKED BIT(17)
+/* DAC Channel controls */
+#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0408 + (c) * 0x40)
+#define AXI_DAC_SCALE_SIGN BIT(15)
+#define AXI_DAC_SCALE_INT BIT(14)
+#define AXI_DAC_SCALE GENMASK(14, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40)
+#define AXI_DAC_PHASE GENMASK(31, 16)
+#define AXI_DAC_FREQUENCY GENMASK(15, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40)
+#define AXI_DAC_DATA_SEL GENMASK(3, 0)
+
+/* 360 degrees in rad */
+#define AXI_DAC_2_PI_MEGA 6283190
+enum {
+ AXI_DAC_DATA_INTERNAL_TONE,
+ AXI_DAC_DATA_DMA = 2,
+};
+
+struct axi_dac_state {
+ struct regmap *regmap;
+ struct device *dev;
+ /*
+ * lock to protect multiple accesses to the device registers and global
+ * data/variables.
+ */
+ struct mutex lock;
+ u64 dac_clk;
+ u32 reg_config;
+ bool int_tone;
+};
+
+static int axi_dac_enable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int __val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_MMCM_RSTN);
+ if (ret)
+ return ret;
+ /*
+ * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
+ * designs really use it but if they don't we still get the lock bit
+ * set. So let's do it all the time so the code is generic.
+ */
+ ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
+ __val & AXI_DAC_DRP_LOCKED, 100, 1000);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
+}
+
+static void axi_dac_disable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ guard(mutex)(&st->lock);
+ regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+}
+
+static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
+ struct iio_dev *indio_dev)
+{
+ struct axi_dac_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 = "tx";
+
+ 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;
+ iio_buffer_set_dir(buffer, IIO_BUFFER_DIRECTION_OUT);
+
+ ret = iio_device_attach_buffer(indio_dev, buffer);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return buffer;
+}
+
+static void axi_dac_free_buffer(struct iio_backend *back,
+ struct iio_buffer *buffer)
+{
+ iio_dmaengine_buffer_free(buffer);
+}
+
+enum {
+ AXI_DAC_FREQ_TONE_1,
+ AXI_DAC_FREQ_TONE_2,
+ AXI_DAC_SCALE_TONE_1,
+ AXI_DAC_SCALE_TONE_2,
+ AXI_DAC_PHASE_TONE_1,
+ AXI_DAC_PHASE_TONE_2,
+};
+
+static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
+ unsigned int tone, unsigned int *freq)
+{
+ u32 reg, raw;
+ int ret;
+
+ if (!st->dac_clk) {
+ dev_err(st->dev, "Sampling rate is 0...\n");
+ return -EINVAL;
+ }
+
+ if (tone == AXI_DAC_FREQ_TONE_1)
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
+ *freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
+
+ return 0;
+}
+
+static int axi_dac_frequency_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone)
+{
+ unsigned int freq;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ ret = __axi_dac_frequency_get(st, chan->channel, tone, &freq);
+ if (ret)
+ return ret;
+ }
+
+ return sysfs_emit(buf, "%u\n", freq);
+}
+
+static int axi_dac_scale_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone)
+{
+ unsigned int scale, sign;
+ int ret, vals[2];
+ u32 reg, raw;
+
+ if (tone == AXI_DAC_SCALE_TONE_1)
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw);
+ raw = FIELD_GET(AXI_DAC_SCALE, raw);
+ scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT);
+
+ vals[0] = scale / MEGA;
+ vals[1] = scale % MEGA;
+
+ if (sign) {
+ vals[0] *= -1;
+ if (!vals[0])
+ vals[1] *= -1;
+ }
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+ vals);
+}
+
+static int axi_dac_phase_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone)
+{
+ u32 reg, raw, phase;
+ int ret, vals[2];
+
+ if (tone == AXI_DAC_PHASE_TONE_1)
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(AXI_DAC_PHASE, raw);
+ phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX);
+
+ vals[0] = phase / MEGA;
+ vals[1] = phase % MEGA;
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+ vals);
+}
+
+static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan,
+ u64 sample_rate, unsigned int freq,
+ unsigned int tone)
+{
+ u32 reg;
+ u16 raw;
+ int ret;
+
+ if (!sample_rate || freq > sample_rate / 2) {
+ dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n",
+ freq, sample_rate);
+ return -EINVAL;
+ }
+
+ if (tone == AXI_DAC_FREQ_TONE_1)
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+
+ raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate);
+
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, raw);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_frequency_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone)
+{
+ unsigned int freq;
+ int ret;
+
+ ret = kstrtou32(buf, 10, &freq);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq,
+ tone);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_scale_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone)
+{
+ int integer, frac, scale;
+ u32 raw = 0, reg;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+ if (ret)
+ return ret;
+
+ scale = integer * MEGA + frac;
+ if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
+ return -EINVAL;
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+ if (scale < 0) {
+ raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
+ scale *= -1;
+ }
+
+ raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
+
+ if (tone == AXI_DAC_SCALE_TONE_1)
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_write(st->regmap, reg, raw);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_phase_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone)
+{
+ int integer, frac, phase;
+ u32 raw, reg;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+ if (ret)
+ return ret;
+
+ phase = integer * MEGA + frac;
+ if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
+ return -EINVAL;
+
+ raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
+
+ if (tone == AXI_DAC_PHASE_TONE_1)
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
+ FIELD_PREP(AXI_DAC_PHASE, raw));
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (private) {
+ case AXI_DAC_FREQ_TONE_1:
+ case AXI_DAC_FREQ_TONE_2:
+ return axi_dac_frequency_set(st, chan, buf, len, private);
+ case AXI_DAC_SCALE_TONE_1:
+ case AXI_DAC_SCALE_TONE_2:
+ return axi_dac_scale_set(st, chan, buf, len, private);
+ case AXI_DAC_PHASE_TONE_1:
+ case AXI_DAC_PHASE_TONE_2:
+ return axi_dac_phase_set(st, chan, buf, len, private);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (private) {
+ case AXI_DAC_FREQ_TONE_1:
+ case AXI_DAC_FREQ_TONE_2:
+ return axi_dac_frequency_get(st, chan, buf, private);
+ case AXI_DAC_SCALE_TONE_1:
+ case AXI_DAC_SCALE_TONE_2:
+ return axi_dac_scale_get(st, chan, buf, private);
+ case AXI_DAC_PHASE_TONE_1:
+ case AXI_DAC_PHASE_TONE_2:
+ return axi_dac_phase_get(st, chan, buf, private);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
+ IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
+ IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
+ IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
+ IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
+ IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
+ IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
+ {}
+};
+
+static int axi_dac_extend_chan(struct iio_backend *back,
+ struct iio_chan_spec *chan)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ if (chan->type != IIO_ALTVOLTAGE)
+ return -EINVAL;
+ if (st->reg_config & AXI_DDS_DISABLE)
+ /* nothing to extend */
+ return 0;
+
+ chan->ext_info = axi_dac_ext_info;
+
+ return 0;
+}
+
+static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (data) {
+ case IIO_BACKEND_INTERNAL_CW:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL,
+ AXI_DAC_DATA_INTERNAL_TONE);
+ case IIO_BACKEND_EXTERNAL:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int freq;
+ int ret, tone;
+
+ if (!sample_rate)
+ return -EINVAL;
+ if (st->reg_config & AXI_DDS_DISABLE)
+ /* nothing to care if DDS is disabled */
+ return 0;
+
+ guard(mutex)(&st->lock);
+ /*
+ * If dac_clk is 0 then this must be the first time we're being notified
+ * about the interface sample rate. Hence, just update our internal
+ * variable and bail... If it's not 0, then we get the current DDS
+ * frequency (for the old rate) and update the registers for the new
+ * sample rate.
+ */
+ if (!st->dac_clk) {
+ st->dac_clk = sample_rate;
+ return 0;
+ }
+
+ for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
+ ret = __axi_dac_frequency_get(st, chan, tone, &freq);
+ if (ret)
+ return ret;
+
+ ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
+ if (ret)
+ return ret;
+ }
+
+ st->dac_clk = sample_rate;
+
+ return 0;
+}
+
+static const struct iio_backend_ops axi_dac_generic = {
+ .enable = axi_dac_enable,
+ .disable = axi_dac_disable,
+ .request_buffer = axi_dac_request_buffer,
+ .free_buffer = axi_dac_free_buffer,
+ .extend_chan_spec = axi_dac_extend_chan,
+ .ext_info_set = axi_dac_ext_info_set,
+ .ext_info_get = axi_dac_ext_info_get,
+ .data_source_set = axi_dac_data_source_set,
+ .set_sample_rate = axi_dac_set_sample_rate,
+};
+
+static const struct regmap_config axi_dac_regmap_config = {
+ .val_bits = 32,
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x0800,
+};
+
+static int axi_dac_probe(struct platform_device *pdev)
+{
+ const unsigned int *expected_ver;
+ struct axi_dac_state *st;
+ void __iomem *base;
+ unsigned int ver;
+ struct clk *clk;
+ int ret;
+
+ st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ expected_ver = device_get_match_data(&pdev->dev);
+ if (!expected_ver)
+ return -ENODEV;
+
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ st->dev = &pdev->dev;
+ st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_dac_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ /*
+ * Force disable the core. Up to the frontend to enable us. And we can
+ * still read/write registers...
+ */
+ ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+ dev_err(&pdev->dev,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
+ ADI_AXI_PCORE_VER_MINOR(*expected_ver),
+ ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+ return -ENODEV;
+ }
+
+ /* Let's get the core read only configuration */
+ ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
+ if (ret)
+ return ret;
+
+ /*
+ * In some designs, setting the R1_MODE bit to 0 (which is the default
+ * value) causes all channels of the frontend to be routed to the same
+ * DMA (so they are sampled together). This is for things like
+ * Multiple-Input and Multiple-Output (MIMO). As most of the times we
+ * want independent channels let's override the core's default value and
+ * set the R1_MODE bit.
+ */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE);
+ if (ret)
+ return ret;
+
+ mutex_init(&st->lock);
+ ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ return 0;
+}
+
+static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+
+static const struct of_device_id axi_dac_of_match[] = {
+ { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axi_dac_of_match);
+
+static struct platform_driver axi_dac_driver = {
+ .driver = {
+ .name = "adi-axi-dac",
+ .of_match_table = axi_dac_of_match,
+ },
+ .probe = axi_dac_probe,
+};
+module_platform_driver(axi_dac_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
+MODULE_IMPORT_NS(IIO_BACKEND);
--
2.44.0
^ permalink raw reply related
* Re: [PATCH v5 1/7] iio: accel: adxl345: Make data_range obsolete
From: Jonathan Cameron @ 2024-03-28 13:37 UTC (permalink / raw)
To: Lothar Rubusch
Cc: lars, Michael.Hennerich, robh+dt, krzysztof.kozlowski+dt,
conor+dt, linux-iio, devicetree, linux-kernel, eraretuya
In-Reply-To: <20240327220320.15509-2-l.rubusch@gmail.com>
On Wed, 27 Mar 2024 22:03:14 +0000
Lothar Rubusch <l.rubusch@gmail.com> wrote:
> Replace write() data_format by regmap_update_bits(), because bus specific
> pre-configuration may have happened before on the same register. For
> further updates to the data_format register then bus pre-configuration
> needs to be masked out.
>
> Remove the data_range field from the struct adxl345_data, because it is
> not used anymore.
>
> Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
> ---
> drivers/iio/accel/adxl345_core.c | 18 ++++++++++++------
> 1 file changed, 12 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
> index 8bd30a23e..35df5e372 100644
> --- a/drivers/iio/accel/adxl345_core.c
> +++ b/drivers/iio/accel/adxl345_core.c
> @@ -37,7 +37,15 @@
> #define ADXL345_POWER_CTL_MEASURE BIT(3)
> #define ADXL345_POWER_CTL_STANDBY 0x00
>
> +#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */
> +#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */
> #define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
> +#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */
> +#define ADXL345_DATA_FORMAT_MSK (ADXL345_DATA_FORMAT_RANGE | \
> + ADXL345_DATA_FORMAT_JUSTIFY | \
> + ADXL345_DATA_FORMAT_FULL_RES | \
> + ADXL345_DATA_FORMAT_SELF_TEST)
This needs renaming. It's not a mask of everything in the register, or
even just of everything related to format.
Actually I'd just not have this definition. Use the build up value
from all the submasks at the call site. Then we are just making it clear
only a subset of fields are being cleared.
Jonathan
> +
> #define ADXL345_DATA_FORMAT_2G 0
> #define ADXL345_DATA_FORMAT_4G 1
> #define ADXL345_DATA_FORMAT_8G 2
> @@ -48,7 +56,6 @@
> struct adxl345_data {
> const struct adxl345_chip_info *info;
> struct regmap *regmap;
> - u8 data_range;
> };
>
> #define ADXL345_CHANNEL(index, axis) { \
> @@ -218,15 +225,14 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap)
>
> data = iio_priv(indio_dev);
> data->regmap = regmap;
> - /* Enable full-resolution mode */
> - data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
> data->info = device_get_match_data(dev);
> if (!data->info)
> return -ENODEV;
>
> - ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
> - data->data_range);
> - if (ret < 0)
> + /* Enable full-resolution mode */
> + ret = regmap_update_bits(regmap, ADXL345_REG_DATA_FORMAT,
> + ADXL345_DATA_FORMAT_MSK, ADXL345_DATA_FORMAT_FULL_RES);
> + if (ret)
> return dev_err_probe(dev, ret, "Failed to set data range\n");
>
> indio_dev->name = data->info->name;
^ permalink raw reply
* [PATCH v1 0/2] thermal: amlogic: introduce A1 SoC family Thermal Sensor controller
From: Dmitry Rokosov @ 2024-03-28 13:37 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
It is primarily based on the G12A thermal controller, with only a slight
variation in the offset value of the efuse parameters. Therefore, this
patch series provides appropriate platform data and dt-bindings to
ensure proper support.
Dmitry Rokosov (2):
dt-bindings: thermal: amlogic: add support for A1 thermal sensor
thermal: amlogic: support A1 SoC family Thermal Sensor controller
.../bindings/thermal/amlogic,thermal.yaml | 14 +++++++++-----
drivers/thermal/amlogic_thermal.c | 10 ++++++++++
2 files changed, 19 insertions(+), 5 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v1 1/2] dt-bindings: thermal: amlogic: add support for A1 thermal sensor
From: Dmitry Rokosov @ 2024-03-28 13:37 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
In-Reply-To: <20240328133802.15651-1-ddrokosov@salutedevices.com>
Provide right compatible properties for Amlogic A1 Thermal Sensor
controller. A1 family supports only one thermal node - CPU thermal
sensor.
Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
---
.../bindings/thermal/amlogic,thermal.yaml | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
index 20f8f9b3b971..0e7f6568d385 100644
--- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
+++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
@@ -13,11 +13,15 @@ description: Binding for Amlogic Thermal
properties:
compatible:
- items:
- - enum:
- - amlogic,g12a-cpu-thermal
- - amlogic,g12a-ddr-thermal
- - const: amlogic,g12a-thermal
+ oneOf:
+ - items:
+ - enum:
+ - amlogic,g12a-cpu-thermal
+ - amlogic,g12a-ddr-thermal
+ - const: amlogic,g12a-thermal
+ - items:
+ - const: amlogic,a1-cpu-thermal
+ - const: amlogic,a1-thermal
reg:
maxItems: 1
--
2.43.0
^ permalink raw reply related
* [PATCH v1 2/2] thermal: amlogic: support A1 SoC family Thermal Sensor controller
From: Dmitry Rokosov @ 2024-03-28 13:37 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
In-Reply-To: <20240328133802.15651-1-ddrokosov@salutedevices.com>
In comparison to other Amlogic chips, there is one key difference.
The offset for the sec_ao base, also known as u_efuse_off, is special,
while other aspects remain the same.
Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
---
drivers/thermal/amlogic_thermal.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c
index 5877cde25b79..1d23afd32013 100644
--- a/drivers/thermal/amlogic_thermal.c
+++ b/drivers/thermal/amlogic_thermal.c
@@ -222,6 +222,12 @@ static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = {
.regmap_config = &amlogic_thermal_regmap_config_g12a,
};
+static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param = {
+ .u_efuse_off = 0x114,
+ .calibration_parameters = &amlogic_thermal_g12a,
+ .regmap_config = &amlogic_thermal_regmap_config_g12a,
+};
+
static const struct of_device_id of_amlogic_thermal_match[] = {
{
.compatible = "amlogic,g12a-ddr-thermal",
@@ -231,6 +237,10 @@ static const struct of_device_id of_amlogic_thermal_match[] = {
.compatible = "amlogic,g12a-cpu-thermal",
.data = &amlogic_thermal_g12a_cpu_param,
},
+ {
+ .compatible = "amlogic,a1-cpu-thermal",
+ .data = &amlogic_thermal_a1_cpu_param,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v5 7/7] iio: accel: adxl345: Add spi-3wire option
From: Jonathan Cameron @ 2024-03-28 13:39 UTC (permalink / raw)
To: Lothar Rubusch
Cc: lars, Michael.Hennerich, robh+dt, krzysztof.kozlowski+dt,
conor+dt, linux-iio, devicetree, linux-kernel, eraretuya
In-Reply-To: <20240327220320.15509-8-l.rubusch@gmail.com>
On Wed, 27 Mar 2024 22:03:20 +0000
Lothar Rubusch <l.rubusch@gmail.com> wrote:
> Add a setup function implementation to the spi module to enable spi-3wire
> as option when specified in the device-tree.
>
> Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
> ---
> drivers/iio/accel/adxl345.h | 2 ++
> drivers/iio/accel/adxl345_spi.c | 12 +++++++++++-
> 2 files changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h
> index 4ea9341d4..e6bc3591c 100644
> --- a/drivers/iio/accel/adxl345.h
> +++ b/drivers/iio/accel/adxl345.h
> @@ -30,6 +30,8 @@
> #define ADXL345_POWER_CTL_MEASURE BIT(3)
> #define ADXL345_POWER_CTL_STANDBY 0x00
>
> +#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */
> +
> #define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */
> #define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */
> #define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
> diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c
> index 1c0513bd3..f145d5c1d 100644
> --- a/drivers/iio/accel/adxl345_spi.c
> +++ b/drivers/iio/accel/adxl345_spi.c
> @@ -20,6 +20,16 @@ static const struct regmap_config adxl345_spi_regmap_config = {
> .read_flag_mask = BIT(7) | BIT(6),
> };
>
> +static int adxl345_spi_setup(struct device *dev, struct regmap *regmap)
> +{
> + struct spi_device *spi = container_of(dev, struct spi_device, dev);
> +
> + if (spi->mode & SPI_3WIRE)
> + return regmap_write(regmap, ADXL345_REG_DATA_FORMAT,
> + ADXL345_DATA_FORMAT_SPI_3WIRE);
Your earlier patch carefully (I think) left one or two fields alone, then
this write just comes in and changes them. In particular INT_INVERT.
If it doesn't makes sense to write it there, either write that bit
every time here, or leave it alone every time. Not decide on whether
to write the bit based on SPI_3WIRE or not. As far as I know they
are unconnected features.
> + return 0;
> +}
> +
> static int adxl345_spi_probe(struct spi_device *spi)
> {
> struct regmap *regmap;
> @@ -33,7 +43,7 @@ static int adxl345_spi_probe(struct spi_device *spi)
> if (IS_ERR(regmap))
> return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n");
>
> - return adxl345_core_probe(&spi->dev, regmap, NULL);
> + return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup);
> }
>
> static const struct adxl345_chip_info adxl345_spi_info = {
^ permalink raw reply
* Re: [PATCH net-next v6 10/17] net: pse-pd: Add support for PSE PIs
From: Kory Maincent @ 2024-03-28 13:43 UTC (permalink / raw)
To: Andrew Lunn
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Jonathan Corbet, Luis Chamberlain, Russ Weight,
Greg Kroah-Hartman, Rafael J. Wysocki, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Oleksij Rempel, Mark Brown,
Frank Rowand, Heiner Kallweit, Russell King, Thomas Petazzoni,
netdev, linux-kernel, linux-doc, devicetree, Dent Project
In-Reply-To: <f3bafb50-406b-444a-8411-5ddae8d84c31@lunn.ch>
On Thu, 28 Mar 2024 13:24:00 +0100
Andrew Lunn <andrew@lunn.ch> wrote:
> > +.. code-block::
> > +
> > + +-------------+
> > + | PSE PI |
> > + 8 -----+ +-------------+
> > + 7 -----+ Rail 1 |
> > + 6 -----+------+----------------------+
> > + 5 -----+ | |
> > + 4 -----+ / Rail 2 | PSE 1
> > + 3 -----+----? +-------------+
> > + 2 -----+----+---------? |
> > + 1 -----+---? +-------------+
> > + |
> > + +-------------+
>
> Is ? a standard markup character? I don't remember seeing it used like
> this before.
It seems the Documentation copy-pasted from Oleksij mail bring me few weird
characters.
I will fix it.
> > +static int of_load_single_pse_pi_pairset(struct device_node *node,
> > + struct pse_pi *pi,
> > + int pairset_num)
> > +{
> > + struct device_node *pairset_np;
> > + const char *name;
> > + int ret;
> > +
> > + ret = of_property_read_string_index(node, "pairset-names",
> > + pairset_num, &name);
> > + if (ret)
> > + return ret;
> > +
> > + if (!strcmp(name, "alternative-a")) {
> > + pi->pairset[pairset_num].pinout = ALTERNATIVE_A;
> > + } else if (!strcmp(name, "alternative-b")) {
> > + pi->pairset[pairset_num].pinout = ALTERNATIVE_B;
> > + } else {
> > + pr_err("pse: wrong pairset-names value %s\n", name);
> > + return -EINVAL;
>
> Maybe include the node path in the error message? For a 24 port
> switch, it will help find a typo in one of the ports. I would do this
> for all error messages in this code.
Ok, I will.
Thanks for your review!
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH v1 0/3] arm64: dts: amlogic: a1: introduce thermal setup
From: Dmitry Rokosov @ 2024-03-28 13:44 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
This patch series introduces thermal sensor declaration to the Meson A1
common dtsi file. It also sets up thermal zones for the AD402 reference
board. It depends on the series with A1 thermal support at [1].
Links:
[1] - https://lore.kernel.org/all/20240328133802.15651-1-ddrokosov@salutedevices.com/
Dmitry Rokosov (3):
arm64: dts: amlogic: a1: add cooling-cells for DVFS feature
arm64: dts: amlogic: a1: introduce cpu temperature sensor
arm64: dts: amlogic: ad402: setup thermal-zones
.../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 45 +++++++++++++++++++
arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 14 ++++++
2 files changed, 59 insertions(+)
--
2.43.0
^ permalink raw reply
* [PATCH v1 2/3] arm64: dts: amlogic: a1: introduce cpu temperature sensor
From: Dmitry Rokosov @ 2024-03-28 13:44 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
In-Reply-To: <20240328134459.18446-1-ddrokosov@salutedevices.com>
The A1 SoC family has only one thermal sensor for CPU temperature
measurement. It is required to set the TS clock rate to 500kHz to make
it workable.
Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
---
arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-a1.dtsi b/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
index f65d4a77ee52..6f0d0e07e037 100644
--- a/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
@@ -854,6 +854,18 @@ usb2_phy1: phy@4000 {
power-domains = <&pwrc PWRC_USB_ID>;
};
+ cpu_temp: temperature-sensor@4c00 {
+ compatible = "amlogic,a1-cpu-thermal",
+ "amlogic,a1-thermal";
+ reg = <0x0 0x4c00 0x0 0x50>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clkc_periphs CLKID_TS>;
+ assigned-clocks = <&clkc_periphs CLKID_TS>;
+ assigned-clock-rates = <500000>;
+ #thermal-sensor-cells = <0>;
+ amlogic,ao-secure = <&sec_AO>;
+ };
+
hwrng: rng@5118 {
compatible = "amlogic,meson-rng";
reg = <0x0 0x5118 0x0 0x4>;
--
2.43.0
^ permalink raw reply related
* [PATCH v1 3/3] arm64: dts: amlogic: ad402: setup thermal-zones
From: Dmitry Rokosov @ 2024-03-28 13:44 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
In-Reply-To: <20240328134459.18446-1-ddrokosov@salutedevices.com>
There is one thermal zone with 3 trip points: soc_passive, soc_hot, and
soc_critical, as well as two cooling maps.
Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
---
.../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts b/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts
index 6c02301840ff..2d22e8b45c6d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts
@@ -7,6 +7,7 @@
/dts-v1/;
#include "meson-a1.dtsi"
+#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/gpio/gpio.h>
@@ -177,6 +178,50 @@ codec {
};
};
};
+
+ thermal-zones {
+ soc_thermal: soc_thermal {
+ polling-delay = <1000>;
+ polling-delay-passive = <100>;
+ sustainable-power = <130>;
+
+ thermal-sensors = <&cpu_temp>;
+
+ trips {
+ soc_passive: soc-passive {
+ temperature = <70000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+
+ soc_hot: soc-hot {
+ temperature = <85000>;
+ hysteresis = <5000>;
+ type = "hot";
+ };
+
+ soc_critical: soc-critical {
+ temperature = <110000>;
+ hysteresis = <1000>;
+ type = "critical";
+ };
+ };
+
+ soc_cooling_maps: cooling-maps {
+ map0 {
+ trip = <&soc_passive>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+
+ map1 {
+ trip = <&soc_hot>;
+ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ };
+ };
+ };
+ };
};
/* Bluetooth HCI H4 */
--
2.43.0
^ permalink raw reply related
* [PATCH v1 1/3] arm64: dts: amlogic: a1: add cooling-cells for DVFS feature
From: Dmitry Rokosov @ 2024-03-28 13:44 UTC (permalink / raw)
To: neil.armstrong, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel, Dmitry Rokosov
In-Reply-To: <20240328134459.18446-1-ddrokosov@salutedevices.com>
It's used for CPU with DVFS feature to specify minimum and maximum
cooling state used in the reference.
Without these values DVFS will not work and dtbs_check will raise the
error.
Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
---
arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-a1.dtsi b/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
index fbee986421f1..f65d4a77ee52 100644
--- a/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
@@ -32,6 +32,7 @@ cpu0: cpu@0 {
reg = <0x0 0x0>;
enable-method = "psci";
next-level-cache = <&l2>;
+ #cooling-cells = <2>;
};
cpu1: cpu@1 {
@@ -40,6 +41,7 @@ cpu1: cpu@1 {
reg = <0x0 0x1>;
enable-method = "psci";
next-level-cache = <&l2>;
+ #cooling-cells = <2>;
};
l2: l2-cache0 {
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 7/7] regulator: mcp16502: Update the names from buck regulators
From: Mark Brown @ 2024-03-28 13:45 UTC (permalink / raw)
To: Conor Dooley
Cc: Mihai Sain, robh, krzysztof.kozlowski+dt, conor+dt, nicolas.ferre,
alexandre.belloni, claudiu.beznea, lgirdwood, andrei.simion,
devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20240327-agreed-routine-0cc60186876b@spud>
[-- Attachment #1: Type: text/plain, Size: 911 bytes --]
On Wed, Mar 27, 2024 at 04:28:49PM +0000, Conor Dooley wrote:
> On Wed, Mar 27, 2024 at 12:17:24PM +0200, Mihai Sain wrote:
> > Use generic names for buck regulators to avoid any confusion.
> > Update the names from buck regulators in order to match
> > the datasheet block diagram for the buck regulators.
> I know the regulator core will create dummy regulators when they are not
> provided in the devicetree, so I am not 100% on how backwards
> compatibility works here.
> You'll end up with a bunch of dummies and therefore the regulator-names
> and constraints on the regulator will be lost, no?
> Can you explain how is this backwards compatible with the old
> devicetrees?
It quite simply isn't backwards compatible. The original driver looks
to be pretty broken but this breaks compatibility, we'd need a
transition plan of some kind which probably needs some core work to cope
with fallback names.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH v5 6/6] ARM: dts: imx: Add UNI-T UTi260B thermal camera board
From: Shawn Guo @ 2024-03-28 13:54 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Shawn Guo, Sascha Hauer, Fabio Estevam, imx, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Pengutronix Kernel Team,
Dong Aisheng, Linus Walleij, Dmitry Torokhov, linux-arm-kernel,
devicetree, linux-kernel, Stefan Wahren
In-Reply-To: <20240226212740.2019837-7-sre@kernel.org>
On Mon, Feb 26, 2024 at 10:26:28PM +0100, Sebastian Reichel wrote:
> Add DT for the UNI-T UTi260B handheld thermal camera.
>
> Reviewed-by: Stefan Wahren <wahrenst@gmx.net>
> Signed-off-by: Sebastian Reichel <sre@kernel.org>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH v1 1/2] dt-bindings: thermal: amlogic: add support for A1 thermal sensor
From: neil.armstrong @ 2024-03-28 14:07 UTC (permalink / raw)
To: Dmitry Rokosov, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel
In-Reply-To: <20240328133802.15651-2-ddrokosov@salutedevices.com>
Hi,
On 28/03/2024 14:37, Dmitry Rokosov wrote:
> Provide right compatible properties for Amlogic A1 Thermal Sensor
> controller. A1 family supports only one thermal node - CPU thermal
> sensor.
>
> Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
> ---
> .../bindings/thermal/amlogic,thermal.yaml | 14 +++++++++-----
> 1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
> index 20f8f9b3b971..0e7f6568d385 100644
> --- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
> +++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml
> @@ -13,11 +13,15 @@ description: Binding for Amlogic Thermal
>
> properties:
> compatible:
> - items:
> - - enum:
> - - amlogic,g12a-cpu-thermal
> - - amlogic,g12a-ddr-thermal
> - - const: amlogic,g12a-thermal
> + oneOf:
> + - items:
> + - enum:
> + - amlogic,g12a-cpu-thermal
> + - amlogic,g12a-ddr-thermal
> + - const: amlogic,g12a-thermal
> + - items:
> + - const: amlogic,a1-cpu-thermal
> + - const: amlogic,a1-thermal
In this case you can just use "amlogic,a1-cpu-thermal" or "amlogic,a1-thermal", no need for a fallback.
Thanks,
Neil
>
> reg:
> maxItems: 1
^ permalink raw reply
* Re: [PATCH v1 2/2] thermal: amlogic: support A1 SoC family Thermal Sensor controller
From: neil.armstrong @ 2024-03-28 14:08 UTC (permalink / raw)
To: Dmitry Rokosov, jbrunet, mturquette, khilman, martin.blumenstingl,
glaroque, rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh+dt,
krzysztof.kozlowski+dt, conor+dt
Cc: kernel, rockosov, linux-amlogic, linux-pm, linux-kernel,
devicetree, linux-arm-kernel
In-Reply-To: <20240328133802.15651-3-ddrokosov@salutedevices.com>
On 28/03/2024 14:37, Dmitry Rokosov wrote:
> In comparison to other Amlogic chips, there is one key difference.
> The offset for the sec_ao base, also known as u_efuse_off, is special,
> while other aspects remain the same.
>
> Signed-off-by: Dmitry Rokosov <ddrokosov@salutedevices.com>
> ---
> drivers/thermal/amlogic_thermal.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/drivers/thermal/amlogic_thermal.c b/drivers/thermal/amlogic_thermal.c
> index 5877cde25b79..1d23afd32013 100644
> --- a/drivers/thermal/amlogic_thermal.c
> +++ b/drivers/thermal/amlogic_thermal.c
> @@ -222,6 +222,12 @@ static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = {
> .regmap_config = &amlogic_thermal_regmap_config_g12a,
> };
>
> +static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param = {
> + .u_efuse_off = 0x114,
> + .calibration_parameters = &amlogic_thermal_g12a,
> + .regmap_config = &amlogic_thermal_regmap_config_g12a,
> +};
> +
> static const struct of_device_id of_amlogic_thermal_match[] = {
> {
> .compatible = "amlogic,g12a-ddr-thermal",
> @@ -231,6 +237,10 @@ static const struct of_device_id of_amlogic_thermal_match[] = {
> .compatible = "amlogic,g12a-cpu-thermal",
> .data = &amlogic_thermal_g12a_cpu_param,
> },
> + {
> + .compatible = "amlogic,a1-cpu-thermal",
> + .data = &amlogic_thermal_a1_cpu_param,
> + },
> { /* sentinel */ }
> };
> MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match);
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Keep it even it you change the compatible,
Thanks,
Neil
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox