* [PATCH v2 00/16] Add features, improvements, and fixes
@ 2025-01-27 15:10 Jonathan Santos
2025-01-27 15:11 ` [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign Jonathan Santos
` (15 more replies)
0 siblings, 16 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:10 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
This patch series introduces some new features, improvements,
and fixes for the AD7768-1 ADC driver.
The goal is to support all key functionalities listed in the device
datasheet, including filter mode selection, common mode voltage output
configuration and GPIO support. Additionally, this includes fixes
for SPI communication and for IIO interface, and also code improvements
to enhance maintainability and readability.
---
Changes in v2:
* Removed synchronization over SPI property and replaced it for trigger-sources.
* Added GPIO controller documentation.
* VCM output control changed from an IIO attribute to a devicetree property (static value).
* Converted driver to use regmap and dropped spi_read_reg and spi_write_reg pacthes.
* replaced decimation_rate attribute for oversampling_ratio and dropped device specific documentation patch.
* Added low pass -3dB cutoff attribute.
* Addressed review comments, see individual pacthes.
* Link to v1: https://lore.kernel.org/linux-iio/cover.1736201898.git.Jonathan.Santos@analog.com/T/#t
---
Jonathan Santos (11):
dt-bindings: iio: adc: ad7768-1: add trigger-sources property
dt-bindings: iio: adc: ad7768-1: Document GPIO controller
dt-bindings: iio: adc: ad7768-1: add VMC output property
Documentation: ABI: add wideband filter type to sysfs-bus-iio
iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset
iio: adc: ad7768-1: convert driver to use regmap
iio: adc: ad7768-1: remove unnecessary locking
iio: adc: ad7768-1: add multiple scan types to support 16-bits mode
iio: adc: ad7768-1: add support for Synchronization over SPI
iio: adc: ad7768-1: add filter type and oversampling ratio attributes
iio: adc: ad7768-1: add low pass -3dB cutoff attribute
Sergiu Cuciurean (5):
iio: adc: ad7768-1: Fix conversion result sign
iio: adc: ad7768-1: Add reset gpio
iio: adc: ad7768-1: Move buffer allocation to a separate function
iio: adc: ad7768-1: Add VCM output support
iio: adc: ad7768-1: Add GPIO controller support
Documentation/ABI/testing/sysfs-bus-iio | 2 +
.../bindings/iio/adc/adi,ad7768-1.yaml | 42 +-
drivers/iio/adc/ad7768-1.c | 889 +++++++++++++++---
include/dt-bindings/iio/adc/adi,ad7768-1.h | 16 +
4 files changed, 816 insertions(+), 133 deletions(-)
create mode 100644 include/dt-bindings/iio/adc/adi,ad7768-1.h
base-commit: 5de07b8a24cf44cdb78adeab790704bf577c2c1d
prerequisite-patch-id: 8b531bca46f7c7ea1c0f6d232d162fd05fda52f7
--
2.34.1
^ permalink raw reply [flat|nested] 56+ messages in thread
* [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
@ 2025-01-27 15:11 ` Jonathan Santos
2025-02-01 15:27 ` Jonathan Cameron
2025-02-01 15:36 ` Jonathan Cameron
2025-01-27 15:11 ` [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property Jonathan Santos
` (14 subsequent siblings)
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:11 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1,
David Lechner
From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
The ad7768-1 ADC output code is two's complement, meaning that the voltage
conversion result is a signed value.. Since the value is a 24 bit one,
stored in a 32 bit variable, the sign should be extended in order to get
the correct representation.
Also the channel description has been updated to signed representation,
to match the ADC specifications.
Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support")
Reviewed-by: David Lechner <dlechner@baylibre.com>
Reviewed-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
---
v2 Changes:
* Patch moved to the start of the patch series.
---
drivers/iio/adc/ad7768-1.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 113703fb7245..c3cf04311c40 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -142,7 +142,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
.channel = 0,
.scan_index = 0,
.scan_type = {
- .sign = 'u',
+ .sign = 's',
.realbits = 24,
.storagebits = 32,
.shift = 8,
@@ -371,7 +371,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
ret = ad7768_scan_direct(indio_dev);
if (ret >= 0)
- *val = ret;
+ *val = sign_extend32(ret, chan->scan_type.realbits - 1);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
2025-01-27 15:11 ` [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign Jonathan Santos
@ 2025-01-27 15:11 ` Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
` (2 more replies)
2025-01-27 15:11 ` [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller Jonathan Santos
` (13 subsequent siblings)
15 siblings, 3 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:11 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
Add a new trigger-sources property to enable synchronization across
multiple devices. This property references the main device (or
trigger provider) responsible for generating the pulse to drive the
SYNC_IN of all devices in the setup.
In addition to GPIO synchronization, The AD7768-1 also supports
synchronization over SPI, which use is recommended when the GPIO
cannot provide a pulse synchronous with the base MCLK signal. It
consists of looping back the SYNC_OUT to the SYNC_IN pin and send
a command via SPI to trigger the synchronization.
SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
property. Since adi,sync-in-gpios is not long the only method, remove it
from required properties.
While at it, add description to the interrupt property.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* Patch added as replacement for adi,sync-in-spi patch.
* addressed the request for a description to interrupts property.
---
.../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
index 3ce59d4d065f..3e119cf1754b 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
@@ -26,7 +26,17 @@ properties:
clock-names:
const: mclk
+ trigger-sources:
+ description:
+ References the main device responsible for synchronization. In a single
+ device setup, reference the own node.
+ maxItems: 1
+
interrupts:
+ description:
+ Specifies the interrupt line associated with the ADC. This refers
+ to the DRDY (Data Ready) pin, which signals when conversion results are
+ available.
maxItems: 1
'#address-cells':
@@ -46,6 +56,8 @@ properties:
sampling. A pulse is always required if the configuration is changed
in any way, for example if the filter decimation rate changes.
As the line is active low, it should be marked GPIO_ACTIVE_LOW.
+ In the absence of this property, Synchronization over SPI will be
+ enabled.
reset-gpios:
maxItems: 1
@@ -57,6 +69,9 @@ properties:
"#io-channel-cells":
const: 1
+ "#trigger-source-cells":
+ const: 0
+
required:
- compatible
- reg
@@ -65,7 +80,8 @@ required:
- vref-supply
- spi-cpol
- spi-cpha
- - adi,sync-in-gpios
+ - trigger-sources
+ - #trigger-source-cells
patternProperties:
"^channel@([0-9]|1[0-5])$":
@@ -99,7 +115,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- adc@0 {
+ adc0: adc@0 {
compatible = "adi,ad7768-1";
reg = <0>;
spi-max-frequency = <2000000>;
@@ -109,6 +125,8 @@ examples:
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
+ trigger-sources = <&adc0>;
+ #trigger-source-cells = <0>;
reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
clocks = <&ad7768_mclk>;
clock-names = "mclk";
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
2025-01-27 15:11 ` [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign Jonathan Santos
2025-01-27 15:11 ` [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property Jonathan Santos
@ 2025-01-27 15:11 ` Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-27 16:56 ` Rob Herring (Arm)
2025-01-27 15:12 ` [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property Jonathan Santos
` (12 subsequent siblings)
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:11 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
The AD7768-1 ADC exports four bidirectional GPIOs accessible
via register map.
Document GPIO properties necessary to enable GPIO controller for this
device.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* New patch in v2.
---
.../devicetree/bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
index 3e119cf1754b..da05c8448530 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
@@ -72,6 +72,14 @@ properties:
"#trigger-source-cells":
const: 0
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+ description: |
+ The first cell is for the GPIO number: 0 to 3.
+ The second cell takes standard GPIO flags.
+
required:
- compatible
- reg
@@ -121,6 +129,8 @@ examples:
spi-max-frequency = <2000000>;
spi-cpol;
spi-cpha;
+ gpio-controller;
+ #gpio-cells = <2>;
vref-supply = <&adc_vref>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (2 preceding siblings ...)
2025-01-27 15:11 ` [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller Jonathan Santos
@ 2025-01-27 15:12 ` Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-28 1:28 ` David Lechner
2025-01-27 15:12 ` [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio Jonathan Santos
` (11 subsequent siblings)
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:12 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
The AD7768-1 provides a buffered common-mode voltage output
on the VCM pin that can be used to bias analog input signals.
Add adi,vcm-output to enable the configuration of the VCM output
circuit.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* New patch in v2.
---
.../bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
include/dt-bindings/iio/adc/adi,ad7768-1.h | 16 ++++++++++++++++
2 files changed, 26 insertions(+)
create mode 100644 include/dt-bindings/iio/adc/adi,ad7768-1.h
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
index da05c8448530..e26513a9469b 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
@@ -59,6 +59,15 @@ properties:
In the absence of this property, Synchronization over SPI will be
enabled.
+ adi,vcm-output:
+ description: |
+ Configures the Common-Mode Voltage Output. The VCM is provided by an
+ amplifier external to the AD7768-1 and can be used as common-mode voltage
+ by the ADC. There are 8 output voltage options available, and the macros
+ for these values can be found at dt-bindings/iio/adi,ad7768-1.h
+ items:
+ enum: [0, 1, 2, 3, 4, 5, 6, 7]
+
reset-gpios:
maxItems: 1
@@ -132,6 +141,7 @@ examples:
gpio-controller;
#gpio-cells = <2>;
vref-supply = <&adc_vref>;
+ adi,vcm-output = <AD7768_VCM_OUTPUT_AVDD1_AVSS_2>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
diff --git a/include/dt-bindings/iio/adc/adi,ad7768-1.h b/include/dt-bindings/iio/adc/adi,ad7768-1.h
new file mode 100644
index 000000000000..469ea724c0d5
--- /dev/null
+++ b/include/dt-bindings/iio/adc/adi,ad7768-1.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_ADI_AD7768_1_H
+#define _DT_BINDINGS_ADI_AD7768_1_H
+
+/* Sets VCM output to (AVDD1 − AVSS)/2 */
+#define AD7768_VCM_OUTPUT_AVDD1_AVSS_2 0x00
+#define AD7768_VCM_OUTPUT_2_5V 0x01
+#define AD7768_VCM_OUTPUT_2_05V 0x02
+#define AD7768_VCM_OUTPUT_1_9V 0x03
+#define AD7768_VCM_OUTPUT_1_65V 0x04
+#define AD7768_VCM_OUTPUT_1_1V 0x05
+#define AD7768_VCM_OUTPUT_0_9V 0x06
+#define AD7768_VCM_OUTPUT_OFF 0x07
+
+#endif /* _DT_BINDINGS_ADI_AD7768_1_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (3 preceding siblings ...)
2025-01-27 15:12 ` [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property Jonathan Santos
@ 2025-01-27 15:12 ` Jonathan Santos
2025-01-28 1:32 ` David Lechner
2025-01-27 15:12 ` [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset Jonathan Santos
` (10 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:12 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
The Wideband Low Ripple filter is used for AD7768-1 Driver.
Document wideband filter option into filter_type_available
attribute.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* Removed FIR mentions.
---
Documentation/ABI/testing/sysfs-bus-iio | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index f83bd6829285..9b879e7732cd 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2291,6 +2291,8 @@ Description:
* "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
* "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
* "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
+ * "wideband" - filter with wideband low ripple passband
+ and sharp transition band.
What: /sys/.../events/in_proximity_thresh_either_runningperiod
KernelVersion: 6.6
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (4 preceding siblings ...)
2025-01-27 15:12 ` [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio Jonathan Santos
@ 2025-01-27 15:12 ` Jonathan Santos
2025-02-01 15:31 ` Jonathan Cameron
2025-01-27 15:12 ` [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap Jonathan Santos
` (9 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:12 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
Datasheet recommends Setting the MOSI idle state to high in order to
prevent accidental reset of the device when SCLK is free running.
This happens when the controller clocks out a 1 followed by 63 zeros
while the CS is held low.
Check if SPI controller supports SPI_MOSI_IDLE_HIGH flag and set it.
Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support")
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* Only setup SPI_MOSI_IDLE_HIGH flag if the controller supports it, otherwise the driver
continues the same. I realized that using bits_per_word does not avoid the problem that
MOSI idle state is trying to solve. If the controller drives the MOSI low between bytes
during a transfer, nothing happens.
---
drivers/iio/adc/ad7768-1.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index c3cf04311c40..95ba89435652 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -574,6 +574,22 @@ static int ad7768_probe(struct spi_device *spi)
return -ENOMEM;
st = iio_priv(indio_dev);
+ /*
+ * Datasheet recommends SDI line to be kept high when
+ * data is not being clocked out of the controller
+ * and the spi clock is free running, to prevent
+ * accidental reset.
+ * Since many controllers do not support the
+ * SPI_MOSI_IDLE_HIGH flag yet, only request the MOSI
+ * idle state to enable if the controller supports it.
+ */
+ if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) {
+ spi->mode |= SPI_MOSI_IDLE_HIGH;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+ }
+
st->spi = spi;
st->vref = devm_regulator_get(&spi->dev, "vref");
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (5 preceding siblings ...)
2025-01-27 15:12 ` [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset Jonathan Santos
@ 2025-01-27 15:12 ` Jonathan Santos
2025-01-28 1:29 ` David Lechner
2025-01-27 15:12 ` [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio Jonathan Santos
` (8 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:12 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
Convert the AD7768-1 driver to use the regmap API for register
access. This change simplifies and standardizes register interactions,
reducing code duplication and improving maintainability.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* New patch in v2.
---
drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
1 file changed, 58 insertions(+), 24 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 95ba89435652..fb8d6fae5f8a 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -12,6 +12,7 @@
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
@@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
struct ad7768_state {
struct spi_device *spi;
+ struct regmap *regmap;
struct regulator *vref;
struct mutex lock;
struct clk *mclk;
@@ -176,12 +178,17 @@ struct ad7768_state {
} data __aligned(IIO_DMA_MINALIGN);
};
-static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
- unsigned int len)
+static int ad7768_spi_reg_read(void *context, unsigned int addr,
+ unsigned int *val)
{
- unsigned int shift;
+ struct iio_dev *dev = context;
+ struct ad7768_state *st;
+ unsigned int shift, len;
int ret;
+ st = iio_priv(dev);
+ /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
+ len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
shift = 32 - (8 * len);
st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
@@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
if (ret < 0)
return ret;
- return (be32_to_cpu(st->data.d32) >> shift);
+ *val = be32_to_cpu(st->data.d32) >> shift;
+
+ return 0;
}
-static int ad7768_spi_reg_write(struct ad7768_state *st,
+static int ad7768_spi_reg_write(void *context,
unsigned int addr,
unsigned int val)
{
+ struct iio_dev *dev = context;
+ struct ad7768_state *st;
+
+ st = iio_priv(dev);
st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
st->data.d8[1] = val & 0xFF;
@@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct ad7768_state *st,
static int ad7768_set_mode(struct ad7768_state *st,
enum ad7768_conv_mode mode)
{
- int regval;
+ int regval, ret;
- regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
- if (regval < 0)
- return regval;
+ ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
+ if (ret)
+ return ret;
regval &= ~AD7768_CONV_MODE_MSK;
regval |= AD7768_CONV_MODE(mode);
- return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
+ return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
}
static int ad7768_scan_direct(struct iio_dev *indio_dev)
@@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev)
if (!ret)
return -ETIMEDOUT;
- readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
- if (readval < 0)
- return readval;
+ ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
+ if (ret)
+ return ret;
+
/*
* Any SPI configuration of the AD7768-1 can only be
* performed in continuous conversion mode.
@@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
mutex_lock(&st->lock);
if (readval) {
- ret = ad7768_spi_reg_read(st, reg, 1);
- if (ret < 0)
+ ret = regmap_read(st->regmap, reg, readval);
+ if (ret)
goto err_unlock;
- *readval = ret;
- ret = 0;
} else {
- ret = ad7768_spi_reg_write(st, reg, writeval);
+ ret = regmap_write(st->regmap, reg, writeval);
}
err_unlock:
mutex_unlock(&st->lock);
@@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
else
mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
- ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
+ ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
if (ret < 0)
return ret;
@@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
*/
pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
- ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
+ ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
if (ret < 0)
return ret;
@@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
* to 10. When the sequence is detected, the reset occurs.
* See the datasheet, page 70.
*/
- ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
+ ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
if (ret)
return ret;
- ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
+ ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
if (ret)
return ret;
@@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
* continuous read mode. Subsequent data reads do not require an
* initial 8-bit write to query the ADC_DATA register.
*/
- return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
+ return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01);
}
static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int regval;
/*
* To exit continuous read mode, perform a single read of the ADC_DATA
* reg (0x2C), which allows further configuration of the device.
*/
- return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
+ return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
}
static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
@@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev,
return 0;
}
+static const struct regmap_bus ad7768_regmap_bus = {
+ .reg_write = ad7768_spi_reg_write,
+ .reg_read = ad7768_spi_reg_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config ad7768_regmap_config = {
+ .name = "ad7768-1",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AD7768_REG_MCLK_COUNTER,
+};
+
static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
@@ -592,6 +619,13 @@ static int ad7768_probe(struct spi_device *spi)
st->spi = spi;
+ st->regmap = devm_regmap_init(&spi->dev,
+ &ad7768_regmap_bus, indio_dev,
+ &ad7768_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap");
+
st->vref = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(st->vref))
return PTR_ERR(st->vref);
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (6 preceding siblings ...)
2025-01-27 15:12 ` [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap Jonathan Santos
@ 2025-01-27 15:12 ` Jonathan Santos
2025-01-27 22:43 ` David Lechner
2025-02-03 13:46 ` Marcelo Schmitt
2025-01-27 15:13 ` [PATCH v2 09/16] iio: adc: ad7768-1: remove unnecessary locking Jonathan Santos
` (7 subsequent siblings)
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:12 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Depending on the controller, the default state of a gpio can vary. This
change excludes the probability that the dafult state of the ADC reset
gpio will be HIGH if it will be passed as reference in the devicetree.
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
---
v2 Changes:
* Replaced usleep_range() for fsleep() and gpiod_direction_output() for
gpiod_set_value_cansleep().
* Reset via SPI register is performed if the Reset GPIO is not defined.
---
drivers/iio/adc/ad7768-1.c | 36 ++++++++++++++++++++++++------------
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index fb8d6fae5f8a..17a49bf74637 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -163,6 +163,7 @@ struct ad7768_state {
struct completion completion;
struct iio_trigger *trig;
struct gpio_desc *gpio_sync_in;
+ struct gpio_desc *gpio_reset;
const char *labels[ARRAY_SIZE(ad7768_channels)];
/*
* DMA (thus cache coherency maintenance) may require the
@@ -453,19 +454,30 @@ static int ad7768_setup(struct ad7768_state *st)
{
int ret;
- /*
- * Two writes to the SPI_RESET[1:0] bits are required to initiate
- * a software reset. The bits must first be set to 11, and then
- * to 10. When the sequence is detected, the reset occurs.
- * See the datasheet, page 70.
- */
- ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
- if (ret)
- return ret;
+ st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
- ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
- if (ret)
- return ret;
+ if (st->gpio_reset) {
+ fsleep(10);
+ gpiod_set_value_cansleep(st->gpio_reset, 0);
+ fsleep(10);
+ } else {
+ /*
+ * Two writes to the SPI_RESET[1:0] bits are required to initiate
+ * a software reset. The bits must first be set to 11, and then
+ * to 10. When the sequence is detected, the reset occurs.
+ * See the datasheet, page 70.
+ */
+ ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
+ if (ret)
+ return ret;
+ }
st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
GPIOD_OUT_LOW);
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 09/16] iio: adc: ad7768-1: remove unnecessary locking
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (7 preceding siblings ...)
2025-01-27 15:12 ` [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio Jonathan Santos
@ 2025-01-27 15:13 ` Jonathan Santos
2025-01-27 22:46 ` David Lechner
2025-01-27 15:13 ` [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function Jonathan Santos
` (6 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:13 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
The current locking is only preventing a triggered buffer Transfer and a
debugfs register access from happening at the same time. If a register
access happens during a buffered read, the action is doomed to fail anyway,
since we need to write a magic value to exit continuous read mode.
Remove locking from the trigger handler and use
iio_device_claim_direct_mode() instead in the register access function.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* New patch in v2. It replaces the guard(mutex) patch.
---
drivers/iio/adc/ad7768-1.c | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 17a49bf74637..5e2093be9b92 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -271,16 +271,16 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
- if (readval) {
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ if (readval)
ret = regmap_read(st->regmap, reg, readval);
- if (ret)
- goto err_unlock;
- } else {
+ else
ret = regmap_write(st->regmap, reg, writeval);
- }
-err_unlock:
- mutex_unlock(&st->lock);
+
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
@@ -495,18 +495,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p)
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
-
ret = spi_read(st->spi, &st->data.scan.chan, 3);
if (ret < 0)
- goto err_unlock;
+ goto out;
iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan,
iio_get_time_ns(indio_dev));
-err_unlock:
+out:
iio_trigger_notify_done(indio_dev->trig);
- mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (8 preceding siblings ...)
2025-01-27 15:13 ` [PATCH v2 09/16] iio: adc: ad7768-1: remove unnecessary locking Jonathan Santos
@ 2025-01-27 15:13 ` Jonathan Santos
2025-02-01 15:35 ` Jonathan Cameron
2025-01-27 15:13 ` [PATCH v2 11/16] iio: adc: ad7768-1: Add VCM output support Jonathan Santos
` (5 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:13 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
This change moves the buffer allocation in a separate function, making
space for adding another type of iio buffer if needed.
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
---
v2 Changes:
* Interrupt and completion moved out from ad7768_triggered_buffer_alloc().
---
drivers/iio/adc/ad7768-1.c | 44 ++++++++++++++++++++++----------------
1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 5e2093be9b92..8487b9a06609 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -599,6 +599,31 @@ static const struct regmap_config ad7768_regmap_config = {
.max_register = AD7768_REG_MCLK_COUNTER,
};
+static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7768_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7768_trigger_handler,
+ &ad7768_buffer_ops);
+}
+
static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
@@ -669,20 +694,6 @@ static int ad7768_probe(struct spi_device *spi)
return ret;
}
- st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
- indio_dev->name,
- iio_device_id(indio_dev));
- if (!st->trig)
- return -ENOMEM;
-
- st->trig->ops = &ad7768_trigger_ops;
- iio_trigger_set_drvdata(st->trig, indio_dev);
- ret = devm_iio_trigger_register(&spi->dev, st->trig);
- if (ret)
- return ret;
-
- indio_dev->trig = iio_trigger_get(st->trig);
-
init_completion(&st->completion);
ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels));
@@ -696,10 +707,7 @@ static int ad7768_probe(struct spi_device *spi)
if (ret)
return ret;
- ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
- &iio_pollfunc_store_time,
- &ad7768_trigger_handler,
- &ad7768_buffer_ops);
+ ret = ad7768_triggered_buffer_alloc(indio_dev);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 11/16] iio: adc: ad7768-1: Add VCM output support
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (9 preceding siblings ...)
2025-01-27 15:13 ` [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function Jonathan Santos
@ 2025-01-27 15:13 ` Jonathan Santos
2025-01-27 23:07 ` David Lechner
2025-01-27 15:13 ` [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support Jonathan Santos
` (4 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:13 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1,
Jonathan Santos
From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
The VCM output voltage can be used as a common-mode voltage within the
amplifier preconditioning circuits external to the AD7768-1.
This change allows the user to configure VCM output trough a devicetree
attribute.
Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
---
v2 Changes:
* VCM output support is now defined by a devicetree property, instead of
and IIO attribute.
---
drivers/iio/adc/ad7768-1.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 8487b9a06609..c540583808c2 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -24,6 +24,8 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
+#include <dt-bindings/iio/adc/adi,ad7768-1.h>
+
/* AD7768 registers definition */
#define AD7768_REG_CHIP_TYPE 0x3
#define AD7768_REG_PROD_ID_L 0x4
@@ -347,6 +349,11 @@ static int ad7768_set_freq(struct ad7768_state *st,
return 0;
}
+static int ad7768_set_vcm_output(struct ad7768_state *st, unsigned int mode)
+{
+ return regmap_write(st->regmap, AD7768_REG_ANALOG2, mode);
+}
+
static ssize_t ad7768_sampling_freq_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -628,6 +635,7 @@ static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
struct iio_dev *indio_dev;
+ u32 val;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
@@ -688,6 +696,18 @@ static int ad7768_probe(struct spi_device *spi)
indio_dev->info = &ad7768_info;
indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = device_property_read_u32(&spi->dev, "adi,vcm-output", &val);
+ if (!ret) {
+ if (val > AD7768_VCM_OUTPUT_OFF) {
+ dev_err(&spi->dev, "Invalid VCM output value\n");
+ return -EINVAL;
+ }
+
+ ret = ad7768_set_vcm_output(st, val);
+ if (ret)
+ return ret;
+ }
+
ret = ad7768_setup(st);
if (ret < 0) {
dev_err(&spi->dev, "AD7768 setup failed\n");
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (10 preceding siblings ...)
2025-01-27 15:13 ` [PATCH v2 11/16] iio: adc: ad7768-1: Add VCM output support Jonathan Santos
@ 2025-01-27 15:13 ` Jonathan Santos
2025-01-27 23:34 ` David Lechner
2025-02-01 15:50 ` Jonathan Cameron
2025-01-27 15:13 ` [PATCH v2 13/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode Jonathan Santos
` (3 subsequent siblings)
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:13 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1,
Jonathan Santos
From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
The AD7768-1 has the ability to control other local hardware (such as gain
stages),to power down other blocks in the signal chain, or read local
status signals over the SPI interface.
This change exports the AD7768-1's four gpios and makes them accessible
at an upper layer.
Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
---
v2 Changes:
* Replaced mutex for iio_device_claim_direct_mode().
* Use gpio-controller property to conditionally enable the
GPIO support.
* OBS: when the GPIO is configured as output, we should read
the current state value from AD7768_REG_GPIO_WRITE.
---
drivers/iio/adc/ad7768-1.c | 148 ++++++++++++++++++++++++++++++++++++-
1 file changed, 146 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index c540583808c2..e3ea078e6ec4 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -9,6 +9,8 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -79,6 +81,19 @@
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
+/* AD7768_REG_GPIO_CONTROL */
+#define AD7768_GPIO_UNIVERSAL_EN BIT(7)
+#define AD7768_GPIO_CONTROL_MSK GENMASK(3, 0)
+
+/* AD7768_REG_GPIO_WRITE */
+#define AD7768_GPIO_WRITE_MSK GENMASK(3, 0)
+
+/* AD7768_REG_GPIO_READ */
+#define AD7768_GPIO_READ_MSK GENMASK(3, 0)
+
+#define AD7768_GPIO_INPUT(x) 0x00
+#define AD7768_GPIO_OUTPUT(x) BIT(x)
+
#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
@@ -160,6 +175,8 @@ struct ad7768_state {
struct regulator *vref;
struct mutex lock;
struct clk *mclk;
+ struct gpio_chip gpiochip;
+ unsigned int gpio_avail_map;
unsigned int mclk_freq;
unsigned int samp_freq;
struct completion completion;
@@ -309,6 +326,125 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
return 0;
}
+static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(chip);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap,
+ AD7768_REG_GPIO_CONTROL,
+ BIT(offset),
+ AD7768_GPIO_INPUT(offset));
+}
+
+static int ad7768_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(chip);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap,
+ AD7768_REG_GPIO_CONTROL,
+ BIT(offset),
+ AD7768_GPIO_OUTPUT(offset));
+}
+
+static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(chip);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & BIT(offset))
+ ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val);
+ else
+ ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & BIT(offset));
+}
+
+static void ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(chip);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return;
+
+ ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
+ if (ret < 0)
+ return;
+
+ if (val & BIT(offset))
+ regmap_update_bits(st->regmap,
+ AD7768_REG_GPIO_WRITE,
+ BIT(offset),
+ (value << offset));
+}
+
+static int ad7768_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct iio_dev *indio_dev = gpiochip_get_data(chip);
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (!(st->gpio_avail_map & BIT(offset)))
+ return -ENODEV;
+
+ st->gpio_avail_map &= ~BIT(offset);
+
+ return 0;
+}
+
+static int ad7768_gpio_init(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_GPIO_CONTROL,
+ AD7768_GPIO_UNIVERSAL_EN);
+ if (ret < 0)
+ return ret;
+
+ st->gpio_avail_map = AD7768_GPIO_CONTROL_MSK;
+ st->gpiochip.label = "ad7768_1_gpios";
+ st->gpiochip.base = -1;
+ st->gpiochip.ngpio = 4;
+ st->gpiochip.parent = &st->spi->dev;
+ st->gpiochip.can_sleep = true;
+ st->gpiochip.direction_input = ad7768_gpio_direction_input;
+ st->gpiochip.direction_output = ad7768_gpio_direction_output;
+ st->gpiochip.get = ad7768_gpio_get;
+ st->gpiochip.set = ad7768_gpio_set;
+ st->gpiochip.request = ad7768_gpio_request;
+ st->gpiochip.owner = THIS_MODULE;
+
+ return gpiochip_add_data(&st->gpiochip, indio_dev);
+}
+
static int ad7768_set_freq(struct ad7768_state *st,
unsigned int freq)
{
@@ -457,8 +593,9 @@ static const struct iio_info ad7768_info = {
.debugfs_reg_access = &ad7768_reg_access,
};
-static int ad7768_setup(struct ad7768_state *st)
+static int ad7768_setup(struct iio_dev *indio_dev)
{
+ struct ad7768_state *st = iio_priv(indio_dev);
int ret;
st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
@@ -491,6 +628,13 @@ static int ad7768_setup(struct ad7768_state *st)
if (IS_ERR(st->gpio_sync_in))
return PTR_ERR(st->gpio_sync_in);
+ /* Only create a Chip GPIO if flagged for it */
+ if (device_property_read_bool(&st->spi->dev, "gpio-controller")) {
+ ret = ad7768_gpio_init(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
/* Set the default sampling frequency to 32000 kSPS */
return ad7768_set_freq(st, 32000);
}
@@ -708,7 +852,7 @@ static int ad7768_probe(struct spi_device *spi)
return ret;
}
- ret = ad7768_setup(st);
+ ret = ad7768_setup(indio_dev);
if (ret < 0) {
dev_err(&spi->dev, "AD7768 setup failed\n");
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 13/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (11 preceding siblings ...)
2025-01-27 15:13 ` [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support Jonathan Santos
@ 2025-01-27 15:13 ` Jonathan Santos
2025-01-27 23:47 ` David Lechner
2025-01-27 15:14 ` [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI Jonathan Santos
` (2 subsequent siblings)
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:13 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
When the device is configured to Sinc5 filter and decimation x8,
output data is reduced to 16-bits in order to support 1 MHz of
sampling frequency due to clock limitation.
Use multiple scan types feature to enable the driver to switch
scan type in runtime, making possible to support both 24-bit and
16-bit resolution.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* Included the ".shift" value back to scan_type.
* Changed the number of bytes from regmap_read instead of shifting the
ADC sample value when the word size is lower (16-bits).
---
drivers/iio/adc/ad7768-1.c | 73 ++++++++++++++++++++++++++++++++------
1 file changed, 62 insertions(+), 11 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index e3ea078e6ec4..7686556c7808 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -136,6 +136,15 @@ struct ad7768_clk_configuration {
enum ad7768_pwrmode pwrmode;
};
+enum ad7768_scan_type {
+ AD7768_SCAN_TYPE_NORMAL,
+ AD7768_SCAN_TYPE_HIGH_SPEED,
+};
+
+static const int ad7768_mclk_div_rates[4] = {
+ 16, 8, 4, 2,
+};
+
static const struct ad7768_clk_configuration ad7768_clk_config[] = {
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
@@ -150,6 +159,23 @@ static const struct ad7768_clk_configuration ad7768_clk_config[] = {
{ AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
};
+static const struct iio_scan_type ad7768_scan_type[] = {
+ [AD7768_SCAN_TYPE_NORMAL] = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ [AD7768_SCAN_TYPE_HIGH_SPEED] = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 32,
+ .shift = 16,
+ .endianness = IIO_BE,
+ },
+};
+
static const struct iio_chan_spec ad7768_channels[] = {
{
.type = IIO_VOLTAGE,
@@ -159,13 +185,9 @@ static const struct iio_chan_spec ad7768_channels[] = {
.indexed = 1,
.channel = 0,
.scan_index = 0,
- .scan_type = {
- .sign = 's',
- .realbits = 24,
- .storagebits = 32,
- .shift = 8,
- .endianness = IIO_BE,
- },
+ .has_ext_scan_type = 1,
+ .ext_scan_type = ad7768_scan_type,
+ .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type),
},
};
@@ -178,6 +200,7 @@ struct ad7768_state {
struct gpio_chip gpiochip;
unsigned int gpio_avail_map;
unsigned int mclk_freq;
+ unsigned int dec_rate;
unsigned int samp_freq;
struct completion completion;
struct iio_trigger *trig;
@@ -201,14 +224,19 @@ struct ad7768_state {
static int ad7768_spi_reg_read(void *context, unsigned int addr,
unsigned int *val)
{
+ const struct iio_scan_type *scan_type;
struct iio_dev *dev = context;
struct ad7768_state *st;
unsigned int shift, len;
int ret;
st = iio_priv(dev);
+ scan_type = iio_get_current_scan_type(dev, &dev->channels[0]);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
/* Regular value size is 1 Byte, but 3 Bytes for ADC data */
- len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
+ len = (addr == AD7768_REG_ADC_DATA) ? BITS_TO_BYTES(scan_type->realbits) : 1;
shift = 32 - (8 * len);
st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
@@ -479,6 +507,8 @@ static int ad7768_set_freq(struct ad7768_state *st,
if (ret < 0)
return ret;
+ st->dec_rate = ad7768_clk_config[idx].clk_div /
+ ad7768_mclk_div_rates[ad7768_clk_config[idx].mclk_div];
st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
ad7768_clk_config[idx].clk_div);
@@ -517,8 +547,13 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long info)
{
struct ad7768_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
int scale_uv, ret;
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
@@ -527,7 +562,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
ret = ad7768_scan_direct(indio_dev);
if (ret >= 0)
- *val = sign_extend32(ret, chan->scan_type.realbits - 1);
+ *val = sign_extend32(ret, scan_type->realbits - 1);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
@@ -541,7 +576,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
return scale_uv;
*val = (scale_uv * 2) / 1000;
- *val2 = chan->scan_type.realbits;
+ *val2 = scan_type->realbits;
return IIO_VAL_FRACTIONAL_LOG2;
@@ -585,11 +620,21 @@ static const struct attribute_group ad7768_group = {
.attrs = ad7768_attributes,
};
+static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ return st->dec_rate == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
+ AD7768_SCAN_TYPE_NORMAL;
+}
+
static const struct iio_info ad7768_info = {
.attrs = &ad7768_group,
.read_raw = &ad7768_read_raw,
.write_raw = &ad7768_write_raw,
.read_label = ad7768_read_label,
+ .get_current_scan_type = &ad7768_get_current_scan_type,
.debugfs_reg_access = &ad7768_reg_access,
};
@@ -644,9 +689,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7768_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
int ret;
- ret = spi_read(st->spi, &st->data.scan.chan, 3);
+ scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ ret = spi_read(st->spi, &st->data.scan.chan,
+ BITS_TO_BYTES(scan_type->realbits));
if (ret < 0)
goto out;
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (12 preceding siblings ...)
2025-01-27 15:13 ` [PATCH v2 13/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode Jonathan Santos
@ 2025-01-27 15:14 ` Jonathan Santos
2025-01-28 0:08 ` David Lechner
2025-02-03 15:28 ` Marcelo Schmitt
2025-01-27 15:14 ` [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes Jonathan Santos
2025-01-27 15:14 ` [PATCH v2 16/16] iio: adc: ad7768-1: add low pass -3dB cutoff attribute Jonathan Santos
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:14 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
The synchronization method using GPIO requires the generated pulse to be
truly synchronous with the base MCLK signal. When it is not possible to
do that in hardware, the datasheet recommends using synchronization over
SPI, where the generated pulse is already synchronous with MCLK. This
requires the SYNC_OUT pin to be connected to SYNC_IN pin. In
multidevices setup, the SYNC_OUT from other devices can be used as
synchronization source.
Use trigger-sources property to enable device synchronization over SPI
for single and multiple devices.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* Synchronization via SPI is enabled when the Sync GPIO is not defined.
* now trigger-sources property indicates the synchronization provider or
main device. The main device will be used to drive the SYNC_IN when
requested (via GPIO or SPI).
---
drivers/iio/adc/ad7768-1.c | 81 ++++++++++++++++++++++++++++++++++----
1 file changed, 73 insertions(+), 8 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 7686556c7808..01ccbe0aa708 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -193,6 +193,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
struct ad7768_state {
struct spi_device *spi;
+ struct spi_device *sync_source_spi;
struct regmap *regmap;
struct regulator *vref;
struct mutex lock;
@@ -206,6 +207,7 @@ struct ad7768_state {
struct iio_trigger *trig;
struct gpio_desc *gpio_sync_in;
struct gpio_desc *gpio_reset;
+ bool en_spi_sync;
const char *labels[ARRAY_SIZE(ad7768_channels)];
/*
* DMA (thus cache coherency maintenance) may require the
@@ -264,6 +266,21 @@ static int ad7768_spi_reg_write(void *context,
return spi_write(st->spi, st->data.d8, 2);
}
+static int ad7768_send_sync_pulse(struct ad7768_state *st)
+{
+ if (st->en_spi_sync) {
+ st->data.d8[0] = AD7768_WR_FLAG_MSK(AD7768_REG_SYNC_RESET);
+ st->data.d8[1] = 0x00;
+
+ return spi_write(st->sync_source_spi, st->data.d8, 2);
+ } else if (st->gpio_sync_in) {
+ gpiod_set_value_cansleep(st->gpio_sync_in, 1);
+ gpiod_set_value_cansleep(st->gpio_sync_in, 0);
+ }
+
+ return 0;
+}
+
static int ad7768_set_mode(struct ad7768_state *st,
enum ad7768_conv_mode mode)
{
@@ -348,10 +365,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
return ret;
/* A sync-in pulse is required every time the filter dec rate changes */
- gpiod_set_value(st->gpio_sync_in, 1);
- gpiod_set_value(st->gpio_sync_in, 0);
-
- return 0;
+ return ad7768_send_sync_pulse(st);
}
static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
@@ -638,6 +652,58 @@ static const struct iio_info ad7768_info = {
.debugfs_reg_access = &ad7768_reg_access,
};
+static int ad7768_parse_trigger_sources(struct device *dev, struct ad7768_state *st)
+{
+ struct fwnode_reference_args args;
+ int ret;
+
+ /*
+ * The AD7768-1 allows two primary methods for driving the SYNC_IN pin to synchronize
+ * one or more devices:
+ * 1. Using a GPIO to directly drive the SYNC_IN pin.
+ * 2. Using a SPI command, where the SYNC_OUT pin generates a synchronization pulse
+ * that loops back to the SYNC_IN pin.
+ *
+ * In multi-device setups, the SYNC_IN pin can also be driven by the SYNC_OUT pin of
+ * another device. To support this, we use the "trigger-source" property to get a
+ * reference to the "main" device responsible for generating the synchronization pulse.
+ * In a single-device setup, the "trigger-source" property should reference the device's
+ * own node.
+ */
+ ret = fwnode_property_get_reference_args(dev_fwnode(dev), "trigger-sources",
+ "#trigger-source-cells", 0, 0, &args);
+ if (ret) {
+ dev_err(dev, "Failed to get trigger-sources reference: %d\n", ret);
+ return ret;
+ }
+
+ st->gpio_sync_in = devm_gpiod_get_optional(args.fwnode->dev, "adi,sync-in",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_sync_in))
+ return PTR_ERR(st->gpio_sync_in);
+
+ /*
+ * If the SYNC_IN GPIO is not defined, fall back to synchronization over SPI.
+ * Use the trigger-source reference to identify the main SPI device for generating
+ * the synchronization pulse.
+ */
+ if (!st->gpio_sync_in) {
+ st->sync_source_spi = to_spi_device(args.fwnode->dev);
+ if (!st->sync_source_spi) {
+ dev_err(dev,
+ "Synchronization setup failed. GPIO is undefined and trigger-source reference is invalid\n");
+ return -EINVAL;
+ }
+
+ st->en_spi_sync = true;
+ }
+
+ /* Release the fwnode reference after use */
+ fwnode_handle_put(args.fwnode);
+
+ return 0;
+}
+
static int ad7768_setup(struct iio_dev *indio_dev)
{
struct ad7768_state *st = iio_priv(indio_dev);
@@ -668,10 +734,9 @@ static int ad7768_setup(struct iio_dev *indio_dev)
return ret;
}
- st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
- GPIOD_OUT_LOW);
- if (IS_ERR(st->gpio_sync_in))
- return PTR_ERR(st->gpio_sync_in);
+ ret = ad7768_parse_trigger_sources(&st->spi->dev, st);
+ if (ret)
+ return ret;
/* Only create a Chip GPIO if flagged for it */
if (device_property_read_bool(&st->spi->dev, "gpio-controller")) {
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (13 preceding siblings ...)
2025-01-27 15:14 ` [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI Jonathan Santos
@ 2025-01-27 15:14 ` Jonathan Santos
2025-01-28 1:24 ` David Lechner
2025-01-30 16:39 ` Jonathan Cameron
2025-01-27 15:14 ` [PATCH v2 16/16] iio: adc: ad7768-1: add low pass -3dB cutoff attribute Jonathan Santos
15 siblings, 2 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:14 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1,
PopPaul2021
Separate filter type and decimation rate from the sampling frequency
attribute. The new filter type attribute enables SINC3 and WIDEBAND
filters, which were previously unavailable.
Previously, combining decimation and MCLK divider in the sampling
frequency obscured performance trade-offs. Lower MCLK divider
settings increase power usage, while lower decimation rates reduce
precision by decreasing averaging. By creating an oversampling
attribute, which controls the decimation, users gain finer control
over performance.
The addition of those attributes allows a wider range of sampling
frequencies and more access to the device features.
Co-developed-by: PopPaul2021 <paul.pop@analog.com>
Signed-off-by: PopPaul2021 <paul.pop@analog.com>
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* Decimation_rate attribute replaced for oversampling_ratio.
---
drivers/iio/adc/ad7768-1.c | 389 +++++++++++++++++++++++++++++++------
1 file changed, 325 insertions(+), 64 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 01ccbe0aa708..6d0b430a8d54 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -5,6 +5,7 @@
* Copyright 2017 Analog Devices Inc.
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -17,6 +18,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
+#include "linux/util_macros.h"
#include <linux/spi/spi.h>
#include <linux/iio/buffer.h>
@@ -77,6 +79,10 @@
#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
+/* AD7768_SINC3_DEC_RATE */
+#define AD7768_SINC3_DEC_RATE_MSB_MSK GENMASK(12, 8)
+#define AD7768_SINC3_DEC_RATE_LSB_MSK GENMASK(7, 0)
+
/* AD7768_REG_CONVERSION */
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
@@ -97,6 +103,18 @@
#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
+/* Decimation Rate Limits */
+#define SINC5_DEC_RATE_MIN 8
+#define SINC5_DEC_RATE_MAX 1024
+#define SINC3_DEC_RATE_MIN 32
+#define SINC3_DEC_RATE_MAX 163840
+#define WIDEBAND_DEC_RATE_MIN 32
+#define WIDEBAND_DEC_RATE_MAX 1024
+
+enum {
+ DEC_RATE,
+};
+
enum ad7768_conv_mode {
AD7768_CONTINUOUS,
AD7768_ONE_SHOT,
@@ -118,22 +136,12 @@ enum ad7768_mclk_div {
AD7768_MCLK_DIV_2
};
-enum ad7768_dec_rate {
- AD7768_DEC_RATE_32 = 0,
- AD7768_DEC_RATE_64 = 1,
- AD7768_DEC_RATE_128 = 2,
- AD7768_DEC_RATE_256 = 3,
- AD7768_DEC_RATE_512 = 4,
- AD7768_DEC_RATE_1024 = 5,
- AD7768_DEC_RATE_8 = 9,
- AD7768_DEC_RATE_16 = 10
-};
-
-struct ad7768_clk_configuration {
- enum ad7768_mclk_div mclk_div;
- enum ad7768_dec_rate dec_rate;
- unsigned int clk_div;
- enum ad7768_pwrmode pwrmode;
+enum ad7768_flt_type {
+ SINC5,
+ SINC5_DEC_X8,
+ SINC5_DEC_X16,
+ SINC3,
+ WIDEBAND
};
enum ad7768_scan_type {
@@ -145,18 +153,12 @@ static const int ad7768_mclk_div_rates[4] = {
16, 8, 4, 2,
};
-static const struct ad7768_clk_configuration ad7768_clk_config[] = {
- { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
- { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
- { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
- { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
- { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
- { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
- { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
- { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
- { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
- { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
- { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
+static const int ad7768_dec_rate_values[6] = {
+ 32, 64, 128, 256, 512, 1024,
+};
+
+static const char * const ad7768_filter_enum[] = {
+ "sinc5", "sinc3", "wideband"
};
static const struct iio_scan_type ad7768_scan_type[] = {
@@ -176,12 +178,32 @@ static const struct iio_scan_type ad7768_scan_type[] = {
},
};
+static int ad7768_get_fil_type_attr(struct iio_dev *dev,
+ const struct iio_chan_spec *chan);
+static int ad7768_set_fil_type_attr(struct iio_dev *dev,
+ const struct iio_chan_spec *chan, unsigned int filter);
+
+static const struct iio_enum ad7768_flt_type_iio_enum = {
+ .items = ad7768_filter_enum,
+ .num_items = ARRAY_SIZE(ad7768_filter_enum),
+ .set = ad7768_set_fil_type_attr,
+ .get = ad7768_get_fil_type_attr,
+};
+
+static struct iio_chan_spec_ext_info ad7768_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum),
+ { },
+};
+
static const struct iio_chan_spec ad7768_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .ext_info = ad7768_ext_info,
.indexed = 1,
.channel = 0,
.scan_index = 0,
@@ -201,7 +223,9 @@ struct ad7768_state {
struct gpio_chip gpiochip;
unsigned int gpio_avail_map;
unsigned int mclk_freq;
- unsigned int dec_rate;
+ unsigned int mclk_div;
+ unsigned int oversampling_ratio;
+ enum ad7768_flt_type filter_type;
unsigned int samp_freq;
struct completion completion;
struct iio_trigger *trig;
@@ -223,6 +247,9 @@ struct ad7768_state {
} data __aligned(IIO_DMA_MINALIGN);
};
+static int ad7768_set_freq(struct ad7768_state *st,
+ unsigned int freq);
+
static int ad7768_spi_reg_read(void *context, unsigned int addr,
unsigned int *val)
{
@@ -281,6 +308,31 @@ static int ad7768_send_sync_pulse(struct ad7768_state *st)
return 0;
}
+static int ad7768_set_mclk_div(struct ad7768_state *st, unsigned int mclk_div)
+{
+ unsigned int mclk_div_value;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = ad7768_spi_reg_read(st, AD7768_REG_POWER_CLOCK, &mclk_div_value, 1);
+ if (ret)
+ return ret;
+
+ mclk_div_value &= ~(AD7768_PWR_MCLK_DIV_MSK | AD7768_PWR_PWRMODE_MSK);
+ /* Set mclk_div value */
+ mclk_div_value |= AD7768_PWR_MCLK_DIV(mclk_div);
+ /*
+ * Set power mode based on mclk_div value.
+ * ECO_MODE is only recommended for MCLK_DIV 16
+ */
+ if (mclk_div > AD7768_MCLK_DIV_16)
+ mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_FAST_MODE);
+ else
+ mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_ECO_MODE);
+
+ return regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, mclk_div_value);
+}
+
static int ad7768_set_mode(struct ad7768_state *st,
enum ad7768_conv_mode mode)
{
@@ -349,23 +401,183 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
return ret;
}
-static int ad7768_set_dig_fil(struct ad7768_state *st,
- enum ad7768_dec_rate dec_rate)
+static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
+ unsigned int dec_rate)
+{
+ unsigned int dec_rate_msb, dec_rate_lsb, max_dec_rate;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ /*
+ * Maximum dec_rate is limited by the MCLK_DIV value
+ * and by the ODR. The edge case is for MCLK_DIV = 2
+ * ODR = 50 SPS.
+ * max_dec_rate <= MCLK / (2 * 50)
+ */
+ max_dec_rate = st->mclk_freq / 100;
+ dec_rate = clamp_t(unsigned int, dec_rate, 32, max_dec_rate);
+ /*
+ * Calculate the equivalent value to sinc3 decimation ratio
+ * to be written on the SINC3_DECIMATION_RATE register:
+ * Value = (DEC_RATE / 32) -1
+ */
+ dec_rate = DIV_ROUND_UP(dec_rate, 32) - 1;
+ dec_rate_msb = FIELD_GET(AD7768_SINC3_DEC_RATE_MSB_MSK, dec_rate);
+ dec_rate_lsb = FIELD_GET(AD7768_SINC3_DEC_RATE_LSB_MSK, dec_rate);
+
+ ret = regmap_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_MSB, dec_rate_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_LSB, dec_rate_lsb);
+ if (ret)
+ return ret;
+
+ st->oversampling_ratio = (dec_rate + 1) * 32;
+
+ return 0;
+}
+
+static int ad7768_set_dec_rate(struct ad7768_state *st, unsigned int dec_rate)
{
+ unsigned int mode, dec_rate_reg;
+ int ret;
+
+ dec_rate_reg = find_closest(dec_rate, ad7768_dec_rate_values,
+ ARRAY_SIZE(ad7768_dec_rate_values));
+
+ guard(mutex)(&st->lock);
+ ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
+ if (ret)
+ return ret;
+
+ mode &= ~AD7768_DIG_FIL_DEC_MSK;
+ mode |= AD7768_DIG_FIL_DEC_RATE(dec_rate_reg);
+ ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
+ if (ret)
+ return ret;
+
+ st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_reg];
+
+ return 0;
+}
+
+static int ad7768_set_filter_type(struct iio_dev *dev,
+ enum ad7768_flt_type filter_type)
+{
+ struct ad7768_state *st = iio_priv(dev);
unsigned int mode;
int ret;
- if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
- mode = AD7768_DIG_FIL_FIL(dec_rate);
- else
- mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
+ guard(mutex)(&st->lock);
+ ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
+ if (ret)
+ return ret;
+
+ mode &= ~AD7768_DIG_FIL_FIL_MSK;
+ mode |= AD7768_DIG_FIL_FIL(filter_type);
ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
if (ret < 0)
return ret;
- /* A sync-in pulse is required every time the filter dec rate changes */
- return ad7768_send_sync_pulse(st);
+ st->filter_type = filter_type;
+
+ return 0;
+}
+
+static int ad7768_configure_dig_fil(struct iio_dev *dev,
+ enum ad7768_flt_type filter_type,
+ unsigned int dec_rate)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ int ret;
+
+ if (filter_type == SINC3) {
+ ret = ad7768_set_filter_type(dev, SINC3);
+ if (ret)
+ return ret;
+
+ /* recalculate the decimation for this filter mode */
+ ret = ad7768_set_sinc3_dec_rate(st, dec_rate);
+ } else if (filter_type == WIDEBAND) {
+ ret = ad7768_set_filter_type(dev, filter_type);
+ if (ret)
+ return ret;
+
+ /* recalculate the decimation rate */
+ ret = ad7768_set_dec_rate(st, dec_rate);
+ } else {
+ /* For SINC5 filter */
+ /* Decimation 8 and 16 are set in the digital filter field */
+ if (dec_rate <= 8) {
+ ret = ad7768_set_filter_type(dev, SINC5_DEC_X8);
+ if (ret)
+ return ret;
+
+ st->oversampling_ratio = 8;
+ } else if (dec_rate <= 16) {
+ ret = ad7768_set_filter_type(dev, SINC5_DEC_X16);
+ if (ret)
+ return ret;
+
+ st->oversampling_ratio = 16;
+ } else {
+ ret = ad7768_set_filter_type(dev, SINC5);
+ if (ret)
+ return ret;
+
+ ret = ad7768_set_dec_rate(st, dec_rate);
+ }
+ }
+
+ return ret;
+}
+
+static int ad7768_set_fil_type_attr(struct iio_dev *dev,
+ const struct iio_chan_spec *chan,
+ unsigned int filter)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ int ret;
+
+ /*
+ * Filters of types 0, 1, and 2 correspond to SINC5.
+ * For SINC3 and wideband filter types, an offset of 2 is added
+ * to align with the expected register values.
+ */
+ if (filter != SINC5)
+ filter += 2;
+
+ ret = ad7768_configure_dig_fil(dev, filter, st->oversampling_ratio);
+ if (ret)
+ return ret;
+
+ /* Update sampling frequency */
+ return ad7768_set_freq(st, st->samp_freq);
+}
+
+static int ad7768_get_fil_type_attr(struct iio_dev *dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ int ret;
+ unsigned int mode;
+
+ ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
+ if (ret)
+ return ret;
+
+ mode = FIELD_GET(AD7768_DIG_FIL_FIL_MSK, mode);
+ /* Filter types from 0 to 2 are represented as SINC5 */
+ if (mode < SINC3)
+ return SINC5;
+
+ /*
+ * Remove the offset for the sinc3 and wideband filters
+ * to get the corresponding attribute enum value
+ */
+ return mode - 2;
}
static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
@@ -490,43 +702,37 @@ static int ad7768_gpio_init(struct iio_dev *indio_dev)
static int ad7768_set_freq(struct ad7768_state *st,
unsigned int freq)
{
- unsigned int diff_new, diff_old, pwr_mode, i, idx;
+ unsigned int diff_new, diff_old, i, idx;
int res, ret;
+ freq = clamp_t(unsigned int, freq, 50, 1024000);
diff_old = U32_MAX;
idx = 0;
- res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
+ if (freq == 0)
+ return -EINVAL;
+
+ res = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->oversampling_ratio);
/* Find the closest match for the desired sampling frequency */
- for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
- diff_new = abs(res - ad7768_clk_config[i].clk_div);
+ for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) {
+ diff_new = abs(res - ad7768_mclk_div_rates[i]);
if (diff_new < diff_old) {
diff_old = diff_new;
idx = i;
}
}
- /*
- * Set both the mclk_div and pwrmode with a single write to the
- * POWER_CLOCK register
- */
- pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
- AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
- ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
- if (ret < 0)
- return ret;
-
- ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
- if (ret < 0)
+ /* Set both the mclk_div and pwrmode */
+ ret = ad7768_set_mclk_div(st, idx);
+ if (ret)
return ret;
- st->dec_rate = ad7768_clk_config[idx].clk_div /
- ad7768_mclk_div_rates[ad7768_clk_config[idx].mclk_div];
st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
- ad7768_clk_config[idx].clk_div);
+ ad7768_mclk_div_rates[idx] * st->oversampling_ratio);
- return 0;
+ /* A sync-in pulse is required every time the filter dec rate changes */
+ return ad7768_send_sync_pulse(st);
}
static int ad7768_set_vcm_output(struct ad7768_state *st, unsigned int mode)
@@ -540,13 +746,16 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7768_state *st = iio_priv(indio_dev);
- unsigned int freq;
+ unsigned int freq, freq_filtered;
int i, len = 0;
- for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
- freq = DIV_ROUND_CLOSEST(st->mclk_freq,
- ad7768_clk_config[i].clk_div);
- len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
+ freq_filtered = DIV_ROUND_CLOSEST(st->mclk_freq, st->oversampling_ratio);
+ for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) {
+ freq = DIV_ROUND_CLOSEST(freq_filtered,
+ ad7768_mclk_div_rates[i]);
+ /* Sampling frequency cannot be lower than the minimum of 50 SPS */
+ if (freq >= 50)
+ len += sysfs_emit_at(buf, len, "%d ", freq);
}
buf[len - 1] = '\n';
@@ -556,6 +765,37 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev,
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
+static ssize_t oversampling_ratio_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int len = 0;
+
+ /* Return oversampling ratio available in range format */
+ buf[len++] = '[';
+ if (st->filter_type == SINC3) {
+ len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN);
+ len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN);
+ len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MAX);
+ } else if (st->filter_type == WIDEBAND) {
+ len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN);
+ len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN);
+ len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MAX);
+ } else {
+ len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN);
+ len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN);
+ len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MAX); }
+
+ buf[len - 1] = ']';
+ buf[len++] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_RO(oversampling_ratio_available, 0);
+
static int ad7768_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
@@ -597,6 +837,11 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->samp_freq;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = st->oversampling_ratio;
+
return IIO_VAL_INT;
}
@@ -608,10 +853,19 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
return ad7768_set_freq(st, val);
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val);
+ if (ret)
+ return ret;
+
+ /* Update sampling frequency */
+ return ad7768_set_freq(st, st->samp_freq);
default:
return -EINVAL;
}
@@ -627,6 +881,7 @@ static int ad7768_read_label(struct iio_dev *indio_dev,
static struct attribute *ad7768_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
NULL
};
@@ -639,7 +894,7 @@ static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev,
{
struct ad7768_state *st = iio_priv(indio_dev);
- return st->dec_rate == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
+ return st->oversampling_ratio == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
AD7768_SCAN_TYPE_NORMAL;
}
@@ -745,6 +1000,12 @@ static int ad7768_setup(struct iio_dev *indio_dev)
return ret;
}
+ /*
+ * Set Default Digital Filter configuration:
+ * SINC5 filter with x32 Decimation rate
+ */
+ ret = ad7768_configure_dig_fil(indio_dev, SINC5, 32);
+
/* Set the default sampling frequency to 32000 kSPS */
return ad7768_set_freq(st, 32000);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* [PATCH v2 16/16] iio: adc: ad7768-1: add low pass -3dB cutoff attribute
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
` (14 preceding siblings ...)
2025-01-27 15:14 ` [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes Jonathan Santos
@ 2025-01-27 15:14 ` Jonathan Santos
2025-01-28 1:27 ` David Lechner
15 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-27 15:14 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: Jonathan Santos, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
Ad7768-1 has a different -3db frequency multiplier depending on
the filter type configured. The cutoff frequency also varies according
to the current ODR.
Add a readonly low pass -3dB frequency cutoff attribute to clarify to
the user which bandwidth is being allowed depending on the filter
configurations.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
v2 Changes:
* New patch in v2.
---
drivers/iio/adc/ad7768-1.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 6d0b430a8d54..daf91ef6f77b 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -149,6 +149,18 @@ enum ad7768_scan_type {
AD7768_SCAN_TYPE_HIGH_SPEED,
};
+/*
+ * -3dB cutoff frequency multipliers (relative to ODR) for
+ * each filter type. Values are multiplied by 1000.
+ */
+static const int ad7768_filter_3db_odr_multiplier[] = {
+ [SINC5] = 204,
+ [SINC5_DEC_X8] = 204,
+ [SINC5_DEC_X16] = 204,
+ [SINC3] = 261,
+ [WIDEBAND] = 433,
+};
+
static const int ad7768_mclk_div_rates[4] = {
16, 8, 4, 2,
};
@@ -202,7 +214,8 @@ static const struct iio_chan_spec ad7768_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.ext_info = ad7768_ext_info,
.indexed = 1,
.channel = 0,
@@ -802,7 +815,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
{
struct ad7768_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
- int scale_uv, ret;
+ int scale_uv, ret, temp;
scan_type = iio_get_current_scan_type(indio_dev, chan);
if (IS_ERR(scan_type))
@@ -842,6 +855,12 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->oversampling_ratio;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ temp = st->samp_freq * ad7768_filter_3db_odr_multiplier[st->filter_type];
+ *val = DIV_ROUND_CLOSEST(temp, 1000);
+
return IIO_VAL_INT;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread
* Re: [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-27 15:11 ` [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property Jonathan Santos
@ 2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-27 16:55 ` Rob Herring
2025-01-28 1:28 ` David Lechner
2 siblings, 0 replies; 56+ messages in thread
From: Rob Herring (Arm) @ 2025-01-27 16:30 UTC (permalink / raw)
To: Jonathan Santos
Cc: conor+dt, lars, Michael.Hennerich, jic23, linux-kernel,
marcelo.schmitt, devicetree, linux-iio, marcelo.schmitt1, krzk+dt,
jonath4nns
On Mon, 27 Jan 2025 12:11:30 -0300, Jonathan Santos wrote:
> Add a new trigger-sources property to enable synchronization across
> multiple devices. This property references the main device (or
> trigger provider) responsible for generating the pulse to drive the
> SYNC_IN of all devices in the setup.
>
> In addition to GPIO synchronization, The AD7768-1 also supports
> synchronization over SPI, which use is recommended when the GPIO
> cannot provide a pulse synchronous with the base MCLK signal. It
> consists of looping back the SYNC_OUT to the SYNC_IN pin and send
> a command via SPI to trigger the synchronization.
>
> SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
> property. Since adi,sync-in-gpios is not long the only method, remove it
> from required properties.
>
> While at it, add description to the interrupt property.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Patch added as replacement for adi,sync-in-spi patch.
> * addressed the request for a description to interrupts property.
> ---
> .../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml:84:6: [error] missing starting space in comment (comments)
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml: ignoring, error in schema: required: 8
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml: required:8: None is not of type 'string'
from schema $id: http://json-schema.org/draft-07/schema#
Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.example.dtb: /example-0/spi/adc@0: failed to match any schema with compatible: ['adi,ad7768-1']
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/f3972e6aa4ff3869ded1f0dbeb58c43b824b3932.1737985435.git.Jonathan.Santos@analog.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller
2025-01-27 15:11 ` [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller Jonathan Santos
@ 2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-27 16:56 ` Rob Herring (Arm)
1 sibling, 0 replies; 56+ messages in thread
From: Rob Herring (Arm) @ 2025-01-27 16:30 UTC (permalink / raw)
To: Jonathan Santos
Cc: conor+dt, linux-kernel, jonath4nns, marcelo.schmitt1, jic23,
devicetree, krzk+dt, marcelo.schmitt, lars, linux-iio,
Michael.Hennerich
On Mon, 27 Jan 2025 12:11:44 -0300, Jonathan Santos wrote:
> The AD7768-1 ADC exports four bidirectional GPIOs accessible
> via register map.
>
> Document GPIO properties necessary to enable GPIO controller for this
> device.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2.
> ---
> .../devicetree/bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml:92:6: [error] missing starting space in comment (comments)
dtschema/dtc warnings/errors:
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/dc866cb508917828f83242f3438dd1d6ac9d874c.1737985435.git.Jonathan.Santos@analog.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property
2025-01-27 15:12 ` [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property Jonathan Santos
@ 2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-28 1:28 ` David Lechner
1 sibling, 0 replies; 56+ messages in thread
From: Rob Herring (Arm) @ 2025-01-27 16:30 UTC (permalink / raw)
To: Jonathan Santos
Cc: marcelo.schmitt1, marcelo.schmitt, krzk+dt, conor+dt, jic23,
linux-iio, Michael.Hennerich, linux-kernel, jonath4nns,
devicetree, lars
On Mon, 27 Jan 2025 12:12:05 -0300, Jonathan Santos wrote:
> The AD7768-1 provides a buffered common-mode voltage output
> on the VCM pin that can be used to bias analog input signals.
>
> Add adi,vcm-output to enable the configuration of the VCM output
> circuit.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2.
> ---
> .../bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
> include/dt-bindings/iio/adc/adi,ad7768-1.h | 16 ++++++++++++++++
> 2 files changed, 26 insertions(+)
> create mode 100644 include/dt-bindings/iio/adc/adi,ad7768-1.h
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml:69:9: [warning] wrong indentation: expected 6 but found 8 (indentation)
./Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml:101:6: [error] missing starting space in comment (comments)
dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.example.dts:39.35-36 syntax error
FATAL ERROR: Unable to parse input tree
make[2]: *** [scripts/Makefile.dtbs:131: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1506: dt_binding_check] Error 2
make: *** [Makefile:251: __sub-make] Error 2
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/e17337bc3b0f2e95d3d4f7b6daa7755881e11fba.1737985435.git.Jonathan.Santos@analog.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-27 15:11 ` [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
@ 2025-01-27 16:55 ` Rob Herring
2025-01-28 1:28 ` David Lechner
2 siblings, 0 replies; 56+ messages in thread
From: Rob Herring @ 2025-01-27 16:55 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, lars, Michael.Hennerich,
marcelo.schmitt, jic23, krzk+dt, conor+dt, jonath4nns,
marcelo.schmitt1
On Mon, Jan 27, 2025 at 12:11:30PM -0300, Jonathan Santos wrote:
> Add a new trigger-sources property to enable synchronization across
> multiple devices. This property references the main device (or
> trigger provider) responsible for generating the pulse to drive the
> SYNC_IN of all devices in the setup.
>
> In addition to GPIO synchronization, The AD7768-1 also supports
> synchronization over SPI, which use is recommended when the GPIO
> cannot provide a pulse synchronous with the base MCLK signal. It
> consists of looping back the SYNC_OUT to the SYNC_IN pin and send
> a command via SPI to trigger the synchronization.
>
> SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
> property. Since adi,sync-in-gpios is not long the only method, remove it
> from required properties.
>
> While at it, add description to the interrupt property.
interrupts
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Patch added as replacement for adi,sync-in-spi patch.
> * addressed the request for a description to interrupts property.
> ---
> .../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> index 3ce59d4d065f..3e119cf1754b 100644
> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> @@ -26,7 +26,17 @@ properties:
> clock-names:
> const: mclk
>
> + trigger-sources:
> + description:
> + References the main device responsible for synchronization. In a single
> + device setup, reference the own node.
> + maxItems: 1
> +
> interrupts:
> + description:
> + Specifies the interrupt line associated with the ADC. This refers
> + to the DRDY (Data Ready) pin, which signals when conversion results are
> + available.
> maxItems: 1
>
> '#address-cells':
> @@ -46,6 +56,8 @@ properties:
> sampling. A pulse is always required if the configuration is changed
> in any way, for example if the filter decimation rate changes.
> As the line is active low, it should be marked GPIO_ACTIVE_LOW.
> + In the absence of this property, Synchronization over SPI will be
> + enabled.
>
> reset-gpios:
> maxItems: 1
> @@ -57,6 +69,9 @@ properties:
> "#io-channel-cells":
> const: 1
>
> + "#trigger-source-cells":
> + const: 0
> +
> required:
> - compatible
> - reg
> @@ -65,7 +80,8 @@ required:
> - vref-supply
> - spi-cpol
> - spi-cpha
> - - adi,sync-in-gpios
> + - trigger-sources
> + - #trigger-source-cells
You need quotes here.
This device worked before without these properties, so why are they
required? That's an ABI change.
>
> patternProperties:
> "^channel@([0-9]|1[0-5])$":
> @@ -99,7 +115,7 @@ examples:
> #address-cells = <1>;
> #size-cells = <0>;
>
> - adc@0 {
> + adc0: adc@0 {
> compatible = "adi,ad7768-1";
> reg = <0>;
> spi-max-frequency = <2000000>;
> @@ -109,6 +125,8 @@ examples:
> interrupts = <25 IRQ_TYPE_EDGE_RISING>;
> interrupt-parent = <&gpio>;
> adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
> + trigger-sources = <&adc0>;
> + #trigger-source-cells = <0>;
> reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
> clocks = <&ad7768_mclk>;
> clock-names = "mclk";
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller
2025-01-27 15:11 ` [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
@ 2025-01-27 16:56 ` Rob Herring (Arm)
1 sibling, 0 replies; 56+ messages in thread
From: Rob Herring (Arm) @ 2025-01-27 16:56 UTC (permalink / raw)
To: Jonathan Santos
Cc: marcelo.schmitt, conor+dt, devicetree, jic23, jonath4nns,
linux-iio, lars, linux-kernel, Michael.Hennerich,
marcelo.schmitt1, krzk+dt
On Mon, 27 Jan 2025 12:11:44 -0300, Jonathan Santos wrote:
> The AD7768-1 ADC exports four bidirectional GPIOs accessible
> via register map.
>
> Document GPIO properties necessary to enable GPIO controller for this
> device.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2.
> ---
> .../devicetree/bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio
2025-01-27 15:12 ` [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio Jonathan Santos
@ 2025-01-27 22:43 ` David Lechner
2025-02-03 13:46 ` Marcelo Schmitt
1 sibling, 0 replies; 56+ messages in thread
From: David Lechner @ 2025-01-27 22:43 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:12 AM, Jonathan Santos wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> Depending on the controller, the default state of a gpio can vary. This
> change excludes the probability that the dafult state of the ADC reset
> gpio will be HIGH if it will be passed as reference in the devicetree.
>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Since you (Jonathan Santos) are the one submitting these patches, you should
add your Signed-off-by: here on the last line since you are the last one
touching the patch. And if you feel you made significant changes on top of
Sergiu's original patch, could could even add a Co-developed-by: line before
that.
More info: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin
> ---
> v2 Changes:
> * Replaced usleep_range() for fsleep() and gpiod_direction_output() for
> gpiod_set_value_cansleep().
> * Reset via SPI register is performed if the Reset GPIO is not defined.
> ---
Reviewed-by: David Lechner <dlechner@baylibre.com>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 09/16] iio: adc: ad7768-1: remove unnecessary locking
2025-01-27 15:13 ` [PATCH v2 09/16] iio: adc: ad7768-1: remove unnecessary locking Jonathan Santos
@ 2025-01-27 22:46 ` David Lechner
0 siblings, 0 replies; 56+ messages in thread
From: David Lechner @ 2025-01-27 22:46 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:13 AM, Jonathan Santos wrote:
> The current locking is only preventing a triggered buffer Transfer and a
> debugfs register access from happening at the same time. If a register
> access happens during a buffered read, the action is doomed to fail anyway,
> since we need to write a magic value to exit continuous read mode.
>
> Remove locking from the trigger handler and use
> iio_device_claim_direct_mode() instead in the register access function.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2. It replaces the guard(mutex) patch.
> ---
> drivers/iio/adc/ad7768-1.c | 23 ++++++++++-------------
> 1 file changed, 10 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 17a49bf74637..5e2093be9b92 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -271,16 +271,16 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
> struct ad7768_state *st = iio_priv(indio_dev);
> int ret;
>
> - mutex_lock(&st->lock);
> - if (readval) {
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + if (readval)
> ret = regmap_read(st->regmap, reg, readval);
> - if (ret)
> - goto err_unlock;
> - } else {
> + else
> ret = regmap_write(st->regmap, reg, writeval);
> - }
> -err_unlock:
> - mutex_unlock(&st->lock);
> +
> + iio_device_release_direct_mode(indio_dev);
>
> return ret;
> }
> @@ -495,18 +495,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p)
> struct ad7768_state *st = iio_priv(indio_dev);
> int ret;
>
> - mutex_lock(&st->lock);
> -
> ret = spi_read(st->spi, &st->data.scan.chan, 3);
> if (ret < 0)
> - goto err_unlock;
> + goto out;
>
> iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan,
> iio_get_time_ns(indio_dev));
>
> -err_unlock:
> +out:
> iio_trigger_notify_done(indio_dev->trig);
> - mutex_unlock(&st->lock);
>
> return IRQ_HANDLED;
> }
I think the lock can be fully removed from struct ad7768_state if it isn't
being used anymore (and remove mutex_init() in probe()).
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 11/16] iio: adc: ad7768-1: Add VCM output support
2025-01-27 15:13 ` [PATCH v2 11/16] iio: adc: ad7768-1: Add VCM output support Jonathan Santos
@ 2025-01-27 23:07 ` David Lechner
0 siblings, 0 replies; 56+ messages in thread
From: David Lechner @ 2025-01-27 23:07 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:13 AM, Jonathan Santos wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> The VCM output voltage can be used as a common-mode voltage within the
> amplifier preconditioning circuits external to the AD7768-1.
>
> This change allows the user to configure VCM output trough a devicetree
> attribute.
>
> Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Correct order according to [1]:
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
[1]: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by
> ---
> v2 Changes:
> * VCM output support is now defined by a devicetree property, instead of
> and IIO attribute.
> ---
> drivers/iio/adc/ad7768-1.c | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 8487b9a06609..c540583808c2 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -24,6 +24,8 @@
> #include <linux/iio/triggered_buffer.h>
> #include <linux/iio/trigger_consumer.h>
>
> +#include <dt-bindings/iio/adc/adi,ad7768-1.h>
> +
> /* AD7768 registers definition */
> #define AD7768_REG_CHIP_TYPE 0x3
> #define AD7768_REG_PROD_ID_L 0x4
> @@ -347,6 +349,11 @@ static int ad7768_set_freq(struct ad7768_state *st,
> return 0;
> }
>
> +static int ad7768_set_vcm_output(struct ad7768_state *st, unsigned int mode)
> +{
Is setting CHOP_FREQUENCY bit to 0 intentional here? Could use a comment or
use regmap_update_bits() + FIELD_PREP() instead.
> + return regmap_write(st->regmap, AD7768_REG_ANALOG2, mode);
> +}
IMHO, one-line helper function just makes more code to read, so would be
simpler without it.
> +
> static ssize_t ad7768_sampling_freq_avail(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> @@ -628,6 +635,7 @@ static int ad7768_probe(struct spi_device *spi)
> {
> struct ad7768_state *st;
> struct iio_dev *indio_dev;
> + u32 val;
> int ret;
>
> indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> @@ -688,6 +696,18 @@ static int ad7768_probe(struct spi_device *spi)
> indio_dev->info = &ad7768_info;
> indio_dev->modes = INDIO_DIRECT_MODE;
>
> + ret = device_property_read_u32(&spi->dev, "adi,vcm-output", &val);
> + if (!ret) {
> + if (val > AD7768_VCM_OUTPUT_OFF) {
> + dev_err(&spi->dev, "Invalid VCM output value\n");
> + return -EINVAL;
return dev_err_probe(...);
> + }
> +
> + ret = ad7768_set_vcm_output(st, val);
> + if (ret)
> + return ret;
> + }
> +
> ret = ad7768_setup(st);
> if (ret < 0) {
> dev_err(&spi->dev, "AD7768 setup failed\n");
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support
2025-01-27 15:13 ` [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support Jonathan Santos
@ 2025-01-27 23:34 ` David Lechner
2025-02-03 13:08 ` Jonathan Santos
2025-02-01 15:50 ` Jonathan Cameron
1 sibling, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-27 23:34 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:13 AM, Jonathan Santos wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> The AD7768-1 has the ability to control other local hardware (such as gain
> stages),to power down other blocks in the signal chain, or read local
> status signals over the SPI interface.
>
> This change exports the AD7768-1's four gpios and makes them accessible
> at an upper layer.
>
> Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> ---
> v2 Changes:
> * Replaced mutex for iio_device_claim_direct_mode().
> * Use gpio-controller property to conditionally enable the
> GPIO support.
> * OBS: when the GPIO is configured as output, we should read
> the current state value from AD7768_REG_GPIO_WRITE.
> ---
> drivers/iio/adc/ad7768-1.c | 148 ++++++++++++++++++++++++++++++++++++-
> 1 file changed, 146 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index c540583808c2..e3ea078e6ec4 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -9,6 +9,8 @@
> #include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/err.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/driver.h>
> #include <linux/gpio/consumer.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
> @@ -79,6 +81,19 @@
> #define AD7768_CONV_MODE_MSK GENMASK(2, 0)
> #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
>
> +/* AD7768_REG_GPIO_CONTROL */
> +#define AD7768_GPIO_UNIVERSAL_EN BIT(7)
> +#define AD7768_GPIO_CONTROL_MSK GENMASK(3, 0)
> +
> +/* AD7768_REG_GPIO_WRITE */
> +#define AD7768_GPIO_WRITE_MSK GENMASK(3, 0)
> +
> +/* AD7768_REG_GPIO_READ */
> +#define AD7768_GPIO_READ_MSK GENMASK(3, 0)
> +
> +#define AD7768_GPIO_INPUT(x) 0x00
> +#define AD7768_GPIO_OUTPUT(x) BIT(x)
> +
> #define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
> #define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
>
> @@ -160,6 +175,8 @@ struct ad7768_state {
> struct regulator *vref;
> struct mutex lock;
> struct clk *mclk;
> + struct gpio_chip gpiochip;
> + unsigned int gpio_avail_map;
> unsigned int mclk_freq;
> unsigned int samp_freq;
> struct completion completion;
> @@ -309,6 +326,125 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
> return 0;
> }
>
> +static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
Missing iio_device_release_direct_mode() here and in other functions.
(And we are in the process of removing iio_device_claim_direct_scoped(), so
don't use that.)
> +
> + return regmap_update_bits(st->regmap,
> + AD7768_REG_GPIO_CONTROL,
> + BIT(offset),
> + AD7768_GPIO_INPUT(offset));
Can be simplified to regmap_clear_bits(), then we can get rid of the odd
AD7768_GPIO_INPUT macro that ignores the argument.
> +}
> +
> +static int ad7768_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(st->regmap,
> + AD7768_REG_GPIO_CONTROL,
> + BIT(offset),
> + AD7768_GPIO_OUTPUT(offset));
And regmap_set_bits() here.
> +}
> +
> +static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + unsigned int val;
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & BIT(offset))
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val);
> + else
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val);
Can we get a comment explaining why GPIO_READ doesn't work in output mode?
Or if it does work, we can simplify this function.
> + if (ret < 0)
> + return ret;
> +
> + return !!(val & BIT(offset));
> +}
> +
> +static void ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + unsigned int val;
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return;
> +
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
> + if (ret < 0)
> + return;
> +
> + if (val & BIT(offset))
> + regmap_update_bits(st->regmap,
> + AD7768_REG_GPIO_WRITE,
> + BIT(offset),
> + (value << offset));
Can remove extra ().
> +}
> +
> +static int ad7768_gpio_request(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> +
> + if (!(st->gpio_avail_map & BIT(offset)))
> + return -ENODEV;
> +
> + st->gpio_avail_map &= ~BIT(offset);
Is this really needed? It seems like GPIO core would be keeping track already.
Also would need a .free callback to undo this action.
It seems like most ADC's with GPIO controllers don't implement .request though.
> +
> + return 0;
> +}
> +
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 13/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode
2025-01-27 15:13 ` [PATCH v2 13/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode Jonathan Santos
@ 2025-01-27 23:47 ` David Lechner
0 siblings, 0 replies; 56+ messages in thread
From: David Lechner @ 2025-01-27 23:47 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:13 AM, Jonathan Santos wrote:
> When the device is configured to Sinc5 filter and decimation x8,
> output data is reduced to 16-bits in order to support 1 MHz of
> sampling frequency due to clock limitation.
>
> Use multiple scan types feature to enable the driver to switch
> scan type in runtime, making possible to support both 24-bit and
> 16-bit resolution.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Included the ".shift" value back to scan_type.
> * Changed the number of bytes from regmap_read instead of shifting the
> ADC sample value when the word size is lower (16-bits).
> ---
> drivers/iio/adc/ad7768-1.c | 73 ++++++++++++++++++++++++++++++++------
> 1 file changed, 62 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index e3ea078e6ec4..7686556c7808 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -136,6 +136,15 @@ struct ad7768_clk_configuration {
> enum ad7768_pwrmode pwrmode;
> };
>
> +enum ad7768_scan_type {
> + AD7768_SCAN_TYPE_NORMAL,
> + AD7768_SCAN_TYPE_HIGH_SPEED,
> +};
> +
> +static const int ad7768_mclk_div_rates[4] = {
> + 16, 8, 4, 2,
> +};
> +
> static const struct ad7768_clk_configuration ad7768_clk_config[] = {
> { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
> { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
> @@ -150,6 +159,23 @@ static const struct ad7768_clk_configuration ad7768_clk_config[] = {
> { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
> };
>
> +static const struct iio_scan_type ad7768_scan_type[] = {
> + [AD7768_SCAN_TYPE_NORMAL] = {
> + .sign = 's',
> + .realbits = 24,
> + .storagebits = 32,
> + .shift = 8,
> + .endianness = IIO_BE,
> + },
> + [AD7768_SCAN_TYPE_HIGH_SPEED] = {
> + .sign = 's',
> + .realbits = 16,
> + .storagebits = 32,
Why not make storagebits 16 here?
> + .shift = 16,
> + .endianness = IIO_BE,
> + },
> +};
> +
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI
2025-01-27 15:14 ` [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI Jonathan Santos
@ 2025-01-28 0:08 ` David Lechner
2025-02-03 15:28 ` Marcelo Schmitt
1 sibling, 0 replies; 56+ messages in thread
From: David Lechner @ 2025-01-28 0:08 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:14 AM, Jonathan Santos wrote:
> The synchronization method using GPIO requires the generated pulse to be
> truly synchronous with the base MCLK signal. When it is not possible to
> do that in hardware, the datasheet recommends using synchronization over
> SPI, where the generated pulse is already synchronous with MCLK. This
> requires the SYNC_OUT pin to be connected to SYNC_IN pin. In
> multidevices setup, the SYNC_OUT from other devices can be used as
> synchronization source.
>
> Use trigger-sources property to enable device synchronization over SPI
> for single and multiple devices.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Synchronization via SPI is enabled when the Sync GPIO is not defined.
> * now trigger-sources property indicates the synchronization provider or
> main device. The main device will be used to drive the SYNC_IN when
> requested (via GPIO or SPI).
> ---
> drivers/iio/adc/ad7768-1.c | 81 ++++++++++++++++++++++++++++++++++----
> 1 file changed, 73 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 7686556c7808..01ccbe0aa708 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -193,6 +193,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
>
> struct ad7768_state {
> struct spi_device *spi;
> + struct spi_device *sync_source_spi;
> struct regmap *regmap;
> struct regulator *vref;
> struct mutex lock;
> @@ -206,6 +207,7 @@ struct ad7768_state {
> struct iio_trigger *trig;
> struct gpio_desc *gpio_sync_in;
> struct gpio_desc *gpio_reset;
> + bool en_spi_sync;
> const char *labels[ARRAY_SIZE(ad7768_channels)];
> /*
> * DMA (thus cache coherency maintenance) may require the
> @@ -264,6 +266,21 @@ static int ad7768_spi_reg_write(void *context,
> return spi_write(st->spi, st->data.d8, 2);
> }
>
> +static int ad7768_send_sync_pulse(struct ad7768_state *st)
> +{
> + if (st->en_spi_sync) {
> + st->data.d8[0] = AD7768_WR_FLAG_MSK(AD7768_REG_SYNC_RESET);
> + st->data.d8[1] = 0x00;
> +
> + return spi_write(st->sync_source_spi, st->data.d8, 2);
> + } else if (st->gpio_sync_in) {
Redundant else after return.
> + gpiod_set_value_cansleep(st->gpio_sync_in, 1);
> + gpiod_set_value_cansleep(st->gpio_sync_in, 0);
> + }
> +
> + return 0;
> +}
> +
> static int ad7768_set_mode(struct ad7768_state *st,
> enum ad7768_conv_mode mode)
> {
> @@ -348,10 +365,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
> return ret;
>
> /* A sync-in pulse is required every time the filter dec rate changes */
> - gpiod_set_value(st->gpio_sync_in, 1);
> - gpiod_set_value(st->gpio_sync_in, 0);
> -
> - return 0;
> + return ad7768_send_sync_pulse(st);
> }
>
> static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
> @@ -638,6 +652,58 @@ static const struct iio_info ad7768_info = {
> .debugfs_reg_access = &ad7768_reg_access,
> };
>
> +static int ad7768_parse_trigger_sources(struct device *dev, struct ad7768_state *st)
> +{
> + struct fwnode_reference_args args;
> + int ret;
> +
> + /*
> + * The AD7768-1 allows two primary methods for driving the SYNC_IN pin to synchronize
> + * one or more devices:
> + * 1. Using a GPIO to directly drive the SYNC_IN pin.
> + * 2. Using a SPI command, where the SYNC_OUT pin generates a synchronization pulse
> + * that loops back to the SYNC_IN pin.
> + *
> + * In multi-device setups, the SYNC_IN pin can also be driven by the SYNC_OUT pin of
> + * another device. To support this, we use the "trigger-source" property to get a
> + * reference to the "main" device responsible for generating the synchronization pulse.
> + * In a single-device setup, the "trigger-source" property should reference the device's
> + * own node.
> + */
> + ret = fwnode_property_get_reference_args(dev_fwnode(dev), "trigger-sources",
> + "#trigger-source-cells", 0, 0, &args);
The trigger-sources property is optional, so we should have a special case for
ret == -EINVAL here. Otherwise existing users that use the gpio binding will
fail here.
> + if (ret) {
> + dev_err(dev, "Failed to get trigger-sources reference: %d\n", ret);
> + return ret;
return dev_err_probe(...);
> + }
> +
> + st->gpio_sync_in = devm_gpiod_get_optional(args.fwnode->dev, "adi,sync-in",
> + GPIOD_OUT_LOW);
I would put this in the -EINVAL special case mentioned above, then we don't
nee to use the _optional variant.
> + if (IS_ERR(st->gpio_sync_in))
> + return PTR_ERR(st->gpio_sync_in);
This leaks args.fwnode reference on return (but would be fixed if making the
above suggested change).
> +
> + /*
> + * If the SYNC_IN GPIO is not defined, fall back to synchronization over SPI.
> + * Use the trigger-source reference to identify the main SPI device for generating
> + * the synchronization pulse.
> + */
> + if (!st->gpio_sync_in) {
> + st->sync_source_spi = to_spi_device(args.fwnode->dev);
This seems unsafe. The DT could be pointing to something other than a SPI device.
to_spi_device() calls container_of() so would cause undefined behavior if dev
was something else. Also, even if it is a spi device, we don't know what state
it is in and if it is safe to use it.
I think it would be sufficient for now to just check that args.fw_node is the
the same one as this SPI device since that is the supported case.
> + if (!st->sync_source_spi) {
> + dev_err(dev,
> + "Synchronization setup failed. GPIO is undefined and trigger-source reference is invalid\n");
> + return -EINVAL;
Leaks args.fwnode on return. Also, dev_err_probe().
> + }
> +
> + st->en_spi_sync = true;
> + }
> +
> + /* Release the fwnode reference after use */
> + fwnode_handle_put(args.fwnode);
> +
> + return 0;
> +}
> +
> static int ad7768_setup(struct iio_dev *indio_dev)
> {
> struct ad7768_state *st = iio_priv(indio_dev);
> @@ -668,10 +734,9 @@ static int ad7768_setup(struct iio_dev *indio_dev)
> return ret;
> }
>
> - st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
> - GPIOD_OUT_LOW);
> - if (IS_ERR(st->gpio_sync_in))
> - return PTR_ERR(st->gpio_sync_in);
> + ret = ad7768_parse_trigger_sources(&st->spi->dev, st);
> + if (ret)
> + return ret;
>
> /* Only create a Chip GPIO if flagged for it */
> if (device_property_read_bool(&st->spi->dev, "gpio-controller")) {
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes
2025-01-27 15:14 ` [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes Jonathan Santos
@ 2025-01-28 1:24 ` David Lechner
2025-02-03 14:58 ` Jonathan Santos
2025-01-30 16:39 ` Jonathan Cameron
1 sibling, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-28 1:24 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1, PopPaul2021
On 1/27/25 9:14 AM, Jonathan Santos wrote:
> Separate filter type and decimation rate from the sampling frequency
> attribute. The new filter type attribute enables SINC3 and WIDEBAND
> filters, which were previously unavailable.
>
> Previously, combining decimation and MCLK divider in the sampling
> frequency obscured performance trade-offs. Lower MCLK divider
> settings increase power usage, while lower decimation rates reduce
> precision by decreasing averaging. By creating an oversampling
> attribute, which controls the decimation, users gain finer control
> over performance.
>
> The addition of those attributes allows a wider range of sampling
> frequencies and more access to the device features.
>
> Co-developed-by: PopPaul2021 <paul.pop@analog.com>
> Signed-off-by: PopPaul2021 <paul.pop@analog.com>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Decimation_rate attribute replaced for oversampling_ratio.
> ---
> drivers/iio/adc/ad7768-1.c | 389 +++++++++++++++++++++++++++++++------
> 1 file changed, 325 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 01ccbe0aa708..6d0b430a8d54 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -5,6 +5,7 @@
> * Copyright 2017 Analog Devices Inc.
> */
> #include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> #include <linux/clk.h>
> #include <linux/delay.h>
> #include <linux/device.h>
> @@ -17,6 +18,7 @@
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/sysfs.h>
> +#include "linux/util_macros.h"
nit: alphabetical order
> #include <linux/spi/spi.h>
>
> #include <linux/iio/buffer.h>
> @@ -77,6 +79,10 @@
> #define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
> #define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
>
> +/* AD7768_SINC3_DEC_RATE */
> +#define AD7768_SINC3_DEC_RATE_MSB_MSK GENMASK(12, 8)
> +#define AD7768_SINC3_DEC_RATE_LSB_MSK GENMASK(7, 0)
> +
> /* AD7768_REG_CONVERSION */
> #define AD7768_CONV_MODE_MSK GENMASK(2, 0)
> #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
> @@ -97,6 +103,18 @@
> #define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
> #define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
>
> +/* Decimation Rate Limits */
> +#define SINC5_DEC_RATE_MIN 8
> +#define SINC5_DEC_RATE_MAX 1024
> +#define SINC3_DEC_RATE_MIN 32
> +#define SINC3_DEC_RATE_MAX 163840
> +#define WIDEBAND_DEC_RATE_MIN 32
> +#define WIDEBAND_DEC_RATE_MAX 1024
> +
> +enum {
> + DEC_RATE,
Odd to have enum with one member. Also should have AD7768_ namespace prefix.
> +};
> +
> enum ad7768_conv_mode {
> AD7768_CONTINUOUS,
> AD7768_ONE_SHOT,
> @@ -118,22 +136,12 @@ enum ad7768_mclk_div {
> AD7768_MCLK_DIV_2
> };
>
> -enum ad7768_dec_rate {
> - AD7768_DEC_RATE_32 = 0,
> - AD7768_DEC_RATE_64 = 1,
> - AD7768_DEC_RATE_128 = 2,
> - AD7768_DEC_RATE_256 = 3,
> - AD7768_DEC_RATE_512 = 4,
> - AD7768_DEC_RATE_1024 = 5,
> - AD7768_DEC_RATE_8 = 9,
> - AD7768_DEC_RATE_16 = 10
> -};
> -
> -struct ad7768_clk_configuration {
> - enum ad7768_mclk_div mclk_div;
> - enum ad7768_dec_rate dec_rate;
> - unsigned int clk_div;
> - enum ad7768_pwrmode pwrmode;
> +enum ad7768_flt_type {
> + SINC5,
> + SINC5_DEC_X8,
> + SINC5_DEC_X16,
> + SINC3,
> + WIDEBAND
> };
>
> enum ad7768_scan_type {
> @@ -145,18 +153,12 @@ static const int ad7768_mclk_div_rates[4] = {
> 16, 8, 4, 2,
> };
>
> -static const struct ad7768_clk_configuration ad7768_clk_config[] = {
> - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
> - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
> - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
> - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
> - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
> - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
> - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
> - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
> - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
> - { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
> - { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
> +static const int ad7768_dec_rate_values[6] = {
> + 32, 64, 128, 256, 512, 1024,
> +};
> +
> +static const char * const ad7768_filter_enum[] = {
> + "sinc5", "sinc3", "wideband"
Do we also need to consider "sinc3+rej60" to account for the EN_60HZ_REJ bit
in the DIGITAL_FILTER register?
> };
>
> static const struct iio_scan_type ad7768_scan_type[] = {
> @@ -176,12 +178,32 @@ static const struct iio_scan_type ad7768_scan_type[] = {
> },
> };
>
> +static int ad7768_get_fil_type_attr(struct iio_dev *dev,
> + const struct iio_chan_spec *chan);
> +static int ad7768_set_fil_type_attr(struct iio_dev *dev,
> + const struct iio_chan_spec *chan, unsigned int filter);
> +
> +static const struct iio_enum ad7768_flt_type_iio_enum = {
> + .items = ad7768_filter_enum,
> + .num_items = ARRAY_SIZE(ad7768_filter_enum),
> + .set = ad7768_set_fil_type_attr,
> + .get = ad7768_get_fil_type_attr,
> +};
> +
> +static struct iio_chan_spec_ext_info ad7768_ext_info[] = {
> + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum),
> + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum),
> + { },
> +};
> +
> static const struct iio_chan_spec ad7768_channels[] = {
> {
> .type = IIO_VOLTAGE,
> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
info_mask_shared_by_type might be safer in case we ever have to add a non-
voltage channel for some reason.
> + .ext_info = ad7768_ext_info,
> .indexed = 1,
> .channel = 0,
> .scan_index = 0,
> @@ -201,7 +223,9 @@ struct ad7768_state {
> struct gpio_chip gpiochip;
> unsigned int gpio_avail_map;
> unsigned int mclk_freq;
> - unsigned int dec_rate;
> + unsigned int mclk_div;
> + unsigned int oversampling_ratio;
> + enum ad7768_flt_type filter_type;
> unsigned int samp_freq;
> struct completion completion;
> struct iio_trigger *trig;
> @@ -223,6 +247,9 @@ struct ad7768_state {
> } data __aligned(IIO_DMA_MINALIGN);
> };
>
> +static int ad7768_set_freq(struct ad7768_state *st,
> + unsigned int freq);
Would be nice if we can reorder functions to avoid forward declaration.
> +
> static int ad7768_spi_reg_read(void *context, unsigned int addr,
> unsigned int *val)
> {
> @@ -281,6 +308,31 @@ static int ad7768_send_sync_pulse(struct ad7768_state *st)
> return 0;
> }
>
> +static int ad7768_set_mclk_div(struct ad7768_state *st, unsigned int mclk_div)
> +{
> + unsigned int mclk_div_value;
> + int ret;
> +
> + guard(mutex)(&st->lock);
> + ret = ad7768_spi_reg_read(st, AD7768_REG_POWER_CLOCK, &mclk_div_value, 1);
> + if (ret)
> + return ret;
> +
> + mclk_div_value &= ~(AD7768_PWR_MCLK_DIV_MSK | AD7768_PWR_PWRMODE_MSK);
> + /* Set mclk_div value */
> + mclk_div_value |= AD7768_PWR_MCLK_DIV(mclk_div);
> + /*
> + * Set power mode based on mclk_div value.
> + * ECO_MODE is only recommended for MCLK_DIV 16
> + */
> + if (mclk_div > AD7768_MCLK_DIV_16)
> + mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_FAST_MODE);
> + else
> + mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_ECO_MODE);
> +
> + return regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, mclk_div_value);
Can we do this with regmap_update_bits() and FIELD_PREP() instead?
> +}
> +
> static int ad7768_set_mode(struct ad7768_state *st,
> enum ad7768_conv_mode mode)
> {
> @@ -349,23 +401,183 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
> return ret;
> }
>
> -static int ad7768_set_dig_fil(struct ad7768_state *st,
> - enum ad7768_dec_rate dec_rate)
> +static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
> + unsigned int dec_rate)
> +{
> + unsigned int dec_rate_msb, dec_rate_lsb, max_dec_rate;
> + int ret;
> +
> + guard(mutex)(&st->lock);
> + /*
> + * Maximum dec_rate is limited by the MCLK_DIV value
> + * and by the ODR. The edge case is for MCLK_DIV = 2
> + * ODR = 50 SPS.
> + * max_dec_rate <= MCLK / (2 * 50)
> + */
> + max_dec_rate = st->mclk_freq / 100;
> + dec_rate = clamp_t(unsigned int, dec_rate, 32, max_dec_rate);
> + /*
> + * Calculate the equivalent value to sinc3 decimation ratio
> + * to be written on the SINC3_DECIMATION_RATE register:
> + * Value = (DEC_RATE / 32) -1
> + */
> + dec_rate = DIV_ROUND_UP(dec_rate, 32) - 1;
> + dec_rate_msb = FIELD_GET(AD7768_SINC3_DEC_RATE_MSB_MSK, dec_rate);
> + dec_rate_lsb = FIELD_GET(AD7768_SINC3_DEC_RATE_LSB_MSK, dec_rate);
> +
> + ret = regmap_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_MSB, dec_rate_msb);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_LSB, dec_rate_lsb);
> + if (ret)
> + return ret;
Can we use regmap_bulk_write()?
> +
> + st->oversampling_ratio = (dec_rate + 1) * 32;
> +
> + return 0;
> +}
> +
> +static int ad7768_set_dec_rate(struct ad7768_state *st, unsigned int dec_rate)
> {
> + unsigned int mode, dec_rate_reg;
> + int ret;
> +
> + dec_rate_reg = find_closest(dec_rate, ad7768_dec_rate_values,
> + ARRAY_SIZE(ad7768_dec_rate_values));
> +
> + guard(mutex)(&st->lock);
> + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
> + if (ret)
> + return ret;
> +
> + mode &= ~AD7768_DIG_FIL_DEC_MSK;
> + mode |= AD7768_DIG_FIL_DEC_RATE(dec_rate_reg);
> + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
regmap_update_bits()?
> + if (ret)
> + return ret;
> +
> + st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_reg];
> +
> + return 0;
> +}
> +
> +static int ad7768_set_filter_type(struct iio_dev *dev,
> + enum ad7768_flt_type filter_type)
> +{
> + struct ad7768_state *st = iio_priv(dev);
> unsigned int mode;
> int ret;
>
> - if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
> - mode = AD7768_DIG_FIL_FIL(dec_rate);
> - else
> - mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> + guard(mutex)(&st->lock);
Lock was removed in previous patch, so shouldn't be using it here.
> + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
> + if (ret)
> + return ret;
> +
> + mode &= ~AD7768_DIG_FIL_FIL_MSK;
> + mode |= AD7768_DIG_FIL_FIL(filter_type);
>
> ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
> if (ret < 0)
> return ret;
Looks like we could drop the helper function and just use regmap_update_bits()
directly now.
>
> - /* A sync-in pulse is required every time the filter dec rate changes */
> - return ad7768_send_sync_pulse(st);
> + st->filter_type = filter_type;
> +
> + return 0;
> +}
> +
> +static int ad7768_configure_dig_fil(struct iio_dev *dev,
> + enum ad7768_flt_type filter_type,
> + unsigned int dec_rate)
> +{
> + struct ad7768_state *st = iio_priv(dev);
> + int ret;
> +
> + if (filter_type == SINC3) {
Using a switch statement instead would be more like other IIO code.
> + ret = ad7768_set_filter_type(dev, SINC3);
> + if (ret)
> + return ret;
> +
> + /* recalculate the decimation for this filter mode */
> + ret = ad7768_set_sinc3_dec_rate(st, dec_rate);
Just return directly.
> + } else if (filter_type == WIDEBAND) {
> + ret = ad7768_set_filter_type(dev, filter_type);
> + if (ret)
> + return ret;
> +
> + /* recalculate the decimation rate */
> + ret = ad7768_set_dec_rate(st, dec_rate);
> + } else {
> + /* For SINC5 filter */
> + /* Decimation 8 and 16 are set in the digital filter field */
> + if (dec_rate <= 8) {
> + ret = ad7768_set_filter_type(dev, SINC5_DEC_X8);
> + if (ret)
> + return ret;
> +
> + st->oversampling_ratio = 8;
> + } else if (dec_rate <= 16) {
> + ret = ad7768_set_filter_type(dev, SINC5_DEC_X16);
> + if (ret)
> + return ret;
> +
> + st->oversampling_ratio = 16;
> + } else {
> + ret = ad7768_set_filter_type(dev, SINC5);
> + if (ret)
> + return ret;
> +
> + ret = ad7768_set_dec_rate(st, dec_rate);
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int ad7768_set_fil_type_attr(struct iio_dev *dev,
> + const struct iio_chan_spec *chan,
> + unsigned int filter)
> +{
> + struct ad7768_state *st = iio_priv(dev);
> + int ret;
> +
> + /*
> + * Filters of types 0, 1, and 2 correspond to SINC5.
> + * For SINC3 and wideband filter types, an offset of 2 is added
> + * to align with the expected register values.
> + */
> + if (filter != SINC5)
> + filter += 2;
> +
> + ret = ad7768_configure_dig_fil(dev, filter, st->oversampling_ratio);
> + if (ret)
> + return ret;
> +
> + /* Update sampling frequency */
> + return ad7768_set_freq(st, st->samp_freq);
> +}
> +
> +static int ad7768_get_fil_type_attr(struct iio_dev *dev,
> + const struct iio_chan_spec *chan)
> +{
> + struct ad7768_state *st = iio_priv(dev);
> + int ret;
> + unsigned int mode;
> +
> + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
> + if (ret)
> + return ret;
> +
> + mode = FIELD_GET(AD7768_DIG_FIL_FIL_MSK, mode);
> + /* Filter types from 0 to 2 are represented as SINC5 */
> + if (mode < SINC3)
> + return SINC5;
> +
> + /*
> + * Remove the offset for the sinc3 and wideband filters
> + * to get the corresponding attribute enum value
> + */
> + return mode - 2;
> }
>
> static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
> @@ -490,43 +702,37 @@ static int ad7768_gpio_init(struct iio_dev *indio_dev)
> static int ad7768_set_freq(struct ad7768_state *st,
> unsigned int freq)
> {
> - unsigned int diff_new, diff_old, pwr_mode, i, idx;
> + unsigned int diff_new, diff_old, i, idx;
> int res, ret;
>
> + freq = clamp_t(unsigned int, freq, 50, 1024000);
> diff_old = U32_MAX;
> idx = 0;
>
> - res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
> + if (freq == 0)
> + return -EINVAL;
> +
> + res = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->oversampling_ratio);
>
> /* Find the closest match for the desired sampling frequency */
> - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
> - diff_new = abs(res - ad7768_clk_config[i].clk_div);
> + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) {
> + diff_new = abs(res - ad7768_mclk_div_rates[i]);
> if (diff_new < diff_old) {
> diff_old = diff_new;
> idx = i;
> }
> }
>
> - /*
> - * Set both the mclk_div and pwrmode with a single write to the
> - * POWER_CLOCK register
> - */
> - pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
> - AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> - ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
> - if (ret < 0)
> - return ret;
> -
> - ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
> - if (ret < 0)
> + /* Set both the mclk_div and pwrmode */
> + ret = ad7768_set_mclk_div(st, idx);
> + if (ret)
> return ret;
>
> - st->dec_rate = ad7768_clk_config[idx].clk_div /
> - ad7768_mclk_div_rates[ad7768_clk_config[idx].mclk_div];
> st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
> - ad7768_clk_config[idx].clk_div);
> + ad7768_mclk_div_rates[idx] * st->oversampling_ratio);
>
> - return 0;
> + /* A sync-in pulse is required every time the filter dec rate changes */
Does this function actually change oversampling_ration/decimation rate? Or do
we also need to sync after changing other filter parameters?
> + return ad7768_send_sync_pulse(st);
> }
>
> static int ad7768_set_vcm_output(struct ad7768_state *st, unsigned int mode)
> @@ -540,13 +746,16 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev,
> {
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> struct ad7768_state *st = iio_priv(indio_dev);
> - unsigned int freq;
> + unsigned int freq, freq_filtered;
> int i, len = 0;
>
> - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
> - freq = DIV_ROUND_CLOSEST(st->mclk_freq,
> - ad7768_clk_config[i].clk_div);
> - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
> + freq_filtered = DIV_ROUND_CLOSEST(st->mclk_freq, st->oversampling_ratio);
> + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) {
> + freq = DIV_ROUND_CLOSEST(freq_filtered,
> + ad7768_mclk_div_rates[i]);
> + /* Sampling frequency cannot be lower than the minimum of 50 SPS */
> + if (freq >= 50)
> + len += sysfs_emit_at(buf, len, "%d ", freq);
> }
>
> buf[len - 1] = '\n';
> @@ -556,6 +765,37 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev,
>
> static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
>
> +static ssize_t oversampling_ratio_available_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + int len = 0;
> +
> + /* Return oversampling ratio available in range format */
> + buf[len++] = '[';
> + if (st->filter_type == SINC3) {
> + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN);
> + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN);
> + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MAX);
> + } else if (st->filter_type == WIDEBAND) {
> + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN);
> + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN);
> + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MAX);
> + } else {
> + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN);
> + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN);
> + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MAX); }
> +
> + buf[len - 1] = ']';
> + buf[len++] = '\n';
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR_RO(oversampling_ratio_available, 0);
> +
> static int ad7768_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan,
> int *val, int *val2, long info)
> @@ -597,6 +837,11 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
> case IIO_CHAN_INFO_SAMP_FREQ:
> *val = st->samp_freq;
>
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + *val = st->oversampling_ratio;
> +
> return IIO_VAL_INT;
> }
>
> @@ -608,10 +853,19 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
> int val, int val2, long info)
> {
> struct ad7768_state *st = iio_priv(indio_dev);
> + int ret;
>
> switch (info) {
> case IIO_CHAN_INFO_SAMP_FREQ:
> return ad7768_set_freq(st, val);
> +
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
Probably should have iio_device_claim_direct_mode() here since this is poking
registers.
case IIO_CHAN_INFO_SAMP_FREQ: needs it too, so I would suggest to rename this
function to __ad7768_write_raw() and then write:
static int ad7768_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = __ad7768_write_raw(indio_dev, val, val2, info);
iio_device_release_direct_mode(indio_dev);
return ret;
}
This is the preferred style to avoid extra indent and error unwinding complexity.
> + ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val);
> + if (ret)
> + return ret;
> +
> + /* Update sampling frequency */
> + return ad7768_set_freq(st, st->samp_freq);
> default:
> return -EINVAL;
> }
> @@ -627,6 +881,7 @@ static int ad7768_read_label(struct iio_dev *indio_dev,
>
> static struct attribute *ad7768_attributes[] = {
> &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> + &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
> NULL
> };
Opportunity for another preliminary cleanup patch. IIO has core support for
*_available attributes now, so we can implement struct iio_info.read_avail
callback instead of manually creating attribute. Existing
sampling_frequency_available attribute can be converted to use this, then
use it for oversampling_ratio_available as well.
>
> @@ -639,7 +894,7 @@ static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev,
> {
> struct ad7768_state *st = iio_priv(indio_dev);
>
> - return st->dec_rate == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
> + return st->oversampling_ratio == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
> AD7768_SCAN_TYPE_NORMAL;
> }
>
> @@ -745,6 +1000,12 @@ static int ad7768_setup(struct iio_dev *indio_dev)
> return ret;
> }
>
> + /*
> + * Set Default Digital Filter configuration:
> + * SINC5 filter with x32 Decimation rate
> + */
> + ret = ad7768_configure_dig_fil(indio_dev, SINC5, 32);
> +
> /* Set the default sampling frequency to 32000 kSPS */
> return ad7768_set_freq(st, 32000);
> }
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 16/16] iio: adc: ad7768-1: add low pass -3dB cutoff attribute
2025-01-27 15:14 ` [PATCH v2 16/16] iio: adc: ad7768-1: add low pass -3dB cutoff attribute Jonathan Santos
@ 2025-01-28 1:27 ` David Lechner
0 siblings, 0 replies; 56+ messages in thread
From: David Lechner @ 2025-01-28 1:27 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:14 AM, Jonathan Santos wrote:
> Ad7768-1 has a different -3db frequency multiplier depending on
> the filter type configured. The cutoff frequency also varies according
> to the current ODR.
>
> Add a readonly low pass -3dB frequency cutoff attribute to clarify to
> the user which bandwidth is being allowed depending on the filter
> configurations.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2.
> ---
> drivers/iio/adc/ad7768-1.c | 23 +++++++++++++++++++++--
> 1 file changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 6d0b430a8d54..daf91ef6f77b 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -149,6 +149,18 @@ enum ad7768_scan_type {
> AD7768_SCAN_TYPE_HIGH_SPEED,
> };
>
> +/*
> + * -3dB cutoff frequency multipliers (relative to ODR) for
> + * each filter type. Values are multiplied by 1000.
> + */
> +static const int ad7768_filter_3db_odr_multiplier[] = {
> + [SINC5] = 204,
> + [SINC5_DEC_X8] = 204,
> + [SINC5_DEC_X16] = 204,
> + [SINC3] = 261,
> + [WIDEBAND] = 433,
> +};
> +
> static const int ad7768_mclk_div_rates[4] = {
> 16, 8, 4, 2,
> };
> @@ -202,7 +214,8 @@ static const struct iio_chan_spec ad7768_channels[] = {
> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
> + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
> + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
> .ext_info = ad7768_ext_info,
> .indexed = 1,
> .channel = 0,
> @@ -802,7 +815,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
> {
> struct ad7768_state *st = iio_priv(indio_dev);
> const struct iio_scan_type *scan_type;
> - int scale_uv, ret;
> + int scale_uv, ret, temp;
>
> scan_type = iio_get_current_scan_type(indio_dev, chan);
> if (IS_ERR(scan_type))
> @@ -842,6 +855,12 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
> case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> *val = st->oversampling_ratio;
>
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> + temp = st->samp_freq * ad7768_filter_3db_odr_multiplier[st->filter_type];
> + *val = DIV_ROUND_CLOSEST(temp, 1000);
> +
> return IIO_VAL_INT;
> }
>
If this is read-only, do we actually need it? It looks like the attribute is
going to return 1 of 3 values that exactly matches the filter_type attribute,
so this is not really giving any additional info.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property
2025-01-27 15:12 ` [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
@ 2025-01-28 1:28 ` David Lechner
2025-01-30 16:21 ` Jonathan Cameron
1 sibling, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-28 1:28 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:12 AM, Jonathan Santos wrote:
> The AD7768-1 provides a buffered common-mode voltage output
> on the VCM pin that can be used to bias analog input signals.
>
> Add adi,vcm-output to enable the configuration of the VCM output
> circuit.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2.
> ---
> .../bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
> include/dt-bindings/iio/adc/adi,ad7768-1.h | 16 ++++++++++++++++
> 2 files changed, 26 insertions(+)
> create mode 100644 include/dt-bindings/iio/adc/adi,ad7768-1.h
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> index da05c8448530..e26513a9469b 100644
> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> @@ -59,6 +59,15 @@ properties:
> In the absence of this property, Synchronization over SPI will be
> enabled.
>
> + adi,vcm-output:
> + description: |
> + Configures the Common-Mode Voltage Output. The VCM is provided by an
> + amplifier external to the AD7768-1 and can be used as common-mode voltage
> + by the ADC. There are 8 output voltage options available, and the macros
> + for these values can be found at dt-bindings/iio/adi,ad7768-1.h
> + items:
> + enum: [0, 1, 2, 3, 4, 5, 6, 7]
> +
I was expecting this to use regulator provider bindings rather than using a
custom property. Then the regulator consumer could request the voltage that
they need. But maybe that is more complicated than what is practical.
If we don't need regulator bindings, then this should be vcm-microvolt to use
standard units [1].
[1]: https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/property-units.yaml
> reset-gpios:
> maxItems: 1
>
> @@ -132,6 +141,7 @@ examples:
> gpio-controller;
> #gpio-cells = <2>;
> vref-supply = <&adc_vref>;
> + adi,vcm-output = <AD7768_VCM_OUTPUT_AVDD1_AVSS_2>;
> interrupts = <25 IRQ_TYPE_EDGE_RISING>;
> interrupt-parent = <&gpio>;
> adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
> diff --git a/include/dt-bindings/iio/adc/adi,ad7768-1.h b/include/dt-bindings/iio/adc/adi,ad7768-1.h
> new file mode 100644
> index 000000000000..469ea724c0d5
> --- /dev/null
> +++ b/include/dt-bindings/iio/adc/adi,ad7768-1.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +
> +#ifndef _DT_BINDINGS_ADI_AD7768_1_H
> +#define _DT_BINDINGS_ADI_AD7768_1_H
> +
> +/* Sets VCM output to (AVDD1 − AVSS)/2 */
> +#define AD7768_VCM_OUTPUT_AVDD1_AVSS_2 0x00
> +#define AD7768_VCM_OUTPUT_2_5V 0x01
> +#define AD7768_VCM_OUTPUT_2_05V 0x02
> +#define AD7768_VCM_OUTPUT_1_9V 0x03
> +#define AD7768_VCM_OUTPUT_1_65V 0x04
> +#define AD7768_VCM_OUTPUT_1_1V 0x05
> +#define AD7768_VCM_OUTPUT_0_9V 0x06
> +#define AD7768_VCM_OUTPUT_OFF 0x07
> +
> +#endif /* _DT_BINDINGS_ADI_AD7768_1_H */
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-27 15:11 ` [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-27 16:55 ` Rob Herring
@ 2025-01-28 1:28 ` David Lechner
2025-01-28 15:04 ` Jonathan Santos
2 siblings, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-28 1:28 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:11 AM, Jonathan Santos wrote:
> Add a new trigger-sources property to enable synchronization across
> multiple devices. This property references the main device (or
> trigger provider) responsible for generating the pulse to drive the
> SYNC_IN of all devices in the setup.
>
> In addition to GPIO synchronization, The AD7768-1 also supports
> synchronization over SPI, which use is recommended when the GPIO
> cannot provide a pulse synchronous with the base MCLK signal. It
> consists of looping back the SYNC_OUT to the SYNC_IN pin and send
> a command via SPI to trigger the synchronization.
>
> SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
> property. Since adi,sync-in-gpios is not long the only method, remove it
> from required properties.
>
> While at it, add description to the interrupt property.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Patch added as replacement for adi,sync-in-spi patch.
> * addressed the request for a description to interrupts property.
> ---
> .../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> index 3ce59d4d065f..3e119cf1754b 100644
> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> @@ -26,7 +26,17 @@ properties:
> clock-names:
> const: mclk
>
> + trigger-sources:
> + description:
> + References the main device responsible for synchronization. In a single
> + device setup, reference the own node.
> + maxItems: 1
We probably actually need 2 here. One for /SYNC_IN and one for a GPIO3 pin
acting as the /START signal.
> +
> interrupts:
> + description:
> + Specifies the interrupt line associated with the ADC. This refers
> + to the DRDY (Data Ready) pin, which signals when conversion results are
> + available.
> maxItems: 1
>
> '#address-cells':
> @@ -46,6 +56,8 @@ properties:
> sampling. A pulse is always required if the configuration is changed
> in any way, for example if the filter decimation rate changes.
> As the line is active low, it should be marked GPIO_ACTIVE_LOW.
> + In the absence of this property, Synchronization over SPI will be
> + enabled.
Isn't /SYNC_OUT connected to /SYNC_IN required for synchronization over SPI?
If yes, instead of adding this text, I would make the binding have:
oneOf:
- required:
- trigger-sources
- required:
- adi,sync-in-gpios
>
> reset-gpios:
> maxItems: 1
> @@ -57,6 +69,9 @@ properties:
> "#io-channel-cells":
> const: 1
>
> + "#trigger-source-cells":
> + const: 0
> +
> required:
> - compatible
> - reg
> @@ -65,7 +80,8 @@ required:
> - vref-supply
> - spi-cpol
> - spi-cpha
> - - adi,sync-in-gpios
> + - trigger-sources
> + - #trigger-source-cells
>
> patternProperties:
> "^channel@([0-9]|1[0-5])$":
> @@ -99,7 +115,7 @@ examples:
> #address-cells = <1>;
> #size-cells = <0>;
>
> - adc@0 {
> + adc0: adc@0 {
> compatible = "adi,ad7768-1";
> reg = <0>;
> spi-max-frequency = <2000000>;
> @@ -109,6 +125,8 @@ examples:
> interrupts = <25 IRQ_TYPE_EDGE_RISING>;
> interrupt-parent = <&gpio>;
> adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
Don't we need to drop adi,sync-in-gpios here? I don't think we would have two
things connected to /SYNC_IN at the same time.
> + trigger-sources = <&adc0>;
> + #trigger-source-cells = <0>;
> reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
> clocks = <&ad7768_mclk>;
> clock-names = "mclk";
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-27 15:12 ` [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap Jonathan Santos
@ 2025-01-28 1:29 ` David Lechner
2025-01-28 13:25 ` Nuno Sá
0 siblings, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-28 1:29 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:12 AM, Jonathan Santos wrote:
> Convert the AD7768-1 driver to use the regmap API for register
> access. This change simplifies and standardizes register interactions,
> reducing code duplication and improving maintainability.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * New patch in v2.
> ---
> drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
> 1 file changed, 58 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 95ba89435652..fb8d6fae5f8a 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -12,6 +12,7 @@
> #include <linux/gpio/consumer.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
> +#include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/sysfs.h>
> #include <linux/spi/spi.h>
> @@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
>
> struct ad7768_state {
> struct spi_device *spi;
> + struct regmap *regmap;
> struct regulator *vref;
> struct mutex lock;
> struct clk *mclk;
> @@ -176,12 +178,17 @@ struct ad7768_state {
> } data __aligned(IIO_DMA_MINALIGN);
> };
>
> -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
> - unsigned int len)
> +static int ad7768_spi_reg_read(void *context, unsigned int addr,
> + unsigned int *val)
> {
> - unsigned int shift;
> + struct iio_dev *dev = context;
> + struct ad7768_state *st;
> + unsigned int shift, len;
> int ret;
>
> + st = iio_priv(dev);
This can be combined with the variable declaration.
> + /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
Probably not currently needed but COEFF_DATA register is also 3 bytes.
> + len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> shift = 32 - (8 * len);
> st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
>
> @@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
> if (ret < 0)
> return ret;
>
> - return (be32_to_cpu(st->data.d32) >> shift);
> + *val = be32_to_cpu(st->data.d32) >> shift;
> +
> + return 0;
> }
>
> -static int ad7768_spi_reg_write(struct ad7768_state *st,
> +static int ad7768_spi_reg_write(void *context,
> unsigned int addr,
> unsigned int val)
> {
> + struct iio_dev *dev = context;
> + struct ad7768_state *st;
> +
> + st = iio_priv(dev);
> st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
> st->data.d8[1] = val & 0xFF;
>
> @@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct ad7768_state *st,
> static int ad7768_set_mode(struct ad7768_state *st,
> enum ad7768_conv_mode mode)
> {
> - int regval;
> + int regval, ret;
>
> - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
> - if (regval < 0)
> - return regval;
> + ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
> + if (ret)
> + return ret;
>
> regval &= ~AD7768_CONV_MODE_MSK;
> regval |= AD7768_CONV_MODE(mode);
>
> - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
> + return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
> }
>
> static int ad7768_scan_direct(struct iio_dev *indio_dev)
> @@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev)
> if (!ret)
> return -ETIMEDOUT;
>
> - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> - if (readval < 0)
> - return readval;
> + ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
> + if (ret)
> + return ret;
> +
> /*
> * Any SPI configuration of the AD7768-1 can only be
> * performed in continuous conversion mode.
> @@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
>
> mutex_lock(&st->lock);
> if (readval) {
> - ret = ad7768_spi_reg_read(st, reg, 1);
> - if (ret < 0)
> + ret = regmap_read(st->regmap, reg, readval);
> + if (ret)
> goto err_unlock;
Can drop the if and goto.
> - *readval = ret;
> - ret = 0;
> } else {
> - ret = ad7768_spi_reg_write(st, reg, writeval);
> + ret = regmap_write(st->regmap, reg, writeval);
> }
> err_unlock:
> mutex_unlock(&st->lock);
> @@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
> else
> mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
>
> - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
> + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
> if (ret < 0)
> return ret;
>
> @@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
> */
> pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
> AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
> + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
> if (ret < 0)
> return ret;
>
> @@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
> * to 10. When the sequence is detected, the reset occurs.
> * See the datasheet, page 70.
> */
> - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
> + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> if (ret)
> return ret;
>
> - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
> + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> if (ret)
> return ret;
>
> @@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
> * continuous read mode. Subsequent data reads do not require an
> * initial 8-bit write to query the ADC_DATA register.
> */
> - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
> + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01);
> }
>
> static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
> {
> struct ad7768_state *st = iio_priv(indio_dev);
> + unsigned int regval;
Intention could be more clear by calling this "unused". Otherwise, it can look
like a bug if you don't fully understand what the comment below means.
>
> /*
> * To exit continuous read mode, perform a single read of the ADC_DATA
> * reg (0x2C), which allows further configuration of the device.
> */
> - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> + return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
> }
>
> static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
> @@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev,
> return 0;
> }
>
> +static const struct regmap_bus ad7768_regmap_bus = {
> + .reg_write = ad7768_spi_reg_write,
> + .reg_read = ad7768_spi_reg_read,
> + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> + .val_format_endian_default = REGMAP_ENDIAN_BIG,
The bus read function is calling be32_to_cpu(), so we probably want to remove
that or change the default here.
> +};
> +
> +static const struct regmap_config ad7768_regmap_config = {
> + .name = "ad7768-1",
> + .reg_bits = 8,
> + .val_bits = 8,
Should this be 24 since the largest registers are 24-bit?
Another option could be to just use a regular spi_*() API for that register
instead of regmap_*() and avoid trying to do something that regmap doesn't
really handle.
Or we could possibly use regmap_bulk_read(), but that feels a bit hacky too
since it isn't actually how that function was intended to be used.
> + .max_register = AD7768_REG_MCLK_COUNTER,
> +};
> +
> static int ad7768_probe(struct spi_device *spi)
> {
> struct ad7768_state *st;
> @@ -592,6 +619,13 @@ static int ad7768_probe(struct spi_device *spi)
>
> st->spi = spi;
>
> + st->regmap = devm_regmap_init(&spi->dev,
> + &ad7768_regmap_bus, indio_dev,
> + &ad7768_regmap_config);
> + if (IS_ERR(st->regmap))
> + return dev_err_probe(&spi->dev, PTR_ERR(st->regmap),
> + "Failed to initialize regmap");
> +
> st->vref = devm_regulator_get(&spi->dev, "vref");
> if (IS_ERR(st->vref))
> return PTR_ERR(st->vref);
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio
2025-01-27 15:12 ` [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio Jonathan Santos
@ 2025-01-28 1:32 ` David Lechner
2025-01-30 16:29 ` Jonathan Cameron
0 siblings, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-28 1:32 UTC (permalink / raw)
To: Jonathan Santos, linux-iio, devicetree, linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On 1/27/25 9:12 AM, Jonathan Santos wrote:
> The Wideband Low Ripple filter is used for AD7768-1 Driver.
> Document wideband filter option into filter_type_available
> attribute.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Removed FIR mentions.
> ---
> Documentation/ABI/testing/sysfs-bus-iio | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index f83bd6829285..9b879e7732cd 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -2291,6 +2291,8 @@ Description:
> * "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
> * "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
> * "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
> + * "wideband" - filter with wideband low ripple passband
> + and sharp transition band.
>
> What: /sys/.../events/in_proximity_thresh_either_runningperiod
> KernelVersion: 6.6
I'm a bit shy to make any more suggestions on this one since my previous
suggestions were clearly not the "right way". :-)
But, the key takeaway I got from the discussion on v1 is that this describes the
_shape_ of the filter. To me, "wideband" describes the size but not the shape.
Would rectangular be the correct shape?
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-28 1:29 ` David Lechner
@ 2025-01-28 13:25 ` Nuno Sá
2025-01-28 14:46 ` Jonathan Santos
0 siblings, 1 reply; 56+ messages in thread
From: Nuno Sá @ 2025-01-28 13:25 UTC (permalink / raw)
To: David Lechner, Jonathan Santos, linux-iio, devicetree,
linux-kernel
Cc: lars, Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On Mon, 2025-01-27 at 19:29 -0600, David Lechner wrote:
> On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > Convert the AD7768-1 driver to use the regmap API for register
> > access. This change simplifies and standardizes register interactions,
> > reducing code duplication and improving maintainability.
> >
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > ---
> > v2 Changes:
> > * New patch in v2.
> > ---
> > drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
> > 1 file changed, 58 insertions(+), 24 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > index 95ba89435652..fb8d6fae5f8a 100644
> > --- a/drivers/iio/adc/ad7768-1.c
> > +++ b/drivers/iio/adc/ad7768-1.c
> > @@ -12,6 +12,7 @@
> > #include <linux/gpio/consumer.h>
> > #include <linux/kernel.h>
> > #include <linux/module.h>
> > +#include <linux/regmap.h>
> > #include <linux/regulator/consumer.h>
> > #include <linux/sysfs.h>
> > #include <linux/spi/spi.h>
> > @@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
> >
> > struct ad7768_state {
> > struct spi_device *spi;
> > + struct regmap *regmap;
> > struct regulator *vref;
> > struct mutex lock;
> > struct clk *mclk;
> > @@ -176,12 +178,17 @@ struct ad7768_state {
> > } data __aligned(IIO_DMA_MINALIGN);
> > };
> >
> > -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
> > - unsigned int len)
> > +static int ad7768_spi_reg_read(void *context, unsigned int addr,
> > + unsigned int *val)
> > {
> > - unsigned int shift;
> > + struct iio_dev *dev = context;
> > + struct ad7768_state *st;
> > + unsigned int shift, len;
> > int ret;
> >
> > + st = iio_priv(dev);
>
> This can be combined with the variable declaration.
>
> > + /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
>
> Probably not currently needed but COEFF_DATA register is also 3 bytes.
>
> > + len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > shift = 32 - (8 * len);
> > st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
> >
> > @@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state
> > *st, unsigned int addr,
> > if (ret < 0)
> > return ret;
> >
> > - return (be32_to_cpu(st->data.d32) >> shift);
> > + *val = be32_to_cpu(st->data.d32) >> shift;
> > +
> > + return 0;
> > }
> >
> > -static int ad7768_spi_reg_write(struct ad7768_state *st,
> > +static int ad7768_spi_reg_write(void *context,
> > unsigned int addr,
> > unsigned int val)
> > {
> > + struct iio_dev *dev = context;
> > + struct ad7768_state *st;
> > +
> > + st = iio_priv(dev);
> > st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
> > st->data.d8[1] = val & 0xFF;
> >
> > @@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct ad7768_state
> > *st,
> > static int ad7768_set_mode(struct ad7768_state *st,
> > enum ad7768_conv_mode mode)
> > {
> > - int regval;
> > + int regval, ret;
> >
> > - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
> > - if (regval < 0)
> > - return regval;
> > + ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
> > + if (ret)
> > + return ret;
> >
> > regval &= ~AD7768_CONV_MODE_MSK;
> > regval |= AD7768_CONV_MODE(mode);
> >
> > - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
> > + return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
> > }
> >
> > static int ad7768_scan_direct(struct iio_dev *indio_dev)
> > @@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev
> > *indio_dev)
> > if (!ret)
> > return -ETIMEDOUT;
> >
> > - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > - if (readval < 0)
> > - return readval;
> > + ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
> > + if (ret)
> > + return ret;
> > +
> > /*
> > * Any SPI configuration of the AD7768-1 can only be
> > * performed in continuous conversion mode.
> > @@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev
> > *indio_dev,
> >
> > mutex_lock(&st->lock);
> > if (readval) {
> > - ret = ad7768_spi_reg_read(st, reg, 1);
> > - if (ret < 0)
> > + ret = regmap_read(st->regmap, reg, readval);
> > + if (ret)
> > goto err_unlock;
>
> Can drop the if and goto.
>
> > - *readval = ret;
> > - ret = 0;
> > } else {
> > - ret = ad7768_spi_reg_write(st, reg, writeval);
> > + ret = regmap_write(st->regmap, reg, writeval);
> > }
> > err_unlock:
> > mutex_unlock(&st->lock);
> > @@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
> > else
> > mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> >
> > - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
> > + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
> > if (ret < 0)
> > return ret;
> >
> > @@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
> > */
> > pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
> > AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> > - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
> > + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
> > if (ret < 0)
> > return ret;
> >
> > @@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
> > * to 10. When the sequence is detected, the reset occurs.
> > * See the datasheet, page 70.
> > */
> > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
> > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> > if (ret)
> > return ret;
> >
> > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
> > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> > if (ret)
> > return ret;
> >
> > @@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev
> > *indio_dev)
> > * continuous read mode. Subsequent data reads do not require an
> > * initial 8-bit write to query the ADC_DATA register.
> > */
> > - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
> > + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01);
> > }
> >
> > static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
> > {
> > struct ad7768_state *st = iio_priv(indio_dev);
> > + unsigned int regval;
>
> Intention could be more clear by calling this "unused". Otherwise, it can look
> like a bug if you don't fully understand what the comment below means.
>
> >
> > /*
> > * To exit continuous read mode, perform a single read of the
> > ADC_DATA
> > * reg (0x2C), which allows further configuration of the device.
> > */
> > - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > + return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
> > }
> >
> > static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
> > @@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev
> > *indio_dev,
> > return 0;
> > }
> >
> > +static const struct regmap_bus ad7768_regmap_bus = {
> > + .reg_write = ad7768_spi_reg_write,
> > + .reg_read = ad7768_spi_reg_read,
> > + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> > + .val_format_endian_default = REGMAP_ENDIAN_BIG,
>
> The bus read function is calling be32_to_cpu(), so we probably want to remove
> that or change the default here.
>
> > +};
> > +
> > +static const struct regmap_config ad7768_regmap_config = {
> > + .name = "ad7768-1",
> > + .reg_bits = 8,
> > + .val_bits = 8,
>
> Should this be 24 since the largest registers are 24-bit?
>
> Another option could be to just use a regular spi_*() API for that register
> instead of regmap_*() and avoid trying to do something that regmap doesn't
> really handle.
>
> Or we could possibly use regmap_bulk_read(), but that feels a bit hacky too
> since it isn't actually how that function was intended to be used.
>
Hmm I might be missing something but looking at the register map, It seems we do
have 8bit registers? We do have values that span multiple registers (3 for the
24bit values) and regmap_bulk_read() should actually fit right? I mean, looking
at the docs:
"regmap_bulk_read() - Read multiple sequential registers from the device"
But I do agree that what we have right now does not make much sense. If we need
to do
len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
for supporting regmap, then I have to question using it. Also note that we have
things like gain and offset that are also 3 bytes which means that our custom
read would need to become more questionable if we add support for it.
Jonathan, did you tried to use plain regmap (without the custom bus)? Assuming
bulk reads work, I'm not seeing an apparent reason for the custom bus... I would
also suspect that if bulk reads don't work out of the box, providing a regmap
cache would make it work but relying on implementation details is not a very
good practice.
Anyways, I would try would normal regmap and if bulk reads don't work I would
either:
1) Just do three regmap_reads() for 3byte values;
2) Or do what David suggests and use normal spi_*() and forget about regmap.
Either way is fine to me.
- Nuno Sá
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-28 13:25 ` Nuno Sá
@ 2025-01-28 14:46 ` Jonathan Santos
2025-01-28 15:09 ` Nuno Sá
0 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-28 14:46 UTC (permalink / raw)
To: Nuno Sá
Cc: David Lechner, Jonathan Santos, linux-iio, devicetree,
linux-kernel, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, marcelo.schmitt1
On 01/28, Nuno Sá wrote:
> On Mon, 2025-01-27 at 19:29 -0600, David Lechner wrote:
> > On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > > Convert the AD7768-1 driver to use the regmap API for register
> > > access. This change simplifies and standardizes register interactions,
> > > reducing code duplication and improving maintainability.
> > >
> > > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > > ---
> > > v2 Changes:
> > > * New patch in v2.
> > > ---
> > > drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
> > > 1 file changed, 58 insertions(+), 24 deletions(-)
> > >
> > > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > > index 95ba89435652..fb8d6fae5f8a 100644
> > > --- a/drivers/iio/adc/ad7768-1.c
> > > +++ b/drivers/iio/adc/ad7768-1.c
> > > @@ -12,6 +12,7 @@
> > > #include <linux/gpio/consumer.h>
> > > #include <linux/kernel.h>
> > > #include <linux/module.h>
> > > +#include <linux/regmap.h>
> > > #include <linux/regulator/consumer.h>
> > > #include <linux/sysfs.h>
> > > #include <linux/spi/spi.h>
> > > @@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
> > >
> > > struct ad7768_state {
> > > struct spi_device *spi;
> > > + struct regmap *regmap;
> > > struct regulator *vref;
> > > struct mutex lock;
> > > struct clk *mclk;
> > > @@ -176,12 +178,17 @@ struct ad7768_state {
> > > } data __aligned(IIO_DMA_MINALIGN);
> > > };
> > >
> > > -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
> > > - unsigned int len)
> > > +static int ad7768_spi_reg_read(void *context, unsigned int addr,
> > > + unsigned int *val)
> > > {
> > > - unsigned int shift;
> > > + struct iio_dev *dev = context;
> > > + struct ad7768_state *st;
> > > + unsigned int shift, len;
> > > int ret;
> > >
> > > + st = iio_priv(dev);
> >
> > This can be combined with the variable declaration.
> >
> > > + /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
> >
> > Probably not currently needed but COEFF_DATA register is also 3 bytes.
> >
> > > + len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > > shift = 32 - (8 * len);
> > > st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
> > >
> > > @@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state
> > > *st, unsigned int addr,
> > > if (ret < 0)
> > > return ret;
> > >
> > > - return (be32_to_cpu(st->data.d32) >> shift);
> > > + *val = be32_to_cpu(st->data.d32) >> shift;
> > > +
> > > + return 0;
> > > }
> > >
> > > -static int ad7768_spi_reg_write(struct ad7768_state *st,
> > > +static int ad7768_spi_reg_write(void *context,
> > > unsigned int addr,
> > > unsigned int val)
> > > {
> > > + struct iio_dev *dev = context;
> > > + struct ad7768_state *st;
> > > +
> > > + st = iio_priv(dev);
> > > st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
> > > st->data.d8[1] = val & 0xFF;
> > >
> > > @@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct ad7768_state
> > > *st,
> > > static int ad7768_set_mode(struct ad7768_state *st,
> > > enum ad7768_conv_mode mode)
> > > {
> > > - int regval;
> > > + int regval, ret;
> > >
> > > - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
> > > - if (regval < 0)
> > > - return regval;
> > > + ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
> > > + if (ret)
> > > + return ret;
> > >
> > > regval &= ~AD7768_CONV_MODE_MSK;
> > > regval |= AD7768_CONV_MODE(mode);
> > >
> > > - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
> > > + return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
> > > }
> > >
> > > static int ad7768_scan_direct(struct iio_dev *indio_dev)
> > > @@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev
> > > *indio_dev)
> > > if (!ret)
> > > return -ETIMEDOUT;
> > >
> > > - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > - if (readval < 0)
> > > - return readval;
> > > + ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
> > > + if (ret)
> > > + return ret;
> > > +
> > > /*
> > > * Any SPI configuration of the AD7768-1 can only be
> > > * performed in continuous conversion mode.
> > > @@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev
> > > *indio_dev,
> > >
> > > mutex_lock(&st->lock);
> > > if (readval) {
> > > - ret = ad7768_spi_reg_read(st, reg, 1);
> > > - if (ret < 0)
> > > + ret = regmap_read(st->regmap, reg, readval);
> > > + if (ret)
> > > goto err_unlock;
> >
> > Can drop the if and goto.
> >
> > > - *readval = ret;
> > > - ret = 0;
> > > } else {
> > > - ret = ad7768_spi_reg_write(st, reg, writeval);
> > > + ret = regmap_write(st->regmap, reg, writeval);
> > > }
> > > err_unlock:
> > > mutex_unlock(&st->lock);
> > > @@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
> > > else
> > > mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> > >
> > > - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
> > > + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
> > > if (ret < 0)
> > > return ret;
> > >
> > > @@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
> > > */
> > > pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
> > > AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> > > - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
> > > + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
> > > if (ret < 0)
> > > return ret;
> > >
> > > @@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
> > > * to 10. When the sequence is detected, the reset occurs.
> > > * See the datasheet, page 70.
> > > */
> > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
> > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> > > if (ret)
> > > return ret;
> > >
> > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
> > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> > > if (ret)
> > > return ret;
> > >
> > > @@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev
> > > *indio_dev)
> > > * continuous read mode. Subsequent data reads do not require an
> > > * initial 8-bit write to query the ADC_DATA register.
> > > */
> > > - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
> > > + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01);
> > > }
> > >
> > > static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
> > > {
> > > struct ad7768_state *st = iio_priv(indio_dev);
> > > + unsigned int regval;
> >
> > Intention could be more clear by calling this "unused". Otherwise, it can look
> > like a bug if you don't fully understand what the comment below means.
> >
> > >
> > > /*
> > > * To exit continuous read mode, perform a single read of the
> > > ADC_DATA
> > > * reg (0x2C), which allows further configuration of the device.
> > > */
> > > - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > + return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
> > > }
> > >
> > > static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
> > > @@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev
> > > *indio_dev,
> > > return 0;
> > > }
> > >
> > > +static const struct regmap_bus ad7768_regmap_bus = {
> > > + .reg_write = ad7768_spi_reg_write,
> > > + .reg_read = ad7768_spi_reg_read,
> > > + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> > > + .val_format_endian_default = REGMAP_ENDIAN_BIG,
> >
> > The bus read function is calling be32_to_cpu(), so we probably want to remove
> > that or change the default here.
> >
> > > +};
> > > +
> > > +static const struct regmap_config ad7768_regmap_config = {
> > > + .name = "ad7768-1",
> > > + .reg_bits = 8,
> > > + .val_bits = 8,
> >
> > Should this be 24 since the largest registers are 24-bit?
> >
> > Another option could be to just use a regular spi_*() API for that register
> > instead of regmap_*() and avoid trying to do something that regmap doesn't
> > really handle.
> >
> > Or we could possibly use regmap_bulk_read(), but that feels a bit hacky too
> > since it isn't actually how that function was intended to be used.
> >
>
> Hmm I might be missing something but looking at the register map, It seems we do
> have 8bit registers? We do have values that span multiple registers (3 for the
> 24bit values) and regmap_bulk_read() should actually fit right? I mean, looking
> at the docs:
>
> "regmap_bulk_read() - Read multiple sequential registers from the device"
>
Isn't regmap_bulk_*() for reading a value spread in sequential registers,
like the offset calibration (registers 0x22, 0x23 and 0x24, 8 bits value
for each reg)? For the ADC data (0x2C) we have a 24 bits value in only one
register, so I beleive this does not apply.
> But I do agree that what we have right now does not make much sense. If we need
> to do
>
> len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
>
> for supporting regmap, then I have to question using it. Also note that we have
> things like gain and offset that are also 3 bytes which means that our custom
> read would need to become more questionable if we add support for it.
>
For those cases the regmap_bulk_*() works.
> Jonathan, did you tried to use plain regmap (without the custom bus)? Assuming
> bulk reads work, I'm not seeing an apparent reason for the custom bus... I would
> also suspect that if bulk reads don't work out of the box, providing a regmap
> cache would make it work but relying on implementation details is not a very
> good practice.
>
Yes, i tried and only works for the register with 8-bits value. David
suggested using regular spi_*() functions for the unsual registers with
24-bits value, such as the ADC data (0x2C). That is the only way of
having the default spi bus interface using regmap. Otherwise we should
drop the regmap.
> Anyways, I would try would normal regmap and if bulk reads don't work I would
> either:
>
> 1) Just do three regmap_reads() for 3byte values;
Also does not work.
> 2) Or do what David suggests and use normal spi_*() and forget about regmap.
>
Either that or using spi_*() only for ADC data and regmap for the rest
of the registers.
> Either way is fine to me.
>
> - Nuno Sá
> >
>
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-28 1:28 ` David Lechner
@ 2025-01-28 15:04 ` Jonathan Santos
2025-01-28 15:56 ` David Lechner
0 siblings, 1 reply; 56+ messages in thread
From: Jonathan Santos @ 2025-01-28 15:04 UTC (permalink / raw)
To: David Lechner
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, marcelo.schmitt1
On 01/27, David Lechner wrote:
> On 1/27/25 9:11 AM, Jonathan Santos wrote:
> > Add a new trigger-sources property to enable synchronization across
> > multiple devices. This property references the main device (or
> > trigger provider) responsible for generating the pulse to drive the
> > SYNC_IN of all devices in the setup.
> >
> > In addition to GPIO synchronization, The AD7768-1 also supports
> > synchronization over SPI, which use is recommended when the GPIO
> > cannot provide a pulse synchronous with the base MCLK signal. It
> > consists of looping back the SYNC_OUT to the SYNC_IN pin and send
> > a command via SPI to trigger the synchronization.
> >
> > SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
> > property. Since adi,sync-in-gpios is not long the only method, remove it
> > from required properties.
> >
> > While at it, add description to the interrupt property.
> >
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > ---
> > v2 Changes:
> > * Patch added as replacement for adi,sync-in-spi patch.
> > * addressed the request for a description to interrupts property.
> > ---
> > .../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
> > 1 file changed, 20 insertions(+), 2 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> > index 3ce59d4d065f..3e119cf1754b 100644
> > --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> > +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> > @@ -26,7 +26,17 @@ properties:
> > clock-names:
> > const: mclk
> >
> > + trigger-sources:
> > + description:
> > + References the main device responsible for synchronization. In a single
> > + device setup, reference the own node.
> > + maxItems: 1
>
> We probably actually need 2 here. One for /SYNC_IN and one for a GPIO3 pin
> acting as the /START signal.
>
> > +
> > interrupts:
> > + description:
> > + Specifies the interrupt line associated with the ADC. This refers
> > + to the DRDY (Data Ready) pin, which signals when conversion results are
> > + available.
> > maxItems: 1
> >
> > '#address-cells':
> > @@ -46,6 +56,8 @@ properties:
> > sampling. A pulse is always required if the configuration is changed
> > in any way, for example if the filter decimation rate changes.
> > As the line is active low, it should be marked GPIO_ACTIVE_LOW.
> > + In the absence of this property, Synchronization over SPI will be
> > + enabled.
>
> Isn't /SYNC_OUT connected to /SYNC_IN required for synchronization over SPI?
>
> If yes, instead of adding this text, I would make the binding have:
>
Yes, but the synchronization over SPI is enabled in the absence of the GPIO.
The trigger-sources property would indicate if the sync provider is the
own device or not. As i said below, maybe i misunderstood.
> oneOf:
> - required:
> - trigger-sources
> - required:
> - adi,sync-in-gpios
>
Wouldn't be simpler to consider the absence of sync-in-gpio? this way we
have less changes in the ABI.
> >
> > reset-gpios:
> > maxItems: 1
> > @@ -57,6 +69,9 @@ properties:
> > "#io-channel-cells":
> > const: 1
> >
> > + "#trigger-source-cells":
> > + const: 0
> > +
> > required:
> > - compatible
> > - reg
> > @@ -65,7 +80,8 @@ required:
> > - vref-supply
> > - spi-cpol
> > - spi-cpha
> > - - adi,sync-in-gpios
> > + - trigger-sources
> > + - #trigger-source-cells
> >
> > patternProperties:
> > "^channel@([0-9]|1[0-5])$":
> > @@ -99,7 +115,7 @@ examples:
> > #address-cells = <1>;
> > #size-cells = <0>;
> >
> > - adc@0 {
> > + adc0: adc@0 {
> > compatible = "adi,ad7768-1";
> > reg = <0>;
> > spi-max-frequency = <2000000>;
> > @@ -109,6 +125,8 @@ examples:
> > interrupts = <25 IRQ_TYPE_EDGE_RISING>;
> > interrupt-parent = <&gpio>;
> > adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
>
> Don't we need to drop adi,sync-in-gpios here? I don't think we would have two
> things connected to /SYNC_IN at the same time.
>
I guess i misunderstood the use of trigger-sources. I thought it would
indicate the trigger provider or main device. Like if it points to other
device we should use it to drive the SYNC_IN of all devices.
Then what happens if the trigger-sources points to other node? we would't be
able to driver the SYNC_IN in case of any configuration change?
> > + trigger-sources = <&adc0>;
> > + #trigger-source-cells = <0>;
> > reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
> > clocks = <&ad7768_mclk>;
> > clock-names = "mclk";
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-28 14:46 ` Jonathan Santos
@ 2025-01-28 15:09 ` Nuno Sá
2025-01-30 16:32 ` Jonathan Cameron
0 siblings, 1 reply; 56+ messages in thread
From: Nuno Sá @ 2025-01-28 15:09 UTC (permalink / raw)
To: 648eedbee0e7702eda10034531de4611597cd9f2.camel
Cc: David Lechner, Jonathan Santos, linux-iio, devicetree,
linux-kernel, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, marcelo.schmitt1
On Tue, 2025-01-28 at 11:46 -0300, Jonathan Santos wrote:
> On 01/28, Nuno Sá wrote:
> > On Mon, 2025-01-27 at 19:29 -0600, David Lechner wrote:
> > > On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > > > Convert the AD7768-1 driver to use the regmap API for register
> > > > access. This change simplifies and standardizes register interactions,
> > > > reducing code duplication and improving maintainability.
> > > >
> > > > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > > > ---
> > > > v2 Changes:
> > > > * New patch in v2.
> > > > ---
> > > > drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
> > > > 1 file changed, 58 insertions(+), 24 deletions(-)
> > > >
> > > > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > > > index 95ba89435652..fb8d6fae5f8a 100644
> > > > --- a/drivers/iio/adc/ad7768-1.c
> > > > +++ b/drivers/iio/adc/ad7768-1.c
> > > > @@ -12,6 +12,7 @@
> > > > #include <linux/gpio/consumer.h>
> > > > #include <linux/kernel.h>
> > > > #include <linux/module.h>
> > > > +#include <linux/regmap.h>
> > > > #include <linux/regulator/consumer.h>
> > > > #include <linux/sysfs.h>
> > > > #include <linux/spi/spi.h>
> > > > @@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[]
> > > > = {
> > > >
> > > > struct ad7768_state {
> > > > struct spi_device *spi;
> > > > + struct regmap *regmap;
> > > > struct regulator *vref;
> > > > struct mutex lock;
> > > > struct clk *mclk;
> > > > @@ -176,12 +178,17 @@ struct ad7768_state {
> > > > } data __aligned(IIO_DMA_MINALIGN);
> > > > };
> > > >
> > > > -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int
> > > > addr,
> > > > - unsigned int len)
> > > > +static int ad7768_spi_reg_read(void *context, unsigned int addr,
> > > > + unsigned int *val)
> > > > {
> > > > - unsigned int shift;
> > > > + struct iio_dev *dev = context;
> > > > + struct ad7768_state *st;
> > > > + unsigned int shift, len;
> > > > int ret;
> > > >
> > > > + st = iio_priv(dev);
> > >
> > > This can be combined with the variable declaration.
> > >
> > > > + /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
> > >
> > > Probably not currently needed but COEFF_DATA register is also 3 bytes.
> > >
> > > > + len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > > > shift = 32 - (8 * len);
> > > > st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
> > > >
> > > > @@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state
> > > > *st, unsigned int addr,
> > > > if (ret < 0)
> > > > return ret;
> > > >
> > > > - return (be32_to_cpu(st->data.d32) >> shift);
> > > > + *val = be32_to_cpu(st->data.d32) >> shift;
> > > > +
> > > > + return 0;
> > > > }
> > > >
> > > > -static int ad7768_spi_reg_write(struct ad7768_state *st,
> > > > +static int ad7768_spi_reg_write(void *context,
> > > > unsigned int addr,
> > > > unsigned int val)
> > > > {
> > > > + struct iio_dev *dev = context;
> > > > + struct ad7768_state *st;
> > > > +
> > > > + st = iio_priv(dev);
> > > > st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
> > > > st->data.d8[1] = val & 0xFF;
> > > >
> > > > @@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct
> > > > ad7768_state
> > > > *st,
> > > > static int ad7768_set_mode(struct ad7768_state *st,
> > > > enum ad7768_conv_mode mode)
> > > > {
> > > > - int regval;
> > > > + int regval, ret;
> > > >
> > > > - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
> > > > - if (regval < 0)
> > > > - return regval;
> > > > + ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > regval &= ~AD7768_CONV_MODE_MSK;
> > > > regval |= AD7768_CONV_MODE(mode);
> > > >
> > > > - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
> > > > + return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
> > > > }
> > > >
> > > > static int ad7768_scan_direct(struct iio_dev *indio_dev)
> > > > @@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev
> > > > *indio_dev)
> > > > if (!ret)
> > > > return -ETIMEDOUT;
> > > >
> > > > - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > > - if (readval < 0)
> > > > - return readval;
> > > > + ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > /*
> > > > * Any SPI configuration of the AD7768-1 can only be
> > > > * performed in continuous conversion mode.
> > > > @@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev
> > > > *indio_dev,
> > > >
> > > > mutex_lock(&st->lock);
> > > > if (readval) {
> > > > - ret = ad7768_spi_reg_read(st, reg, 1);
> > > > - if (ret < 0)
> > > > + ret = regmap_read(st->regmap, reg, readval);
> > > > + if (ret)
> > > > goto err_unlock;
> > >
> > > Can drop the if and goto.
> > >
> > > > - *readval = ret;
> > > > - ret = 0;
> > > > } else {
> > > > - ret = ad7768_spi_reg_write(st, reg, writeval);
> > > > + ret = regmap_write(st->regmap, reg, writeval);
> > > > }
> > > > err_unlock:
> > > > mutex_unlock(&st->lock);
> > > > @@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state
> > > > *st,
> > > > else
> > > > mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> > > >
> > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER,
> > > > mode);
> > > > + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER,
> > > > mode);
> > > > if (ret < 0)
> > > > return ret;
> > > >
> > > > @@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
> > > > */
> > > > pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div)
> > > > |
> > > > AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK,
> > > > pwr_mode);
> > > > + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK,
> > > > pwr_mode);
> > > > if (ret < 0)
> > > > return ret;
> > > >
> > > > @@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
> > > > * to 10. When the sequence is detected, the reset occurs.
> > > > * See the datasheet, page 70.
> > > > */
> > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
> > > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> > > > if (ret)
> > > > return ret;
> > > >
> > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
> > > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> > > > if (ret)
> > > > return ret;
> > > >
> > > > @@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev
> > > > *indio_dev)
> > > > * continuous read mode. Subsequent data reads do not require
> > > > an
> > > > * initial 8-bit write to query the ADC_DATA register.
> > > > */
> > > > - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT,
> > > > 0x01);
> > > > + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT,
> > > > 0x01);
> > > > }
> > > >
> > > > static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
> > > > {
> > > > struct ad7768_state *st = iio_priv(indio_dev);
> > > > + unsigned int regval;
> > >
> > > Intention could be more clear by calling this "unused". Otherwise, it can
> > > look
> > > like a bug if you don't fully understand what the comment below means.
> > >
> > > >
> > > > /*
> > > > * To exit continuous read mode, perform a single read of the
> > > > ADC_DATA
> > > > * reg (0x2C), which allows further configuration of the
> > > > device.
> > > > */
> > > > - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > > + return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
> > > > }
> > > >
> > > > static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
> > > > @@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev
> > > > *indio_dev,
> > > > return 0;
> > > > }
> > > >
> > > > +static const struct regmap_bus ad7768_regmap_bus = {
> > > > + .reg_write = ad7768_spi_reg_write,
> > > > + .reg_read = ad7768_spi_reg_read,
> > > > + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> > > > + .val_format_endian_default = REGMAP_ENDIAN_BIG,
> > >
> > > The bus read function is calling be32_to_cpu(), so we probably want to
> > > remove
> > > that or change the default here.
> > >
> > > > +};
> > > > +
> > > > +static const struct regmap_config ad7768_regmap_config = {
> > > > + .name = "ad7768-1",
> > > > + .reg_bits = 8,
> > > > + .val_bits = 8,
> > >
> > > Should this be 24 since the largest registers are 24-bit?
> > >
> > > Another option could be to just use a regular spi_*() API for that
> > > register
> > > instead of regmap_*() and avoid trying to do something that regmap doesn't
> > > really handle.
> > >
> > > Or we could possibly use regmap_bulk_read(), but that feels a bit hacky
> > > too
> > > since it isn't actually how that function was intended to be used.
> > >
> >
> > Hmm I might be missing something but looking at the register map, It seems
> > we do
> > have 8bit registers? We do have values that span multiple registers (3 for
> > the
> > 24bit values) and regmap_bulk_read() should actually fit right? I mean,
> > looking
> > at the docs:
> >
> > "regmap_bulk_read() - Read multiple sequential registers from the device"
> >
>
> Isn't regmap_bulk_*() for reading a value spread in sequential registers,
> like the offset calibration (registers 0x22, 0x23 and 0x24, 8 bits value
> for each reg)? For the ADC data (0x2C) we have a 24 bits value in only one
> register, so I beleive this does not apply.
>
Ah got it. I failed to see that. Yeah, in that case you're right... it won't
work out of the box.
> > But I do agree that what we have right now does not make much sense. If we
> > need
> > to do
> >
> > len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> >
> > for supporting regmap, then I have to question using it. Also note that we
> > have
> > things like gain and offset that are also 3 bytes which means that our
> > custom
> > read would need to become more questionable if we add support for it.
> >
>
> For those cases the regmap_bulk_*() works.
>
> > Jonathan, did you tried to use plain regmap (without the custom bus)?
> > Assuming
> > bulk reads work, I'm not seeing an apparent reason for the custom bus... I
> > would
> > also suspect that if bulk reads don't work out of the box, providing a
> > regmap
> > cache would make it work but relying on implementation details is not a very
> > good practice.
> >
>
> Yes, i tried and only works for the register with 8-bits value. David
> suggested using regular spi_*() functions for the unsual registers with
> 24-bits value, such as the ADC data (0x2C). That is the only way of
> having the default spi bus interface using regmap. Otherwise we should
> drop the regmap.
Yeah, might be better to do plain spi for the unusual registers rather than the
custom bus. But no strong feelings on my side...
- Nuno Sá
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-28 15:04 ` Jonathan Santos
@ 2025-01-28 15:56 ` David Lechner
2025-01-30 16:16 ` Jonathan Cameron
0 siblings, 1 reply; 56+ messages in thread
From: David Lechner @ 2025-01-28 15:56 UTC (permalink / raw)
To: 0044dd4b-01ce-4ca0-9855-8c239b9bfb6f
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, marcelo.schmitt1
On 1/28/25 9:04 AM, Jonathan Santos wrote:
> On 01/27, David Lechner wrote:
>> On 1/27/25 9:11 AM, Jonathan Santos wrote:
>>> Add a new trigger-sources property to enable synchronization across
>>> multiple devices. This property references the main device (or
>>> trigger provider) responsible for generating the pulse to drive the
>>> SYNC_IN of all devices in the setup.
>>>
>>> In addition to GPIO synchronization, The AD7768-1 also supports
>>> synchronization over SPI, which use is recommended when the GPIO
>>> cannot provide a pulse synchronous with the base MCLK signal. It
>>> consists of looping back the SYNC_OUT to the SYNC_IN pin and send
>>> a command via SPI to trigger the synchronization.
>>>
>>> SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
>>> property. Since adi,sync-in-gpios is not long the only method, remove it
>>> from required properties.
>>>
>>> While at it, add description to the interrupt property.
>>>
>>> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
>>> ---
>>> v2 Changes:
>>> * Patch added as replacement for adi,sync-in-spi patch.
>>> * addressed the request for a description to interrupts property.
>>> ---
>>> .../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
>>> 1 file changed, 20 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
>>> index 3ce59d4d065f..3e119cf1754b 100644
>>> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
>>> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
>>> @@ -26,7 +26,17 @@ properties:
>>> clock-names:
>>> const: mclk
>>>
>>> + trigger-sources:
>>> + description:
>>> + References the main device responsible for synchronization. In a single
>>> + device setup, reference the own node.
>>> + maxItems: 1
>>
>> We probably actually need 2 here. One for /SYNC_IN and one for a GPIO3 pin
>> acting as the /START signal.
>>
>>> +
>>> interrupts:
>>> + description:
>>> + Specifies the interrupt line associated with the ADC. This refers
>>> + to the DRDY (Data Ready) pin, which signals when conversion results are
>>> + available.
>>> maxItems: 1
>>>
>>> '#address-cells':
>>> @@ -46,6 +56,8 @@ properties:
>>> sampling. A pulse is always required if the configuration is changed
>>> in any way, for example if the filter decimation rate changes.
>>> As the line is active low, it should be marked GPIO_ACTIVE_LOW.
>>> + In the absence of this property, Synchronization over SPI will be
>>> + enabled.
>>
>> Isn't /SYNC_OUT connected to /SYNC_IN required for synchronization over SPI?
>>
>> If yes, instead of adding this text, I would make the binding have:
>>
>
> Yes, but the synchronization over SPI is enabled in the absence of the GPIO.
> The trigger-sources property would indicate if the sync provider is the
> own device or not. As i said below, maybe i misunderstood.
>
>> oneOf:
>> - required:
>> - trigger-sources
>> - required:
>> - adi,sync-in-gpios
>>
>
> Wouldn't be simpler to consider the absence of sync-in-gpio? this way we
> have less changes in the ABI.
Maybe it is me that missed something, but if I'm reading the datasheet
correctly, then sync over SPI only works if /SYNC_IN is wired to /SYNC_OUT.
And the chip isn't going to work correctly without some sort of sync. So we
need something wired to /SYNC_IN no matter what.
In any case, the DT bindings should just say how the chip is wired up and not
dictate how the driver should behave. So what I was going for with this is to
have the bindings say that something has to be wired to /SYNC_IN and we can
leave it up to the driver to decide what to do with this information.
>
>>>
>>> reset-gpios:
>>> maxItems: 1
>>> @@ -57,6 +69,9 @@ properties:
>>> "#io-channel-cells":
>>> const: 1
>>>
>>> + "#trigger-source-cells":
>>> + const: 0
>>> +
>>> required:
>>> - compatible
>>> - reg
>>> @@ -65,7 +80,8 @@ required:
>>> - vref-supply
>>> - spi-cpol
>>> - spi-cpha
>>> - - adi,sync-in-gpios
>>> + - trigger-sources
>>> + - #trigger-source-cells
>>>
>>> patternProperties:
>>> "^channel@([0-9]|1[0-5])$":
>>> @@ -99,7 +115,7 @@ examples:
>>> #address-cells = <1>;
>>> #size-cells = <0>;
>>>
>>> - adc@0 {
>>> + adc0: adc@0 {
>>> compatible = "adi,ad7768-1";
>>> reg = <0>;
>>> spi-max-frequency = <2000000>;
>>> @@ -109,6 +125,8 @@ examples:
>>> interrupts = <25 IRQ_TYPE_EDGE_RISING>;
>>> interrupt-parent = <&gpio>;
>>> adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
>>
>> Don't we need to drop adi,sync-in-gpios here? I don't think we would have two
>> things connected to /SYNC_IN at the same time.
>>
>
> I guess i misunderstood the use of trigger-sources. I thought it would
> indicate the trigger provider or main device. Like if it points to other
> device we should use it to drive the SYNC_IN of all devices.
>
> Then what happens if the trigger-sources points to other node? we would't be
> able to driver the SYNC_IN in case of any configuration change?
I think you understand the trigger-source bindings correctly.
The driver doesn't have to support everything that the DT bindings allow. This
series is big enough already, so we can defer figuring out how to implement
triggers other than the loopback case later. :-) We just want to make the DT
bindings as complete as we can now.
>
>>> + trigger-sources = <&adc0>;
>>> + #trigger-source-cells = <0>;
>>> reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
>>> clocks = <&ad7768_mclk>;
>>> clock-names = "mclk";
>>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property
2025-01-28 15:56 ` David Lechner
@ 2025-01-30 16:16 ` Jonathan Cameron
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-01-30 16:16 UTC (permalink / raw)
To: David Lechner
Cc: 0044dd4b-01ce-4ca0-9855-8c239b9bfb6f, Jonathan Santos, linux-iio,
devicetree, linux-kernel, lars, Michael.Hennerich,
marcelo.schmitt, jic23, robh, krzk+dt, conor+dt, marcelo.schmitt1
On Tue, 28 Jan 2025 09:56:37 -0600
David Lechner <dlechner@baylibre.com> wrote:
> On 1/28/25 9:04 AM, Jonathan Santos wrote:
> > On 01/27, David Lechner wrote:
> >> On 1/27/25 9:11 AM, Jonathan Santos wrote:
> >>> Add a new trigger-sources property to enable synchronization across
> >>> multiple devices. This property references the main device (or
> >>> trigger provider) responsible for generating the pulse to drive the
> >>> SYNC_IN of all devices in the setup.
> >>>
> >>> In addition to GPIO synchronization, The AD7768-1 also supports
> >>> synchronization over SPI, which use is recommended when the GPIO
> >>> cannot provide a pulse synchronous with the base MCLK signal. It
> >>> consists of looping back the SYNC_OUT to the SYNC_IN pin and send
> >>> a command via SPI to trigger the synchronization.
> >>>
> >>> SPI-based synchronization is enabled in the absence of adi,sync-in-gpios
> >>> property. Since adi,sync-in-gpios is not long the only method, remove it
> >>> from required properties.
> >>>
> >>> While at it, add description to the interrupt property.
> >>>
> >>> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> >>> ---
> >>> v2 Changes:
> >>> * Patch added as replacement for adi,sync-in-spi patch.
> >>> * addressed the request for a description to interrupts property.
> >>> ---
> >>> .../bindings/iio/adc/adi,ad7768-1.yaml | 22 +++++++++++++++++--
> >>> 1 file changed, 20 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> >>> index 3ce59d4d065f..3e119cf1754b 100644
> >>> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> >>> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> >>> @@ -26,7 +26,17 @@ properties:
> >>> clock-names:
> >>> const: mclk
> >>>
> >>> + trigger-sources:
> >>> + description:
> >>> + References the main device responsible for synchronization. In a single
> >>> + device setup, reference the own node.
> >>> + maxItems: 1
> >>
> >> We probably actually need 2 here. One for /SYNC_IN and one for a GPIO3 pin
> >> acting as the /START signal.
> >>
> >>> +
> >>> interrupts:
> >>> + description:
> >>> + Specifies the interrupt line associated with the ADC. This refers
> >>> + to the DRDY (Data Ready) pin, which signals when conversion results are
> >>> + available.
> >>> maxItems: 1
> >>>
> >>> '#address-cells':
> >>> @@ -46,6 +56,8 @@ properties:
> >>> sampling. A pulse is always required if the configuration is changed
> >>> in any way, for example if the filter decimation rate changes.
> >>> As the line is active low, it should be marked GPIO_ACTIVE_LOW.
> >>> + In the absence of this property, Synchronization over SPI will be
> >>> + enabled.
> >>
> >> Isn't /SYNC_OUT connected to /SYNC_IN required for synchronization over SPI?
> >>
> >> If yes, instead of adding this text, I would make the binding have:
> >>
> >
> > Yes, but the synchronization over SPI is enabled in the absence of the GPIO.
> > The trigger-sources property would indicate if the sync provider is the
> > own device or not. As i said below, maybe i misunderstood.
> >
> >> oneOf:
> >> - required:
> >> - trigger-sources
> >> - required:
> >> - adi,sync-in-gpios
> >>
> >
> > Wouldn't be simpler to consider the absence of sync-in-gpio? this way we
> > have less changes in the ABI.
>
> Maybe it is me that missed something, but if I'm reading the datasheet
> correctly, then sync over SPI only works if /SYNC_IN is wired to /SYNC_OUT.
> And the chip isn't going to work correctly without some sort of sync. So we
> need something wired to /SYNC_IN no matter what.
>
> In any case, the DT bindings should just say how the chip is wired up and not
> dictate how the driver should behave. So what I was going for with this is to
> have the bindings say that something has to be wired to /SYNC_IN and we can
> leave it up to the driver to decide what to do with this information.
I was seeing this more like an optional clock / supply etc.
If we don't provide it we assume the driver knows what to do.
So in this case lack of sync in routing off to another device means
to me it is connected to sync out.
I don't mind the self trigger thing if it fits nicely in the binding
though as we are using a trigger that is provided for other chips anyway.
So wired to sync in of this chip and potentially sync in of another
N chips.
>
> >
> >>>
> >>> reset-gpios:
> >>> maxItems: 1
> >>> @@ -57,6 +69,9 @@ properties:
> >>> "#io-channel-cells":
> >>> const: 1
> >>>
> >>> + "#trigger-source-cells":
> >>> + const: 0
> >>> +
> >>> required:
> >>> - compatible
> >>> - reg
> >>> @@ -65,7 +80,8 @@ required:
> >>> - vref-supply
> >>> - spi-cpol
> >>> - spi-cpha
> >>> - - adi,sync-in-gpios
> >>> + - trigger-sources
> >>> + - #trigger-source-cells
> >>>
> >>> patternProperties:
> >>> "^channel@([0-9]|1[0-5])$":
> >>> @@ -99,7 +115,7 @@ examples:
> >>> #address-cells = <1>;
> >>> #size-cells = <0>;
> >>>
> >>> - adc@0 {
> >>> + adc0: adc@0 {
> >>> compatible = "adi,ad7768-1";
> >>> reg = <0>;
> >>> spi-max-frequency = <2000000>;
> >>> @@ -109,6 +125,8 @@ examples:
> >>> interrupts = <25 IRQ_TYPE_EDGE_RISING>;
> >>> interrupt-parent = <&gpio>;
> >>> adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
> >>
> >> Don't we need to drop adi,sync-in-gpios here? I don't think we would have two
> >> things connected to /SYNC_IN at the same time.
> >>
> >
> > I guess i misunderstood the use of trigger-sources. I thought it would
> > indicate the trigger provider or main device. Like if it points to other
> > device we should use it to drive the SYNC_IN of all devices.
> >
> > Then what happens if the trigger-sources points to other node? we would't be
> > able to driver the SYNC_IN in case of any configuration change?
>
> I think you understand the trigger-source bindings correctly.
>
> The driver doesn't have to support everything that the DT bindings allow. This
> series is big enough already, so we can defer figuring out how to implement
> triggers other than the loopback case later. :-) We just want to make the DT
> bindings as complete as we can now.
>
> >
> >>> + trigger-sources = <&adc0>;
> >>> + #trigger-source-cells = <0>;
> >>> reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
> >>> clocks = <&ad7768_mclk>;
> >>> clock-names = "mclk";
> >>
>
>
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property
2025-01-28 1:28 ` David Lechner
@ 2025-01-30 16:21 ` Jonathan Cameron
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-01-30 16:21 UTC (permalink / raw)
To: David Lechner
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On Mon, 27 Jan 2025 19:28:10 -0600
David Lechner <dlechner@baylibre.com> wrote:
> On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > The AD7768-1 provides a buffered common-mode voltage output
> > on the VCM pin that can be used to bias analog input signals.
> >
> > Add adi,vcm-output to enable the configuration of the VCM output
> > circuit.
> >
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > ---
> > v2 Changes:
> > * New patch in v2.
> > ---
> > .../bindings/iio/adc/adi,ad7768-1.yaml | 10 ++++++++++
> > include/dt-bindings/iio/adc/adi,ad7768-1.h | 16 ++++++++++++++++
> > 2 files changed, 26 insertions(+)
> > create mode 100644 include/dt-bindings/iio/adc/adi,ad7768-1.h
> >
> > diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> > index da05c8448530..e26513a9469b 100644
> > --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> > +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
> > @@ -59,6 +59,15 @@ properties:
> > In the absence of this property, Synchronization over SPI will be
> > enabled.
> >
> > + adi,vcm-output:
> > + description: |
> > + Configures the Common-Mode Voltage Output. The VCM is provided by an
> > + amplifier external to the AD7768-1 and can be used as common-mode voltage
> > + by the ADC. There are 8 output voltage options available, and the macros
> > + for these values can be found at dt-bindings/iio/adi,ad7768-1.h
> > + items:
> > + enum: [0, 1, 2, 3, 4, 5, 6, 7]
> > +
>
> I was expecting this to use regulator provider bindings rather than using a
> custom property. Then the regulator consumer could request the voltage that
> they need. But maybe that is more complicated than what is practical.
If we need to represent the analog front end (likely we do as will affect scaling
etc) then regulator makes sense. That front end will need a driver. Perhaps
an extension of drivers/iio/afe/iio-rescale.c
>
> If we don't need regulator bindings, then this should be vcm-microvolt to use
> standard units [1].
>
> [1]: https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/property-units.yaml
>
> > reset-gpios:
> > maxItems: 1
> >
> > @@ -132,6 +141,7 @@ examples:
> > gpio-controller;
> > #gpio-cells = <2>;
> > vref-supply = <&adc_vref>;
> > + adi,vcm-output = <AD7768_VCM_OUTPUT_AVDD1_AVSS_2>;
> > interrupts = <25 IRQ_TYPE_EDGE_RISING>;
> > interrupt-parent = <&gpio>;
> > adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
> > diff --git a/include/dt-bindings/iio/adc/adi,ad7768-1.h b/include/dt-bindings/iio/adc/adi,ad7768-1.h
> > new file mode 100644
> > index 000000000000..469ea724c0d5
> > --- /dev/null
> > +++ b/include/dt-bindings/iio/adc/adi,ad7768-1.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> > +
> > +#ifndef _DT_BINDINGS_ADI_AD7768_1_H
> > +#define _DT_BINDINGS_ADI_AD7768_1_H
> > +
> > +/* Sets VCM output to (AVDD1 − AVSS)/2 */
> > +#define AD7768_VCM_OUTPUT_AVDD1_AVSS_2 0x00
> > +#define AD7768_VCM_OUTPUT_2_5V 0x01
> > +#define AD7768_VCM_OUTPUT_2_05V 0x02
> > +#define AD7768_VCM_OUTPUT_1_9V 0x03
> > +#define AD7768_VCM_OUTPUT_1_65V 0x04
> > +#define AD7768_VCM_OUTPUT_1_1V 0x05
> > +#define AD7768_VCM_OUTPUT_0_9V 0x06
> > +#define AD7768_VCM_OUTPUT_OFF 0x07
> > +
> > +#endif /* _DT_BINDINGS_ADI_AD7768_1_H */
>
>
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio
2025-01-28 1:32 ` David Lechner
@ 2025-01-30 16:29 ` Jonathan Cameron
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-01-30 16:29 UTC (permalink / raw)
To: David Lechner
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns, marcelo.schmitt1
On Mon, 27 Jan 2025 19:32:45 -0600
David Lechner <dlechner@baylibre.com> wrote:
> On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > The Wideband Low Ripple filter is used for AD7768-1 Driver.
> > Document wideband filter option into filter_type_available
> > attribute.
> >
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > ---
> > v2 Changes:
> > * Removed FIR mentions.
> > ---
> > Documentation/ABI/testing/sysfs-bus-iio | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > index f83bd6829285..9b879e7732cd 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -2291,6 +2291,8 @@ Description:
> > * "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
> > * "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
> > * "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
> > + * "wideband" - filter with wideband low ripple passband
> > + and sharp transition band.
> >
> > What: /sys/.../events/in_proximity_thresh_either_runningperiod
> > KernelVersion: 6.6
>
> I'm a bit shy to make any more suggestions on this one since my previous
> suggestions were clearly not the "right way". :-)
>
> But, the key takeaway I got from the discussion on v1 is that this describes the
> _shape_ of the filter. To me, "wideband" describes the size but not the shape.
> Would rectangular be the correct shape?
>
It's tricky. The filter definition is really vague! No filter is ever rectangular.
Also, I've just noticed. This one is fully programable. It could be anything!
I kind of hope no one cares about that feature as it looks really tricky to support.
Wideband normally means 'flattish to a wide range of frequencies'. Not sure
how best to convey that.
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-28 15:09 ` Nuno Sá
@ 2025-01-30 16:32 ` Jonathan Cameron
2025-02-03 11:44 ` Jonathan Santos
0 siblings, 1 reply; 56+ messages in thread
From: Jonathan Cameron @ 2025-01-30 16:32 UTC (permalink / raw)
To: Nuno Sá
Cc: 648eedbee0e7702eda10034531de4611597cd9f2.camel, David Lechner,
Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, marcelo.schmitt1
On Tue, 28 Jan 2025 15:09:36 +0000
Nuno Sá <noname.nuno@gmail.com> wrote:
> On Tue, 2025-01-28 at 11:46 -0300, Jonathan Santos wrote:
> > On 01/28, Nuno Sá wrote:
> > > On Mon, 2025-01-27 at 19:29 -0600, David Lechner wrote:
> > > > On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > > > > Convert the AD7768-1 driver to use the regmap API for register
> > > > > access. This change simplifies and standardizes register interactions,
> > > > > reducing code duplication and improving maintainability.
> > > > >
> > > > > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > > > > ---
> > > > > v2 Changes:
> > > > > * New patch in v2.
> > > > > ---
> > > > > drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
> > > > > 1 file changed, 58 insertions(+), 24 deletions(-)
> > > > >
> > > > > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > > > > index 95ba89435652..fb8d6fae5f8a 100644
> > > > > --- a/drivers/iio/adc/ad7768-1.c
> > > > > +++ b/drivers/iio/adc/ad7768-1.c
> > > > > @@ -12,6 +12,7 @@
> > > > > #include <linux/gpio/consumer.h>
> > > > > #include <linux/kernel.h>
> > > > > #include <linux/module.h>
> > > > > +#include <linux/regmap.h>
> > > > > #include <linux/regulator/consumer.h>
> > > > > #include <linux/sysfs.h>
> > > > > #include <linux/spi/spi.h>
> > > > > @@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[]
> > > > > = {
> > > > >
> > > > > struct ad7768_state {
> > > > > struct spi_device *spi;
> > > > > + struct regmap *regmap;
> > > > > struct regulator *vref;
> > > > > struct mutex lock;
> > > > > struct clk *mclk;
> > > > > @@ -176,12 +178,17 @@ struct ad7768_state {
> > > > > } data __aligned(IIO_DMA_MINALIGN);
> > > > > };
> > > > >
> > > > > -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int
> > > > > addr,
> > > > > - unsigned int len)
> > > > > +static int ad7768_spi_reg_read(void *context, unsigned int addr,
> > > > > + unsigned int *val)
> > > > > {
> > > > > - unsigned int shift;
> > > > > + struct iio_dev *dev = context;
> > > > > + struct ad7768_state *st;
> > > > > + unsigned int shift, len;
> > > > > int ret;
> > > > >
> > > > > + st = iio_priv(dev);
> > > >
> > > > This can be combined with the variable declaration.
> > > >
> > > > > + /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
> > > >
> > > > Probably not currently needed but COEFF_DATA register is also 3 bytes.
> > > >
> > > > > + len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > > > > shift = 32 - (8 * len);
> > > > > st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
> > > > >
> > > > > @@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state
> > > > > *st, unsigned int addr,
> > > > > if (ret < 0)
> > > > > return ret;
> > > > >
> > > > > - return (be32_to_cpu(st->data.d32) >> shift);
> > > > > + *val = be32_to_cpu(st->data.d32) >> shift;
> > > > > +
> > > > > + return 0;
> > > > > }
> > > > >
> > > > > -static int ad7768_spi_reg_write(struct ad7768_state *st,
> > > > > +static int ad7768_spi_reg_write(void *context,
> > > > > unsigned int addr,
> > > > > unsigned int val)
> > > > > {
> > > > > + struct iio_dev *dev = context;
> > > > > + struct ad7768_state *st;
> > > > > +
> > > > > + st = iio_priv(dev);
> > > > > st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
> > > > > st->data.d8[1] = val & 0xFF;
> > > > >
> > > > > @@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct
> > > > > ad7768_state
> > > > > *st,
> > > > > static int ad7768_set_mode(struct ad7768_state *st,
> > > > > enum ad7768_conv_mode mode)
> > > > > {
> > > > > - int regval;
> > > > > + int regval, ret;
> > > > >
> > > > > - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
> > > > > - if (regval < 0)
> > > > > - return regval;
> > > > > + ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
> > > > > + if (ret)
> > > > > + return ret;
> > > > >
> > > > > regval &= ~AD7768_CONV_MODE_MSK;
> > > > > regval |= AD7768_CONV_MODE(mode);
> > > > >
> > > > > - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
> > > > > + return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
> > > > > }
> > > > >
> > > > > static int ad7768_scan_direct(struct iio_dev *indio_dev)
> > > > > @@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev
> > > > > *indio_dev)
> > > > > if (!ret)
> > > > > return -ETIMEDOUT;
> > > > >
> > > > > - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > > > - if (readval < 0)
> > > > > - return readval;
> > > > > + ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > /*
> > > > > * Any SPI configuration of the AD7768-1 can only be
> > > > > * performed in continuous conversion mode.
> > > > > @@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev
> > > > > *indio_dev,
> > > > >
> > > > > mutex_lock(&st->lock);
> > > > > if (readval) {
> > > > > - ret = ad7768_spi_reg_read(st, reg, 1);
> > > > > - if (ret < 0)
> > > > > + ret = regmap_read(st->regmap, reg, readval);
> > > > > + if (ret)
> > > > > goto err_unlock;
> > > >
> > > > Can drop the if and goto.
> > > >
> > > > > - *readval = ret;
> > > > > - ret = 0;
> > > > > } else {
> > > > > - ret = ad7768_spi_reg_write(st, reg, writeval);
> > > > > + ret = regmap_write(st->regmap, reg, writeval);
> > > > > }
> > > > > err_unlock:
> > > > > mutex_unlock(&st->lock);
> > > > > @@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state
> > > > > *st,
> > > > > else
> > > > > mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> > > > >
> > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER,
> > > > > mode);
> > > > > + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER,
> > > > > mode);
> > > > > if (ret < 0)
> > > > > return ret;
> > > > >
> > > > > @@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
> > > > > */
> > > > > pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div)
> > > > > |
> > > > > AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK,
> > > > > pwr_mode);
> > > > > + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK,
> > > > > pwr_mode);
> > > > > if (ret < 0)
> > > > > return ret;
> > > > >
> > > > > @@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
> > > > > * to 10. When the sequence is detected, the reset occurs.
> > > > > * See the datasheet, page 70.
> > > > > */
> > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
> > > > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> > > > > if (ret)
> > > > > return ret;
> > > > >
> > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
> > > > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> > > > > if (ret)
> > > > > return ret;
> > > > >
> > > > > @@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev
> > > > > *indio_dev)
> > > > > * continuous read mode. Subsequent data reads do not require
> > > > > an
> > > > > * initial 8-bit write to query the ADC_DATA register.
> > > > > */
> > > > > - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT,
> > > > > 0x01);
> > > > > + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT,
> > > > > 0x01);
> > > > > }
> > > > >
> > > > > static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
> > > > > {
> > > > > struct ad7768_state *st = iio_priv(indio_dev);
> > > > > + unsigned int regval;
> > > >
> > > > Intention could be more clear by calling this "unused". Otherwise, it can
> > > > look
> > > > like a bug if you don't fully understand what the comment below means.
> > > >
> > > > >
> > > > > /*
> > > > > * To exit continuous read mode, perform a single read of the
> > > > > ADC_DATA
> > > > > * reg (0x2C), which allows further configuration of the
> > > > > device.
> > > > > */
> > > > > - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > > > + return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
> > > > > }
> > > > >
> > > > > static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
> > > > > @@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev
> > > > > *indio_dev,
> > > > > return 0;
> > > > > }
> > > > >
> > > > > +static const struct regmap_bus ad7768_regmap_bus = {
> > > > > + .reg_write = ad7768_spi_reg_write,
> > > > > + .reg_read = ad7768_spi_reg_read,
> > > > > + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> > > > > + .val_format_endian_default = REGMAP_ENDIAN_BIG,
> > > >
> > > > The bus read function is calling be32_to_cpu(), so we probably want to
> > > > remove
> > > > that or change the default here.
> > > >
> > > > > +};
> > > > > +
> > > > > +static const struct regmap_config ad7768_regmap_config = {
> > > > > + .name = "ad7768-1",
> > > > > + .reg_bits = 8,
> > > > > + .val_bits = 8,
> > > >
> > > > Should this be 24 since the largest registers are 24-bit?
> > > >
> > > > Another option could be to just use a regular spi_*() API for that
> > > > register
> > > > instead of regmap_*() and avoid trying to do something that regmap doesn't
> > > > really handle.
> > > >
> > > > Or we could possibly use regmap_bulk_read(), but that feels a bit hacky
> > > > too
> > > > since it isn't actually how that function was intended to be used.
> > > >
> > >
> > > Hmm I might be missing something but looking at the register map, It seems
> > > we do
> > > have 8bit registers? We do have values that span multiple registers (3 for
> > > the
> > > 24bit values) and regmap_bulk_read() should actually fit right? I mean,
> > > looking
> > > at the docs:
> > >
> > > "regmap_bulk_read() - Read multiple sequential registers from the device"
> > >
> >
> > Isn't regmap_bulk_*() for reading a value spread in sequential registers,
> > like the offset calibration (registers 0x22, 0x23 and 0x24, 8 bits value
> > for each reg)? For the ADC data (0x2C) we have a 24 bits value in only one
> > register, so I beleive this does not apply.
> >
>
> Ah got it. I failed to see that. Yeah, in that case you're right... it won't
> work out of the box.
>
> > > But I do agree that what we have right now does not make much sense. If we
> > > need
> > > to do
> > >
> > > len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > >
> > > for supporting regmap, then I have to question using it. Also note that we
> > > have
> > > things like gain and offset that are also 3 bytes which means that our
> > > custom
> > > read would need to become more questionable if we add support for it.
> > >
> >
> > For those cases the regmap_bulk_*() works.
> >
> > > Jonathan, did you tried to use plain regmap (without the custom bus)?
> > > Assuming
> > > bulk reads work, I'm not seeing an apparent reason for the custom bus... I
> > > would
> > > also suspect that if bulk reads don't work out of the box, providing a
> > > regmap
> > > cache would make it work but relying on implementation details is not a very
> > > good practice.
> > >
> >
> > Yes, i tried and only works for the register with 8-bits value. David
> > suggested using regular spi_*() functions for the unsual registers with
> > 24-bits value, such as the ADC data (0x2C). That is the only way of
> > having the default spi bus interface using regmap. Otherwise we should
> > drop the regmap.
>
> Yeah, might be better to do plain spi for the unusual registers rather than the
> custom bus. But no strong feelings on my side...
I'm not keen on a mix or on a regmap that handles the size under the hood.
I think this quirk should be obvious.
Can we do two regmap, one for each of the register size? We have other
drivers taking that approach and I'm not sure if it was ruled out for
this one.
Jonathan
>
> - Nuno Sá
> >
>
>
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes
2025-01-27 15:14 ` [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes Jonathan Santos
2025-01-28 1:24 ` David Lechner
@ 2025-01-30 16:39 ` Jonathan Cameron
1 sibling, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-01-30 16:39 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, lars, Michael.Hennerich,
marcelo.schmitt, jic23, robh, krzk+dt, conor+dt, jonath4nns,
marcelo.schmitt1, PopPaul2021
On Mon, 27 Jan 2025 12:14:19 -0300
Jonathan Santos <Jonathan.Santos@analog.com> wrote:
> Separate filter type and decimation rate from the sampling frequency
> attribute. The new filter type attribute enables SINC3 and WIDEBAND
> filters, which were previously unavailable.
>
> Previously, combining decimation and MCLK divider in the sampling
> frequency obscured performance trade-offs. Lower MCLK divider
> settings increase power usage, while lower decimation rates reduce
> precision by decreasing averaging. By creating an oversampling
> attribute, which controls the decimation, users gain finer control
> over performance.
>
> The addition of those attributes allows a wider range of sampling
> frequencies and more access to the device features.
>
> Co-developed-by: PopPaul2021 <paul.pop@analog.com>
> Signed-off-by: PopPaul2021 <paul.pop@analog.com>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
On trivial thing I noticed.
> @@ -745,6 +1000,12 @@ static int ad7768_setup(struct iio_dev *indio_dev)
> return ret;
> }
>
> + /*
> + * Set Default Digital Filter configuration:
> + * SINC5 filter with x32 Decimation rate
> + */
> + ret = ad7768_configure_dig_fil(indio_dev, SINC5, 32);
Check ret?
> +
> /* Set the default sampling frequency to 32000 kSPS */
> return ad7768_set_freq(st, 32000);
> }
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign
2025-01-27 15:11 ` [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign Jonathan Santos
@ 2025-02-01 15:27 ` Jonathan Cameron
2025-02-01 15:36 ` Jonathan Cameron
1 sibling, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-02-01 15:27 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, Sergiu Cuciurean, lars,
Michael.Hennerich, marcelo.schmitt, robh, krzk+dt, conor+dt,
jonath4nns, marcelo.schmitt1, David Lechner
On Mon, 27 Jan 2025 12:11:01 -0300
Jonathan Santos <Jonathan.Santos@analog.com> wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> The ad7768-1 ADC output code is two's complement, meaning that the voltage
> conversion result is a signed value.. Since the value is a 24 bit one,
> stored in a 32 bit variable, the sign should be extended in order to get
> the correct representation.
>
> Also the channel description has been updated to signed representation,
> to match the ADC specifications.
>
> Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support")
> Reviewed-by: David Lechner <dlechner@baylibre.com>
> Reviewed-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> ---
> v2 Changes:
> * Patch moved to the start of the patch series.
Applied to the fixes-togreg branch of iio.git.
Thanks,
Jonathan
> ---
> drivers/iio/adc/ad7768-1.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 113703fb7245..c3cf04311c40 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -142,7 +142,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
> .channel = 0,
> .scan_index = 0,
> .scan_type = {
> - .sign = 'u',
> + .sign = 's',
> .realbits = 24,
> .storagebits = 32,
> .shift = 8,
> @@ -371,7 +371,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
>
> ret = ad7768_scan_direct(indio_dev);
> if (ret >= 0)
> - *val = ret;
> + *val = sign_extend32(ret, chan->scan_type.realbits - 1);
>
> iio_device_release_direct_mode(indio_dev);
> if (ret < 0)
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset
2025-01-27 15:12 ` [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset Jonathan Santos
@ 2025-02-01 15:31 ` Jonathan Cameron
2025-02-03 11:34 ` Jonathan Santos
0 siblings, 1 reply; 56+ messages in thread
From: Jonathan Cameron @ 2025-02-01 15:31 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, lars, Michael.Hennerich,
marcelo.schmitt, robh, krzk+dt, conor+dt, jonath4nns,
marcelo.schmitt1
On Mon, 27 Jan 2025 12:12:29 -0300
Jonathan Santos <Jonathan.Santos@analog.com> wrote:
> Datasheet recommends Setting the MOSI idle state to high in order to
> prevent accidental reset of the device when SCLK is free running.
> This happens when the controller clocks out a 1 followed by 63 zeros
> while the CS is held low.
>
> Check if SPI controller supports SPI_MOSI_IDLE_HIGH flag and set it.
>
> Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support")
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
Ideally drag fix to start of patch set to make it obvious it can be
picked up without the rest. I'm not grabbing it yet as doesn't
feel like it has enough review yet.
> ---
> v2 Changes:
> * Only setup SPI_MOSI_IDLE_HIGH flag if the controller supports it, otherwise the driver
> continues the same. I realized that using bits_per_word does not avoid the problem that
> MOSI idle state is trying to solve. If the controller drives the MOSI low between bytes
> during a transfer, nothing happens.
> ---
> drivers/iio/adc/ad7768-1.c | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index c3cf04311c40..95ba89435652 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -574,6 +574,22 @@ static int ad7768_probe(struct spi_device *spi)
> return -ENOMEM;
>
> st = iio_priv(indio_dev);
> + /*
> + * Datasheet recommends SDI line to be kept high when
> + * data is not being clocked out of the controller
Very short line wrap. Go up to 80 chars. If this picks up
some review, I can fix that whilst applying. If not do it for
your v3.
> + * and the spi clock is free running, to prevent
> + * accidental reset.
> + * Since many controllers do not support the
> + * SPI_MOSI_IDLE_HIGH flag yet, only request the MOSI
> + * idle state to enable if the controller supports it.
> + */
> + if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) {
> + spi->mode |= SPI_MOSI_IDLE_HIGH;
> + ret = spi_setup(spi);
> + if (ret < 0)
> + return ret;
> + }
> +
> st->spi = spi;
>
> st->vref = devm_regulator_get(&spi->dev, "vref");
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function
2025-01-27 15:13 ` [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function Jonathan Santos
@ 2025-02-01 15:35 ` Jonathan Cameron
2025-02-03 12:03 ` Jonathan Santos
0 siblings, 1 reply; 56+ messages in thread
From: Jonathan Cameron @ 2025-02-01 15:35 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, Sergiu Cuciurean, lars,
Michael.Hennerich, marcelo.schmitt, robh, krzk+dt, conor+dt,
jonath4nns, marcelo.schmitt1
On Mon, 27 Jan 2025 12:13:19 -0300
Jonathan Santos <Jonathan.Santos@analog.com> wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> This change moves the buffer allocation in a separate function, making
> space for adding another type of iio buffer if needed.
>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Jonathan, this one needs your sign off to reflect that you handled
the patch as part of it's path to upstream. I can't apply
anything that is missing such SoB.
> ---
> v2 Changes:
> * Interrupt and completion moved out from ad7768_triggered_buffer_alloc().
> ---
> drivers/iio/adc/ad7768-1.c | 44 ++++++++++++++++++++++----------------
> 1 file changed, 26 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 5e2093be9b92..8487b9a06609 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -599,6 +599,31 @@ static const struct regmap_config ad7768_regmap_config = {
> .max_register = AD7768_REG_MCLK_COUNTER,
> };
>
> +static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev)
> +{
> + struct ad7768_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d",
> + indio_dev->name,
> + iio_device_id(indio_dev));
> + if (!st->trig)
> + return -ENOMEM;
> +
> + st->trig->ops = &ad7768_trigger_ops;
> + iio_trigger_set_drvdata(st->trig, indio_dev);
> + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig);
> + if (ret)
> + return ret;
> +
> + indio_dev->trig = iio_trigger_get(st->trig);
> +
> + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
> + &iio_pollfunc_store_time,
> + &ad7768_trigger_handler,
> + &ad7768_buffer_ops);
> +}
> +
> static int ad7768_probe(struct spi_device *spi)
> {
> struct ad7768_state *st;
> @@ -669,20 +694,6 @@ static int ad7768_probe(struct spi_device *spi)
> return ret;
> }
>
> - st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
> - indio_dev->name,
> - iio_device_id(indio_dev));
> - if (!st->trig)
> - return -ENOMEM;
> -
> - st->trig->ops = &ad7768_trigger_ops;
> - iio_trigger_set_drvdata(st->trig, indio_dev);
> - ret = devm_iio_trigger_register(&spi->dev, st->trig);
> - if (ret)
> - return ret;
> -
> - indio_dev->trig = iio_trigger_get(st->trig);
> -
> init_completion(&st->completion);
>
> ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels));
> @@ -696,10 +707,7 @@ static int ad7768_probe(struct spi_device *spi)
> if (ret)
> return ret;
>
> - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
> - &iio_pollfunc_store_time,
> - &ad7768_trigger_handler,
> - &ad7768_buffer_ops);
> + ret = ad7768_triggered_buffer_alloc(indio_dev);
> if (ret)
> return ret;
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign
2025-01-27 15:11 ` [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign Jonathan Santos
2025-02-01 15:27 ` Jonathan Cameron
@ 2025-02-01 15:36 ` Jonathan Cameron
1 sibling, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-02-01 15:36 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, Sergiu Cuciurean, lars,
Michael.Hennerich, marcelo.schmitt, robh, krzk+dt, conor+dt,
jonath4nns, marcelo.schmitt1, David Lechner
On Mon, 27 Jan 2025 12:11:01 -0300
Jonathan Santos <Jonathan.Santos@analog.com> wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> The ad7768-1 ADC output code is two's complement, meaning that the voltage
> conversion result is a signed value.. Since the value is a 24 bit one,
> stored in a 32 bit variable, the sign should be extended in order to get
> the correct representation.
>
> Also the channel description has been updated to signed representation,
> to match the ADC specifications.
>
> Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support")
> Reviewed-by: David Lechner <dlechner@baylibre.com>
> Reviewed-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Dropped. Jonathan, this is missing your sign off.
> ---
> v2 Changes:
> * Patch moved to the start of the patch series.
> ---
> drivers/iio/adc/ad7768-1.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 113703fb7245..c3cf04311c40 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -142,7 +142,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
> .channel = 0,
> .scan_index = 0,
> .scan_type = {
> - .sign = 'u',
> + .sign = 's',
> .realbits = 24,
> .storagebits = 32,
> .shift = 8,
> @@ -371,7 +371,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
>
> ret = ad7768_scan_direct(indio_dev);
> if (ret >= 0)
> - *val = ret;
> + *val = sign_extend32(ret, chan->scan_type.realbits - 1);
>
> iio_device_release_direct_mode(indio_dev);
> if (ret < 0)
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support
2025-01-27 15:13 ` [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support Jonathan Santos
2025-01-27 23:34 ` David Lechner
@ 2025-02-01 15:50 ` Jonathan Cameron
1 sibling, 0 replies; 56+ messages in thread
From: Jonathan Cameron @ 2025-02-01 15:50 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, Sergiu Cuciurean, lars,
Michael.Hennerich, marcelo.schmitt, robh, krzk+dt, conor+dt,
jonath4nns, marcelo.schmitt1
On Mon, 27 Jan 2025 12:13:45 -0300
Jonathan Santos <Jonathan.Santos@analog.com> wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> The AD7768-1 has the ability to control other local hardware (such as gain
> stages),to power down other blocks in the signal chain, or read local
> status signals over the SPI interface.
>
> This change exports the AD7768-1's four gpios and makes them accessible
> at an upper layer.
>
> Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
As David observed, the direct mode release calls are missing.
Also, there are a few places where you wrap lines much shorter than needed.
> +static int ad7768_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(st->regmap,
> + AD7768_REG_GPIO_CONTROL,
> + BIT(offset),
> + AD7768_GPIO_OUTPUT(offset));
Again, wrap less.
> +}
> +
> +static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + unsigned int val;
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
You aren't releasing it. That should have deadlocked the second time
you called this.
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & BIT(offset))
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val);
> + else
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val);
> + if (ret < 0)
> + return ret;
> +
> + return !!(val & BIT(offset));
> +}
> +
> +static void ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
> +{
> + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> + struct ad7768_state *st = iio_priv(indio_dev);
> + unsigned int val;
> + int ret;
> +
> + ret = iio_device_claim_direct_mode(indio_dev);
As above, needs a matching release. May mean you want to factor
out the guts of this as a helper function so you can still do
direct returns on error.
> + if (ret)
> + return;
> +
> + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
> + if (ret < 0)
> + return;
> +
> + if (val & BIT(offset))
> + regmap_update_bits(st->regmap,
> + AD7768_REG_GPIO_WRITE,
> + BIT(offset),
> + (value << offset));
Wrap a little less and drop the unnecessary brackets.
regmap_update_bits(st->regmap, AD7768_REG_GPIO_WRITE,
BIT(offset), value << offset);
Also, check return value?
> +}
> +
> +
> +static int ad7768_gpio_init(struct iio_dev *indio_dev)
> +{
> + struct ad7768_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = regmap_write(st->regmap, AD7768_REG_GPIO_CONTROL,
> + AD7768_GPIO_UNIVERSAL_EN);
> + if (ret < 0)
> + return ret;
> +
> + st->gpio_avail_map = AD7768_GPIO_CONTROL_MSK;
> + st->gpiochip.label = "ad7768_1_gpios";
> + st->gpiochip.base = -1;
> + st->gpiochip.ngpio = 4;
> + st->gpiochip.parent = &st->spi->dev;
> + st->gpiochip.can_sleep = true;
> + st->gpiochip.direction_input = ad7768_gpio_direction_input;
> + st->gpiochip.direction_output = ad7768_gpio_direction_output;
> + st->gpiochip.get = ad7768_gpio_get;
> + st->gpiochip.set = ad7768_gpio_set;
> + st->gpiochip.request = ad7768_gpio_request;
> + st->gpiochip.owner = THIS_MODULE;
Might not be worth it but I'd be tempted to do
st->gpiochip = (struct gpio_chip) {
.label = "ad7768_1_gpios",
.base = -1,
.ngpio = 4,
...
};
perhaps. This one is entirely up to your preference.
> +
> + return gpiochip_add_data(&st->gpiochip, indio_dev);
> +}
> +
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset
2025-02-01 15:31 ` Jonathan Cameron
@ 2025-02-03 11:34 ` Jonathan Santos
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-02-03 11:34 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, robh, krzk+dt, conor+dt,
marcelo.schmitt1
On 02/01, Jonathan Cameron wrote:
> On Mon, 27 Jan 2025 12:12:29 -0300
> Jonathan Santos <Jonathan.Santos@analog.com> wrote:
>
> > Datasheet recommends Setting the MOSI idle state to high in order to
> > prevent accidental reset of the device when SCLK is free running.
> > This happens when the controller clocks out a 1 followed by 63 zeros
> > while the CS is held low.
> >
> > Check if SPI controller supports SPI_MOSI_IDLE_HIGH flag and set it.
> >
> > Fixes: a5f8c7da3dbe ("iio: adc: Add AD7768-1 ADC basic support")
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
>
> Ideally drag fix to start of patch set to make it obvious it can be
> picked up without the rest. I'm not grabbing it yet as doesn't
> feel like it has enough review yet.
>
Sure, will do.
> > ---
> > v2 Changes:
> > * Only setup SPI_MOSI_IDLE_HIGH flag if the controller supports it, otherwise the driver
> > continues the same. I realized that using bits_per_word does not avoid the problem that
> > MOSI idle state is trying to solve. If the controller drives the MOSI low between bytes
> > during a transfer, nothing happens.
> > ---
> > drivers/iio/adc/ad7768-1.c | 16 ++++++++++++++++
> > 1 file changed, 16 insertions(+)
> >
> > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > index c3cf04311c40..95ba89435652 100644
> > --- a/drivers/iio/adc/ad7768-1.c
> > +++ b/drivers/iio/adc/ad7768-1.c
> > @@ -574,6 +574,22 @@ static int ad7768_probe(struct spi_device *spi)
> > return -ENOMEM;
> >
> > st = iio_priv(indio_dev);
> > + /*
> > + * Datasheet recommends SDI line to be kept high when
> > + * data is not being clocked out of the controller
>
> Very short line wrap. Go up to 80 chars. If this picks up
> some review, I can fix that whilst applying. If not do it for
> your v3.
>
Right, i will do it in the v3.
> > + * and the spi clock is free running, to prevent
> > + * accidental reset.
> > + * Since many controllers do not support the
> > + * SPI_MOSI_IDLE_HIGH flag yet, only request the MOSI
> > + * idle state to enable if the controller supports it.
> > + */
> > + if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) {
> > + spi->mode |= SPI_MOSI_IDLE_HIGH;
> > + ret = spi_setup(spi);
> > + if (ret < 0)
> > + return ret;
> > + }
> > +
> > st->spi = spi;
> >
> > st->vref = devm_regulator_get(&spi->dev, "vref");
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap
2025-01-30 16:32 ` Jonathan Cameron
@ 2025-02-03 11:44 ` Jonathan Santos
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-02-03 11:44 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Nuno Sá, 648eedbee0e7702eda10034531de4611597cd9f2.camel,
David Lechner, Jonathan Santos, linux-iio, devicetree,
linux-kernel, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, marcelo.schmitt1
On 01/30, Jonathan Cameron wrote:
> On Tue, 28 Jan 2025 15:09:36 +0000
> Nuno Sá <noname.nuno@gmail.com> wrote:
>
> > On Tue, 2025-01-28 at 11:46 -0300, Jonathan Santos wrote:
> > > On 01/28, Nuno Sá wrote:
> > > > On Mon, 2025-01-27 at 19:29 -0600, David Lechner wrote:
> > > > > On 1/27/25 9:12 AM, Jonathan Santos wrote:
> > > > > > Convert the AD7768-1 driver to use the regmap API for register
> > > > > > access. This change simplifies and standardizes register interactions,
> > > > > > reducing code duplication and improving maintainability.
> > > > > >
> > > > > > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > > > > > ---
> > > > > > v2 Changes:
> > > > > > * New patch in v2.
> > > > > > ---
> > > > > > drivers/iio/adc/ad7768-1.c | 82 +++++++++++++++++++++++++++-----------
> > > > > > 1 file changed, 58 insertions(+), 24 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > > > > > index 95ba89435652..fb8d6fae5f8a 100644
> > > > > > --- a/drivers/iio/adc/ad7768-1.c
> > > > > > +++ b/drivers/iio/adc/ad7768-1.c
> > > > > > @@ -12,6 +12,7 @@
> > > > > > #include <linux/gpio/consumer.h>
> > > > > > #include <linux/kernel.h>
> > > > > > #include <linux/module.h>
> > > > > > +#include <linux/regmap.h>
> > > > > > #include <linux/regulator/consumer.h>
> > > > > > #include <linux/sysfs.h>
> > > > > > #include <linux/spi/spi.h>
> > > > > > @@ -153,6 +154,7 @@ static const struct iio_chan_spec ad7768_channels[]
> > > > > > = {
> > > > > >
> > > > > > struct ad7768_state {
> > > > > > struct spi_device *spi;
> > > > > > + struct regmap *regmap;
> > > > > > struct regulator *vref;
> > > > > > struct mutex lock;
> > > > > > struct clk *mclk;
> > > > > > @@ -176,12 +178,17 @@ struct ad7768_state {
> > > > > > } data __aligned(IIO_DMA_MINALIGN);
> > > > > > };
> > > > > >
> > > > > > -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int
> > > > > > addr,
> > > > > > - unsigned int len)
> > > > > > +static int ad7768_spi_reg_read(void *context, unsigned int addr,
> > > > > > + unsigned int *val)
> > > > > > {
> > > > > > - unsigned int shift;
> > > > > > + struct iio_dev *dev = context;
> > > > > > + struct ad7768_state *st;
> > > > > > + unsigned int shift, len;
> > > > > > int ret;
> > > > > >
> > > > > > + st = iio_priv(dev);
> > > > >
> > > > > This can be combined with the variable declaration.
> > > > >
> > > > > > + /* Regular value size is 1 Byte, but 3 Bytes for ADC data */
> > > > >
> > > > > Probably not currently needed but COEFF_DATA register is also 3 bytes.
> > > > >
> > > > > > + len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > > > > > shift = 32 - (8 * len);
> > > > > > st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
> > > > > >
> > > > > > @@ -190,13 +197,19 @@ static int ad7768_spi_reg_read(struct ad7768_state
> > > > > > *st, unsigned int addr,
> > > > > > if (ret < 0)
> > > > > > return ret;
> > > > > >
> > > > > > - return (be32_to_cpu(st->data.d32) >> shift);
> > > > > > + *val = be32_to_cpu(st->data.d32) >> shift;
> > > > > > +
> > > > > > + return 0;
> > > > > > }
> > > > > >
> > > > > > -static int ad7768_spi_reg_write(struct ad7768_state *st,
> > > > > > +static int ad7768_spi_reg_write(void *context,
> > > > > > unsigned int addr,
> > > > > > unsigned int val)
> > > > > > {
> > > > > > + struct iio_dev *dev = context;
> > > > > > + struct ad7768_state *st;
> > > > > > +
> > > > > > + st = iio_priv(dev);
> > > > > > st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
> > > > > > st->data.d8[1] = val & 0xFF;
> > > > > >
> > > > > > @@ -206,16 +219,16 @@ static int ad7768_spi_reg_write(struct
> > > > > > ad7768_state
> > > > > > *st,
> > > > > > static int ad7768_set_mode(struct ad7768_state *st,
> > > > > > enum ad7768_conv_mode mode)
> > > > > > {
> > > > > > - int regval;
> > > > > > + int regval, ret;
> > > > > >
> > > > > > - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
> > > > > > - if (regval < 0)
> > > > > > - return regval;
> > > > > > + ret = regmap_read(st->regmap, AD7768_REG_CONVERSION, ®val);
> > > > > > + if (ret)
> > > > > > + return ret;
> > > > > >
> > > > > > regval &= ~AD7768_CONV_MODE_MSK;
> > > > > > regval |= AD7768_CONV_MODE(mode);
> > > > > >
> > > > > > - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
> > > > > > + return regmap_write(st->regmap, AD7768_REG_CONVERSION, regval);
> > > > > > }
> > > > > >
> > > > > > static int ad7768_scan_direct(struct iio_dev *indio_dev)
> > > > > > @@ -234,9 +247,10 @@ static int ad7768_scan_direct(struct iio_dev
> > > > > > *indio_dev)
> > > > > > if (!ret)
> > > > > > return -ETIMEDOUT;
> > > > > >
> > > > > > - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > > > > - if (readval < 0)
> > > > > > - return readval;
> > > > > > + ret = regmap_read(st->regmap, AD7768_REG_ADC_DATA, &readval);
> > > > > > + if (ret)
> > > > > > + return ret;
> > > > > > +
> > > > > > /*
> > > > > > * Any SPI configuration of the AD7768-1 can only be
> > > > > > * performed in continuous conversion mode.
> > > > > > @@ -258,13 +272,11 @@ static int ad7768_reg_access(struct iio_dev
> > > > > > *indio_dev,
> > > > > >
> > > > > > mutex_lock(&st->lock);
> > > > > > if (readval) {
> > > > > > - ret = ad7768_spi_reg_read(st, reg, 1);
> > > > > > - if (ret < 0)
> > > > > > + ret = regmap_read(st->regmap, reg, readval);
> > > > > > + if (ret)
> > > > > > goto err_unlock;
> > > > >
> > > > > Can drop the if and goto.
> > > > >
> > > > > > - *readval = ret;
> > > > > > - ret = 0;
> > > > > > } else {
> > > > > > - ret = ad7768_spi_reg_write(st, reg, writeval);
> > > > > > + ret = regmap_write(st->regmap, reg, writeval);
> > > > > > }
> > > > > > err_unlock:
> > > > > > mutex_unlock(&st->lock);
> > > > > > @@ -283,7 +295,7 @@ static int ad7768_set_dig_fil(struct ad7768_state
> > > > > > *st,
> > > > > > else
> > > > > > mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> > > > > >
> > > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER,
> > > > > > mode);
> > > > > > + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER,
> > > > > > mode);
> > > > > > if (ret < 0)
> > > > > > return ret;
> > > > > >
> > > > > > @@ -320,7 +332,7 @@ static int ad7768_set_freq(struct ad7768_state *st,
> > > > > > */
> > > > > > pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div)
> > > > > > |
> > > > > > AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> > > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK,
> > > > > > pwr_mode);
> > > > > > + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK,
> > > > > > pwr_mode);
> > > > > > if (ret < 0)
> > > > > > return ret;
> > > > > >
> > > > > > @@ -447,11 +459,11 @@ static int ad7768_setup(struct ad7768_state *st)
> > > > > > * to 10. When the sequence is detected, the reset occurs.
> > > > > > * See the datasheet, page 70.
> > > > > > */
> > > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
> > > > > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> > > > > > if (ret)
> > > > > > return ret;
> > > > > >
> > > > > > - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
> > > > > > + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> > > > > > if (ret)
> > > > > > return ret;
> > > > > >
> > > > > > @@ -509,18 +521,19 @@ static int ad7768_buffer_postenable(struct iio_dev
> > > > > > *indio_dev)
> > > > > > * continuous read mode. Subsequent data reads do not require
> > > > > > an
> > > > > > * initial 8-bit write to query the ADC_DATA register.
> > > > > > */
> > > > > > - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT,
> > > > > > 0x01);
> > > > > > + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT,
> > > > > > 0x01);
> > > > > > }
> > > > > >
> > > > > > static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
> > > > > > {
> > > > > > struct ad7768_state *st = iio_priv(indio_dev);
> > > > > > + unsigned int regval;
> > > > >
> > > > > Intention could be more clear by calling this "unused". Otherwise, it can
> > > > > look
> > > > > like a bug if you don't fully understand what the comment below means.
> > > > >
> > > > > >
> > > > > > /*
> > > > > > * To exit continuous read mode, perform a single read of the
> > > > > > ADC_DATA
> > > > > > * reg (0x2C), which allows further configuration of the
> > > > > > device.
> > > > > > */
> > > > > > - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
> > > > > > + return regmap_read(st->regmap, AD7768_REG_ADC_DATA, ®val);
> > > > > > }
> > > > > >
> > > > > > static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
> > > > > > @@ -563,6 +576,20 @@ static int ad7768_set_channel_label(struct iio_dev
> > > > > > *indio_dev,
> > > > > > return 0;
> > > > > > }
> > > > > >
> > > > > > +static const struct regmap_bus ad7768_regmap_bus = {
> > > > > > + .reg_write = ad7768_spi_reg_write,
> > > > > > + .reg_read = ad7768_spi_reg_read,
> > > > > > + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> > > > > > + .val_format_endian_default = REGMAP_ENDIAN_BIG,
> > > > >
> > > > > The bus read function is calling be32_to_cpu(), so we probably want to
> > > > > remove
> > > > > that or change the default here.
> > > > >
> > > > > > +};
> > > > > > +
> > > > > > +static const struct regmap_config ad7768_regmap_config = {
> > > > > > + .name = "ad7768-1",
> > > > > > + .reg_bits = 8,
> > > > > > + .val_bits = 8,
> > > > >
> > > > > Should this be 24 since the largest registers are 24-bit?
> > > > >
> > > > > Another option could be to just use a regular spi_*() API for that
> > > > > register
> > > > > instead of regmap_*() and avoid trying to do something that regmap doesn't
> > > > > really handle.
> > > > >
> > > > > Or we could possibly use regmap_bulk_read(), but that feels a bit hacky
> > > > > too
> > > > > since it isn't actually how that function was intended to be used.
> > > > >
> > > >
> > > > Hmm I might be missing something but looking at the register map, It seems
> > > > we do
> > > > have 8bit registers? We do have values that span multiple registers (3 for
> > > > the
> > > > 24bit values) and regmap_bulk_read() should actually fit right? I mean,
> > > > looking
> > > > at the docs:
> > > >
> > > > "regmap_bulk_read() - Read multiple sequential registers from the device"
> > > >
> > >
> > > Isn't regmap_bulk_*() for reading a value spread in sequential registers,
> > > like the offset calibration (registers 0x22, 0x23 and 0x24, 8 bits value
> > > for each reg)? For the ADC data (0x2C) we have a 24 bits value in only one
> > > register, so I beleive this does not apply.
> > >
> >
> > Ah got it. I failed to see that. Yeah, in that case you're right... it won't
> > work out of the box.
> >
> > > > But I do agree that what we have right now does not make much sense. If we
> > > > need
> > > > to do
> > > >
> > > > len = (addr == AD7768_REG_ADC_DATA) ? 3 : 1;
> > > >
> > > > for supporting regmap, then I have to question using it. Also note that we
> > > > have
> > > > things like gain and offset that are also 3 bytes which means that our
> > > > custom
> > > > read would need to become more questionable if we add support for it.
> > > >
> > >
> > > For those cases the regmap_bulk_*() works.
> > >
> > > > Jonathan, did you tried to use plain regmap (without the custom bus)?
> > > > Assuming
> > > > bulk reads work, I'm not seeing an apparent reason for the custom bus... I
> > > > would
> > > > also suspect that if bulk reads don't work out of the box, providing a
> > > > regmap
> > > > cache would make it work but relying on implementation details is not a very
> > > > good practice.
> > > >
> > >
> > > Yes, i tried and only works for the register with 8-bits value. David
> > > suggested using regular spi_*() functions for the unsual registers with
> > > 24-bits value, such as the ADC data (0x2C). That is the only way of
> > > having the default spi bus interface using regmap. Otherwise we should
> > > drop the regmap.
> >
> > Yeah, might be better to do plain spi for the unusual registers rather than the
> > custom bus. But no strong feelings on my side...
> I'm not keen on a mix or on a regmap that handles the size under the hood.
> I think this quirk should be obvious.
>
> Can we do two regmap, one for each of the register size? We have other
> drivers taking that approach and I'm not sure if it was ruled out for
> this one.
>
> Jonathan
>
Yes, it makes sense a regmap for each register size, if everyone agrees.
I will try this here.
> >
> > - Nuno Sá
> > >
> >
> >
> >
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function
2025-02-01 15:35 ` Jonathan Cameron
@ 2025-02-03 12:03 ` Jonathan Santos
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-02-03 12:03 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel,
Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, robh,
krzk+dt, conor+dt, marcelo.schmitt1
On 02/01, Jonathan Cameron wrote:
> On Mon, 27 Jan 2025 12:13:19 -0300
> Jonathan Santos <Jonathan.Santos@analog.com> wrote:
>
> > From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> >
> > This change moves the buffer allocation in a separate function, making
> > space for adding another type of iio buffer if needed.
> >
> > Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> Jonathan, this one needs your sign off to reflect that you handled
> the patch as part of it's path to upstream. I can't apply
> anything that is missing such SoB.
>
Sorry about that, I am fixing the SoBs for v3.
>
> > ---
> > v2 Changes:
> > * Interrupt and completion moved out from ad7768_triggered_buffer_alloc().
> > ---
> > drivers/iio/adc/ad7768-1.c | 44 ++++++++++++++++++++++----------------
> > 1 file changed, 26 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > index 5e2093be9b92..8487b9a06609 100644
> > --- a/drivers/iio/adc/ad7768-1.c
> > +++ b/drivers/iio/adc/ad7768-1.c
> > @@ -599,6 +599,31 @@ static const struct regmap_config ad7768_regmap_config = {
> > .max_register = AD7768_REG_MCLK_COUNTER,
> > };
> >
> > +static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev)
> > +{
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > + int ret;
> > +
> > + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d",
> > + indio_dev->name,
> > + iio_device_id(indio_dev));
> > + if (!st->trig)
> > + return -ENOMEM;
> > +
> > + st->trig->ops = &ad7768_trigger_ops;
> > + iio_trigger_set_drvdata(st->trig, indio_dev);
> > + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig);
> > + if (ret)
> > + return ret;
> > +
> > + indio_dev->trig = iio_trigger_get(st->trig);
> > +
> > + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
> > + &iio_pollfunc_store_time,
> > + &ad7768_trigger_handler,
> > + &ad7768_buffer_ops);
> > +}
> > +
> > static int ad7768_probe(struct spi_device *spi)
> > {
> > struct ad7768_state *st;
> > @@ -669,20 +694,6 @@ static int ad7768_probe(struct spi_device *spi)
> > return ret;
> > }
> >
> > - st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
> > - indio_dev->name,
> > - iio_device_id(indio_dev));
> > - if (!st->trig)
> > - return -ENOMEM;
> > -
> > - st->trig->ops = &ad7768_trigger_ops;
> > - iio_trigger_set_drvdata(st->trig, indio_dev);
> > - ret = devm_iio_trigger_register(&spi->dev, st->trig);
> > - if (ret)
> > - return ret;
> > -
> > - indio_dev->trig = iio_trigger_get(st->trig);
> > -
> > init_completion(&st->completion);
> >
> > ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels));
> > @@ -696,10 +707,7 @@ static int ad7768_probe(struct spi_device *spi)
> > if (ret)
> > return ret;
> >
> > - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
> > - &iio_pollfunc_store_time,
> > - &ad7768_trigger_handler,
> > - &ad7768_buffer_ops);
> > + ret = ad7768_triggered_buffer_alloc(indio_dev);
> > if (ret)
> > return ret;
> >
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support
2025-01-27 23:34 ` David Lechner
@ 2025-02-03 13:08 ` Jonathan Santos
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-02-03 13:08 UTC (permalink / raw)
To: David Lechner
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel,
Sergiu Cuciurean, lars, Michael.Hennerich, marcelo.schmitt, jic23,
robh, krzk+dt, conor+dt, marcelo.schmitt1
On 01/27, David Lechner wrote:
> On 1/27/25 9:13 AM, Jonathan Santos wrote:
> > From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> >
> > The AD7768-1 has the ability to control other local hardware (such as gain
> > stages),to power down other blocks in the signal chain, or read local
> > status signals over the SPI interface.
> >
> > This change exports the AD7768-1's four gpios and makes them accessible
> > at an upper layer.
> >
> > Co-developed-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> > ---
> > v2 Changes:
> > * Replaced mutex for iio_device_claim_direct_mode().
> > * Use gpio-controller property to conditionally enable the
> > GPIO support.
> > * OBS: when the GPIO is configured as output, we should read
> > the current state value from AD7768_REG_GPIO_WRITE.
> > ---
> > drivers/iio/adc/ad7768-1.c | 148 ++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 146 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > index c540583808c2..e3ea078e6ec4 100644
> > --- a/drivers/iio/adc/ad7768-1.c
> > +++ b/drivers/iio/adc/ad7768-1.c
> > @@ -9,6 +9,8 @@
> > #include <linux/delay.h>
> > #include <linux/device.h>
> > #include <linux/err.h>
> > +#include <linux/gpio.h>
> > +#include <linux/gpio/driver.h>
> > #include <linux/gpio/consumer.h>
> > #include <linux/kernel.h>
> > #include <linux/module.h>
> > @@ -79,6 +81,19 @@
> > #define AD7768_CONV_MODE_MSK GENMASK(2, 0)
> > #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
> >
> > +/* AD7768_REG_GPIO_CONTROL */
> > +#define AD7768_GPIO_UNIVERSAL_EN BIT(7)
> > +#define AD7768_GPIO_CONTROL_MSK GENMASK(3, 0)
> > +
> > +/* AD7768_REG_GPIO_WRITE */
> > +#define AD7768_GPIO_WRITE_MSK GENMASK(3, 0)
> > +
> > +/* AD7768_REG_GPIO_READ */
> > +#define AD7768_GPIO_READ_MSK GENMASK(3, 0)
> > +
> > +#define AD7768_GPIO_INPUT(x) 0x00
> > +#define AD7768_GPIO_OUTPUT(x) BIT(x)
> > +
> > #define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
> > #define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
> >
> > @@ -160,6 +175,8 @@ struct ad7768_state {
> > struct regulator *vref;
> > struct mutex lock;
> > struct clk *mclk;
> > + struct gpio_chip gpiochip;
> > + unsigned int gpio_avail_map;
> > unsigned int mclk_freq;
> > unsigned int samp_freq;
> > struct completion completion;
> > @@ -309,6 +326,125 @@ static int ad7768_set_dig_fil(struct ad7768_state *st,
> > return 0;
> > }
> >
> > +static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
> > +{
> > + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > + int ret;
> > +
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
>
> Missing iio_device_release_direct_mode() here and in other functions.
>
> (And we are in the process of removing iio_device_claim_direct_scoped(), so
> don't use that.)
>
Sure, my mistake. I am fixing this.
> > +
> > + return regmap_update_bits(st->regmap,
> > + AD7768_REG_GPIO_CONTROL,
> > + BIT(offset),
> > + AD7768_GPIO_INPUT(offset));
>
> Can be simplified to regmap_clear_bits(), then we can get rid of the odd
> AD7768_GPIO_INPUT macro that ignores the argument.
>
> > +}
> > +
> > +static int ad7768_gpio_direction_output(struct gpio_chip *chip,
> > + unsigned int offset, int value)
> > +{
> > + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > + int ret;
> > +
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > +
> > + return regmap_update_bits(st->regmap,
> > + AD7768_REG_GPIO_CONTROL,
> > + BIT(offset),
> > + AD7768_GPIO_OUTPUT(offset));
>
> And regmap_set_bits() here.
>
> > +}
> > +
> > +static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset)
> > +{
> > + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > + unsigned int val;
> > + int ret;
> > +
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
> > + if (ret < 0)
> > + return ret;
> > +
> > + if (val & BIT(offset))
> > + ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val);
> > + else
> > + ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val);
>
> Can we get a comment explaining why GPIO_READ doesn't work in output mode?
>
> Or if it does work, we can simplify this function.
>
>
The datasheet does not mention this; I reached this conclusion through testing.
It seems they separate the output state from the read register. Anyway, I will
add a comment.
> > + if (ret < 0)
> > + return ret;
> > +
> > + return !!(val & BIT(offset));
> > +}
> > +
> > +static void ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
> > +{
> > + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > + unsigned int val;
> > + int ret;
> > +
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret)
> > + return;
> > +
> > + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val);
> > + if (ret < 0)
> > + return;
> > +
> > + if (val & BIT(offset))
> > + regmap_update_bits(st->regmap,
> > + AD7768_REG_GPIO_WRITE,
> > + BIT(offset),
> > + (value << offset));
>
> Can remove extra ().
>
> > +}
> > +
> > +static int ad7768_gpio_request(struct gpio_chip *chip, unsigned int offset)
> > +{
> > + struct iio_dev *indio_dev = gpiochip_get_data(chip);
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > +
> > + if (!(st->gpio_avail_map & BIT(offset)))
> > + return -ENODEV;
> > +
> > + st->gpio_avail_map &= ~BIT(offset);
>
> Is this really needed? It seems like GPIO core would be keeping track already.
>
> Also would need a .free callback to undo this action.
>
> It seems like most ADC's with GPIO controllers don't implement .request though.
>
Indeed, .request is optional and does not seem to make a pratical
difference if the core handles that. If that is the case i can remove
this
> > +
> > + return 0;
> > +}
> > +
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio
2025-01-27 15:12 ` [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio Jonathan Santos
2025-01-27 22:43 ` David Lechner
@ 2025-02-03 13:46 ` Marcelo Schmitt
1 sibling, 0 replies; 56+ messages in thread
From: Marcelo Schmitt @ 2025-02-03 13:46 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, Sergiu Cuciurean, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, jonath4nns
On 01/27, Jonathan Santos wrote:
> From: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>
> Depending on the controller, the default state of a gpio can vary. This
> change excludes the probability that the dafult state of the ADC reset
> gpio will be HIGH if it will be passed as reference in the devicetree.
>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> ---
> v2 Changes:
> * Replaced usleep_range() for fsleep() and gpiod_direction_output() for
> gpiod_set_value_cansleep().
> * Reset via SPI register is performed if the Reset GPIO is not defined.
> ---
LGTM.
Aside from including your the SoB, one minor thing about reset timings.
Reviewed-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> drivers/iio/adc/ad7768-1.c | 36 ++++++++++++++++++++++++------------
> 1 file changed, 24 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index fb8d6fae5f8a..17a49bf74637 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -163,6 +163,7 @@ struct ad7768_state {
> struct completion completion;
> struct iio_trigger *trig;
> struct gpio_desc *gpio_sync_in;
> + struct gpio_desc *gpio_reset;
> const char *labels[ARRAY_SIZE(ad7768_channels)];
> /*
> * DMA (thus cache coherency maintenance) may require the
> @@ -453,19 +454,30 @@ static int ad7768_setup(struct ad7768_state *st)
> {
> int ret;
>
> - /*
> - * Two writes to the SPI_RESET[1:0] bits are required to initiate
> - * a software reset. The bits must first be set to 11, and then
> - * to 10. When the sequence is detected, the reset occurs.
> - * See the datasheet, page 70.
> - */
> - ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> - if (ret)
> - return ret;
> + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
> + GPIOD_OUT_HIGH);
> + if (IS_ERR(st->gpio_reset))
> + return PTR_ERR(st->gpio_reset);
>
> - ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> - if (ret)
> - return ret;
> + if (st->gpio_reset) {
> + fsleep(10);
> + gpiod_set_value_cansleep(st->gpio_reset, 0);
> + fsleep(10);
Is 10 us enough time here? AD7768-1 datasheet page 58 sais
"The time taken from RESET to an SPI write must be at least 200 μs."
> + } else {
> + /*
> + * Two writes to the SPI_RESET[1:0] bits are required to initiate
> + * a software reset. The bits must first be set to 11, and then
> + * to 10. When the sequence is detected, the reset occurs.
> + * See the datasheet, page 70.
> + */
> + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2);
> + if (ret)
> + return ret;
> + }
>
> st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
> GPIOD_OUT_LOW);
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes
2025-01-28 1:24 ` David Lechner
@ 2025-02-03 14:58 ` Jonathan Santos
0 siblings, 0 replies; 56+ messages in thread
From: Jonathan Santos @ 2025-02-03 14:58 UTC (permalink / raw)
To: David Lechner
Cc: Jonathan Santos, linux-iio, devicetree, linux-kernel, lars,
Michael.Hennerich, marcelo.schmitt, jic23, robh, krzk+dt,
conor+dt, marcelo.schmitt1, PopPaul2021
On 01/27, David Lechner wrote:
> On 1/27/25 9:14 AM, Jonathan Santos wrote:
> > Separate filter type and decimation rate from the sampling frequency
> > attribute. The new filter type attribute enables SINC3 and WIDEBAND
> > filters, which were previously unavailable.
> >
> > Previously, combining decimation and MCLK divider in the sampling
> > frequency obscured performance trade-offs. Lower MCLK divider
> > settings increase power usage, while lower decimation rates reduce
> > precision by decreasing averaging. By creating an oversampling
> > attribute, which controls the decimation, users gain finer control
> > over performance.
> >
> > The addition of those attributes allows a wider range of sampling
> > frequencies and more access to the device features.
> >
> > Co-developed-by: PopPaul2021 <paul.pop@analog.com>
> > Signed-off-by: PopPaul2021 <paul.pop@analog.com>
> > Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> > ---
> > v2 Changes:
> > * Decimation_rate attribute replaced for oversampling_ratio.
> > ---
> > drivers/iio/adc/ad7768-1.c | 389 +++++++++++++++++++++++++++++++------
> > 1 file changed, 325 insertions(+), 64 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> > index 01ccbe0aa708..6d0b430a8d54 100644
> > --- a/drivers/iio/adc/ad7768-1.c
> > +++ b/drivers/iio/adc/ad7768-1.c
> > @@ -5,6 +5,7 @@
> > * Copyright 2017 Analog Devices Inc.
> > */
> > #include <linux/bitfield.h>
> > +#include <linux/cleanup.h>
> > #include <linux/clk.h>
> > #include <linux/delay.h>
> > #include <linux/device.h>
> > @@ -17,6 +18,7 @@
> > #include <linux/regmap.h>
> > #include <linux/regulator/consumer.h>
> > #include <linux/sysfs.h>
> > +#include "linux/util_macros.h"
>
> nit: alphabetical order
>
> > #include <linux/spi/spi.h>
> >
> > #include <linux/iio/buffer.h>
> > @@ -77,6 +79,10 @@
> > #define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
> > #define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
> >
> > +/* AD7768_SINC3_DEC_RATE */
> > +#define AD7768_SINC3_DEC_RATE_MSB_MSK GENMASK(12, 8)
> > +#define AD7768_SINC3_DEC_RATE_LSB_MSK GENMASK(7, 0)
> > +
> > /* AD7768_REG_CONVERSION */
> > #define AD7768_CONV_MODE_MSK GENMASK(2, 0)
> > #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
> > @@ -97,6 +103,18 @@
> > #define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
> > #define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
> >
> > +/* Decimation Rate Limits */
> > +#define SINC5_DEC_RATE_MIN 8
> > +#define SINC5_DEC_RATE_MAX 1024
> > +#define SINC3_DEC_RATE_MIN 32
> > +#define SINC3_DEC_RATE_MAX 163840
> > +#define WIDEBAND_DEC_RATE_MIN 32
> > +#define WIDEBAND_DEC_RATE_MAX 1024
> > +
> > +enum {
> > + DEC_RATE,
>
> Odd to have enum with one member. Also should have AD7768_ namespace prefix.
>
This is not in use anymore, i will remove it.
> > +};
> > +
> > enum ad7768_conv_mode {
> > AD7768_CONTINUOUS,
> > AD7768_ONE_SHOT,
> > @@ -118,22 +136,12 @@ enum ad7768_mclk_div {
> > AD7768_MCLK_DIV_2
> > };
> >
> > -enum ad7768_dec_rate {
> > - AD7768_DEC_RATE_32 = 0,
> > - AD7768_DEC_RATE_64 = 1,
> > - AD7768_DEC_RATE_128 = 2,
> > - AD7768_DEC_RATE_256 = 3,
> > - AD7768_DEC_RATE_512 = 4,
> > - AD7768_DEC_RATE_1024 = 5,
> > - AD7768_DEC_RATE_8 = 9,
> > - AD7768_DEC_RATE_16 = 10
> > -};
> > -
> > -struct ad7768_clk_configuration {
> > - enum ad7768_mclk_div mclk_div;
> > - enum ad7768_dec_rate dec_rate;
> > - unsigned int clk_div;
> > - enum ad7768_pwrmode pwrmode;
> > +enum ad7768_flt_type {
> > + SINC5,
> > + SINC5_DEC_X8,
> > + SINC5_DEC_X16,
> > + SINC3,
> > + WIDEBAND
> > };
> >
> > enum ad7768_scan_type {
> > @@ -145,18 +153,12 @@ static const int ad7768_mclk_div_rates[4] = {
> > 16, 8, 4, 2,
> > };
> >
> > -static const struct ad7768_clk_configuration ad7768_clk_config[] = {
> > - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
> > - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
> > - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
> > - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
> > - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
> > - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
> > - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
> > - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
> > - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
> > - { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
> > - { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
> > +static const int ad7768_dec_rate_values[6] = {
> > + 32, 64, 128, 256, 512, 1024,
> > +};
> > +
> > +static const char * const ad7768_filter_enum[] = {
> > + "sinc5", "sinc3", "wideband"
>
> Do we also need to consider "sinc3+rej60" to account for the EN_60HZ_REJ bit
> in the DIGITAL_FILTER register?
>
Sure, i will also include this filter.
> > };
> >
> > static const struct iio_scan_type ad7768_scan_type[] = {
> > @@ -176,12 +178,32 @@ static const struct iio_scan_type ad7768_scan_type[] = {
> > },
> > };
> >
> > +static int ad7768_get_fil_type_attr(struct iio_dev *dev,
> > + const struct iio_chan_spec *chan);
> > +static int ad7768_set_fil_type_attr(struct iio_dev *dev,
> > + const struct iio_chan_spec *chan, unsigned int filter);
> > +
> > +static const struct iio_enum ad7768_flt_type_iio_enum = {
> > + .items = ad7768_filter_enum,
> > + .num_items = ARRAY_SIZE(ad7768_filter_enum),
> > + .set = ad7768_set_fil_type_attr,
> > + .get = ad7768_get_fil_type_attr,
> > +};
> > +
> > +static struct iio_chan_spec_ext_info ad7768_ext_info[] = {
> > + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum),
> > + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum),
> > + { },
> > +};
> > +
> > static const struct iio_chan_spec ad7768_channels[] = {
> > {
> > .type = IIO_VOLTAGE,
> > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> > - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> > + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
>
> info_mask_shared_by_type might be safer in case we ever have to add a non-
> voltage channel for some reason.
>
ok!
> > + .ext_info = ad7768_ext_info,
> > .indexed = 1,
> > .channel = 0,
> > .scan_index = 0,
> > @@ -201,7 +223,9 @@ struct ad7768_state {
> > struct gpio_chip gpiochip;
> > unsigned int gpio_avail_map;
> > unsigned int mclk_freq;
> > - unsigned int dec_rate;
> > + unsigned int mclk_div;
> > + unsigned int oversampling_ratio;
> > + enum ad7768_flt_type filter_type;
> > unsigned int samp_freq;
> > struct completion completion;
> > struct iio_trigger *trig;
> > @@ -223,6 +247,9 @@ struct ad7768_state {
> > } data __aligned(IIO_DMA_MINALIGN);
> > };
> >
> > +static int ad7768_set_freq(struct ad7768_state *st,
> > + unsigned int freq);
>
> Would be nice if we can reorder functions to avoid forward declaration.
>
> > +
> > static int ad7768_spi_reg_read(void *context, unsigned int addr,
> > unsigned int *val)
> > {
> > @@ -281,6 +308,31 @@ static int ad7768_send_sync_pulse(struct ad7768_state *st)
> > return 0;
> > }
> >
> > +static int ad7768_set_mclk_div(struct ad7768_state *st, unsigned int mclk_div)
> > +{
> > + unsigned int mclk_div_value;
> > + int ret;
> > +
> > + guard(mutex)(&st->lock);
> > + ret = ad7768_spi_reg_read(st, AD7768_REG_POWER_CLOCK, &mclk_div_value, 1);
> > + if (ret)
> > + return ret;
> > +
> > + mclk_div_value &= ~(AD7768_PWR_MCLK_DIV_MSK | AD7768_PWR_PWRMODE_MSK);
> > + /* Set mclk_div value */
> > + mclk_div_value |= AD7768_PWR_MCLK_DIV(mclk_div);
> > + /*
> > + * Set power mode based on mclk_div value.
> > + * ECO_MODE is only recommended for MCLK_DIV 16
> > + */
> > + if (mclk_div > AD7768_MCLK_DIV_16)
> > + mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_FAST_MODE);
> > + else
> > + mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_ECO_MODE);
> > +
> > + return regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, mclk_div_value);
>
> Can we do this with regmap_update_bits() and FIELD_PREP() instead?
>
Sure
> > +}
> > +
> > static int ad7768_set_mode(struct ad7768_state *st,
> > enum ad7768_conv_mode mode)
> > {
> > @@ -349,23 +401,183 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
> > return ret;
> > }
> >
> > -static int ad7768_set_dig_fil(struct ad7768_state *st,
> > - enum ad7768_dec_rate dec_rate)
> > +static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
> > + unsigned int dec_rate)
> > +{
> > + unsigned int dec_rate_msb, dec_rate_lsb, max_dec_rate;
> > + int ret;
> > +
> > + guard(mutex)(&st->lock);
> > + /*
> > + * Maximum dec_rate is limited by the MCLK_DIV value
> > + * and by the ODR. The edge case is for MCLK_DIV = 2
> > + * ODR = 50 SPS.
> > + * max_dec_rate <= MCLK / (2 * 50)
> > + */
> > + max_dec_rate = st->mclk_freq / 100;
> > + dec_rate = clamp_t(unsigned int, dec_rate, 32, max_dec_rate);
> > + /*
> > + * Calculate the equivalent value to sinc3 decimation ratio
> > + * to be written on the SINC3_DECIMATION_RATE register:
> > + * Value = (DEC_RATE / 32) -1
> > + */
> > + dec_rate = DIV_ROUND_UP(dec_rate, 32) - 1;
> > + dec_rate_msb = FIELD_GET(AD7768_SINC3_DEC_RATE_MSB_MSK, dec_rate);
> > + dec_rate_lsb = FIELD_GET(AD7768_SINC3_DEC_RATE_LSB_MSK, dec_rate);
> > +
> > + ret = regmap_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_MSB, dec_rate_msb);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regmap_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_LSB, dec_rate_lsb);
> > + if (ret)
> > + return ret;
>
> Can we use regmap_bulk_write()?
>
> > +
> > + st->oversampling_ratio = (dec_rate + 1) * 32;
> > +
> > + return 0;
> > +}
> > +
> > +static int ad7768_set_dec_rate(struct ad7768_state *st, unsigned int dec_rate)
> > {
> > + unsigned int mode, dec_rate_reg;
> > + int ret;
> > +
> > + dec_rate_reg = find_closest(dec_rate, ad7768_dec_rate_values,
> > + ARRAY_SIZE(ad7768_dec_rate_values));
> > +
> > + guard(mutex)(&st->lock);
> > + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
> > + if (ret)
> > + return ret;
> > +
> > + mode &= ~AD7768_DIG_FIL_DEC_MSK;
> > + mode |= AD7768_DIG_FIL_DEC_RATE(dec_rate_reg);
> > + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
>
> regmap_update_bits()?
>
> > + if (ret)
> > + return ret;
> > +
> > + st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_reg];
> > +
> > + return 0;
> > +}
> > +
> > +static int ad7768_set_filter_type(struct iio_dev *dev,
> > + enum ad7768_flt_type filter_type)
> > +{
> > + struct ad7768_state *st = iio_priv(dev);
> > unsigned int mode;
> > int ret;
> >
> > - if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
> > - mode = AD7768_DIG_FIL_FIL(dec_rate);
> > - else
> > - mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
> > + guard(mutex)(&st->lock);
>
> Lock was removed in previous patch, so shouldn't be using it here.
>
> > + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
> > + if (ret)
> > + return ret;
> > +
> > + mode &= ~AD7768_DIG_FIL_FIL_MSK;
> > + mode |= AD7768_DIG_FIL_FIL(filter_type);
> >
> > ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode);
> > if (ret < 0)
> > return ret;
>
> Looks like we could drop the helper function and just use regmap_update_bits()
> directly now.
>
> >
> > - /* A sync-in pulse is required every time the filter dec rate changes */
> > - return ad7768_send_sync_pulse(st);
> > + st->filter_type = filter_type;
> > +
> > + return 0;
> > +}
> > +
> > +static int ad7768_configure_dig_fil(struct iio_dev *dev,
> > + enum ad7768_flt_type filter_type,
> > + unsigned int dec_rate)
> > +{
> > + struct ad7768_state *st = iio_priv(dev);
> > + int ret;
> > +
> > + if (filter_type == SINC3) {
>
> Using a switch statement instead would be more like other IIO code.
>
I did some changes in this function to be able to use the switch
appropriately.
> > + ret = ad7768_set_filter_type(dev, SINC3);
> > + if (ret)
> > + return ret;
> > +
> > + /* recalculate the decimation for this filter mode */
> > + ret = ad7768_set_sinc3_dec_rate(st, dec_rate);
>
> Just return directly.
>
> > + } else if (filter_type == WIDEBAND) {
> > + ret = ad7768_set_filter_type(dev, filter_type);
> > + if (ret)
> > + return ret;
> > +
> > + /* recalculate the decimation rate */
> > + ret = ad7768_set_dec_rate(st, dec_rate);
> > + } else {
> > + /* For SINC5 filter */
> > + /* Decimation 8 and 16 are set in the digital filter field */
> > + if (dec_rate <= 8) {
> > + ret = ad7768_set_filter_type(dev, SINC5_DEC_X8);
> > + if (ret)
> > + return ret;
> > +
> > + st->oversampling_ratio = 8;
> > + } else if (dec_rate <= 16) {
> > + ret = ad7768_set_filter_type(dev, SINC5_DEC_X16);
> > + if (ret)
> > + return ret;
> > +
> > + st->oversampling_ratio = 16;
> > + } else {
> > + ret = ad7768_set_filter_type(dev, SINC5);
> > + if (ret)
> > + return ret;
> > +
> > + ret = ad7768_set_dec_rate(st, dec_rate);
> > + }
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int ad7768_set_fil_type_attr(struct iio_dev *dev,
> > + const struct iio_chan_spec *chan,
> > + unsigned int filter)
> > +{
> > + struct ad7768_state *st = iio_priv(dev);
> > + int ret;
> > +
> > + /*
> > + * Filters of types 0, 1, and 2 correspond to SINC5.
> > + * For SINC3 and wideband filter types, an offset of 2 is added
> > + * to align with the expected register values.
> > + */
> > + if (filter != SINC5)
> > + filter += 2;
> > +
> > + ret = ad7768_configure_dig_fil(dev, filter, st->oversampling_ratio);
> > + if (ret)
> > + return ret;
> > +
> > + /* Update sampling frequency */
> > + return ad7768_set_freq(st, st->samp_freq);
> > +}
> > +
> > +static int ad7768_get_fil_type_attr(struct iio_dev *dev,
> > + const struct iio_chan_spec *chan)
> > +{
> > + struct ad7768_state *st = iio_priv(dev);
> > + int ret;
> > + unsigned int mode;
> > +
> > + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode);
> > + if (ret)
> > + return ret;
> > +
> > + mode = FIELD_GET(AD7768_DIG_FIL_FIL_MSK, mode);
> > + /* Filter types from 0 to 2 are represented as SINC5 */
> > + if (mode < SINC3)
> > + return SINC5;
> > +
> > + /*
> > + * Remove the offset for the sinc3 and wideband filters
> > + * to get the corresponding attribute enum value
> > + */
> > + return mode - 2;
> > }
> >
> > static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
> > @@ -490,43 +702,37 @@ static int ad7768_gpio_init(struct iio_dev *indio_dev)
> > static int ad7768_set_freq(struct ad7768_state *st,
> > unsigned int freq)
> > {
> > - unsigned int diff_new, diff_old, pwr_mode, i, idx;
> > + unsigned int diff_new, diff_old, i, idx;
> > int res, ret;
> >
> > + freq = clamp_t(unsigned int, freq, 50, 1024000);
> > diff_old = U32_MAX;
> > idx = 0;
> >
> > - res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
> > + if (freq == 0)
> > + return -EINVAL;
> > +
> > + res = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->oversampling_ratio);
> >
> > /* Find the closest match for the desired sampling frequency */
> > - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
> > - diff_new = abs(res - ad7768_clk_config[i].clk_div);
> > + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) {
> > + diff_new = abs(res - ad7768_mclk_div_rates[i]);
> > if (diff_new < diff_old) {
> > diff_old = diff_new;
> > idx = i;
> > }
> > }
> >
> > - /*
> > - * Set both the mclk_div and pwrmode with a single write to the
> > - * POWER_CLOCK register
> > - */
> > - pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
> > - AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
> > - ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode);
> > - if (ret < 0)
> > - return ret;
> > -
> > - ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
> > - if (ret < 0)
> > + /* Set both the mclk_div and pwrmode */
> > + ret = ad7768_set_mclk_div(st, idx);
> > + if (ret)
> > return ret;
> >
> > - st->dec_rate = ad7768_clk_config[idx].clk_div /
> > - ad7768_mclk_div_rates[ad7768_clk_config[idx].mclk_div];
> > st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
> > - ad7768_clk_config[idx].clk_div);
> > + ad7768_mclk_div_rates[idx] * st->oversampling_ratio);
> >
> > - return 0;
> > + /* A sync-in pulse is required every time the filter dec rate changes */
>
> Does this function actually change oversampling_ration/decimation rate? Or do
> we also need to sync after changing other filter parameters?
>
This function would only change the power mode and mclk divider, so i
will fix the comment and add the sync to the other configuration
changes.
> > + return ad7768_send_sync_pulse(st);
> > }
> >
> > static int ad7768_set_vcm_output(struct ad7768_state *st, unsigned int mode)
> > @@ -540,13 +746,16 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev,
> > {
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > struct ad7768_state *st = iio_priv(indio_dev);
> > - unsigned int freq;
> > + unsigned int freq, freq_filtered;
> > int i, len = 0;
> >
> > - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
> > - freq = DIV_ROUND_CLOSEST(st->mclk_freq,
> > - ad7768_clk_config[i].clk_div);
> > - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
> > + freq_filtered = DIV_ROUND_CLOSEST(st->mclk_freq, st->oversampling_ratio);
> > + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) {
> > + freq = DIV_ROUND_CLOSEST(freq_filtered,
> > + ad7768_mclk_div_rates[i]);
> > + /* Sampling frequency cannot be lower than the minimum of 50 SPS */
> > + if (freq >= 50)
> > + len += sysfs_emit_at(buf, len, "%d ", freq);
> > }
> >
> > buf[len - 1] = '\n';
> > @@ -556,6 +765,37 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev,
> >
> > static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
> >
> > +static ssize_t oversampling_ratio_available_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > + struct ad7768_state *st = iio_priv(indio_dev);
> > + int len = 0;
> > +
> > + /* Return oversampling ratio available in range format */
> > + buf[len++] = '[';
> > + if (st->filter_type == SINC3) {
> > + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN);
> > + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN);
> > + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MAX);
> > + } else if (st->filter_type == WIDEBAND) {
> > + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN);
> > + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN);
> > + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MAX);
> > + } else {
> > + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN);
> > + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN);
> > + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MAX); }
> > +
> > + buf[len - 1] = ']';
> > + buf[len++] = '\n';
> > +
> > + return len;
> > +}
> > +
> > +static IIO_DEVICE_ATTR_RO(oversampling_ratio_available, 0);
> > +
> > static int ad7768_read_raw(struct iio_dev *indio_dev,
> > struct iio_chan_spec const *chan,
> > int *val, int *val2, long info)
> > @@ -597,6 +837,11 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
> > case IIO_CHAN_INFO_SAMP_FREQ:
> > *val = st->samp_freq;
> >
> > + return IIO_VAL_INT;
> > +
> > + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> > + *val = st->oversampling_ratio;
> > +
> > return IIO_VAL_INT;
> > }
> >
> > @@ -608,10 +853,19 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
> > int val, int val2, long info)
> > {
> > struct ad7768_state *st = iio_priv(indio_dev);
> > + int ret;
> >
> > switch (info) {
> > case IIO_CHAN_INFO_SAMP_FREQ:
> > return ad7768_set_freq(st, val);
> > +
> > + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>
> Probably should have iio_device_claim_direct_mode() here since this is poking
> registers.
>
> case IIO_CHAN_INFO_SAMP_FREQ: needs it too, so I would suggest to rename this
> function to __ad7768_write_raw() and then write:
>
> static int ad7768_write_raw(struct iio_dev *indio_dev,
> int val, int val2, long info)
> {
> int ret;
>
> ret = iio_device_claim_direct_mode(indio_dev);
> if (ret)
> return ret;
>
> ret = __ad7768_write_raw(indio_dev, val, val2, info);
> iio_device_release_direct_mode(indio_dev);
>
> return ret;
> }
>
> This is the preferred style to avoid extra indent and error unwinding complexity.
>
Thanks for the suggestion!
> > + ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val);
> > + if (ret)
> > + return ret;
> > +
> > + /* Update sampling frequency */
> > + return ad7768_set_freq(st, st->samp_freq);
> > default:
> > return -EINVAL;
> > }
> > @@ -627,6 +881,7 @@ static int ad7768_read_label(struct iio_dev *indio_dev,
> >
> > static struct attribute *ad7768_attributes[] = {
> > &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> > + &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
> > NULL
> > };
>
> Opportunity for another preliminary cleanup patch. IIO has core support for
> *_available attributes now, so we can implement struct iio_info.read_avail
> callback instead of manually creating attribute. Existing
> sampling_frequency_available attribute can be converted to use this, then
> use it for oversampling_ratio_available as well.
>
Sure, will do!
> >
> > @@ -639,7 +894,7 @@ static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev,
> > {
> > struct ad7768_state *st = iio_priv(indio_dev);
> >
> > - return st->dec_rate == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
> > + return st->oversampling_ratio == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED :
> > AD7768_SCAN_TYPE_NORMAL;
> > }
> >
> > @@ -745,6 +1000,12 @@ static int ad7768_setup(struct iio_dev *indio_dev)
> > return ret;
> > }
> >
> > + /*
> > + * Set Default Digital Filter configuration:
> > + * SINC5 filter with x32 Decimation rate
> > + */
> > + ret = ad7768_configure_dig_fil(indio_dev, SINC5, 32);
> > +
> > /* Set the default sampling frequency to 32000 kSPS */
> > return ad7768_set_freq(st, 32000);
> > }
>
^ permalink raw reply [flat|nested] 56+ messages in thread
* Re: [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI
2025-01-27 15:14 ` [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI Jonathan Santos
2025-01-28 0:08 ` David Lechner
@ 2025-02-03 15:28 ` Marcelo Schmitt
1 sibling, 0 replies; 56+ messages in thread
From: Marcelo Schmitt @ 2025-02-03 15:28 UTC (permalink / raw)
To: Jonathan Santos
Cc: linux-iio, devicetree, linux-kernel, lars, Michael.Hennerich,
marcelo.schmitt, jic23, robh, krzk+dt, conor+dt, jonath4nns
On 01/27, Jonathan Santos wrote:
> The synchronization method using GPIO requires the generated pulse to be
> truly synchronous with the base MCLK signal. When it is not possible to
> do that in hardware, the datasheet recommends using synchronization over
> SPI, where the generated pulse is already synchronous with MCLK. This
> requires the SYNC_OUT pin to be connected to SYNC_IN pin. In
> multidevices setup, the SYNC_OUT from other devices can be used as
> synchronization source.
>
> Use trigger-sources property to enable device synchronization over SPI
> for single and multiple devices.
>
> Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
> ---
> v2 Changes:
> * Synchronization via SPI is enabled when the Sync GPIO is not defined.
> * now trigger-sources property indicates the synchronization provider or
> main device. The main device will be used to drive the SYNC_IN when
> requested (via GPIO or SPI).
> ---
> drivers/iio/adc/ad7768-1.c | 81 ++++++++++++++++++++++++++++++++++----
> 1 file changed, 73 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
> index 7686556c7808..01ccbe0aa708 100644
> --- a/drivers/iio/adc/ad7768-1.c
> +++ b/drivers/iio/adc/ad7768-1.c
> @@ -193,6 +193,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
>
> struct ad7768_state {
> struct spi_device *spi;
> + struct spi_device *sync_source_spi;
> struct regmap *regmap;
> struct regulator *vref;
> struct mutex lock;
> @@ -206,6 +207,7 @@ struct ad7768_state {
> struct iio_trigger *trig;
> struct gpio_desc *gpio_sync_in;
> struct gpio_desc *gpio_reset;
> + bool en_spi_sync;
> const char *labels[ARRAY_SIZE(ad7768_channels)];
> /*
> * DMA (thus cache coherency maintenance) may require the
> @@ -264,6 +266,21 @@ static int ad7768_spi_reg_write(void *context,
> return spi_write(st->spi, st->data.d8, 2);
> }
>
> +static int ad7768_send_sync_pulse(struct ad7768_state *st)
> +{
> + if (st->en_spi_sync) {
> + st->data.d8[0] = AD7768_WR_FLAG_MSK(AD7768_REG_SYNC_RESET);
> + st->data.d8[1] = 0x00;
> +
> + return spi_write(st->sync_source_spi, st->data.d8, 2);
Hmm, is it really OK to do something like that? I mean, is that safe?
I wonder if that can lead to race conditions if, for example, sync_source_spi
is already registered as an IIO device doing buffered capture.
Or maybe sync_source_spi is in the middle of some read-modify-write cycle
and we sneak in this transfer. I think we also don't know from here if
sync_source_spi device is expecting to have it's AD7768_REG_SYNC_RESET changed.
For multi-device synchronization, I'm still biased towards something like we
had for for quad-adaq7768-1 [1] with one device managing the others.
I would like to try something like that along with the component infrastructure
(include/linux/component.h, drivers/base/component.c), but I already have
a bunch of pending stuff.
Basic idea would be to treat multiple ADCs that are intended for synchronized
read as a single aggregated device (that would have and manage all individual
SPI devices) with component infrastructure.
I suspect that might be useful for the AD7192 [2] case as well.
[1]: https://github.com/analogdevicesinc/linux/tree/adaq7768-aggregate-dev
[2]: https://lore.kernel.org/linux-iio/20241128125811.11913-4-alisa.roman@analog.com/
Anyway, I guess better to just do what David suggested and check the
trigger-source device is the same as this SPI device and then write to our own
registers or error out otherwise.
> + } else if (st->gpio_sync_in) {
> + gpiod_set_value_cansleep(st->gpio_sync_in, 1);
> + gpiod_set_value_cansleep(st->gpio_sync_in, 0);
> + }
> +
> + return 0;
> +}
> +
...
^ permalink raw reply [flat|nested] 56+ messages in thread
end of thread, other threads:[~2025-02-03 15:27 UTC | newest]
Thread overview: 56+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-27 15:10 [PATCH v2 00/16] Add features, improvements, and fixes Jonathan Santos
2025-01-27 15:11 ` [PATCH v2 01/16] iio: adc: ad7768-1: Fix conversion result sign Jonathan Santos
2025-02-01 15:27 ` Jonathan Cameron
2025-02-01 15:36 ` Jonathan Cameron
2025-01-27 15:11 ` [PATCH v2 02/16] dt-bindings: iio: adc: ad7768-1: add trigger-sources property Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-27 16:55 ` Rob Herring
2025-01-28 1:28 ` David Lechner
2025-01-28 15:04 ` Jonathan Santos
2025-01-28 15:56 ` David Lechner
2025-01-30 16:16 ` Jonathan Cameron
2025-01-27 15:11 ` [PATCH v2 03/16] dt-bindings: iio: adc: ad7768-1: Document GPIO controller Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-27 16:56 ` Rob Herring (Arm)
2025-01-27 15:12 ` [PATCH v2 04/16] dt-bindings: iio: adc: ad7768-1: add VMC output property Jonathan Santos
2025-01-27 16:30 ` Rob Herring (Arm)
2025-01-28 1:28 ` David Lechner
2025-01-30 16:21 ` Jonathan Cameron
2025-01-27 15:12 ` [PATCH v2 05/16] Documentation: ABI: add wideband filter type to sysfs-bus-iio Jonathan Santos
2025-01-28 1:32 ` David Lechner
2025-01-30 16:29 ` Jonathan Cameron
2025-01-27 15:12 ` [PATCH v2 06/16] iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset Jonathan Santos
2025-02-01 15:31 ` Jonathan Cameron
2025-02-03 11:34 ` Jonathan Santos
2025-01-27 15:12 ` [PATCH v2 07/16] iio: adc: ad7768-1: convert driver to use regmap Jonathan Santos
2025-01-28 1:29 ` David Lechner
2025-01-28 13:25 ` Nuno Sá
2025-01-28 14:46 ` Jonathan Santos
2025-01-28 15:09 ` Nuno Sá
2025-01-30 16:32 ` Jonathan Cameron
2025-02-03 11:44 ` Jonathan Santos
2025-01-27 15:12 ` [PATCH v2 08/16] iio: adc: ad7768-1: Add reset gpio Jonathan Santos
2025-01-27 22:43 ` David Lechner
2025-02-03 13:46 ` Marcelo Schmitt
2025-01-27 15:13 ` [PATCH v2 09/16] iio: adc: ad7768-1: remove unnecessary locking Jonathan Santos
2025-01-27 22:46 ` David Lechner
2025-01-27 15:13 ` [PATCH v2 10/16] iio: adc: ad7768-1: Move buffer allocation to a separate function Jonathan Santos
2025-02-01 15:35 ` Jonathan Cameron
2025-02-03 12:03 ` Jonathan Santos
2025-01-27 15:13 ` [PATCH v2 11/16] iio: adc: ad7768-1: Add VCM output support Jonathan Santos
2025-01-27 23:07 ` David Lechner
2025-01-27 15:13 ` [PATCH v2 12/16] iio: adc: ad7768-1: Add GPIO controller support Jonathan Santos
2025-01-27 23:34 ` David Lechner
2025-02-03 13:08 ` Jonathan Santos
2025-02-01 15:50 ` Jonathan Cameron
2025-01-27 15:13 ` [PATCH v2 13/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode Jonathan Santos
2025-01-27 23:47 ` David Lechner
2025-01-27 15:14 ` [PATCH v2 14/16] iio: adc: ad7768-1: add support for Synchronization over SPI Jonathan Santos
2025-01-28 0:08 ` David Lechner
2025-02-03 15:28 ` Marcelo Schmitt
2025-01-27 15:14 ` [PATCH v2 15/16] iio: adc: ad7768-1: add filter type and oversampling ratio attributes Jonathan Santos
2025-01-28 1:24 ` David Lechner
2025-02-03 14:58 ` Jonathan Santos
2025-01-30 16:39 ` Jonathan Cameron
2025-01-27 15:14 ` [PATCH v2 16/16] iio: adc: ad7768-1: add low pass -3dB cutoff attribute Jonathan Santos
2025-01-28 1:27 ` David Lechner
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).