* [PATCH 0/5] ad7380: add support for single-ended parts
@ 2024-07-26 15:20 Julien Stephan
2024-07-26 15:20 ` [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts Julien Stephan
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Julien Stephan @ 2024-07-26 15:20 UTC (permalink / raw)
To: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: Michael Hennerich, linux-iio, devicetree, linux-kernel, linux-doc,
Julien Stephan
This series will add support for ad7386/7/8 (16/14/12 bits) unsigned,
dual simultaneous sampling, single-ended compatible parts, and the
corresponding ad7386-4/7-4/8-4 4 channels to ad7380 driver.
These parts have a 2:1 multiplexer in front of each ADC. They also include
additional configuration registers that allow for either manual selection
or automatic switching (sequencer mode), of the multiplexer inputs.
From an IIO point of view, all inputs are exported, i.e ad7386/7/8
export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
Inputs AinX0 of multiplexers correspond to the first half of IIO channels
(i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
4-7). Example for AD7386/7/8 (2 channels parts):
IIO | AD7386/7/8
| +----------------------------
| | _____ ______
| | | | | |
voltage0 | AinA0 --|--->| | | |
| | | mux |----->| ADCA |---
voltage2 | AinA1 --|--->| | | |
| | |_____| |_____ |
| | _____ ______
| | | | | |
voltage1 | AinB0 --|--->| | | |
| | | mux |----->| ADCB |---
voltage3 | AinB1 --|--->| | | |
| | |_____| |______|
| |
| +----------------------------
To ease the review, this series is split on several commits, in
particular, sequencer mode is added as an additional commit.
Cheers
Julien
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
Julien Stephan (5):
dt-bindings: iio: adc: ad7380: add single-ended compatible parts
ad7380: prepare driver for single-ended parts support
ad7380: add support for single-ended parts
ad7380: enable sequencer for single-ended parts
docs: iio: ad7380: add support for single-ended parts
.../devicetree/bindings/iio/adc/adi,ad7380.yaml | 13 +
Documentation/iio/ad7380.rst | 42 ++
drivers/iio/adc/ad7380.c | 511 +++++++++++++++++----
3 files changed, 488 insertions(+), 78 deletions(-)
---
base-commit: 472438c7e0e2261c6737a8321f46ef176eef1c8f
change-id: 20240726-ad7380-add-single-ended-chips-b437d1cc8b8b
Best regards,
--
Julien Stephan <jstephan@baylibre.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts
2024-07-26 15:20 [PATCH 0/5] ad7380: add support for single-ended parts Julien Stephan
@ 2024-07-26 15:20 ` Julien Stephan
2024-07-28 10:33 ` Krzysztof Kozlowski
2024-07-28 10:34 ` Krzysztof Kozlowski
2024-07-26 15:20 ` [PATCH 2/5] ad7380: prepare driver for single-ended parts support Julien Stephan
` (3 subsequent siblings)
4 siblings, 2 replies; 12+ messages in thread
From: Julien Stephan @ 2024-07-26 15:20 UTC (permalink / raw)
To: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: Michael Hennerich, linux-iio, devicetree, linux-kernel, linux-doc,
Julien Stephan
Adding ad7386/7/8 single-ended compatible parts, and the corresponding
ad7386-4/7-4/8-4 4 channels.
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
index 899b777017ce..bd19abb867d9 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
@@ -15,10 +15,17 @@ description: |
* https://www.analog.com/en/products/ad7381.html
* https://www.analog.com/en/products/ad7383.html
* https://www.analog.com/en/products/ad7384.html
+ * https://www.analog.com/en/products/ad7386.html
+ * https://www.analog.com/en/products/ad7387.html
+ * https://www.analog.com/en/products/ad7388.html
* https://www.analog.com/en/products/ad7380-4.html
* https://www.analog.com/en/products/ad7381-4.html
* https://www.analog.com/en/products/ad7383-4.html
* https://www.analog.com/en/products/ad7384-4.html
+ * https://www.analog.com/en/products/ad7386-4.html
+ * https://www.analog.com/en/products/ad7387-4.html
+ * https://www.analog.com/en/products/ad7388-4.html
+
$ref: /schemas/spi/spi-peripheral-props.yaml#
@@ -29,10 +36,16 @@ properties:
- adi,ad7381
- adi,ad7383
- adi,ad7384
+ - adi,ad7386
+ - adi,ad7387
+ - adi,ad7388
- adi,ad7380-4
- adi,ad7381-4
- adi,ad7383-4
- adi,ad7384-4
+ - adi,ad7386-4
+ - adi,ad7387-4
+ - adi,ad7388-4
reg:
maxItems: 1
--
2.45.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/5] ad7380: prepare driver for single-ended parts support
2024-07-26 15:20 [PATCH 0/5] ad7380: add support for single-ended parts Julien Stephan
2024-07-26 15:20 ` [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts Julien Stephan
@ 2024-07-26 15:20 ` Julien Stephan
2024-07-26 15:20 ` [PATCH 3/5] ad7380: add support for single-ended parts Julien Stephan
` (2 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Julien Stephan @ 2024-07-26 15:20 UTC (permalink / raw)
To: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: Michael Hennerich, linux-iio, devicetree, linux-kernel, linux-doc,
Julien Stephan
ad738x family contains single-ended parts that have a 2:1 mux in front
of ADC, so the number of IIO channels is different from the number of
simultaneous channels that can be sampled.
To prepare the support for single-ended parts, introduce a new
num_simultaneous_channels variable. For currently supported parts,
num_simultaneous_channels is equal to num_channels minus 1 (the
timestamps channel)
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
drivers/iio/adc/ad7380.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 7568cd0a2b32..03bbd561fb23 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -80,6 +80,7 @@ struct ad7380_chip_info {
const char *name;
const struct iio_chan_spec *channels;
unsigned int num_channels;
+ unsigned int num_simult_channels;
const char * const *vcm_supplies;
unsigned int num_vcm_supplies;
const unsigned long *available_scan_masks;
@@ -208,6 +209,7 @@ static const struct ad7380_chip_info ad7380_chip_info = {
.name = "ad7380",
.channels = ad7380_channels,
.num_channels = ARRAY_SIZE(ad7380_channels),
+ .num_simult_channels = 2,
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
@@ -216,6 +218,7 @@ static const struct ad7380_chip_info ad7381_chip_info = {
.name = "ad7381",
.channels = ad7381_channels,
.num_channels = ARRAY_SIZE(ad7381_channels),
+ .num_simult_channels = 2,
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
@@ -224,6 +227,7 @@ static const struct ad7380_chip_info ad7383_chip_info = {
.name = "ad7383",
.channels = ad7383_channels,
.num_channels = ARRAY_SIZE(ad7383_channels),
+ .num_simult_channels = 2,
.vcm_supplies = ad7380_2_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
@@ -234,6 +238,7 @@ static const struct ad7380_chip_info ad7384_chip_info = {
.name = "ad7384",
.channels = ad7384_channels,
.num_channels = ARRAY_SIZE(ad7384_channels),
+ .num_simult_channels = 2,
.vcm_supplies = ad7380_2_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
@@ -244,6 +249,7 @@ static const struct ad7380_chip_info ad7380_4_chip_info = {
.name = "ad7380-4",
.channels = ad7380_4_channels,
.num_channels = ARRAY_SIZE(ad7380_4_channels),
+ .num_simult_channels = 4,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
@@ -252,6 +258,7 @@ static const struct ad7380_chip_info ad7381_4_chip_info = {
.name = "ad7381-4",
.channels = ad7381_4_channels,
.num_channels = ARRAY_SIZE(ad7381_4_channels),
+ .num_simult_channels = 4,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
@@ -260,6 +267,7 @@ static const struct ad7380_chip_info ad7383_4_chip_info = {
.name = "ad7383-4",
.channels = ad7383_4_channels,
.num_channels = ARRAY_SIZE(ad7383_4_channels),
+ .num_simult_channels = 4,
.vcm_supplies = ad7380_4_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
@@ -270,6 +278,7 @@ static const struct ad7380_chip_info ad7384_4_chip_info = {
.name = "ad7384-4",
.channels = ad7384_4_channels,
.num_channels = ARRAY_SIZE(ad7384_4_channels),
+ .num_simult_channels = 4,
.vcm_supplies = ad7380_4_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
@@ -407,7 +416,7 @@ static void ad7380_update_xfers(struct ad7380_state *st,
*/
st->xfer[1].bits_per_word = scan_type->realbits;
st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
- (st->chip_info->num_channels - 1);
+ st->chip_info->num_simult_channels;
}
static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
--
2.45.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/5] ad7380: add support for single-ended parts
2024-07-26 15:20 [PATCH 0/5] ad7380: add support for single-ended parts Julien Stephan
2024-07-26 15:20 ` [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts Julien Stephan
2024-07-26 15:20 ` [PATCH 2/5] ad7380: prepare driver for single-ended parts support Julien Stephan
@ 2024-07-26 15:20 ` Julien Stephan
2024-07-28 16:23 ` Jonathan Cameron
2024-07-26 15:20 ` [PATCH 4/5] ad7380: enable sequencer " Julien Stephan
2024-07-26 15:20 ` [PATCH 5/5] docs: iio: ad7380: add support " Julien Stephan
4 siblings, 1 reply; 12+ messages in thread
From: Julien Stephan @ 2024-07-26 15:20 UTC (permalink / raw)
To: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: Michael Hennerich, linux-iio, devicetree, linux-kernel, linux-doc,
Julien Stephan
Adding ad7386/7/8 (16/14/12 bits) unsigned, dual simultaneous sampling,
single-ended compatible parts, and the corresponding ad7386-4/7-4/8-4
4 channels.
These parts have a 2:1 multiplexer in front of each ADC. They also include
additional configuration registers that allow for either manual selection
or automatic switching (sequencer mode), of the multiplexer inputs.
This commit focus on integrating manual selection. Sequencer mode will
be implemented later.
From an IIO point of view, all inputs are exported, i.e ad7386/7/8
export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
Inputs AinX0 of multiplexers correspond to the first half of IIO channels
(i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
4-7). Example for AD7386/7/8 (2 channels parts):
IIO | AD7386/7/8
| +----------------------------
| | _____ ______
| | | | | |
voltage0 | AinA0 --|--->| | | |
| | | mux |----->| ADCA |---
voltage2 | AinA1 --|--->| | | |
| | |_____| |_____ |
| | _____ ______
| | | | | |
voltage1 | AinB0 --|--->| | | |
| | | mux |----->| ADCB |---
voltage3 | AinB1 --|--->| | | |
| | |_____| |______|
| |
| +----------------------------
When switching channel, the ADC require an additional settling time.
According to the datasheet, data is valid on the third CS low. We already
have an extra toggle before each read (either direct reads or buffered
reads) to sample correct data, so we just add a single CS toggle at the
end of the register write.
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
drivers/iio/adc/ad7380.c | 352 +++++++++++++++++++++++++++++++++++++++++------
1 file changed, 310 insertions(+), 42 deletions(-)
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 03bbd561fb23..25d42fff1839 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -8,9 +8,11 @@
* Datasheets of supported parts:
* ad7380/1 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7380-7381.pdf
* ad7383/4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-7384.pdf
+ * ad7386/7/8 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7386-7387-7388.pdf
* ad7380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7380-4.pdf
* ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf
* ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf
+ * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf
*/
#include <linux/align.h>
@@ -49,6 +51,7 @@
#define AD7380_REG_ADDR_ALERT_LOW_TH 0x4
#define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5
+#define AD7380_CONFIG1_CH BIT(11)
#define AD7380_CONFIG1_OS_MODE BIT(9)
#define AD7380_CONFIG1_OSR GENMASK(8, 6)
#define AD7380_CONFIG1_CRC_W BIT(5)
@@ -81,6 +84,7 @@ struct ad7380_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
unsigned int num_simult_channels;
+ bool has_mux;
const char * const *vcm_supplies;
unsigned int num_vcm_supplies;
const unsigned long *available_scan_masks;
@@ -92,8 +96,24 @@ enum {
AD7380_SCAN_TYPE_RESOLUTION_BOOST,
};
-/* Extended scan types for 14-bit chips. */
-static const struct iio_scan_type ad7380_scan_type_14[] = {
+/* Extended scan types for 12-bit unsigned chips. */
+static const struct iio_scan_type ad7380_scan_type_12_u[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+};
+
+/* Extended scan types for 14-bit signed chips. */
+static const struct iio_scan_type ad7380_scan_type_14_s[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 's',
.realbits = 14,
@@ -108,8 +128,24 @@ static const struct iio_scan_type ad7380_scan_type_14[] = {
},
};
-/* Extended scan types for 16-bit chips. */
-static const struct iio_scan_type ad7380_scan_type_16[] = {
+/* Extended scan types for 14-bit unsigned chips. */
+static const struct iio_scan_type ad7380_scan_type_14_u[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+};
+
+/* Extended scan types for 16-bit signed_chips. */
+static const struct iio_scan_type ad7380_scan_type_16_s[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 's',
.realbits = 16,
@@ -124,50 +160,87 @@ static const struct iio_scan_type ad7380_scan_type_16[] = {
},
};
-#define AD7380_CHANNEL(index, bits, diff) { \
- .type = IIO_VOLTAGE, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
- .info_mask_shared_by_type_available = \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
- .indexed = 1, \
- .differential = (diff), \
- .channel = (diff) ? (2 * (index)) : (index), \
- .channel2 = (diff) ? (2 * (index) + 1) : 0, \
- .scan_index = (index), \
- .has_ext_scan_type = 1, \
- .ext_scan_type = ad7380_scan_type_##bits, \
- .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits),\
+/* Extended scan types for 16-bit unsigned chips. */
+static const struct iio_scan_type ad7380_scan_type_16_u[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 18,
+ .storagebits = 32,
+ .endianness = IIO_CPU
+ },
+};
+
+#define AD7380_CHANNEL(index, bits, diff, sign) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .indexed = 1, \
+ .differential = (diff), \
+ .channel = (diff) ? (2 * (index)) : (index), \
+ .channel2 = (diff) ? (2 * (index) + 1) : 0, \
+ .scan_index = (index), \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad7380_scan_type_##bits##_##sign, \
+ .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \
}
-#define DEFINE_AD7380_2_CHANNEL(name, bits, diff) \
+#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff), \
- AD7380_CHANNEL(1, bits, diff), \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(2), \
}
-#define DEFINE_AD7380_4_CHANNEL(name, bits, diff) \
+#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff), \
- AD7380_CHANNEL(1, bits, diff), \
- AD7380_CHANNEL(2, bits, diff), \
- AD7380_CHANNEL(3, bits, diff), \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ AD7380_CHANNEL(2, bits, diff, sign), \
+ AD7380_CHANNEL(3, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(4), \
}
+#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ AD7380_CHANNEL(2, bits, diff, sign), \
+ AD7380_CHANNEL(3, bits, diff, sign), \
+ AD7380_CHANNEL(4, bits, diff, sign), \
+ AD7380_CHANNEL(5, bits, diff, sign), \
+ AD7380_CHANNEL(6, bits, diff, sign), \
+ AD7380_CHANNEL(7, bits, diff, sign), \
+ IIO_CHAN_SOFT_TIMESTAMP(8), \
+}
+
/* fully differential */
-DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1);
-DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1);
-DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1);
-DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1);
+DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s);
+DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s);
+DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s);
+DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s);
/* pseudo differential */
-DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0);
-DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0);
-DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0);
-DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0);
+DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s);
+DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s);
+DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0, s);
+DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0, s);
+
+/* Single ended */
+DEFINE_AD7380_4_CHANNEL(ad7386_channels, 16, 0, u);
+DEFINE_AD7380_4_CHANNEL(ad7387_channels, 14, 0, u);
+DEFINE_AD7380_4_CHANNEL(ad7388_channels, 12, 0, u);
+DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u);
+DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u);
+DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u);
static const char * const ad7380_2_channel_vcm_supplies[] = {
"aina", "ainb",
@@ -188,6 +261,48 @@ static const unsigned long ad7380_4_channel_scan_masks[] = {
0
};
+/*
+ * Single ended parts have a 2:1 multiplexer in front of each ADC.
+ *
+ * From an IIO point of view, all inputs are exported, i.e ad7386/7/8
+ * export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
+ *
+ * Inputs AinX0 of multiplexers correspond to the first half of IIO channels
+ * (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
+ * 4-7). Example for AD7386/7/8 (2 channels parts):
+ *
+ * IIO | AD7386/7/8
+ * | +----------------------------
+ * | | _____ ______
+ * | | | | | |
+ * voltage0 | AinA0 --|--->| | | |
+ * | | | mux |----->| ADCA |---
+ * voltage2 | AinA1 --|--->| | | |
+ * | | |_____| |_____ |
+ * | | _____ ______
+ * | | | | | |
+ * voltage1 | AinB0 --|--->| | | |
+ * | | | mux |----->| ADCB |---
+ * voltage3 | AinB1 --|--->| | | |
+ * | | |_____| |______|
+ * | |
+ * | +----------------------------
+ *
+ * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
+ * scan masks.
+ */
+static const unsigned long ad7380_2x2_channel_scan_masks[] = {
+ GENMASK(1, 0),
+ GENMASK(3, 2),
+ 0
+};
+
+static const unsigned long ad7380_2x4_channel_scan_masks[] = {
+ GENMASK(3, 0),
+ GENMASK(7, 4),
+ 0
+};
+
static const struct ad7380_timing_specs ad7380_timing = {
.t_csh_ns = 10,
};
@@ -245,6 +360,36 @@ static const struct ad7380_chip_info ad7384_chip_info = {
.timing_specs = &ad7380_timing,
};
+static const struct ad7380_chip_info ad7386_chip_info = {
+ .name = "ad7386",
+ .channels = ad7386_channels,
+ .num_channels = ARRAY_SIZE(ad7386_channels),
+ .num_simult_channels = 2,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x2_channel_scan_masks,
+ .timing_specs = &ad7380_timing,
+};
+
+static const struct ad7380_chip_info ad7387_chip_info = {
+ .name = "ad7387",
+ .channels = ad7387_channels,
+ .num_channels = ARRAY_SIZE(ad7387_channels),
+ .num_simult_channels = 2,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x2_channel_scan_masks,
+ .timing_specs = &ad7380_timing,
+};
+
+static const struct ad7380_chip_info ad7388_chip_info = {
+ .name = "ad7388",
+ .channels = ad7388_channels,
+ .num_channels = ARRAY_SIZE(ad7388_channels),
+ .num_simult_channels = 2,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x2_channel_scan_masks,
+ .timing_specs = &ad7380_timing,
+};
+
static const struct ad7380_chip_info ad7380_4_chip_info = {
.name = "ad7380-4",
.channels = ad7380_4_channels,
@@ -285,12 +430,43 @@ static const struct ad7380_chip_info ad7384_4_chip_info = {
.timing_specs = &ad7380_4_timing,
};
+static const struct ad7380_chip_info ad7386_4_chip_info = {
+ .name = "ad7386-4",
+ .channels = ad7386_4_channels,
+ .num_channels = ARRAY_SIZE(ad7386_4_channels),
+ .num_simult_channels = 4,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
+static const struct ad7380_chip_info ad7387_4_chip_info = {
+ .name = "ad7387-4",
+ .channels = ad7387_4_channels,
+ .num_channels = ARRAY_SIZE(ad7387_4_channels),
+ .num_simult_channels = 4,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
+static const struct ad7380_chip_info ad7388_4_chip_info = {
+ .name = "ad7388-4",
+ .channels = ad7388_4_channels,
+ .num_channels = ARRAY_SIZE(ad7388_4_channels),
+ .num_simult_channels = 4,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
struct ad7380_state {
const struct ad7380_chip_info *chip_info;
struct spi_device *spi;
struct regmap *regmap;
unsigned int oversampling_ratio;
bool resolution_boost_enabled;
+ unsigned int ch;
unsigned int vref_mv;
unsigned int vcm_mv[MAX_NUM_CHANNELS];
/* xfers, message an buffer for reading sample data */
@@ -388,6 +564,43 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
unreachable();
}
+/*
+ * When switching channel, the ADC require an additional settling time.
+ * According to the datasheet, data is value on the third CS low. We already
+ * have an extra toggle before each read (either direct reads or buffered reads)
+ * to sample correct data, so we just add a single CS toggle at the end of the
+ * register write.
+ */
+static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
+{
+ struct spi_transfer xfer = {
+ .delay = {
+ .value = T_CONVERT_NS,
+ .unit = SPI_DELAY_UNIT_NSECS,
+ }
+ };
+ int ret;
+
+ if (st->ch == ch)
+ return 0;
+
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_CH,
+ FIELD_PREP(AD7380_CONFIG1_CH, ch));
+
+ if (ret)
+ return ret;
+
+ st->ch = ch;
+
+ if (st->oversampling_ratio > 1)
+ xfer.delay.value = T_CONVERT_0_NS +
+ T_CONVERT_X_NS * (st->oversampling_ratio - 1);
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
/**
* ad7380_update_xfers - update the SPI transfers base on the current scan type
* @st: device instance specific state
@@ -432,6 +645,25 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
if (IS_ERR(scan_type))
return PTR_ERR(scan_type);
+ if (st->chip_info->has_mux) {
+ unsigned int num_simult_channels = st->chip_info->num_simult_channels;
+ unsigned long active_scan_mask = *indio_dev->active_scan_mask;
+ unsigned int ch = 0;
+ int ret;
+
+ /*
+ * Depending on the requested scan_mask and current state,
+ * we need to change CH bit to sample correct data.
+ */
+ if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
+ num_simult_channels))
+ ch = 1;
+
+ ret = ad7380_set_ch(st, ch);
+ if (ret)
+ return ret;
+ }
+
ad7380_update_xfers(st, scan_type);
return spi_optimize_message(st->spi, &st->msg);
@@ -474,20 +706,43 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p)
static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index,
const struct iio_scan_type *scan_type, int *val)
{
+ unsigned int index = scan_index;
int ret;
+ if (st->chip_info->has_mux) {
+ unsigned int ch = 0;
+
+ if (index >= st->chip_info->num_simult_channels) {
+ index -= st->chip_info->num_simult_channels;
+ ch = 1;
+ }
+
+ ret = ad7380_set_ch(st, ch);
+ if (ret)
+ return ret;
+ }
+
ad7380_update_xfers(st, scan_type);
ret = spi_sync(st->spi, &st->msg);
if (ret < 0)
return ret;
- if (scan_type->storagebits > 16)
- *val = sign_extend32(*(u32 *)(st->scan_data + 4 * scan_index),
- scan_type->realbits - 1);
- else
- *val = sign_extend32(*(u16 *)(st->scan_data + 2 * scan_index),
- scan_type->realbits - 1);
+ if (scan_type->storagebits > 16) {
+ if (scan_type->sign == 's')
+ *val = sign_extend32(*(u32 *)(st->scan_data + 4 * index),
+ scan_type->realbits - 1);
+ else
+ *val = *(u32 *)(st->scan_data + 4 * index) &
+ GENMASK(scan_type->realbits - 1, 0);
+ } else {
+ if (scan_type->sign == 's')
+ *val = sign_extend32(*(u16 *)(st->scan_data + 2 * index),
+ scan_type->realbits - 1);
+ else
+ *val = *(u16 *)(st->scan_data + 2 * index) &
+ GENMASK(scan_type->realbits - 1, 0);
+ }
return IIO_VAL_INT;
}
@@ -664,6 +919,7 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref)
/* This is the default value after reset. */
st->oversampling_ratio = 1;
+ st->ch = 0;
/* SPI 1-wire mode */
return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
@@ -807,10 +1063,16 @@ static const struct of_device_id ad7380_of_match_table[] = {
{ .compatible = "adi,ad7381", .data = &ad7381_chip_info },
{ .compatible = "adi,ad7383", .data = &ad7383_chip_info },
{ .compatible = "adi,ad7384", .data = &ad7384_chip_info },
+ { .compatible = "adi,ad7386", .data = &ad7386_chip_info },
+ { .compatible = "adi,ad7387", .data = &ad7387_chip_info },
+ { .compatible = "adi,ad7388", .data = &ad7388_chip_info },
{ .compatible = "adi,ad7380-4", .data = &ad7380_4_chip_info },
{ .compatible = "adi,ad7381-4", .data = &ad7381_4_chip_info },
{ .compatible = "adi,ad7383-4", .data = &ad7383_4_chip_info },
{ .compatible = "adi,ad7384-4", .data = &ad7384_4_chip_info },
+ { .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info },
+ { .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info },
+ { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info },
{ }
};
@@ -819,10 +1081,16 @@ static const struct spi_device_id ad7380_id_table[] = {
{ "ad7381", (kernel_ulong_t)&ad7381_chip_info },
{ "ad7383", (kernel_ulong_t)&ad7383_chip_info },
{ "ad7384", (kernel_ulong_t)&ad7384_chip_info },
+ { "ad7386", (kernel_ulong_t)&ad7386_chip_info },
+ { "ad7387", (kernel_ulong_t)&ad7387_chip_info },
+ { "ad7388", (kernel_ulong_t)&ad7388_chip_info },
{ "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info },
{ "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info },
{ "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info },
{ "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info },
+ { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info },
+ { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info },
+ { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7380_id_table);
--
2.45.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/5] ad7380: enable sequencer for single-ended parts
2024-07-26 15:20 [PATCH 0/5] ad7380: add support for single-ended parts Julien Stephan
` (2 preceding siblings ...)
2024-07-26 15:20 ` [PATCH 3/5] ad7380: add support for single-ended parts Julien Stephan
@ 2024-07-26 15:20 ` Julien Stephan
2024-07-28 16:35 ` Jonathan Cameron
2024-07-26 15:20 ` [PATCH 5/5] docs: iio: ad7380: add support " Julien Stephan
4 siblings, 1 reply; 12+ messages in thread
From: Julien Stephan @ 2024-07-26 15:20 UTC (permalink / raw)
To: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: Michael Hennerich, linux-iio, devicetree, linux-kernel, linux-doc,
Julien Stephan
ad7386/7/8(-4) single-ended parts have a 2:1 mux in front of each ADC.
From an IIO point of view, all inputs are exported, i.e ad7386/7/8
export 4 channels and ad7386-4/7-4/8-4 export 8 channels. First inputs
of muxes correspond to the first half of IIO channels (i.e 0-1 or 0-3)
and second inputs correspond to second half (i.e 2-3 or 4-7)
Currently, the driver supports only sampling first half OR second half of
the IIO channels. To enable sampling all channels simultaneously, these
parts have an internal sequencer that automatically cycle through the
mux entries.
When enabled, the maximum throughput is divided by two. Moreover, the ADCs
need additional settling time, so we add an extra CS toggle to correctly
propagate setting, and an additional spi transfer to read the second
half.
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
drivers/iio/adc/ad7380.c | 164 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 121 insertions(+), 43 deletions(-)
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 25d42fff1839..11ed010431cf 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -33,7 +33,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
-#define MAX_NUM_CHANNELS 4
+#define MAX_NUM_CHANNELS 8
/* 2.5V internal reference voltage */
#define AD7380_INTERNAL_REF_MV 2500
@@ -52,6 +52,7 @@
#define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5
#define AD7380_CONFIG1_CH BIT(11)
+#define AD7380_CONFIG1_SEQ BIT(10)
#define AD7380_CONFIG1_OS_MODE BIT(9)
#define AD7380_CONFIG1_OSR GENMASK(8, 6)
#define AD7380_CONFIG1_CRC_W BIT(5)
@@ -290,16 +291,22 @@ static const unsigned long ad7380_4_channel_scan_masks[] = {
*
* Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
* scan masks.
+ * When sequencer mode is enabled, chip automatically cycle through
+ * AinX0 and AinX1 channels. From an IIO point of view, we ca enable all
+ * channels, at the cost of an extra read, thus dividing the maximum rate by
+ * two.
*/
static const unsigned long ad7380_2x2_channel_scan_masks[] = {
GENMASK(1, 0),
GENMASK(3, 2),
+ GENMASK(3, 0),
0
};
static const unsigned long ad7380_2x4_channel_scan_masks[] = {
GENMASK(3, 0),
GENMASK(7, 4),
+ GENMASK(7, 0),
0
};
@@ -467,11 +474,14 @@ struct ad7380_state {
unsigned int oversampling_ratio;
bool resolution_boost_enabled;
unsigned int ch;
+ bool seq;
unsigned int vref_mv;
unsigned int vcm_mv[MAX_NUM_CHANNELS];
/* xfers, message an buffer for reading sample data */
- struct spi_transfer xfer[2];
- struct spi_message msg;
+ struct spi_transfer normal_xfer[2];
+ struct spi_message normal_msg;
+ struct spi_transfer seq_xfer[4];
+ struct spi_message seq_msg;
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
@@ -609,33 +619,47 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
static void ad7380_update_xfers(struct ad7380_state *st,
const struct iio_scan_type *scan_type)
{
- /*
- * First xfer only triggers conversion and has to be long enough for
- * all conversions to complete, which can be multiple conversion in the
- * case of oversampling. Technically T_CONVERT_X_NS is lower for some
- * chips, but we use the maximum value for simplicity for now.
- */
- if (st->oversampling_ratio > 1)
- st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS *
- (st->oversampling_ratio - 1);
- else
- st->xfer[0].delay.value = T_CONVERT_NS;
-
- st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+ struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer;
+ unsigned int t_convert = T_CONVERT_NS;
/*
- * Second xfer reads all channels. Data size depends on if resolution
- * boost is enabled or not.
+ * In the case of oversampling, conversion time is higher than in normal
+ * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use
+ * the maximum value for simplicity for now.
*/
- st->xfer[1].bits_per_word = scan_type->realbits;
- st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
- st->chip_info->num_simult_channels;
+ if (st->oversampling_ratio > 1)
+ t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
+ (st->oversampling_ratio - 1);
+
+ if (st->seq) {
+ xfer[0].delay.value = xfer[1].delay.value = t_convert;
+ xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer[2].bits_per_word = xfer[3].bits_per_word =
+ scan_type->realbits;
+ xfer[2].len = xfer[3].len =
+ BITS_TO_BYTES(scan_type->storagebits) *
+ st->chip_info->num_simult_channels;
+ xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len;
+ /* Additional delay required here when oversampling is enabled */
+ if (st->oversampling_ratio > 1)
+ xfer[2].delay.value = t_convert;
+ else
+ xfer[2].delay.value = 0;
+ xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS;
+ } else {
+ xfer[0].delay.value = t_convert;
+ xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer[1].bits_per_word = scan_type->realbits;
+ xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
+ st->chip_info->num_simult_channels;
+ }
}
static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
+ struct spi_message *msg = &st->normal_msg;
/*
* Currently, we always read all channels at the same time. The scan_type
@@ -646,34 +670,62 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
return PTR_ERR(scan_type);
if (st->chip_info->has_mux) {
- unsigned int num_simult_channels = st->chip_info->num_simult_channels;
+ unsigned int num_simult_channels =
+ st->chip_info->num_simult_channels;
unsigned long active_scan_mask = *indio_dev->active_scan_mask;
unsigned int ch = 0;
int ret;
/*
* Depending on the requested scan_mask and current state,
- * we need to change CH bit to sample correct data.
+ * we need to either change CH bit, or enable sequencer mode
+ * to sample correct data.
+ * Sequencer mode is enabled if active mask corresponds to all
+ * IIO channels enabled. Otherwise, CH bit is set.
*/
- if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
- num_simult_channels))
- ch = 1;
+ if (active_scan_mask == GENMASK(2 * num_simult_channels - 1, 0)) {
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_SEQ,
+ FIELD_PREP(AD7380_CONFIG1_SEQ, 1));
+ msg = &st->seq_msg;
+ st->seq = true;
+ } else {
+ if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
+ num_simult_channels))
+ ch = 1;
+
+ ret = ad7380_set_ch(st, ch);
+ }
- ret = ad7380_set_ch(st, ch);
if (ret)
return ret;
}
ad7380_update_xfers(st, scan_type);
- return spi_optimize_message(st->spi, &st->msg);
+ return spi_optimize_message(st->spi, msg);
}
static int ad7380_triggered_buffer_postdisable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
+ struct spi_message *msg = &st->normal_msg;
+ int ret;
+
+ if (st->seq) {
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_SEQ,
+ FIELD_PREP(AD7380_CONFIG1_SEQ, 0));
+ if (ret)
+ return ret;
+
+ msg = &st->seq_msg;
+ st->seq = false;
+ }
- spi_unoptimize_message(&st->msg);
+ spi_unoptimize_message(msg);
return 0;
}
@@ -688,9 +740,10 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7380_state *st = iio_priv(indio_dev);
+ struct spi_message *msg = st->seq ? &st->seq_msg : &st->normal_msg;
int ret;
- ret = spi_sync(st->spi, &st->msg);
+ ret = spi_sync(st->spi, msg);
if (ret)
goto out;
@@ -724,7 +777,7 @@ static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index,
ad7380_update_xfers(st, scan_type);
- ret = spi_sync(st->spi, &st->msg);
+ ret = spi_sync(st->spi, &st->normal_msg);
if (ret < 0)
return ret;
@@ -920,6 +973,7 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref)
/* This is the default value after reset. */
st->oversampling_ratio = 1;
st->ch = 0;
+ st->seq = false;
/* SPI 1-wire mode */
return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
@@ -1021,21 +1075,45 @@ static int ad7380_probe(struct spi_device *spi)
"failed to allocate register map\n");
/*
- * Setting up a low latency read for getting sample data. Used for both
- * direct read an triggered buffer. Additional fields will be set up in
- * ad7380_update_xfers() based on the current state of the driver at the
- * time of the read.
+ * Setting up xfer structures for both normal and sequence mode. These
+ * struct are used for both direct read and triggered buffer. Additional
+ * fields will be set up in ad7380_update_xfers() based on the current
+ * state of the driver at the time of the read.
*/
- /* toggle CS (no data xfer) to trigger a conversion */
- st->xfer[0].cs_change = 1;
- st->xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
- st->xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
-
- /* then do a second xfer to read the data */
- st->xfer[1].rx_buf = st->scan_data;
+ /*
+ * In normal mode a read is composed of two steps:
+ * - first, toggle CS (no data xfer) to trigger a conversion
+ * - then, read data
+ */
+ st->normal_xfer[0].cs_change = 1;
+ st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ st->normal_xfer[1].rx_buf = st->scan_data;
- spi_message_init_with_transfers(&st->msg, st->xfer, ARRAY_SIZE(st->xfer));
+ spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer,
+ ARRAY_SIZE(st->normal_xfer));
+ /*
+ * In sequencer mode a read is composed of four steps:
+ * - CS toggle (no data xfer) to get the right point in the sequence
+ * - CS toggle (no data xfer) to trigger a conversion of AinX0 and
+ * acquisition of AinX1
+ * - 2 data reads, to read AinX0 and AinX1
+ */
+ st->seq_xfer[0].cs_change = 1;
+ st->seq_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->seq_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ st->seq_xfer[1].cs_change = 1;
+ st->seq_xfer[1].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->seq_xfer[1].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ st->seq_xfer[2].rx_buf = st->scan_data;
+ st->seq_xfer[2].cs_change = 1;
+ st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer,
+ ARRAY_SIZE(st->seq_xfer));
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
--
2.45.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 5/5] docs: iio: ad7380: add support for single-ended parts
2024-07-26 15:20 [PATCH 0/5] ad7380: add support for single-ended parts Julien Stephan
` (3 preceding siblings ...)
2024-07-26 15:20 ` [PATCH 4/5] ad7380: enable sequencer " Julien Stephan
@ 2024-07-26 15:20 ` Julien Stephan
2024-07-28 16:37 ` Jonathan Cameron
4 siblings, 1 reply; 12+ messages in thread
From: Julien Stephan @ 2024-07-26 15:20 UTC (permalink / raw)
To: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: Michael Hennerich, linux-iio, devicetree, linux-kernel, linux-doc,
Julien Stephan
The AD7380 family has some compatible single-ended chips: AD7386/7/8(-4).
These single-ended chips have a 2:1 multiplexer in front of each ADC.
They also include additional configuration registers that allow for either
manual selection or automatic switching (sequencer mode), of the
multiplexer inputs. Add a section to describe this.
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
Documentation/iio/ad7380.rst | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/Documentation/iio/ad7380.rst b/Documentation/iio/ad7380.rst
index 061cd632b5df..81dfa39519fb 100644
--- a/Documentation/iio/ad7380.rst
+++ b/Documentation/iio/ad7380.rst
@@ -17,10 +17,16 @@ The following chips are supported by this driver:
* `AD7381 <https://www.analog.com/en/products/ad7381.html>`_
* `AD7383 <https://www.analog.com/en/products/ad7383.html>`_
* `AD7384 <https://www.analog.com/en/products/ad7384.html>`_
+* `AD7386 <https://www.analog.com/en/products/ad7386.html>`_
+* `AD7387 <https://www.analog.com/en/products/ad7387.html>`_
+* `AD7388 <https://www.analog.com/en/products/ad7388.html>`_
* `AD7380-4 <https://www.analog.com/en/products/ad7380-4.html>`_
* `AD7381-4 <https://www.analog.com/en/products/ad7381-4.html>`_
* `AD7383-4 <https://www.analog.com/en/products/ad7383-4.html>`_
* `AD7384-4 <https://www.analog.com/en/products/ad7384-4.html>`_
+* `AD7386-4 <https://www.analog.com/en/products/ad7386-4.html>`_
+* `AD7387-4 <https://www.analog.com/en/products/ad7387-4.html>`_
+* `AD7388-4 <https://www.analog.com/en/products/ad7388-4.html>`_
Supported features
@@ -69,6 +75,42 @@ must restart iiod using the following command:
root:~# systemctl restart iiod
+Channel selection and sequencer (single-end chips only)
+-------------------------------------------------------
+
+Single-ended chips of this family (ad7386/7/8(-4)) have a 2:1 multiplexer in
+front of each ADC. They also include additional configuration registers that
+allow for either manual selection or automatic switching (sequencer mode),of the
+multiplexer inputs.
+
+From an IIO point of view, all inputs are exported, i.e ad7386/7/8
+export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
+
+Inputs ``AinX0`` of multiplexers correspond to the first half of IIO channels (i.e
+0-1 or 0-3) and inputs ``AinX1`` correspond to second half (i.e 2-3 or 4-7).
+Example for AD7386/7/8 (2 channels parts):
+
+.. code-block::
+
+ IIO | AD7386/7/8
+ | +----------------------------
+ | | _____ ______
+ | | | | | |
+ voltage0 | AinA0 --|--->| | | |
+ | | | mux |----->| ADCA |---
+ voltage2 | AinA1 --|--->| | | |
+ | | |_____| |_____ |
+ | | _____ ______
+ | | | | | |
+ voltage1 | AinB0 --|--->| | | |
+ | | | mux |----->| ADCB |---
+ voltage3 | AinB1 --|--->| | | |
+ | | |_____| |______|
+ | |
+ | +----------------------------
+
+
+When enabling sequencer mode, the effective sampling rate is divided by two.
Unimplemented features
----------------------
--
2.45.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts
2024-07-26 15:20 ` [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts Julien Stephan
@ 2024-07-28 10:33 ` Krzysztof Kozlowski
2024-07-28 10:34 ` Krzysztof Kozlowski
1 sibling, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2024-07-28 10:33 UTC (permalink / raw)
To: Julien Stephan, Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: linux-iio, devicetree, linux-kernel, linux-doc
On 26/07/2024 17:20, Julien Stephan wrote:
> Adding ad7386/7/8 single-ended compatible parts, and the corresponding
> ad7386-4/7-4/8-4 4 channels.
>
> Signed-off-by: Julien Stephan <jstephan@baylibre.com>
> ---
> Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
> index 899b777017ce..bd19abb867d9 100644
> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
> @@ -15,10 +15,17 @@ description: |
> * https://www.analog.com/en/products/ad7381.html
> * https://www.analog.com/en/products/ad7383.html
> * https://www.analog.com/en/products/ad7384.html
> + * https://www.analog.com/en/products/ad7386.html
> + * https://www.analog.com/en/products/ad7387.html
> + * https://www.analog.com/en/products/ad7388.html
> * https://www.analog.com/en/products/ad7380-4.html
> * https://www.analog.com/en/products/ad7381-4.html
> * https://www.analog.com/en/products/ad7383-4.html
> * https://www.analog.com/en/products/ad7384-4.html
> + * https://www.analog.com/en/products/ad7386-4.html
> + * https://www.analog.com/en/products/ad7387-4.html
> + * https://www.analog.com/en/products/ad7388-4.html
> +
>
> $ref: /schemas/spi/spi-peripheral-props.yaml#
>
> @@ -29,10 +36,16 @@ properties:
> - adi,ad7381
> - adi,ad7383
> - adi,ad7384
> + - adi,ad7386
> + - adi,ad7387
> + - adi,ad7388
> - adi,ad7380-4
> - adi,ad7381-4
> - adi,ad7383-4
> - adi,ad7384-4
> + - adi,ad7386-4
Lists are ordered alphabetically. Do not add new entries to the end of
the lists (like in your commit "add support for ad738x-4 4") because
that is conflict prone.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts
2024-07-26 15:20 ` [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts Julien Stephan
2024-07-28 10:33 ` Krzysztof Kozlowski
@ 2024-07-28 10:34 ` Krzysztof Kozlowski
1 sibling, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2024-07-28 10:34 UTC (permalink / raw)
To: Julien Stephan, Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet
Cc: linux-iio, devicetree, linux-kernel, linux-doc
On 26/07/2024 17:20, Julien Stephan wrote:
> Adding ad7386/7/8 single-ended compatible parts, and the corresponding
> ad7386-4/7-4/8-4 4 channels.
>
> Signed-off-by: Julien Stephan <jstephan@baylibre.com>
> ---
> Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml | 13 +++++++++++++
FWIW, since ordering is already broken:
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/5] ad7380: add support for single-ended parts
2024-07-26 15:20 ` [PATCH 3/5] ad7380: add support for single-ended parts Julien Stephan
@ 2024-07-28 16:23 ` Jonathan Cameron
0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-07-28 16:23 UTC (permalink / raw)
To: Julien Stephan
Cc: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, linux-iio, devicetree,
linux-kernel, linux-doc
On Fri, 26 Jul 2024 17:20:08 +0200
Julien Stephan <jstephan@baylibre.com> wrote:
> Adding ad7386/7/8 (16/14/12 bits) unsigned, dual simultaneous sampling,
> single-ended compatible parts, and the corresponding ad7386-4/7-4/8-4
> 4 channels.
>
> These parts have a 2:1 multiplexer in front of each ADC. They also include
> additional configuration registers that allow for either manual selection
> or automatic switching (sequencer mode), of the multiplexer inputs.
> This commit focus on integrating manual selection. Sequencer mode will
> be implemented later.
>
> From an IIO point of view, all inputs are exported, i.e ad7386/7/8
> export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
>
> Inputs AinX0 of multiplexers correspond to the first half of IIO channels
> (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
> 4-7). Example for AD7386/7/8 (2 channels parts):
>
> IIO | AD7386/7/8
> | +----------------------------
> | | _____ ______
> | | | | | |
> voltage0 | AinA0 --|--->| | | |
> | | | mux |----->| ADCA |---
> voltage2 | AinA1 --|--->| | | |
> | | |_____| |_____ |
> | | _____ ______
> | | | | | |
> voltage1 | AinB0 --|--->| | | |
> | | | mux |----->| ADCB |---
> voltage3 | AinB1 --|--->| | | |
> | | |_____| |______|
> | |
> | +----------------------------
>
> When switching channel, the ADC require an additional settling time.
> According to the datasheet, data is valid on the third CS low. We already
> have an extra toggle before each read (either direct reads or buffered
> reads) to sample correct data, so we just add a single CS toggle at the
> end of the register write.
>
> Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Hi Julien
LGTM - only one trivial comment inline.
If nothing else comes up I can change that whilst applying.
I won't be applying today however given this is a new series and has only been
on the list since Friday.
...
> @@ -92,8 +96,24 @@ enum {
> AD7380_SCAN_TYPE_RESOLUTION_BOOST,
> };
>
> -/* Extended scan types for 14-bit chips. */
> -static const struct iio_scan_type ad7380_scan_type_14[] = {
> +/* Extended scan types for 12-bit unsigned chips. */
> +static const struct iio_scan_type ad7380_scan_type_12_u[] = {
> + [AD7380_SCAN_TYPE_NORMAL] = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16,
> + .endianness = IIO_CPU
Add trailing commas. In theory we might expand this structure
in the future. The only time we don't add trailing commas is
for 'null' terminator type entries where we know anything added
must come before them.
> + },
> + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
> + .sign = 'u',
> + .realbits = 14,
> + .storagebits = 16,
> + .endianness = IIO_CPU
> + },
> +};
>
> +/*
> + * Single ended parts have a 2:1 multiplexer in front of each ADC.
> + *
> + * From an IIO point of view, all inputs are exported, i.e ad7386/7/8
> + * export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
> + *
> + * Inputs AinX0 of multiplexers correspond to the first half of IIO channels
> + * (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
> + * 4-7). Example for AD7386/7/8 (2 channels parts):
> + *
> + * IIO | AD7386/7/8
> + * | +----------------------------
> + * | | _____ ______
> + * | | | | | |
> + * voltage0 | AinA0 --|--->| | | |
> + * | | | mux |----->| ADCA |---
> + * voltage2 | AinA1 --|--->| | | |
> + * | | |_____| |_____ |
> + * | | _____ ______
> + * | | | | | |
> + * voltage1 | AinB0 --|--->| | | |
> + * | | | mux |----->| ADCB |---
> + * voltage3 | AinB1 --|--->| | | |
> + * | | |_____| |______|
> + * | |
> + * | +----------------------------
> + *
> + * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
> + * scan masks.
> + */
Good. I always like some nice art :)
+ your implementation takes the same approach I would have done.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 4/5] ad7380: enable sequencer for single-ended parts
2024-07-26 15:20 ` [PATCH 4/5] ad7380: enable sequencer " Julien Stephan
@ 2024-07-28 16:35 ` Jonathan Cameron
2024-07-30 7:34 ` Julien Stephan
0 siblings, 1 reply; 12+ messages in thread
From: Jonathan Cameron @ 2024-07-28 16:35 UTC (permalink / raw)
To: Julien Stephan
Cc: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, linux-iio, devicetree,
linux-kernel, linux-doc
On Fri, 26 Jul 2024 17:20:09 +0200
Julien Stephan <jstephan@baylibre.com> wrote:
> ad7386/7/8(-4) single-ended parts have a 2:1 mux in front of each ADC.
>
> From an IIO point of view, all inputs are exported, i.e ad7386/7/8
> export 4 channels and ad7386-4/7-4/8-4 export 8 channels. First inputs
> of muxes correspond to the first half of IIO channels (i.e 0-1 or 0-3)
> and second inputs correspond to second half (i.e 2-3 or 4-7)
>
> Currently, the driver supports only sampling first half OR second half of
> the IIO channels. To enable sampling all channels simultaneously, these
> parts have an internal sequencer that automatically cycle through the
> mux entries.
>
> When enabled, the maximum throughput is divided by two. Moreover, the ADCs
> need additional settling time, so we add an extra CS toggle to correctly
> propagate setting, and an additional spi transfer to read the second
> half.
>
> Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Hi Julien,
All looks good. Main comment is a suggestion that we add a core
interface to get the index of the active_scan_mask if it is built
from available_scan_masks. That will avoid the mask matching code
in here.
Implementation for now would be a simple bit of pointer
arithmetic after checking available_scan_masks is set.
Jonathan
> ---
> drivers/iio/adc/ad7380.c | 164 ++++++++++++++++++++++++++++++++++-------------
> 1 file changed, 121 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
> index 25d42fff1839..11ed010431cf 100644
> --- a/drivers/iio/adc/ad7380.c
> +++ b/drivers/iio/adc/ad7380.c
> @@ -33,7 +33,7 @@
> @@ -290,16 +291,22 @@ static const unsigned long ad7380_4_channel_scan_masks[] = {
> *
> * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
> * scan masks.
> + * When sequencer mode is enabled, chip automatically cycle through
cycles
> + * AinX0 and AinX1 channels. From an IIO point of view, we ca enable all
> + * channels, at the cost of an extra read, thus dividing the maximum rate by
> + * two.
> */
...
> * DMA (thus cache coherency maintenance) requires the transfer buffers
> * to live in their own cache lines.
> @@ -609,33 +619,47 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
> static void ad7380_update_xfers(struct ad7380_state *st,
> const struct iio_scan_type *scan_type)
> {
> - /*
> - * First xfer only triggers conversion and has to be long enough for
> - * all conversions to complete, which can be multiple conversion in the
> - * case of oversampling. Technically T_CONVERT_X_NS is lower for some
> - * chips, but we use the maximum value for simplicity for now.
> - */
> - if (st->oversampling_ratio > 1)
> - st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS *
> - (st->oversampling_ratio - 1);
> - else
> - st->xfer[0].delay.value = T_CONVERT_NS;
> -
> - st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
> + struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer;
> + unsigned int t_convert = T_CONVERT_NS;
>
> /*
> - * Second xfer reads all channels. Data size depends on if resolution
> - * boost is enabled or not.
> + * In the case of oversampling, conversion time is higher than in normal
> + * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use
> + * the maximum value for simplicity for now.
> */
> - st->xfer[1].bits_per_word = scan_type->realbits;
> - st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
> - st->chip_info->num_simult_channels;
> + if (st->oversampling_ratio > 1)
> + t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
> + (st->oversampling_ratio - 1);
> +
> + if (st->seq) {
> + xfer[0].delay.value = xfer[1].delay.value = t_convert;
> + xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
> + xfer[2].bits_per_word = xfer[3].bits_per_word =
> + scan_type->realbits;
> + xfer[2].len = xfer[3].len =
> + BITS_TO_BYTES(scan_type->storagebits) *
> + st->chip_info->num_simult_channels;
> + xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len;
> + /* Additional delay required here when oversampling is enabled */
> + if (st->oversampling_ratio > 1)
> + xfer[2].delay.value = t_convert;
> + else
> + xfer[2].delay.value = 0;
> + xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS;
> + } else {
> + xfer[0].delay.value = t_convert;
> + xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
> + xfer[1].bits_per_word = scan_type->realbits;
> + xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
> + st->chip_info->num_simult_channels;
> + }
> }
>
> static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
> {
> struct ad7380_state *st = iio_priv(indio_dev);
> const struct iio_scan_type *scan_type;
> + struct spi_message *msg = &st->normal_msg;
>
> /*
> * Currently, we always read all channels at the same time. The scan_type
> @@ -646,34 +670,62 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
> return PTR_ERR(scan_type);
>
> if (st->chip_info->has_mux) {
> - unsigned int num_simult_channels = st->chip_info->num_simult_channels;
> + unsigned int num_simult_channels =
> + st->chip_info->num_simult_channels;
Unrelated change. Push this back to the earlier patch (or leave it alone - whether
it matters for readability is debatable anyway, so I think this is fine either way).
> unsigned long active_scan_mask = *indio_dev->active_scan_mask;
> unsigned int ch = 0;
> int ret;
>
> /*
> * Depending on the requested scan_mask and current state,
> - * we need to change CH bit to sample correct data.
> + * we need to either change CH bit, or enable sequencer mode
> + * to sample correct data.
> + * Sequencer mode is enabled if active mask corresponds to all
> + * IIO channels enabled. Otherwise, CH bit is set.
> */
> - if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
> - num_simult_channels))
> - ch = 1;
> + if (active_scan_mask == GENMASK(2 * num_simult_channels - 1, 0)) {
Whilst it's an implementation detail that you can (IIRC) just compare the active_scan_mask
address with that of your available_scan_masks array entries, maybe it's worth providing
an interface that gets the index of that array?
int iio_active_scan_mask_index(struct iio_dev *)
that returns an error if available_scan_masks isn't set.
We know the active_scan_mask will always be selected from the available ones
so this interface should be fine even if we change how they are handled internally
in the future.
That would then make all these matches simpler.
> + ret = regmap_update_bits(st->regmap,
> + AD7380_REG_ADDR_CONFIG1,
> + AD7380_CONFIG1_SEQ,
> + FIELD_PREP(AD7380_CONFIG1_SEQ, 1));
> + msg = &st->seq_msg;
> + st->seq = true;
> + } else {
> + if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
> + num_simult_channels))
> + ch = 1;
> +
> + ret = ad7380_set_ch(st, ch);
> + }
>
> - ret = ad7380_set_ch(st, ch);
> if (ret)
> return ret;
I'd just duplicate this if (ret) check as the two calls are very different so to
me this doesn't make logical sense (even if it works).
> }
>
> ad7380_update_xfers(st, scan_type);
>
> - return spi_optimize_message(st->spi, &st->msg);
> + return spi_optimize_message(st->spi, msg);
> }
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 5/5] docs: iio: ad7380: add support for single-ended parts
2024-07-26 15:20 ` [PATCH 5/5] docs: iio: ad7380: add support " Julien Stephan
@ 2024-07-28 16:37 ` Jonathan Cameron
0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2024-07-28 16:37 UTC (permalink / raw)
To: Julien Stephan
Cc: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, linux-iio, devicetree,
linux-kernel, linux-doc
On Fri, 26 Jul 2024 17:20:10 +0200
Julien Stephan <jstephan@baylibre.com> wrote:
> The AD7380 family has some compatible single-ended chips: AD7386/7/8(-4).
> These single-ended chips have a 2:1 multiplexer in front of each ADC.
> They also include additional configuration registers that allow for either
> manual selection or automatic switching (sequencer mode), of the
> multiplexer inputs. Add a section to describe this.
>
> Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Just one trivial missing space.
Nice patch set. Thanks,
Jonathan
> ---
> Documentation/iio/ad7380.rst | 42 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
>
> diff --git a/Documentation/iio/ad7380.rst b/Documentation/iio/ad7380.rst
> index 061cd632b5df..81dfa39519fb 100644
> --- a/Documentation/iio/ad7380.rst
> +++ b/Documentation/iio/ad7380.rst
> @@ -17,10 +17,16 @@ The following chips are supported by this driver:
> * `AD7381 <https://www.analog.com/en/products/ad7381.html>`_
> * `AD7383 <https://www.analog.com/en/products/ad7383.html>`_
> * `AD7384 <https://www.analog.com/en/products/ad7384.html>`_
> +* `AD7386 <https://www.analog.com/en/products/ad7386.html>`_
> +* `AD7387 <https://www.analog.com/en/products/ad7387.html>`_
> +* `AD7388 <https://www.analog.com/en/products/ad7388.html>`_
> * `AD7380-4 <https://www.analog.com/en/products/ad7380-4.html>`_
> * `AD7381-4 <https://www.analog.com/en/products/ad7381-4.html>`_
> * `AD7383-4 <https://www.analog.com/en/products/ad7383-4.html>`_
> * `AD7384-4 <https://www.analog.com/en/products/ad7384-4.html>`_
> +* `AD7386-4 <https://www.analog.com/en/products/ad7386-4.html>`_
> +* `AD7387-4 <https://www.analog.com/en/products/ad7387-4.html>`_
> +* `AD7388-4 <https://www.analog.com/en/products/ad7388-4.html>`_
>
>
> Supported features
> @@ -69,6 +75,42 @@ must restart iiod using the following command:
>
> root:~# systemctl restart iiod
>
> +Channel selection and sequencer (single-end chips only)
> +-------------------------------------------------------
> +
> +Single-ended chips of this family (ad7386/7/8(-4)) have a 2:1 multiplexer in
> +front of each ADC. They also include additional configuration registers that
allow for either manual selection or automatic switching (sequencer mode),of the
space after ,
plus adjust the wrap as that'll make it 81 chars I think.
> +multiplexer inputs.
> +
> +From an IIO point of view, all inputs are exported, i.e ad7386/7/8
> +export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
> +
> +Inputs ``AinX0`` of multiplexers correspond to the first half of IIO channels (i.e
> +0-1 or 0-3) and inputs ``AinX1`` correspond to second half (i.e 2-3 or 4-7).
> +Example for AD7386/7/8 (2 channels parts):
> +
> +.. code-block::
> +
> + IIO | AD7386/7/8
> + | +----------------------------
> + | | _____ ______
> + | | | | | |
> + voltage0 | AinA0 --|--->| | | |
> + | | | mux |----->| ADCA |---
> + voltage2 | AinA1 --|--->| | | |
> + | | |_____| |_____ |
> + | | _____ ______
> + | | | | | |
> + voltage1 | AinB0 --|--->| | | |
> + | | | mux |----->| ADCB |---
> + voltage3 | AinB1 --|--->| | | |
> + | | |_____| |______|
> + | |
> + | +----------------------------
> +
> +
> +When enabling sequencer mode, the effective sampling rate is divided by two.
>
> Unimplemented features
> ----------------------
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 4/5] ad7380: enable sequencer for single-ended parts
2024-07-28 16:35 ` Jonathan Cameron
@ 2024-07-30 7:34 ` Julien Stephan
0 siblings, 0 replies; 12+ messages in thread
From: Julien Stephan @ 2024-07-30 7:34 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Michael Hennerich, Nuno Sá, David Lechner,
Lars-Peter Clausen, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jonathan Corbet, linux-iio, devicetree,
linux-kernel, linux-doc
Le dim. 28 juil. 2024 à 18:36, Jonathan Cameron <jic23@kernel.org> a écrit :
>
> On Fri, 26 Jul 2024 17:20:09 +0200
> Julien Stephan <jstephan@baylibre.com> wrote:
>
> > ad7386/7/8(-4) single-ended parts have a 2:1 mux in front of each ADC.
> >
> > From an IIO point of view, all inputs are exported, i.e ad7386/7/8
> > export 4 channels and ad7386-4/7-4/8-4 export 8 channels. First inputs
> > of muxes correspond to the first half of IIO channels (i.e 0-1 or 0-3)
> > and second inputs correspond to second half (i.e 2-3 or 4-7)
> >
> > Currently, the driver supports only sampling first half OR second half of
> > the IIO channels. To enable sampling all channels simultaneously, these
> > parts have an internal sequencer that automatically cycle through the
> > mux entries.
> >
> > When enabled, the maximum throughput is divided by two. Moreover, the ADCs
> > need additional settling time, so we add an extra CS toggle to correctly
> > propagate setting, and an additional spi transfer to read the second
> > half.
> >
> > Signed-off-by: Julien Stephan <jstephan@baylibre.com>
> Hi Julien,
>
> All looks good. Main comment is a suggestion that we add a core
> interface to get the index of the active_scan_mask if it is built
> from available_scan_masks. That will avoid the mask matching code
> in here.
>
> Implementation for now would be a simple bit of pointer
> arithmetic after checking available_scan_masks is set.
>
> Jonathan
>
> > ---
> > drivers/iio/adc/ad7380.c | 164 ++++++++++++++++++++++++++++++++++-------------
> > 1 file changed, 121 insertions(+), 43 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
> > index 25d42fff1839..11ed010431cf 100644
> > --- a/drivers/iio/adc/ad7380.c
> > +++ b/drivers/iio/adc/ad7380.c
> > @@ -33,7 +33,7 @@
>
> > @@ -290,16 +291,22 @@ static const unsigned long ad7380_4_channel_scan_masks[] = {
> > *
> > * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
> > * scan masks.
> > + * When sequencer mode is enabled, chip automatically cycle through
>
> cycles
>
> > + * AinX0 and AinX1 channels. From an IIO point of view, we ca enable all
> > + * channels, at the cost of an extra read, thus dividing the maximum rate by
> > + * two.
> > */
>
> ...
>
> > * DMA (thus cache coherency maintenance) requires the transfer buffers
> > * to live in their own cache lines.
> > @@ -609,33 +619,47 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
> > static void ad7380_update_xfers(struct ad7380_state *st,
> > const struct iio_scan_type *scan_type)
> > {
> > - /*
> > - * First xfer only triggers conversion and has to be long enough for
> > - * all conversions to complete, which can be multiple conversion in the
> > - * case of oversampling. Technically T_CONVERT_X_NS is lower for some
> > - * chips, but we use the maximum value for simplicity for now.
> > - */
> > - if (st->oversampling_ratio > 1)
> > - st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS *
> > - (st->oversampling_ratio - 1);
> > - else
> > - st->xfer[0].delay.value = T_CONVERT_NS;
> > -
> > - st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
> > + struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer;
> > + unsigned int t_convert = T_CONVERT_NS;
> >
> > /*
> > - * Second xfer reads all channels. Data size depends on if resolution
> > - * boost is enabled or not.
> > + * In the case of oversampling, conversion time is higher than in normal
> > + * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use
> > + * the maximum value for simplicity for now.
> > */
> > - st->xfer[1].bits_per_word = scan_type->realbits;
> > - st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
> > - st->chip_info->num_simult_channels;
> > + if (st->oversampling_ratio > 1)
> > + t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
> > + (st->oversampling_ratio - 1);
> > +
> > + if (st->seq) {
> > + xfer[0].delay.value = xfer[1].delay.value = t_convert;
> > + xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
> > + xfer[2].bits_per_word = xfer[3].bits_per_word =
> > + scan_type->realbits;
> > + xfer[2].len = xfer[3].len =
> > + BITS_TO_BYTES(scan_type->storagebits) *
> > + st->chip_info->num_simult_channels;
> > + xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len;
> > + /* Additional delay required here when oversampling is enabled */
> > + if (st->oversampling_ratio > 1)
> > + xfer[2].delay.value = t_convert;
> > + else
> > + xfer[2].delay.value = 0;
> > + xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS;
> > + } else {
> > + xfer[0].delay.value = t_convert;
> > + xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
> > + xfer[1].bits_per_word = scan_type->realbits;
> > + xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
> > + st->chip_info->num_simult_channels;
> > + }
> > }
> >
> > static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
> > {
> > struct ad7380_state *st = iio_priv(indio_dev);
> > const struct iio_scan_type *scan_type;
> > + struct spi_message *msg = &st->normal_msg;
> >
> > /*
> > * Currently, we always read all channels at the same time. The scan_type
> > @@ -646,34 +670,62 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
> > return PTR_ERR(scan_type);
> >
> > if (st->chip_info->has_mux) {
> > - unsigned int num_simult_channels = st->chip_info->num_simult_channels;
> > + unsigned int num_simult_channels =
> > + st->chip_info->num_simult_channels;
>
> Unrelated change. Push this back to the earlier patch (or leave it alone - whether
> it matters for readability is debatable anyway, so I think this is fine either way).
>
> > unsigned long active_scan_mask = *indio_dev->active_scan_mask;
> > unsigned int ch = 0;
> > int ret;
> >
> > /*
> > * Depending on the requested scan_mask and current state,
> > - * we need to change CH bit to sample correct data.
> > + * we need to either change CH bit, or enable sequencer mode
> > + * to sample correct data.
> > + * Sequencer mode is enabled if active mask corresponds to all
> > + * IIO channels enabled. Otherwise, CH bit is set.
> > */
> > - if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
> > - num_simult_channels))
> > - ch = 1;
> > + if (active_scan_mask == GENMASK(2 * num_simult_channels - 1, 0)) {
>
> Whilst it's an implementation detail that you can (IIRC) just compare the active_scan_mask
> address with that of your available_scan_masks array entries, maybe it's worth providing
> an interface that gets the index of that array?
>
> int iio_active_scan_mask_index(struct iio_dev *)
> that returns an error if available_scan_masks isn't set.
Hi Jonathan,
I'll send a v2 of this series in a couple of days, with all comments
fixed and I'll try to implement an iio_active_scan_mask_index
function.
Cheers
Julien
>
> We know the active_scan_mask will always be selected from the available ones
> so this interface should be fine even if we change how they are handled internally
> in the future.
>
> That would then make all these matches simpler.
>
> > + ret = regmap_update_bits(st->regmap,
> > + AD7380_REG_ADDR_CONFIG1,
> > + AD7380_CONFIG1_SEQ,
> > + FIELD_PREP(AD7380_CONFIG1_SEQ, 1));
> > + msg = &st->seq_msg;
> > + st->seq = true;
> > + } else {
> > + if (active_scan_mask == GENMASK(2 * num_simult_channels - 1,
> > + num_simult_channels))
> > + ch = 1;
> > +
> > + ret = ad7380_set_ch(st, ch);
> > + }
> >
> > - ret = ad7380_set_ch(st, ch);
> > if (ret)
> > return ret;
>
> I'd just duplicate this if (ret) check as the two calls are very different so to
> me this doesn't make logical sense (even if it works).
>
> > }
> >
> > ad7380_update_xfers(st, scan_type);
> >
> > - return spi_optimize_message(st->spi, &st->msg);
> > + return spi_optimize_message(st->spi, msg);
> > }
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-07-30 7:34 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-26 15:20 [PATCH 0/5] ad7380: add support for single-ended parts Julien Stephan
2024-07-26 15:20 ` [PATCH 1/5] dt-bindings: iio: adc: ad7380: add single-ended compatible parts Julien Stephan
2024-07-28 10:33 ` Krzysztof Kozlowski
2024-07-28 10:34 ` Krzysztof Kozlowski
2024-07-26 15:20 ` [PATCH 2/5] ad7380: prepare driver for single-ended parts support Julien Stephan
2024-07-26 15:20 ` [PATCH 3/5] ad7380: add support for single-ended parts Julien Stephan
2024-07-28 16:23 ` Jonathan Cameron
2024-07-26 15:20 ` [PATCH 4/5] ad7380: enable sequencer " Julien Stephan
2024-07-28 16:35 ` Jonathan Cameron
2024-07-30 7:34 ` Julien Stephan
2024-07-26 15:20 ` [PATCH 5/5] docs: iio: ad7380: add support " Julien Stephan
2024-07-28 16:37 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox