* [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support
@ 2025-03-27 21:24 Marcelo Schmitt
2025-03-27 21:24 ` [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload Marcelo Schmitt
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Marcelo Schmitt @ 2025-03-27 21:24 UTC (permalink / raw)
To: linux-iio, linux-doc, linux-kernel
Cc: jic23, lars, Michael.Hennerich, corbet, dlechner,
marcelo.schmitt1
This patch series extends the ad4000 driver to support SPI offloading.
In addition to that, ad4000 IIO documentation is expanded to:
- list PulSAR parts supported by the ad4000 driver.
- describe some characteristics of AD4000 IIO device.
- describe changes when SPI offload is being used.
Change log v3 -> v4
[IIO driver]
- No longer changing spi_device CS timing parameters.
- SPI offload messages now set only one transfer.
- Degraded offload_xfers from array to single entry field.
- Adjusted ad4000_spi_offload_setup() comment.
- Dropped t_quiet1_ns since it's not used anymore.
- Fixed include order.
- Fixed typo: withouth -> without.
[Documentation]
- Picked up review tags.
- Made phrase about SPI offload engine more generic.
- Complemented SPI offload section with supported wiring configurations.
Change log v2 -> v3
- Dropped patch 1 of v2 series and reverted buffer endianness and SPI transfer
bits_per_word configurations to the way they were in v1.
- Removed dummy transfer in ad4000_prepare_offload_turbo_message().
- Unified offload message preparation.
- Removed spi->cs_hold.value to use cs_inactive to set CNV/CS high time.
- Adjusted max sample rate for AD7983 and AD7984.
- Dropped AD4000_TCONV_NS and made all time related constants a time_spec field.
- Removed _offl param from _CHANNELS() macros and added a comment about that.
- Commented IIO_BE check in ad4000_prepare_3wire_mode_message().
- Now using predisable to balance with postenable.
- Put spi/offload/consumer.h include right after spi/spi.h.
- Removed unrelated brackets in info_mask_separate_available assignment
- Added blank line after if.
Change log v1 -> v2
- Fixed passing inappropriate pointer instead of ret to dev_err_probe()
- [new patch] Set transfer bits_per_word to have data in CPU endianness
- Set iio_dev num_channels close to where channels is set.
- Complement offload message comment about first sample being invalid
- Document why the first buffer sample is invalid when offloading
- Added blank line before a 'simple return'.
Link to v3: https://lore.kernel.org/linux-iio/cover.1742992305.git.marcelo.schmitt@analog.com/
Link to v2: https://lore.kernel.org/linux-iio/cover.1742394806.git.marcelo.schmitt@analog.com/
Link to v1: https://lore.kernel.org/linux-iio/cover.1741970538.git.marcelo.schmitt@analog.com/
Marcelo Schmitt (4):
iio: adc: ad4000: Add support for SPI offload
Documentation: iio: ad4000: Add new supported parts
Documentation: iio: ad4000: Add IIO Device characteristics section
Documentation: iio: ad4000: Describe offload support
Documentation/iio/ad4000.rst | 91 ++++++++-
drivers/iio/adc/Kconfig | 7 +-
drivers/iio/adc/ad4000.c | 382 +++++++++++++++++++++++++++++++----
3 files changed, 444 insertions(+), 36 deletions(-)
base-commit: 9f36acefb2621d980734a5bb7d74e0e24e0af166
prerequisite-patch-id: 3d517eef53a799adba5922815fe684b913e36773
--
2.47.2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload
2025-03-27 21:24 [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support Marcelo Schmitt
@ 2025-03-27 21:24 ` Marcelo Schmitt
2025-04-09 17:22 ` David Lechner
2025-03-27 21:24 ` [PATCH v4 2/4] Documentation: iio: ad4000: Add new supported parts Marcelo Schmitt
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Marcelo Schmitt @ 2025-03-27 21:24 UTC (permalink / raw)
To: linux-iio, linux-doc, linux-kernel
Cc: jic23, lars, Michael.Hennerich, corbet, dlechner,
marcelo.schmitt1
FPGA HDL projects can include a PWM generator in addition to SPI-Engine.
The PWM IP is used to trigger SPI-Engine offload modules that in turn set
SPI-Engine to execute transfers to poll data from the ADC. That allows data
to be read at the maximum sample rates. Also, it is possible to set a
specific sample rate by setting the proper PWM duty cycle and related state
parameters, thus allowing an adjustable ADC sample rate when a PWM (offload
trigger) is used in combination with SPI-Engine.
Add support for SPI offload.
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
drivers/iio/adc/Kconfig | 7 +-
drivers/iio/adc/ad4000.c | 382 +++++++++++++++++++++++++++++++++++----
2 files changed, 354 insertions(+), 35 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 75ed633a3c43..eb0ba7457084 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -25,10 +25,15 @@ config AD4000
tristate "Analog Devices AD4000 ADC Driver"
depends on SPI
select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
+ select SPI_OFFLOAD
help
Say yes here to build support for Analog Devices AD4000 high speed
- SPI analog to digital converters (ADC).
+ SPI analog to digital converters (ADC). If intended to use with
+ SPI offloading support, it is recommended to enable
+ CONFIG_SPI_AXI_SPI_ENGINE, CONFIG_PWM_AXI_PWMGEN, and
+ CONFIG_SPI_OFFLOAD_TRIGGER_PWM.
To compile this driver as a module, choose M here: the module will be
called ad4000.
diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c
index 4fe8dee48da9..e69a9d2a3e8c 100644
--- a/drivers/iio/adc/ad4000.c
+++ b/drivers/iio/adc/ad4000.c
@@ -15,12 +15,14 @@
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
+#include <linux/spi/offload/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/util_macros.h>
-#include <linux/iio/iio.h>
+#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
@@ -32,10 +34,11 @@
/* AD4000 Configuration Register programmable bits */
#define AD4000_CFG_SPAN_COMP BIT(3) /* Input span compression */
#define AD4000_CFG_HIGHZ BIT(2) /* High impedance mode */
+#define AD4000_CFG_TURBO BIT(1) /* Turbo mode */
#define AD4000_SCALE_OPTIONS 2
-#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \
+#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access, _offl)\
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
@@ -43,54 +46,65 @@
.channel = 0, \
.channel2 = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \
.info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
.scan_index = 0, \
.scan_type = { \
.sign = _sign, \
.realbits = _real_bits, \
.storagebits = _storage_bits, \
- .shift = _storage_bits - _real_bits, \
- .endianness = IIO_BE, \
+ .shift = (_offl ? 0 : _storage_bits - _real_bits), \
+ .endianness = _offl ? IIO_CPU : IIO_BE \
}, \
}
-#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \
+#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access, _offl) \
__AD4000_DIFF_CHANNEL((_sign), (_real_bits), \
- ((_real_bits) > 16 ? 32 : 16), (_reg_access))
+ (((_offl) || ((_real_bits) > 16)) ? 32 : 16), \
+ (_reg_access), (_offl))
+/*
+ * When SPI offload is configured, transfers are executed without CPU
+ * intervention so no soft timestamp can be recorded when transfers run.
+ * Because of that, the macros that set timestamp channel are only used when
+ * transfers are not offloaded.
+ */
#define AD4000_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \
{ \
- AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access), \
+ AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access, 0), \
IIO_CHAN_SOFT_TIMESTAMP(1), \
}
-#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\
+#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, \
+ _reg_access, _offl) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = 0, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_OFFSET), \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \
.info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
.scan_index = 0, \
.scan_type = { \
.sign = _sign, \
.realbits = _real_bits, \
.storagebits = _storage_bits, \
- .shift = _storage_bits - _real_bits, \
- .endianness = IIO_BE, \
+ .shift = (_offl ? 0 : _storage_bits - _real_bits), \
+ .endianness = _offl ? IIO_CPU : IIO_BE \
}, \
}
-#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \
+#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access, _offl) \
__AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \
- ((_real_bits) > 16 ? 32 : 16), (_reg_access))
+ (((_offl) || ((_real_bits) > 16)) ? 32 : 16),\
+ (_reg_access), (_offl))
#define AD4000_PSEUDO_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \
{ \
- AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access), \
+ AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access, 0), \
IIO_CHAN_SOFT_TIMESTAMP(1), \
}
@@ -184,212 +198,298 @@ struct ad4000_chip_info {
const char *dev_name;
struct iio_chan_spec chan_spec[2];
struct iio_chan_spec reg_access_chan_spec[2];
+ struct iio_chan_spec offload_chan_spec;
+ struct iio_chan_spec reg_access_offload_chan_spec;
const struct ad4000_time_spec *time_spec;
bool has_hardware_gain;
+ int max_rate_hz;
};
static const struct ad4000_chip_info ad4000_chip_info = {
.dev_name = "ad4000",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 2 * MEGA,
};
static const struct ad4000_chip_info ad4001_chip_info = {
.dev_name = "ad4001",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 2 * MEGA,
};
static const struct ad4000_chip_info ad4002_chip_info = {
.dev_name = "ad4002",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 2 * MEGA,
};
static const struct ad4000_chip_info ad4003_chip_info = {
.dev_name = "ad4003",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 2 * MEGA,
};
static const struct ad4000_chip_info ad4004_chip_info = {
.dev_name = "ad4004",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad4005_chip_info = {
.dev_name = "ad4005",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad4006_chip_info = {
.dev_name = "ad4006",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad4007_chip_info = {
.dev_name = "ad4007",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad4008_chip_info = {
.dev_name = "ad4008",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad4010_chip_info = {
.dev_name = "ad4010",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad4011_chip_info = {
.dev_name = "ad4011",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1),
.time_spec = &ad4000_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad4020_chip_info = {
.dev_name = "ad4020",
.chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1),
.time_spec = &ad4020_t_spec,
+ .max_rate_hz = 1800 * KILO,
};
static const struct ad4000_chip_info ad4021_chip_info = {
.dev_name = "ad4021",
.chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1),
.time_spec = &ad4020_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad4022_chip_info = {
.dev_name = "ad4022",
.chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1),
.time_spec = &ad4020_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info adaq4001_chip_info = {
.dev_name = "adaq4001",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1),
.time_spec = &ad4000_t_spec,
.has_hardware_gain = true,
+ .max_rate_hz = 2 * MEGA,
};
static const struct ad4000_chip_info adaq4003_chip_info = {
.dev_name = "adaq4003",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
+ .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1),
.time_spec = &ad4000_t_spec,
.has_hardware_gain = true,
+ .max_rate_hz = 2 * MEGA,
};
static const struct ad4000_chip_info ad7685_chip_info = {
.dev_name = "ad7685",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
.time_spec = &ad7687_t_spec,
+ .max_rate_hz = 250 * KILO,
};
static const struct ad4000_chip_info ad7686_chip_info = {
.dev_name = "ad7686",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
.time_spec = &ad7686_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad7687_chip_info = {
.dev_name = "ad7687",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1),
.time_spec = &ad7687_t_spec,
+ .max_rate_hz = 250 * KILO,
};
static const struct ad4000_chip_info ad7688_chip_info = {
.dev_name = "ad7688",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1),
.time_spec = &ad7686_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad7690_chip_info = {
.dev_name = "ad7690",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
.time_spec = &ad7690_t_spec,
+ .max_rate_hz = 400 * KILO,
};
static const struct ad4000_chip_info ad7691_chip_info = {
.dev_name = "ad7691",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
.time_spec = &ad7691_t_spec,
+ .max_rate_hz = 250 * KILO,
};
static const struct ad4000_chip_info ad7693_chip_info = {
.dev_name = "ad7693",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1),
.time_spec = &ad7686_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad7942_chip_info = {
.dev_name = "ad7942",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 14, 0, 1),
.time_spec = &ad7687_t_spec,
+ .max_rate_hz = 250 * KILO,
};
static const struct ad4000_chip_info ad7946_chip_info = {
.dev_name = "ad7946",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 14, 0, 1),
.time_spec = &ad7686_t_spec,
+ .max_rate_hz = 500 * KILO,
};
static const struct ad4000_chip_info ad7980_chip_info = {
.dev_name = "ad7980",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
.time_spec = &ad7980_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad7982_chip_info = {
.dev_name = "ad7982",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
.time_spec = &ad7980_t_spec,
+ .max_rate_hz = 1 * MEGA,
};
static const struct ad4000_chip_info ad7983_chip_info = {
.dev_name = "ad7983",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
.time_spec = &ad7983_t_spec,
+ .max_rate_hz = 1 * MEGA + 333 * KILO + 333,
};
static const struct ad4000_chip_info ad7984_chip_info = {
.dev_name = "ad7984",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
+ .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1),
.time_spec = &ad7983_t_spec,
+ .max_rate_hz = 1 * MEGA + 333 * KILO + 333,
};
static const struct ad4000_chip_info ad7988_1_chip_info = {
.dev_name = "ad7988-1",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
.time_spec = &ad7988_1_t_spec,
+ .max_rate_hz = 100 * KILO,
};
static const struct ad4000_chip_info ad7988_5_chip_info = {
.dev_name = "ad7988-5",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
+ .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1),
.time_spec = &ad7686_t_spec,
+ .max_rate_hz = 500 * KILO,
+};
+
+static const struct spi_offload_config ad4000_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
};
struct ad4000_state {
@@ -397,6 +497,13 @@ struct ad4000_state {
struct gpio_desc *cnv_gpio;
struct spi_transfer xfers[2];
struct spi_message msg;
+ struct spi_transfer offload_xfer;
+ struct spi_message offload_msg;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ bool using_offload;
+ unsigned long offload_trigger_hz;
+ int max_rate_hz;
struct mutex lock; /* Protect read modify write cycle */
int vref_mv;
enum ad4000_sdi sdi_pin;
@@ -411,8 +518,10 @@ struct ad4000_state {
*/
struct {
union {
- __be16 sample_buf16;
- __be32 sample_buf32;
+ __be16 sample_buf16_be;
+ __be32 sample_buf32_be;
+ u16 sample_buf16;
+ u32 sample_buf32;
} data;
aligned_s64 timestamp;
} scan __aligned(IIO_DMA_MINALIGN);
@@ -487,6 +596,25 @@ static int ad4000_read_reg(struct ad4000_state *st, unsigned int *val)
return ret;
}
+static int ad4000_set_sampling_freq(struct ad4000_state *st, int freq)
+{
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+ .periodic = {
+ .frequency_hz = freq,
+ },
+ };
+ int ret;
+
+ ret = spi_offload_trigger_validate(st->offload_trigger, &config);
+ if (ret)
+ return ret;
+
+ st->offload_trigger_hz = config.periodic.frequency_hz;
+
+ return 0;
+}
+
static int ad4000_convert_and_acquire(struct ad4000_state *st)
{
int ret;
@@ -515,10 +643,17 @@ static int ad4000_single_conversion(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- if (chan->scan_type.storagebits > 16)
- sample = be32_to_cpu(st->scan.data.sample_buf32);
- else
- sample = be16_to_cpu(st->scan.data.sample_buf16);
+ if (chan->scan_type.endianness == IIO_BE) {
+ if (chan->scan_type.realbits > 16)
+ sample = be32_to_cpu(st->scan.data.sample_buf32_be);
+ else
+ sample = be16_to_cpu(st->scan.data.sample_buf16_be);
+ } else {
+ if (chan->scan_type.realbits > 16)
+ sample = st->scan.data.sample_buf32;
+ else
+ sample = st->scan.data.sample_buf16;
+ }
sample >>= chan->scan_type.shift;
@@ -554,6 +689,9 @@ static int ad4000_read_raw(struct iio_dev *indio_dev,
if (st->span_comp)
*val = mult_frac(st->vref_mv, 1, 10);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->offload_trigger_hz;
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -620,6 +758,7 @@ static int ad4000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
+ struct ad4000_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
@@ -629,6 +768,15 @@ static int ad4000_write_raw(struct iio_dev *indio_dev,
ret = __ad4000_write_raw(indio_dev, chan, val2);
iio_device_release_direct(indio_dev);
return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val < 1 || val > st->max_rate_hz)
+ return -EINVAL;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad4000_set_sampling_freq(st, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
default:
return -EINVAL;
}
@@ -659,10 +807,114 @@ static const struct iio_info ad4000_reg_access_info = {
.write_raw_get_fmt = &ad4000_write_raw_get_fmt,
};
+static const struct iio_info ad4000_offload_info = {
+ .read_raw = &ad4000_read_raw,
+ .write_raw = &ad4000_write_raw,
+ .write_raw_get_fmt = &ad4000_write_raw_get_fmt,
+};
+
static const struct iio_info ad4000_info = {
.read_raw = &ad4000_read_raw,
};
+static int ad4000_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad4000_state *st = iio_priv(indio_dev);
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+ .periodic = {
+ .frequency_hz = st->offload_trigger_hz,
+ },
+ };
+
+ return spi_offload_trigger_enable(st->offload, st->offload_trigger,
+ &config);
+}
+
+static int ad4000_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad4000_state *st = iio_priv(indio_dev);
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad4000_offload_buffer_setup_ops = {
+ .postenable = &ad4000_offload_buffer_postenable,
+ .predisable = &ad4000_offload_buffer_predisable,
+};
+
+static int ad4000_spi_offload_setup(struct iio_dev *indio_dev,
+ struct ad4000_state *st)
+{
+ struct spi_device *spi = st->spi;
+ struct device *dev = &spi->dev;
+ struct dma_chan *rx_dma;
+ int ret;
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "Failed to get offload trigger\n");
+
+ ret = ad4000_set_sampling_freq(st, st->max_rate_hz);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set sampling frequency\n");
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma),
+ "Failed to get offload RX DMA\n");
+
+ ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
+ IIO_BUFFER_DIRECTION_IN);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to setup DMA buffer\n");
+
+ return 0;
+}
+
+/*
+ * This executes a data sample transfer when using SPI offloading. The device
+ * connections should be in "3-wire" mode, selected either when the adi,sdi-pin
+ * device tree property is absent or set to "high". Also, the ADC CNV pin must
+ * be connected to a SPI controller CS (it can't be connected to a GPIO).
+ *
+ * In order to achieve the maximum sample rate, we only do one transfer per
+ * SPI offload trigger. Because the ADC output has a one sample latency (delay)
+ * when the device is wired in "3-wire" mode and only one transfer per sample is
+ * being made in turbo mode, the first data sample is not valid because it
+ * contains the output of an earlier conversion result. We also set transfer
+ * `bits_per_word` to achieve higher throughput by using the minimum number of
+ * SCLK cycles. Also, a delay is added to make sure we meet the minimum quiet
+ * time before releasing the CS line.
+ *
+ * Note that, with `bits_per_word` set to the number of ADC precision bits,
+ * transfers use larger word sizes that get stored in 'in-memory wordsizes' that
+ * are always in native CPU byte order. Because of that, IIO buffer elements
+ * ought to be read in CPU endianness which requires setting IIO scan_type
+ * endianness accordingly (i.e. IIO_CPU).
+ */
+static int ad4000_prepare_offload_message(struct ad4000_state *st,
+ const struct iio_chan_spec *chan)
+{
+ struct spi_transfer *xfer = &st->offload_xfer;
+
+ xfer->bits_per_word = chan->scan_type.realbits;
+ xfer->len = chan->scan_type.realbits > 16 ? 4 : 2;
+ xfer->delay.value = st->time_spec->t_quiet2_ns;
+ xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+
+ spi_message_init_with_transfers(&st->offload_msg, xfer, 1);
+ st->offload_msg.offload = st->offload;
+
+ return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->offload_msg);
+}
+
/*
* This executes a data sample transfer for when the device connections are
* in "3-wire" mode, selected when the adi,sdi-pin device tree property is
@@ -690,6 +942,15 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
xfers[1].rx_buf = &st->scan.data;
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+
+ /*
+ * If the device is set up for SPI offloading, IIO channel scan_type is
+ * set to IIO_CPU. When that is the case, use larger SPI word sizes for
+ * single-shot reads too. Thus, sample data can be correctly handled in
+ * ad4000_single_conversion() according to scan_type endianness.
+ */
+ if (chan->scan_type.endianness != IIO_BE)
+ xfers[1].bits_per_word = chan->scan_type.realbits;
xfers[1].delay.value = st->time_spec->t_quiet2_ns;
xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
@@ -733,6 +994,9 @@ static int ad4000_config(struct ad4000_state *st)
if (device_property_present(&st->spi->dev, "adi,high-z-input"))
reg_val |= FIELD_PREP(AD4000_CFG_HIGHZ, 1);
+ if (st->using_offload)
+ reg_val |= FIELD_PREP(AD4000_CFG_TURBO, 1);
+
return ad4000_write_reg(st, reg_val);
}
@@ -755,6 +1019,7 @@ static int ad4000_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
st->spi = spi;
st->time_spec = chip->time_spec;
+ st->max_rate_hz = chip->max_rate_hz;
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies),
ad4000_power_supplies);
@@ -772,6 +1037,26 @@ static int ad4000_probe(struct spi_device *spi)
return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
"Failed to get CNV GPIO");
+ st->offload = devm_spi_offload_get(dev, spi, &ad4000_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get offload\n");
+
+ st->using_offload = !IS_ERR(st->offload);
+ if (st->using_offload) {
+ indio_dev->setup_ops = &ad4000_offload_buffer_setup_ops;
+ ret = ad4000_spi_offload_setup(indio_dev, st);
+ if (ret)
+ return ret;
+ } else {
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad4000_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
+ }
+
ret = device_property_match_property_string(dev, "adi,sdi-pin",
ad4000_sdi_pin,
ARRAY_SIZE(ad4000_sdi_pin));
@@ -784,7 +1069,6 @@ static int ad4000_probe(struct spi_device *spi)
switch (st->sdi_pin) {
case AD4000_SDI_MOSI:
indio_dev->info = &ad4000_reg_access_info;
- indio_dev->channels = chip->reg_access_chan_spec;
/*
* In "3-wire mode", the ADC SDI line must be kept high when
@@ -796,9 +1080,26 @@ static int ad4000_probe(struct spi_device *spi)
if (ret < 0)
return ret;
+ if (st->using_offload) {
+ indio_dev->channels = &chip->reg_access_offload_chan_spec;
+ indio_dev->num_channels = 1;
+ ret = ad4000_prepare_offload_message(st, indio_dev->channels);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to optimize SPI msg\n");
+ } else {
+ indio_dev->channels = chip->reg_access_chan_spec;
+ indio_dev->num_channels = ARRAY_SIZE(chip->reg_access_chan_spec);
+ }
+
+ /*
+ * Call ad4000_prepare_3wire_mode_message() so single-shot read
+ * SPI messages are always initialized.
+ */
ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret,
+ "Failed to optimize SPI msg\n");
ret = ad4000_config(st);
if (ret < 0)
@@ -806,19 +1107,38 @@ static int ad4000_probe(struct spi_device *spi)
break;
case AD4000_SDI_VIO:
- indio_dev->info = &ad4000_info;
- indio_dev->channels = chip->chan_spec;
+ if (st->using_offload) {
+ indio_dev->info = &ad4000_offload_info;
+ indio_dev->channels = &chip->offload_chan_spec;
+ indio_dev->num_channels = 1;
+
+ ret = ad4000_prepare_offload_message(st, indio_dev->channels);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to optimize SPI msg\n");
+ } else {
+ indio_dev->info = &ad4000_info;
+ indio_dev->channels = chip->chan_spec;
+ indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec);
+ }
+
ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret,
+ "Failed to optimize SPI msg\n");
break;
case AD4000_SDI_CS:
+ if (st->using_offload)
+ return dev_err_probe(dev, -EPROTONOSUPPORT,
+ "Unsupported sdi-pin + offload config\n");
indio_dev->info = &ad4000_info;
indio_dev->channels = chip->chan_spec;
+ indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec);
ret = ad4000_prepare_4wire_mode_message(st, &indio_dev->channels[0]);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret,
+ "Failed to optimize SPI msg\n");
break;
case AD4000_SDI_GND:
@@ -830,7 +1150,6 @@ static int ad4000_probe(struct spi_device *spi)
}
indio_dev->name = chip->dev_name;
- indio_dev->num_channels = 2;
ret = devm_mutex_init(dev, &st->lock);
if (ret)
@@ -853,12 +1172,6 @@ static int ad4000_probe(struct spi_device *spi)
ad4000_fill_scale_tbl(st, &indio_dev->channels[0]);
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- &iio_pollfunc_store_time,
- &ad4000_trigger_handler, NULL);
- if (ret)
- return ret;
-
return devm_iio_device_register(dev, indio_dev);
}
@@ -947,3 +1260,4 @@ module_spi_driver(ad4000_driver);
MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD4000 ADC driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
--
2.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 2/4] Documentation: iio: ad4000: Add new supported parts
2025-03-27 21:24 [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support Marcelo Schmitt
2025-03-27 21:24 ` [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload Marcelo Schmitt
@ 2025-03-27 21:24 ` Marcelo Schmitt
2025-03-27 21:25 ` [PATCH v4 3/4] Documentation: iio: ad4000: Add IIO Device characteristics section Marcelo Schmitt
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Marcelo Schmitt @ 2025-03-27 21:24 UTC (permalink / raw)
To: linux-iio, linux-doc, linux-kernel
Cc: jic23, lars, Michael.Hennerich, corbet, dlechner,
marcelo.schmitt1
Commit <c3948d090080> ("iio: adc: ad4000: Add support for PulSAR devices"),
extended the ad4000 driver supports many single-channel PulSAR devices.
Update IIO ad4000 documentation with the extra list of supported devices.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Documentation/iio/ad4000.rst | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/Documentation/iio/ad4000.rst b/Documentation/iio/ad4000.rst
index de8fd3ae6e62..5578a9cfd9d5 100644
--- a/Documentation/iio/ad4000.rst
+++ b/Documentation/iio/ad4000.rst
@@ -4,7 +4,7 @@
AD4000 driver
=============
-Device driver for Analog Devices Inc. AD4000 series of ADCs.
+Device driver for Analog Devices Inc. AD4000 series of ADCs and similar devices.
Supported devices
=================
@@ -25,6 +25,21 @@ Supported devices
* `AD4022 <https://www.analog.com/AD4022>`_
* `ADAQ4001 <https://www.analog.com/ADAQ4001>`_
* `ADAQ4003 <https://www.analog.com/ADAQ4003>`_
+* `AD7685 <https://www.analog.com/AD7685>`_
+* `AD7686 <https://www.analog.com/AD7686>`_
+* `AD7687 <https://www.analog.com/AD7687>`_
+* `AD7688 <https://www.analog.com/AD7688>`_
+* `AD7690 <https://www.analog.com/AD7690>`_
+* `AD7691 <https://www.analog.com/AD7691>`_
+* `AD7693 <https://www.analog.com/AD7693>`_
+* `AD7942 <https://www.analog.com/AD7942>`_
+* `AD7946 <https://www.analog.com/AD7946>`_
+* `AD7980 <https://www.analog.com/AD7980>`_
+* `AD7982 <https://www.analog.com/AD7982>`_
+* `AD7983 <https://www.analog.com/AD7983>`_
+* `AD7984 <https://www.analog.com/AD7984>`_
+* `AD7988-1 <https://www.analog.com/AD7988-1>`_
+* `AD7988-5 <https://www.analog.com/AD7988-5>`_
Wiring connections
------------------
--
2.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 3/4] Documentation: iio: ad4000: Add IIO Device characteristics section
2025-03-27 21:24 [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support Marcelo Schmitt
2025-03-27 21:24 ` [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload Marcelo Schmitt
2025-03-27 21:24 ` [PATCH v4 2/4] Documentation: iio: ad4000: Add new supported parts Marcelo Schmitt
@ 2025-03-27 21:25 ` Marcelo Schmitt
2025-03-27 21:25 ` [PATCH v4 4/4] Documentation: iio: ad4000: Describe offload support Marcelo Schmitt
2025-04-06 9:26 ` [PATCH v4 0/4] iio: adc: ad4000: Add SPI " Jonathan Cameron
4 siblings, 0 replies; 9+ messages in thread
From: Marcelo Schmitt @ 2025-03-27 21:25 UTC (permalink / raw)
To: linux-iio, linux-doc, linux-kernel
Cc: jic23, lars, Michael.Hennerich, corbet, dlechner,
marcelo.schmitt1
Complement ad4000 IIO driver documentation with considerations about
``_scale_available`` attribute and table of typical channel attributes.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Documentation/iio/ad4000.rst | 47 ++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/Documentation/iio/ad4000.rst b/Documentation/iio/ad4000.rst
index 5578a9cfd9d5..468d30dc9214 100644
--- a/Documentation/iio/ad4000.rst
+++ b/Documentation/iio/ad4000.rst
@@ -144,3 +144,50 @@ Set ``adi,sdi-pin`` to ``"cs"`` to select this mode.
^ | |
+--------------------| SCLK |
+-------------+
+
+IIO Device characteristics
+==========================
+
+The AD4000 series driver supports differential and pseudo-differential ADCs.
+
+The span compression feature available in AD4000 series devices can be
+enabled/disabled by changing the ``_scale_available`` attribute of the voltage
+channel. Note that span compression configuration requires writing to AD4000
+configuration register, which is only possible when the ADC is wired in 3-wire
+turbo mode, and the SPI controller is ``SPI_MOSI_IDLE_HIGH`` capable. If those
+conditions are not met, no ``_scale_available`` attribute is provided.
+
+Besides that, differential and pseudo-differential voltage channels present
+slightly different sysfs interfaces.
+
+Pseudo-differential ADCs
+------------------------
+
+Typical voltage channel attributes of a pseudo-differential AD4000 series device:
+
++-------------------------------------------+------------------------------------------+
+| Voltage Channel Attributes | Description |
++===========================================+==========================================+
+| ``in_voltage0_raw`` | Raw ADC output code. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0_offset`` | Offset to convert raw value to mV. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0_scale`` | Scale factor to convert raw value to mV. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0_scale_available`` | Toggles input span compression |
++-------------------------------------------+------------------------------------------+
+
+Differential ADCs
+-----------------
+
+Typical voltage channel attributes of a differential AD4000 series device:
+
++-------------------------------------------+------------------------------------------+
+| Voltage Channel Attributes | Description |
++===========================================+==========================================+
+| ``in_voltage0-voltage1_raw`` | Raw ADC output code. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0-voltage1_scale`` | Scale factor to convert raw value to mV. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0-voltage1_scale_available`` | Toggles input span compression |
++-------------------------------------------+------------------------------------------+
--
2.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 4/4] Documentation: iio: ad4000: Describe offload support
2025-03-27 21:24 [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support Marcelo Schmitt
` (2 preceding siblings ...)
2025-03-27 21:25 ` [PATCH v4 3/4] Documentation: iio: ad4000: Add IIO Device characteristics section Marcelo Schmitt
@ 2025-03-27 21:25 ` Marcelo Schmitt
2025-04-06 9:26 ` [PATCH v4 0/4] iio: adc: ad4000: Add SPI " Jonathan Cameron
4 siblings, 0 replies; 9+ messages in thread
From: Marcelo Schmitt @ 2025-03-27 21:25 UTC (permalink / raw)
To: linux-iio, linux-doc, linux-kernel
Cc: jic23, lars, Michael.Hennerich, corbet, dlechner,
marcelo.schmitt1
When SPI offloading is supported, the IIO device provides different sysfs
interfaces to allow using the adjusting the sample rate. Document SPI
offload support for AD4000 and similar devices.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Documentation/iio/ad4000.rst | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/Documentation/iio/ad4000.rst b/Documentation/iio/ad4000.rst
index 468d30dc9214..c1d04d3436d2 100644
--- a/Documentation/iio/ad4000.rst
+++ b/Documentation/iio/ad4000.rst
@@ -191,3 +191,30 @@ Typical voltage channel attributes of a differential AD4000 series device:
+-------------------------------------------+------------------------------------------+
| ``in_voltage0-voltage1_scale_available`` | Toggles input span compression |
+-------------------------------------------+------------------------------------------+
+
+SPI offload support
+-------------------
+
+To be able to achieve the maximum sample rate, the driver can be used with SPI
+offload engines such as the one usually present in `AXI SPI Engine`_, to provide
+SPI offload support.
+
+.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/pulsar_adc/index.html
+
+To keep up with SPI offloading transfer speeds, the ADC must be connected either
+in 3-wire turbo mode or in 3-wire without busy indicator mode and have SPI
+controller CS line connected to the CNV pin.
+
+When set for SPI offload support, the IIO device will provide different
+interfaces.
+
+* Either ``in_voltage0_sampling_frequency`` or
+ ``in_voltage0-voltage1_sampling_frequency`` file is provided to allow setting
+ the sample rate.
+* IIO trigger device is not provided (no ``trigger`` directory).
+* ``timestamp`` channel is not provided.
+
+Also, because the ADC output has a one sample latency (delay) when the device is
+wired in "3-wire" mode and only one transfer per sample is done when using SPI
+offloading, the first data sample in the buffer is not valid because it contains
+the output of an earlier conversion result.
--
2.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support
2025-03-27 21:24 [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support Marcelo Schmitt
` (3 preceding siblings ...)
2025-03-27 21:25 ` [PATCH v4 4/4] Documentation: iio: ad4000: Describe offload support Marcelo Schmitt
@ 2025-04-06 9:26 ` Jonathan Cameron
4 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2025-04-06 9:26 UTC (permalink / raw)
To: Marcelo Schmitt
Cc: linux-iio, linux-doc, linux-kernel, lars, Michael.Hennerich,
corbet, dlechner, marcelo.schmitt1
On Thu, 27 Mar 2025 18:24:14 -0300
Marcelo Schmitt <marcelo.schmitt@analog.com> wrote:
> This patch series extends the ad4000 driver to support SPI offloading.
> In addition to that, ad4000 IIO documentation is expanded to:
> - list PulSAR parts supported by the ad4000 driver.
> - describe some characteristics of AD4000 IIO device.
> - describe changes when SPI offload is being used.
I left it to sit for a week to see if anyone else had remaining
comments. Seems not on this occasion.
Applied to the tesing branch of iio.git which I'll shortly rebase
on rc1 once available.
Thanks,
Jonathan
>
> Change log v3 -> v4
> [IIO driver]
> - No longer changing spi_device CS timing parameters.
> - SPI offload messages now set only one transfer.
> - Degraded offload_xfers from array to single entry field.
> - Adjusted ad4000_spi_offload_setup() comment.
> - Dropped t_quiet1_ns since it's not used anymore.
> - Fixed include order.
> - Fixed typo: withouth -> without.
> [Documentation]
> - Picked up review tags.
> - Made phrase about SPI offload engine more generic.
> - Complemented SPI offload section with supported wiring configurations.
>
> Change log v2 -> v3
> - Dropped patch 1 of v2 series and reverted buffer endianness and SPI transfer
> bits_per_word configurations to the way they were in v1.
> - Removed dummy transfer in ad4000_prepare_offload_turbo_message().
> - Unified offload message preparation.
> - Removed spi->cs_hold.value to use cs_inactive to set CNV/CS high time.
> - Adjusted max sample rate for AD7983 and AD7984.
> - Dropped AD4000_TCONV_NS and made all time related constants a time_spec field.
> - Removed _offl param from _CHANNELS() macros and added a comment about that.
> - Commented IIO_BE check in ad4000_prepare_3wire_mode_message().
> - Now using predisable to balance with postenable.
> - Put spi/offload/consumer.h include right after spi/spi.h.
> - Removed unrelated brackets in info_mask_separate_available assignment
> - Added blank line after if.
>
> Change log v1 -> v2
> - Fixed passing inappropriate pointer instead of ret to dev_err_probe()
> - [new patch] Set transfer bits_per_word to have data in CPU endianness
> - Set iio_dev num_channels close to where channels is set.
> - Complement offload message comment about first sample being invalid
> - Document why the first buffer sample is invalid when offloading
> - Added blank line before a 'simple return'.
>
> Link to v3: https://lore.kernel.org/linux-iio/cover.1742992305.git.marcelo.schmitt@analog.com/
> Link to v2: https://lore.kernel.org/linux-iio/cover.1742394806.git.marcelo.schmitt@analog.com/
> Link to v1: https://lore.kernel.org/linux-iio/cover.1741970538.git.marcelo.schmitt@analog.com/
>
>
> Marcelo Schmitt (4):
> iio: adc: ad4000: Add support for SPI offload
> Documentation: iio: ad4000: Add new supported parts
> Documentation: iio: ad4000: Add IIO Device characteristics section
> Documentation: iio: ad4000: Describe offload support
>
> Documentation/iio/ad4000.rst | 91 ++++++++-
> drivers/iio/adc/Kconfig | 7 +-
> drivers/iio/adc/ad4000.c | 382 +++++++++++++++++++++++++++++++----
> 3 files changed, 444 insertions(+), 36 deletions(-)
>
>
> base-commit: 9f36acefb2621d980734a5bb7d74e0e24e0af166
> prerequisite-patch-id: 3d517eef53a799adba5922815fe684b913e36773
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload
2025-03-27 21:24 ` [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload Marcelo Schmitt
@ 2025-04-09 17:22 ` David Lechner
2025-04-10 15:08 ` Marcelo Schmitt
0 siblings, 1 reply; 9+ messages in thread
From: David Lechner @ 2025-04-09 17:22 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, linux-doc, linux-kernel
Cc: jic23, lars, Michael.Hennerich, corbet, marcelo.schmitt1
On 3/27/25 4:24 PM, Marcelo Schmitt wrote:
> FPGA HDL projects can include a PWM generator in addition to SPI-Engine.
> The PWM IP is used to trigger SPI-Engine offload modules that in turn set
> SPI-Engine to execute transfers to poll data from the ADC. That allows data
> to be read at the maximum sample rates. Also, it is possible to set a
> specific sample rate by setting the proper PWM duty cycle and related state
> parameters, thus allowing an adjustable ADC sample rate when a PWM (offload
> trigger) is used in combination with SPI-Engine.
>
> Add support for SPI offload.
>
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---
I think I spotted a bug. With that fixed...
Reviewed-by: David Lechner <dlechner@baylibre.com>
> @@ -690,6 +942,15 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
>
> xfers[1].rx_buf = &st->scan.data;
> xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
Now this needs to be:
xfer->len = chan->scan_type.realbits > 16 ? 4 : 2;
Otherwise when there is SPI offload, this could read 2 words because
storagebits is always == 32.
> +
> + /*
> + * If the device is set up for SPI offloading, IIO channel scan_type is
> + * set to IIO_CPU. When that is the case, use larger SPI word sizes for
> + * single-shot reads too. Thus, sample data can be correctly handled in
> + * ad4000_single_conversion() according to scan_type endianness.
> + */
> + if (chan->scan_type.endianness != IIO_BE)
> + xfers[1].bits_per_word = chan->scan_type.realbits;
> xfers[1].delay.value = st->time_spec->t_quiet2_ns;
> xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload
2025-04-09 17:22 ` David Lechner
@ 2025-04-10 15:08 ` Marcelo Schmitt
2025-04-12 10:28 ` Jonathan Cameron
0 siblings, 1 reply; 9+ messages in thread
From: Marcelo Schmitt @ 2025-04-10 15:08 UTC (permalink / raw)
To: David Lechner
Cc: Marcelo Schmitt, linux-iio, linux-doc, linux-kernel, jic23, lars,
Michael.Hennerich, corbet
On 04/09, David Lechner wrote:
> On 3/27/25 4:24 PM, Marcelo Schmitt wrote:
> > FPGA HDL projects can include a PWM generator in addition to SPI-Engine.
> > The PWM IP is used to trigger SPI-Engine offload modules that in turn set
> > SPI-Engine to execute transfers to poll data from the ADC. That allows data
> > to be read at the maximum sample rates. Also, it is possible to set a
> > specific sample rate by setting the proper PWM duty cycle and related state
> > parameters, thus allowing an adjustable ADC sample rate when a PWM (offload
> > trigger) is used in combination with SPI-Engine.
> >
> > Add support for SPI offload.
> >
> > Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> > ---
>
> I think I spotted a bug. With that fixed...
>
> Reviewed-by: David Lechner <dlechner@baylibre.com>
>
> > @@ -690,6 +942,15 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
> >
> > xfers[1].rx_buf = &st->scan.data;
> > xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
>
> Now this needs to be:
>
> xfer->len = chan->scan_type.realbits > 16 ? 4 : 2;
>
> Otherwise when there is SPI offload, this could read 2 words because
> storagebits is always == 32.
I've just tested both versions with ADAQ4003 and with AD7687.
There's no practical difference between the suggestion and what already is in
IIO testing. I don't mind sending a patch with the change, but don't think it
fixes anything.
Thanks,
Marcelo
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload
2025-04-10 15:08 ` Marcelo Schmitt
@ 2025-04-12 10:28 ` Jonathan Cameron
0 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2025-04-12 10:28 UTC (permalink / raw)
To: Marcelo Schmitt
Cc: David Lechner, Marcelo Schmitt, linux-iio, linux-doc,
linux-kernel, lars, Michael.Hennerich, corbet
On Thu, 10 Apr 2025 12:08:27 -0300
Marcelo Schmitt <marcelo.schmitt1@gmail.com> wrote:
> On 04/09, David Lechner wrote:
> > On 3/27/25 4:24 PM, Marcelo Schmitt wrote:
> > > FPGA HDL projects can include a PWM generator in addition to SPI-Engine.
> > > The PWM IP is used to trigger SPI-Engine offload modules that in turn set
> > > SPI-Engine to execute transfers to poll data from the ADC. That allows data
> > > to be read at the maximum sample rates. Also, it is possible to set a
> > > specific sample rate by setting the proper PWM duty cycle and related state
> > > parameters, thus allowing an adjustable ADC sample rate when a PWM (offload
> > > trigger) is used in combination with SPI-Engine.
> > >
> > > Add support for SPI offload.
> > >
> > > Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> > > ---
> >
> > I think I spotted a bug. With that fixed...
> >
> > Reviewed-by: David Lechner <dlechner@baylibre.com>
> >
> > > @@ -690,6 +942,15 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
> > >
> > > xfers[1].rx_buf = &st->scan.data;
> > > xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
> >
> > Now this needs to be:
> >
> > xfer->len = chan->scan_type.realbits > 16 ? 4 : 2;
> >
> > Otherwise when there is SPI offload, this could read 2 words because
> > storagebits is always == 32.
>
> I've just tested both versions with ADAQ4003 and with AD7687.
> There's no practical difference between the suggestion and what already is in
> IIO testing. I don't mind sending a patch with the change, but don't think it
> fixes anything.
If it is an unnecessary over read in some cases, probably sensible to either
reduce the length or add a comment on why we don't care.
Jonathan
>
> Thanks,
> Marcelo
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-04-12 10:29 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-27 21:24 [PATCH v4 0/4] iio: adc: ad4000: Add SPI offload support Marcelo Schmitt
2025-03-27 21:24 ` [PATCH v4 1/4] iio: adc: ad4000: Add support for SPI offload Marcelo Schmitt
2025-04-09 17:22 ` David Lechner
2025-04-10 15:08 ` Marcelo Schmitt
2025-04-12 10:28 ` Jonathan Cameron
2025-03-27 21:24 ` [PATCH v4 2/4] Documentation: iio: ad4000: Add new supported parts Marcelo Schmitt
2025-03-27 21:25 ` [PATCH v4 3/4] Documentation: iio: ad4000: Add IIO Device characteristics section Marcelo Schmitt
2025-03-27 21:25 ` [PATCH v4 4/4] Documentation: iio: ad4000: Describe offload support Marcelo Schmitt
2025-04-06 9:26 ` [PATCH v4 0/4] iio: adc: ad4000: Add SPI " Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).