* [PATCH 01/12] dt-bindings: iio: dac: ad5696: add reset/ldac/gain gpio support
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 02/12] dt-bindings: iio: dac: ad5696: rework on power supplies Rodrigo Alencar via B4 Relay
` (10 subsequent siblings)
11 siblings, 0 replies; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar,
Conor Dooley
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add GPIO property for RESET, LDAC and GAIN pin. RESET is active-low, LDAC
is used to load DAC channels with values from input registers and GAIN
can double the voltage in output channels. The gain-gpios property is
not available to all supported parts.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
.../devicetree/bindings/iio/dac/adi,ad5696.yaml | 34 +++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
index b5a88b03dc2f..2dcc049f30e9 100644
--- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
@@ -37,14 +37,45 @@ properties:
description: |
The regulator supply for DAC reference voltage.
+ reset-gpios:
+ description: Active-low RESET pin to reset the device.
+ maxItems: 1
+
+ ldac-gpios:
+ description:
+ Active-low LDAC pin used to asynchronously update the DAC channels.
+ maxItems: 1
+
+ gain-gpios:
+ description:
+ GAIN pin that sets a multiplier for the DAC output voltage. When high,
+ the DAC output voltage is multiplied by 2, otherwise it is unchanged.
+ maxItems: 1
+
required:
- compatible
- reg
-additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ anyOf:
+ - const: adi,ad5311r
+ - const: adi,ad5691r
+ - const: adi,ad5692r
+ - const: adi,ad5693
+ - const: adi,ad5693r
+ then:
+ properties:
+ gain-gpios: false
+
+unevaluatedProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -53,6 +84,7 @@ examples:
compatible = "adi,ad5696";
reg = <0>;
vcc-supply = <&dac_vref>;
+ ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
};
...
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH 02/12] dt-bindings: iio: dac: ad5696: rework on power supplies
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 01/12] dt-bindings: iio: dac: ad5696: add reset/ldac/gain gpio support Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-03 15:25 ` Conor Dooley
2026-06-02 16:33 ` [PATCH 03/12] dt-bindings: iio: dac: ad5686: add reset/ldac/gain gpio support Rodrigo Alencar via B4 Relay
` (9 subsequent siblings)
11 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add supplies for VDD, VLOGIC and VREF input voltage pins. The vcc-supply
property is deprecated, once it does not really exist as none of the
devices describe any power input with that name. VCC is also misleading as
it sounds like the input power supply, but it is being used as an external
voltage reference, which should be called VREF. Certain devices require
vref-supply to be available once an internal reference voltage is absent.
For correct operation vdd and vlogic supplies are required.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
.../devicetree/bindings/iio/dac/adi,ad5696.yaml | 31 +++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
index 2dcc049f30e9..e5fbaec4adf7 100644
--- a/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5696.yaml
@@ -33,9 +33,19 @@ properties:
reg:
maxItems: 1
+ vdd-supply:
+ description: Input power supply.
+
+ vlogic-supply:
+ description: Digital power supply.
+
+ vref-supply:
+ description:
+ Reference voltage supply. If not supplied the internal reference is used.
+
vcc-supply:
- description: |
- The regulator supply for DAC reference voltage.
+ deprecated: true
+ description: Use vref-supply instead.
reset-gpios:
description: Active-low RESET pin to reset the device.
@@ -55,8 +65,21 @@ properties:
required:
- compatible
- reg
+ - vdd-supply
+ - vlogic-supply
allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ anyOf:
+ - const: adi,ad5693
+ - const: adi,ad5694
+ - const: adi,ad5696
+ then:
+ required:
+ - vref-supply
- if:
properties:
compatible:
@@ -83,7 +106,9 @@ examples:
ad5696: dac@0 {
compatible = "adi,ad5696";
reg = <0>;
- vcc-supply = <&dac_vref>;
+ vdd-supply = <&dac_vdd>;
+ vlogic-supply = <&dac_vlogic>;
+ vref-supply = <&dac_vref>;
ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
};
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 02/12] dt-bindings: iio: dac: ad5696: rework on power supplies
2026-06-02 16:33 ` [PATCH 02/12] dt-bindings: iio: dac: ad5696: rework on power supplies Rodrigo Alencar via B4 Relay
@ 2026-06-03 15:25 ` Conor Dooley
0 siblings, 0 replies; 30+ messages in thread
From: Conor Dooley @ 2026-06-03 15:25 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
[-- Attachment #1: Type: text/plain, Size: 75 bytes --]
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 03/12] dt-bindings: iio: dac: ad5686: add reset/ldac/gain gpio support
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 01/12] dt-bindings: iio: dac: ad5696: add reset/ldac/gain gpio support Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 02/12] dt-bindings: iio: dac: ad5696: rework on power supplies Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 04/12] dt-bindings: iio: dac: ad5686: rework on power supplies Rodrigo Alencar via B4 Relay
` (8 subsequent siblings)
11 siblings, 0 replies; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar,
Conor Dooley
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add GPIO property for RESET, LDAC and GAIN pin. RESET is active-low, LDAC
is used to load DAC channels with values from input registers and GAIN
can double the voltage in output channels. The gain-gpios property is
not available to all supported parts.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
.../devicetree/bindings/iio/dac/adi,ad5686.yaml | 31 ++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml
index 713f535bb33a..4680d4753dd4 100644
--- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml
@@ -35,17 +35,46 @@ properties:
vcc-supply:
description: If not supplied the internal reference is used.
+ reset-gpios:
+ description: Active-low RESET pin to reset the device.
+ maxItems: 1
+
+ ldac-gpios:
+ description:
+ Active-low LDAC pin used to asynchronously update the DAC channels.
+ maxItems: 1
+
+ gain-gpios:
+ description:
+ GAIN pin that sets a multiplier for the DAC output voltage. When high,
+ the DAC output voltage is multiplied by 2, otherwise it is unchanged.
+ maxItems: 1
+
required:
- compatible
- reg
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ anyOf:
+ - const: adi,ad5310r
+ - const: adi,ad5681r
+ - const: adi,ad5682r
+ - const: adi,ad5683
+ - const: adi,ad5683r
+ then:
+ properties:
+ gain-gpios: false
unevaluatedProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
@@ -53,6 +82,8 @@ examples:
reg = <0>;
compatible = "adi,ad5310r";
vcc-supply = <&dac_vref0>;
+ reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+ ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
};
...
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH 04/12] dt-bindings: iio: dac: ad5686: rework on power supplies
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (2 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 03/12] dt-bindings: iio: dac: ad5686: add reset/ldac/gain gpio support Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-03 15:29 ` Conor Dooley
2026-06-02 16:33 ` [PATCH 05/12] iio: dac: ad5686: add support for missing " Rodrigo Alencar via B4 Relay
` (7 subsequent siblings)
11 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add supplies for VDD, VLOGIC and VREF input voltage pins. The vcc-supply
property is deprecated, once it does not really exist as none of the
devices describe any power input with that name. VCC is also misleading as
it sounds like the input power supply, but it is being used as an external
voltage reference, which should be called VREF. Certain devices require
vref-supply to be available once an internal reference voltage is absent.
For correct operation vdd and vlogic supplies are required.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
.../devicetree/bindings/iio/dac/adi,ad5686.yaml | 31 ++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml
index 4680d4753dd4..2abdbf325392 100644
--- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml
@@ -32,8 +32,19 @@ properties:
reg:
maxItems: 1
+ vdd-supply:
+ description: Input power supply.
+
+ vlogic-supply:
+ description: Digital power supply.
+
+ vref-supply:
+ description:
+ Reference voltage supply. If not supplied the internal reference is used.
+
vcc-supply:
- description: If not supplied the internal reference is used.
+ deprecated: true
+ description: Use vref-supply instead.
reset-gpios:
description: Active-low RESET pin to reset the device.
@@ -53,9 +64,23 @@ properties:
required:
- compatible
- reg
+ - vdd-supply
+ - vlogic-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ anyOf:
+ - const: adi,ad5676
+ - const: adi,ad5683
+ - const: adi,ad5684
+ - const: adi,ad5686
+ then:
+ required:
+ - vref-supply
- if:
properties:
compatible:
@@ -81,7 +106,9 @@ examples:
dac@0 {
reg = <0>;
compatible = "adi,ad5310r";
- vcc-supply = <&dac_vref0>;
+ vdd-supply = <&dac_vdd>;
+ vlogic-supply = <&dac_vlogic>;
+ vref-supply = <&dac_vref>;
reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
ldac-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 04/12] dt-bindings: iio: dac: ad5686: rework on power supplies
2026-06-02 16:33 ` [PATCH 04/12] dt-bindings: iio: dac: ad5686: rework on power supplies Rodrigo Alencar via B4 Relay
@ 2026-06-03 15:29 ` Conor Dooley
0 siblings, 0 replies; 30+ messages in thread
From: Conor Dooley @ 2026-06-03 15:29 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
[-- Attachment #1: Type: text/plain, Size: 75 bytes --]
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 05/12] iio: dac: ad5686: add support for missing power supplies
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (3 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 04/12] dt-bindings: iio: dac: ad5686: rework on power supplies Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:02 ` Andy Shevchenko
2026-06-02 16:33 ` [PATCH 06/12] iio: dac: ad5686: consume optional reset signal Rodrigo Alencar via B4 Relay
` (6 subsequent siblings)
11 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Get and enable regulators for vdd, vlogic and vref input power pins. Vdd
is the input power supply, while vlogic powers the digital side. vref is
replacing vcc, which is being deprecated, but still supported. The value
of vref_mv is checked so that a device without internal voltage reference
cannot proceed without an explicit supply. For correct operation, vdd and
vlogic are required, then devm_regulator_get_enable() is used so the
driver can still work without them by using the stub/dummy regulators.
Error report uses dev_err_probe(), which helps debugging an init issue.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 5840fda4b011..4a8c587ff116 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -8,6 +8,7 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kstrtox.h>
@@ -484,13 +485,27 @@ int ad5686_probe(struct device *dev,
st->ops = ops;
st->chip_info = chip_info;
- ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vdd supply\n");
+
+ ret = devm_regulator_get_enable(dev, "vlogic");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable vlogic supply\n");
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */
+ ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
if (ret < 0 && ret != -ENODEV)
- return ret;
+ return dev_err_probe(dev, ret, "failed to read vref voltage\n");
st->use_internal_vref = ret == -ENODEV;
st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000;
+ if (!st->vref_mv)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid or not provided vref voltage\n");
+
/* Initialize masks to all ones */
st->pwr_down_mask = ~0;
st->pwr_down_mode = ~0;
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 05/12] iio: dac: ad5686: add support for missing power supplies
2026-06-02 16:33 ` [PATCH 05/12] iio: dac: ad5686: add support for missing " Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:02 ` Andy Shevchenko
2026-06-03 12:17 ` Rodrigo Alencar
0 siblings, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:02 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:52PM +0100, Rodrigo Alencar via B4 Relay wrote:
>
> Get and enable regulators for vdd, vlogic and vref input power pins. Vdd
> is the input power supply, while vlogic powers the digital side. vref is
> replacing vcc, which is being deprecated, but still supported. The value
> of vref_mv is checked so that a device without internal voltage reference
> cannot proceed without an explicit supply. For correct operation, vdd and
> vlogic are required, then devm_regulator_get_enable() is used so the
> driver can still work without them by using the stub/dummy regulators.
> Error report uses dev_err_probe(), which helps debugging an init issue.
...
> + ret = devm_regulator_get_enable_read_voltage(dev, "vref");
> + if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */
> + ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
> if (ret < 0 && ret != -ENODEV)
It can be deduplicated now with
else if (ret < 0)
> - return ret;
> + return dev_err_probe(dev, ret, "failed to read vref voltage\n");
>
> st->use_internal_vref = ret == -ENODEV;
> st->vref_mv = st->use_internal_vref ? st->chip_info->int_vref_mv : ret / 1000;
>
Drop this blank line as the assignment and check are coupled.
> + if (!st->vref_mv)
> + return dev_err_probe(dev, -EINVAL,
> + "invalid or not provided vref voltage\n");
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 05/12] iio: dac: ad5686: add support for missing power supplies
2026-06-02 19:02 ` Andy Shevchenko
@ 2026-06-03 12:17 ` Rodrigo Alencar
0 siblings, 0 replies; 30+ messages in thread
From: Rodrigo Alencar @ 2026-06-03 12:17 UTC (permalink / raw)
To: Andy Shevchenko, rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On 26/06/02 10:02PM, Andy Shevchenko wrote:
> On Tue, Jun 02, 2026 at 05:33:52PM +0100, Rodrigo Alencar via B4 Relay wrote:
> >
> > Get and enable regulators for vdd, vlogic and vref input power pins. Vdd
> > is the input power supply, while vlogic powers the digital side. vref is
> > replacing vcc, which is being deprecated, but still supported. The value
> > of vref_mv is checked so that a device without internal voltage reference
> > cannot proceed without an explicit supply. For correct operation, vdd and
> > vlogic are required, then devm_regulator_get_enable() is used so the
> > driver can still work without them by using the stub/dummy regulators.
> > Error report uses dev_err_probe(), which helps debugging an init issue.
>
> ...
>
> > + ret = devm_regulator_get_enable_read_voltage(dev, "vref");
> > + if (ret == -ENODEV) /* vcc-supply is deprecated, but supported still */
> > + ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
>
> > if (ret < 0 && ret != -ENODEV)
>
> It can be deduplicated now with
>
> else if (ret < 0)
Not really, because ret is overwritten with
ret = devm_regulator_get_enable_read_voltage(dev, "vcc")
so the check for if (ret < 0 && ret != -ENODEV) is intentional
> > - return ret;
> > + return dev_err_probe(dev, ret, "failed to read vref voltage\n");
--
Kind regards,
Rodrigo Alencar
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 06/12] iio: dac: ad5686: consume optional reset signal
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (4 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 05/12] iio: dac: ad5686: add support for missing " Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:03 ` Andy Shevchenko
2026-06-03 8:28 ` Nuno Sá
2026-06-02 16:33 ` [PATCH 07/12] iio: dac: ad5686: add ldac gpio Rodrigo Alencar via B4 Relay
` (5 subsequent siblings)
11 siblings, 2 replies; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add RESET pin GPIO support through an optional reset control, which is
local to the probe function. Also, include delays for power-up time and
reset pulse width.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 4a8c587ff116..345ca2436332 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -8,12 +8,14 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/sysfs.h>
#include <linux/wordpart.h>
@@ -471,6 +473,7 @@ int ad5686_probe(struct device *dev,
const struct ad5686_chip_info *chip_info,
const char *name, const struct ad5686_bus_ops *ops)
{
+ struct reset_control *rstc;
struct ad5686_state *st;
struct iio_dev *indio_dev;
int ret, i;
@@ -506,6 +509,16 @@ int ad5686_probe(struct device *dev,
return dev_err_probe(dev, -EINVAL,
"invalid or not provided vref voltage\n");
+ rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "Failed to get reset control\n");
+
+ udelay(5); /* power-up time */
+ reset_control_assert(rstc);
+ udelay(1); /* reset pulse: comfortably bigger than the spec */
+ reset_control_deassert(rstc);
+
/* Initialize masks to all ones */
st->pwr_down_mask = ~0;
st->pwr_down_mode = ~0;
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 06/12] iio: dac: ad5686: consume optional reset signal
2026-06-02 16:33 ` [PATCH 06/12] iio: dac: ad5686: consume optional reset signal Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:03 ` Andy Shevchenko
2026-06-03 8:28 ` Nuno Sá
1 sibling, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:03 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:53PM +0100, Rodrigo Alencar via B4 Relay wrote:
> Add RESET pin GPIO support through an optional reset control, which is
> local to the probe function. Also, include delays for power-up time and
> reset pulse width.
...
> + udelay(5); /* power-up time */
> + reset_control_assert(rstc);
> + udelay(1); /* reset pulse: comfortably bigger than the spec */
> + reset_control_deassert(rstc);
This doesn't seem like an atomic context. Let fsleep() to decide what to do.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 06/12] iio: dac: ad5686: consume optional reset signal
2026-06-02 16:33 ` [PATCH 06/12] iio: dac: ad5686: consume optional reset signal Rodrigo Alencar via B4 Relay
2026-06-02 19:03 ` Andy Shevchenko
@ 2026-06-03 8:28 ` Nuno Sá
2026-06-03 12:08 ` Jonathan Cameron
1 sibling, 1 reply; 30+ messages in thread
From: Nuno Sá @ 2026-06-03 8:28 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:53PM +0100, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add RESET pin GPIO support through an optional reset control, which is
> local to the probe function. Also, include delays for power-up time and
> reset pulse width.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
> drivers/iio/dac/ad5686.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> index 4a8c587ff116..345ca2436332 100644
> --- a/drivers/iio/dac/ad5686.c
> +++ b/drivers/iio/dac/ad5686.c
> @@ -8,12 +8,14 @@
> #include <linux/array_size.h>
> #include <linux/bitfield.h>
> #include <linux/bitops.h>
> +#include <linux/delay.h>
> #include <linux/dev_printk.h>
> #include <linux/errno.h>
> #include <linux/export.h>
> #include <linux/kstrtox.h>
> #include <linux/module.h>
> #include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> #include <linux/sysfs.h>
> #include <linux/wordpart.h>
>
> @@ -471,6 +473,7 @@ int ad5686_probe(struct device *dev,
> const struct ad5686_chip_info *chip_info,
> const char *name, const struct ad5686_bus_ops *ops)
> {
> + struct reset_control *rstc;
> struct ad5686_state *st;
> struct iio_dev *indio_dev;
> int ret, i;
> @@ -506,6 +509,16 @@ int ad5686_probe(struct device *dev,
> return dev_err_probe(dev, -EINVAL,
> "invalid or not provided vref voltage\n");
>
> + rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
> + if (IS_ERR(rstc))
> + return dev_err_probe(dev, PTR_ERR(rstc),
> + "Failed to get reset control\n");
On top of what Andy stated, I'm fairly sure
devm_reset_control_get_optional_exclusive() returns with the GPIO
asserted.
> +
> + udelay(5); /* power-up time */
> + reset_control_assert(rstc);
> + udelay(1); /* reset pulse: comfortably bigger than the spec */
> + reset_control_deassert(rstc);
> +
> /* Initialize masks to all ones */
> st->pwr_down_mask = ~0;
> st->pwr_down_mode = ~0;
>
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 06/12] iio: dac: ad5686: consume optional reset signal
2026-06-03 8:28 ` Nuno Sá
@ 2026-06-03 12:08 ` Jonathan Cameron
2026-06-03 12:57 ` Philipp Zabel
0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Cameron @ 2026-06-03 12:08 UTC (permalink / raw)
To: Nuno Sá
Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Wed, 3 Jun 2026 09:28:26 +0100
Nuno Sá <noname.nuno@gmail.com> wrote:
> On Tue, Jun 02, 2026 at 05:33:53PM +0100, Rodrigo Alencar via B4 Relay wrote:
> > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> >
> > Add RESET pin GPIO support through an optional reset control, which is
> > local to the probe function. Also, include delays for power-up time and
> > reset pulse width.
> >
> > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > ---
> > drivers/iio/dac/ad5686.c | 13 +++++++++++++
> > 1 file changed, 13 insertions(+)
> >
> > diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> > index 4a8c587ff116..345ca2436332 100644
> > --- a/drivers/iio/dac/ad5686.c
> > +++ b/drivers/iio/dac/ad5686.c
> > @@ -8,12 +8,14 @@
> > #include <linux/array_size.h>
> > #include <linux/bitfield.h>
> > #include <linux/bitops.h>
> > +#include <linux/delay.h>
> > #include <linux/dev_printk.h>
> > #include <linux/errno.h>
> > #include <linux/export.h>
> > #include <linux/kstrtox.h>
> > #include <linux/module.h>
> > #include <linux/regulator/consumer.h>
> > +#include <linux/reset.h>
> > #include <linux/sysfs.h>
> > #include <linux/wordpart.h>
> >
> > @@ -471,6 +473,7 @@ int ad5686_probe(struct device *dev,
> > const struct ad5686_chip_info *chip_info,
> > const char *name, const struct ad5686_bus_ops *ops)
> > {
> > + struct reset_control *rstc;
> > struct ad5686_state *st;
> > struct iio_dev *indio_dev;
> > int ret, i;
> > @@ -506,6 +509,16 @@ int ad5686_probe(struct device *dev,
> > return dev_err_probe(dev, -EINVAL,
> > "invalid or not provided vref voltage\n");
> >
> > + rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
> > + if (IS_ERR(rstc))
> > + return dev_err_probe(dev, PTR_ERR(rstc),
> > + "Failed to get reset control\n");
>
> On top of what Andy stated, I'm fairly sure
> devm_reset_control_get_optional_exclusive() returns with the GPIO
> asserted.
We've been getting reports on that not being the case from Sashiko
and when I last looked into one of those it definitely isn't documented
as doing so and I got the impression it is a reset controller specific
thing. Do we are fine here because the gpio reset controller reset_gpio_probe()
includes:
priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(priv->reset))
return dev_err_probe(dev, PTR_ERR(priv->reset),
"Could not get reset gpios\n");
Which I guess puts it in to reset?
So do we assume gpio reset or not for this sort of driver that specifies
in the binding reset-gpios. Now if the following is implying we need
a deasserted to asserted transition (maybe?) then we'd need to force
a deassert first.
Btw I used claude to explore this and it hallucinated the reverse polarity
providing otherwise correct code for what was in reset_gpio_probe() but
oddly editing that one line. I was being lazy and using the web UI rather
than a version with access to my git tree so maybe it scraped some
buggy code from a downstream tree. Anyhow watch out for subtle garbage!
It also took a few requests to get it to figure out the logical nature
of the GPIO signals rather than assuming they were controlling whether
the line was high or low directly.
Jonathan
>
> > +
> > + udelay(5); /* power-up time */
> > + reset_control_assert(rstc);
> > + udelay(1); /* reset pulse: comfortably bigger than the spec */
> > + reset_control_deassert(rstc);
> > +
> > /* Initialize masks to all ones */
> > st->pwr_down_mask = ~0;
> > st->pwr_down_mode = ~0;
> >
> > --
> > 2.43.0
> >
> >
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 06/12] iio: dac: ad5686: consume optional reset signal
2026-06-03 12:08 ` Jonathan Cameron
@ 2026-06-03 12:57 ` Philipp Zabel
0 siblings, 0 replies; 30+ messages in thread
From: Philipp Zabel @ 2026-06-03 12:57 UTC (permalink / raw)
To: Jonathan Cameron, Nuno Sá
Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kees Cook, Gustavo A. R. Silva
On Mi, 2026-06-03 at 13:08 +0100, Jonathan Cameron wrote:
> On Wed, 3 Jun 2026 09:28:26 +0100
> Nuno Sá <noname.nuno@gmail.com> wrote:
>
> > On Tue, Jun 02, 2026 at 05:33:53PM +0100, Rodrigo Alencar via B4 Relay wrote:
> > > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > >
> > > Add RESET pin GPIO support through an optional reset control, which is
> > > local to the probe function. Also, include delays for power-up time and
> > > reset pulse width.
> > >
> > > Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > ---
> > > drivers/iio/dac/ad5686.c | 13 +++++++++++++
> > > 1 file changed, 13 insertions(+)
> > >
> > > diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> > > index 4a8c587ff116..345ca2436332 100644
> > > --- a/drivers/iio/dac/ad5686.c
> > > +++ b/drivers/iio/dac/ad5686.c
> > > @@ -8,12 +8,14 @@
> > > #include <linux/array_size.h>
> > > #include <linux/bitfield.h>
> > > #include <linux/bitops.h>
> > > +#include <linux/delay.h>
> > > #include <linux/dev_printk.h>
> > > #include <linux/errno.h>
> > > #include <linux/export.h>
> > > #include <linux/kstrtox.h>
> > > #include <linux/module.h>
> > > #include <linux/regulator/consumer.h>
> > > +#include <linux/reset.h>
> > > #include <linux/sysfs.h>
> > > #include <linux/wordpart.h>
> > >
> > > @@ -471,6 +473,7 @@ int ad5686_probe(struct device *dev,
> > > const struct ad5686_chip_info *chip_info,
> > > const char *name, const struct ad5686_bus_ops *ops)
> > > {
> > > + struct reset_control *rstc;
> > > struct ad5686_state *st;
> > > struct iio_dev *indio_dev;
> > > int ret, i;
> > > @@ -506,6 +509,16 @@ int ad5686_probe(struct device *dev,
> > > return dev_err_probe(dev, -EINVAL,
> > > "invalid or not provided vref voltage\n");
> > >
> > > + rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
> > > + if (IS_ERR(rstc))
> > > + return dev_err_probe(dev, PTR_ERR(rstc),
> > > + "Failed to get reset control\n");
> >
> > On top of what Andy stated, I'm fairly sure
> > devm_reset_control_get_optional_exclusive() returns with the GPIO
> > asserted.
>
> We've been getting reports on that not being the case from Sashiko
> and when I last looked into one of those it definitely isn't documented
> as doing so and I got the impression it is a reset controller specific
> thing.
The reset controller API does not prescribe that freshly acquired
resets start in asserted state, because that isn't possible for self-
clearing resets.
If the chip needs to see the deasserted -> asserted flank, the driver
should start with a deassert.
> Do we are fine here because the gpio reset controller reset_gpio_probe()
> includes:
> priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->reset))
> return dev_err_probe(dev, PTR_ERR(priv->reset),
> "Could not get reset gpios\n");
> Which I guess puts it in to reset?
Yes. I would like consumer drivers to not rely on implicit knowledge
about the reset controller driver, though.
> So do we assume gpio reset or not for this sort of driver that specifies
> in the binding reset-gpios. Now if the following is implying we need
> a deasserted to asserted transition (maybe?) then we'd need to force
> a deassert first.
Iff the transition is necessary, explicitly deassert-then-assert.
regards
Philipp
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 07/12] iio: dac: ad5686: add ldac gpio
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (5 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 06/12] iio: dac: ad5686: consume optional reset signal Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:05 ` Andy Shevchenko
2026-06-02 16:33 ` [PATCH 08/12] iio: dac: ad5686: introduce sync operation Rodrigo Alencar via B4 Relay
` (4 subsequent siblings)
11 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
If wired LDAC, should be held low when unused (pin is active-low), which
allows for synchronous DAC updates. This will be used to update all the
channels at the same time when adding buffer support.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686.c | 5 +++++
drivers/iio/dac/ad5686.h | 3 +++
2 files changed, 8 insertions(+)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 345ca2436332..8f91838c5b53 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -514,6 +514,11 @@ int ad5686_probe(struct device *dev,
return dev_err_probe(dev, PTR_ERR(rstc),
"Failed to get reset control\n");
+ st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH);
+ if (IS_ERR(st->ldac_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->ldac_gpio),
+ "Failed to get LDAC GPIO\n");
+
udelay(5); /* power-up time */
reset_control_assert(rstc);
udelay(1); /* reset pulse: comfortably bigger than the spec */
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index a06fe7d89305..498d05ab12ee 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -9,6 +9,7 @@
#define __DRIVERS_IIO_DAC_AD5686_H__
#include <linux/bits.h>
+#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <linux/types.h>
@@ -119,6 +120,7 @@ extern const struct ad5686_chip_info ad5679r_chip_info;
* @dev: device instance
* @chip_info: chip model specific constants, available modes etc
* @ops: bus specific operations
+ * @ldac_gpio: LDAC pin GPIO descriptor
* @vref_mv: actual reference voltage used
* @pwr_down_mask: power down mask
* @pwr_down_mode: current power down mode
@@ -131,6 +133,7 @@ struct ad5686_state {
struct device *dev;
const struct ad5686_chip_info *chip_info;
const struct ad5686_bus_ops *ops;
+ struct gpio_desc *ldac_gpio;
unsigned short vref_mv;
unsigned int pwr_down_mask;
unsigned int pwr_down_mode;
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 07/12] iio: dac: ad5686: add ldac gpio
2026-06-02 16:33 ` [PATCH 07/12] iio: dac: ad5686: add ldac gpio Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:05 ` Andy Shevchenko
0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:05 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:54PM +0100, Rodrigo Alencar via B4 Relay wrote:
> If wired LDAC, should be held low when unused (pin is active-low), which
> allows for synchronous DAC updates. This will be used to update all the
> channels at the same time when adding buffer support.
...
> --- a/drivers/iio/dac/ad5686.h
> +++ b/drivers/iio/dac/ad5686.h
> @@ -9,6 +9,7 @@
> #define __DRIVERS_IIO_DAC_AD5686_H__
>
> #include <linux/bits.h>
> +#include <linux/gpio/consumer.h>
No user of this. C-file is the one you should include this in.
Hint: The opaque pointers can be just forward declared.
> #include <linux/mutex.h>
> #include <linux/types.h>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 08/12] iio: dac: ad5686: introduce sync operation
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (6 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 07/12] iio: dac: ad5686: add ldac gpio Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus Rodrigo Alencar via B4 Relay
` (3 subsequent siblings)
11 siblings, 0 replies; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add sync() to operation to ad5686_bus_ops, which can be used to flush
multiple pending data transfers at once. This is going to be used when
implementing triggered buffer support.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686.h | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index 498d05ab12ee..414d39863004 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -67,10 +67,12 @@ struct ad5686_state;
* struct ad5686_bus_ops - bus specific read/write operations
* @read: read a register value at the given address
* @write: write a command, address and value to the device
+ * @sync: ensure the completion of the write operation (optional)
*/
struct ad5686_bus_ops {
int (*read)(struct ad5686_state *st, u8 addr);
int (*write)(struct ad5686_state *st, u8 cmd, u8 addr, u16 val);
+ int (*sync)(struct ad5686_state *st);
};
/**
@@ -159,7 +161,13 @@ int ad5686_probe(struct device *dev,
static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val)
{
- return st->ops->write(st, cmd, addr, val);
+ int ret;
+
+ ret = st->ops->write(st, cmd, addr, val);
+ if (ret)
+ return ret;
+
+ return st->ops->sync ? st->ops->sync(st) : 0;
}
static inline int ad5686_read(struct ad5686_state *st, u8 addr)
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (7 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 08/12] iio: dac: ad5686: introduce sync operation Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:14 ` Andy Shevchenko
2026-06-03 12:24 ` Jonathan Cameron
2026-06-02 16:33 ` [PATCH 10/12] iio: dac: ad5686: add triggered buffer support Rodrigo Alencar via B4 Relay
` (2 subsequent siblings)
11 siblings, 2 replies; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Use of local SPI bus data to manage a collection of SPI transfers and
flush them to the SPI platform driver with the sync() operation. This
allows for faster handling of multiple channel DAC writes, avoiding kernel
overhead per spi_sync() call, which will be helpful when enabling
triggered buffer support.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686-spi.c | 109 +++++++++++++++++++++++++++++++------------
drivers/iio/dac/ad5686.c | 4 +-
drivers/iio/dac/ad5686.h | 8 +++-
drivers/iio/dac/ad5696-i2c.c | 2 +-
4 files changed, 88 insertions(+), 35 deletions(-)
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index 6b6ef1d7071f..66a5a2164395 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -12,59 +12,81 @@
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/spi/spi.h>
#include <asm/byteorder.h>
#include "ad5686.h"
+struct ad5686_spi_data {
+ struct spi_message msg;
+ unsigned int size;
+ unsigned int capacity;
+ struct spi_transfer xfers[] __counted_by(capacity);
+};
+
static int ad5686_spi_write(struct ad5686_state *st,
u8 cmd, u8 addr, u16 val)
{
- struct spi_device *spi = to_spi_device(st->dev);
- u8 tx_len, *buf;
+ struct ad5686_spi_data *bus_data = st->bus_data;
+ struct spi_transfer *xfer;
+
+ if (bus_data->size >= bus_data->capacity)
+ return -E2BIG;
+
+ if (bus_data->size)
+ bus_data->xfers[bus_data->size - 1].cs_change = 1;
+ else
+ spi_message_init(&bus_data->msg);
+
+ xfer = &bus_data->xfers[bus_data->size];
+ xfer->rx_buf = NULL;
+ xfer->cs_change = 0;
switch (st->chip_info->regmap_type) {
case AD5310_REGMAP:
- st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) |
- val);
- buf = &st->data[0].d8[0];
- tx_len = 2;
+ st->data[bus_data->size].d16 =
+ cpu_to_be16(AD5310_CMD(cmd) | val);
+ xfer->tx_buf = &st->data[bus_data->size].d8[0];
+ xfer->len = 2;
break;
case AD5683_REGMAP:
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
- AD5683_DATA(val));
- buf = &st->data[0].d8[1];
- tx_len = 3;
+ st->data[bus_data->size].d32 =
+ cpu_to_be32(AD5686_CMD(cmd) | AD5683_DATA(val));
+ xfer->tx_buf = &st->data[bus_data->size].d8[1];
+ xfer->len = 3;
break;
case AD5686_REGMAP:
- st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
- AD5686_ADDR(addr) |
- val);
- buf = &st->data[0].d8[1];
- tx_len = 3;
+ st->data[bus_data->size].d32 =
+ cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) | val);
+ xfer->tx_buf = &st->data[bus_data->size].d8[1];
+ xfer->len = 3;
break;
default:
return -EINVAL;
}
- return spi_write(spi, buf, tx_len);
+ spi_message_add_tail(xfer, &bus_data->msg);
+ bus_data->size++;
+
+ return 0;
+}
+
+static int ad5686_spi_sync(struct ad5686_state *st)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ struct ad5686_spi_data *bus_data = st->bus_data;
+
+ bus_data->size = 0; /* always reset, even on sync failure */
+ return spi_sync(spi, &bus_data->msg);
}
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
{
- struct spi_transfer t[] = {
- {
- .tx_buf = &st->data[0].d8[1],
- .len = 3,
- .cs_change = 1,
- }, {
- .tx_buf = &st->data[1].d8[1],
- .rx_buf = &st->data[2].d8[1],
- .len = 3,
- },
- };
struct spi_device *spi = to_spi_device(st->dev);
+ struct ad5686_spi_data *bus_data = st->bus_data;
+ struct spi_transfer *xfer = &bus_data->xfers[0];
u8 cmd = 0;
int ret;
@@ -85,8 +107,18 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
AD5686_ADDR(addr));
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
- ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
- if (ret < 0)
+ xfer[0].tx_buf = &st->data[0].d8[1];
+ xfer[0].len = 3;
+ xfer[0].cs_change = 1;
+ xfer[1].tx_buf = &st->data[1].d8[1];
+ xfer[1].rx_buf = &st->data[2].d8[1];
+ xfer[1].len = 3;
+ xfer[1].cs_change = 0;
+
+ spi_message_init_with_transfers(&bus_data->msg, xfer, 2);
+
+ ret = spi_sync(spi, &bus_data->msg);
+ if (ret)
return ret;
return be32_to_cpu(st->data[2].d32);
@@ -95,12 +127,27 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
static const struct ad5686_bus_ops ad5686_spi_ops = {
.write = ad5686_spi_write,
.read = ad5686_spi_read,
+ .sync = ad5686_spi_sync,
};
static int ad5686_spi_probe(struct spi_device *spi)
{
- return ad5686_probe(&spi->dev, spi_get_device_match_data(spi),
- spi->modalias, &ad5686_spi_ops);
+ const struct ad5686_chip_info *info = spi_get_device_match_data(spi);
+ struct ad5686_spi_data *bus_data;
+ unsigned int capacity;
+
+ /* read operation requires at least 2 transfers */
+ capacity = max(info->num_channels, 2);
+ bus_data = devm_kzalloc(&spi->dev,
+ struct_size(bus_data, xfers, capacity),
+ GFP_KERNEL);
+ if (!bus_data)
+ return -ENOMEM;
+
+ bus_data->capacity = capacity;
+
+ return ad5686_probe(&spi->dev, info, spi->modalias, &ad5686_spi_ops,
+ bus_data);
}
static const struct spi_device_id ad5686_spi_id[] = {
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 8f91838c5b53..a4cc0f86ea54 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -471,7 +471,8 @@ EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686");
int ad5686_probe(struct device *dev,
const struct ad5686_chip_info *chip_info,
- const char *name, const struct ad5686_bus_ops *ops)
+ const char *name, const struct ad5686_bus_ops *ops,
+ void *bus_data)
{
struct reset_control *rstc;
struct ad5686_state *st;
@@ -486,6 +487,7 @@ int ad5686_probe(struct device *dev,
st->dev = dev;
st->ops = ops;
+ st->bus_data = bus_data;
st->chip_info = chip_info;
ret = devm_regulator_get_enable(dev, "vdd");
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index 414d39863004..bc793179db09 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -24,6 +24,7 @@
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
#define AD5686_ADDR_ALL_DAC 0xF
+#define AD5686_MAX_CHANNELS 16
#define AD5686_CMD_NOOP 0x0
#define AD5686_CMD_WRITE_INPUT_N 0x1
@@ -129,6 +130,7 @@ extern const struct ad5686_chip_info ad5679r_chip_info;
* @use_internal_vref: set to true if the internal reference voltage is used
* @lock: lock to protect access to state fields, which includes
* the data buffer during regmap ops
+ * @bus_data: bus specific data
* @data: transfer buffers
*/
struct ad5686_state {
@@ -141,6 +143,7 @@ struct ad5686_state {
unsigned int pwr_down_mode;
bool use_internal_vref;
struct mutex lock;
+ void *bus_data;
/*
* DMA (thus cache coherency maintenance) may require the
@@ -151,13 +154,14 @@ struct ad5686_state {
__be32 d32;
__be16 d16;
u8 d8[4];
- } data[3] __aligned(IIO_DMA_MINALIGN);
+ } data[AD5686_MAX_CHANNELS] __aligned(IIO_DMA_MINALIGN);
};
int ad5686_probe(struct device *dev,
const struct ad5686_chip_info *chip_info,
- const char *name, const struct ad5686_bus_ops *ops);
+ const char *name, const struct ad5686_bus_ops *ops,
+ void *bus_data);
static inline int ad5686_write(struct ad5686_state *st, u8 cmd, u8 addr, u16 val)
{
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index 279309329b64..28c97ded43ce 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -70,7 +70,7 @@ static const struct ad5686_bus_ops ad5686_i2c_ops = {
static int ad5686_i2c_probe(struct i2c_client *i2c)
{
return ad5686_probe(&i2c->dev, i2c_get_match_data(i2c),
- i2c->name, &ad5686_i2c_ops);
+ i2c->name, &ad5686_i2c_ops, NULL);
}
static const struct i2c_device_id ad5686_i2c_id[] = {
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus
2026-06-02 16:33 ` [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:14 ` Andy Shevchenko
2026-06-03 12:26 ` Rodrigo Alencar
2026-06-03 12:24 ` Jonathan Cameron
1 sibling, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:14 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:56PM +0100, Rodrigo Alencar via B4 Relay wrote:
> Use of local SPI bus data to manage a collection of SPI transfers and
> flush them to the SPI platform driver with the sync() operation. This
> allows for faster handling of multiple channel DAC writes, avoiding kernel
> overhead per spi_sync() call, which will be helpful when enabling
> triggered buffer support.
Why spi_message_alloc() can't be used instead of manual handling?
(Seems no current users, so you even can modify it for your needs.)
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus
2026-06-02 19:14 ` Andy Shevchenko
@ 2026-06-03 12:26 ` Rodrigo Alencar
2026-06-03 12:55 ` Jonathan Cameron
0 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar @ 2026-06-03 12:26 UTC (permalink / raw)
To: Andy Shevchenko, rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On 26/06/02 10:14PM, Andy Shevchenko wrote:
> On Tue, Jun 02, 2026 at 05:33:56PM +0100, Rodrigo Alencar via B4 Relay wrote:
>
> > Use of local SPI bus data to manage a collection of SPI transfers and
> > flush them to the SPI platform driver with the sync() operation. This
> > allows for faster handling of multiple channel DAC writes, avoiding kernel
> > overhead per spi_sync() call, which will be helpful when enabling
> > triggered buffer support.
>
> Why spi_message_alloc() can't be used instead of manual handling?
> (Seems no current users, so you even can modify it for your needs.)
I need to manually call spi_message_add_tail() to append messages.
I suppose that such function is a bit weird and no wonder why it
is not being used. struct spi_message_with_transfers might need
to be properly declared so that users can populate the transfer
array without manually moving pointers or having to redefine the type.
--
Kind regards,
Rodrigo Alencar
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus
2026-06-03 12:26 ` Rodrigo Alencar
@ 2026-06-03 12:55 ` Jonathan Cameron
0 siblings, 0 replies; 30+ messages in thread
From: Jonathan Cameron @ 2026-06-03 12:55 UTC (permalink / raw)
To: Rodrigo Alencar
Cc: Andy Shevchenko, rodrigo.alencar, Michael Auchter, linux,
linux-iio, devicetree, linux-kernel, linux-hardening,
Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Kees Cook,
Gustavo A. R. Silva
On Wed, 3 Jun 2026 13:26:43 +0100
Rodrigo Alencar <455.rodrigo.alencar@gmail.com> wrote:
> On 26/06/02 10:14PM, Andy Shevchenko wrote:
> > On Tue, Jun 02, 2026 at 05:33:56PM +0100, Rodrigo Alencar via B4 Relay wrote:
> >
> > > Use of local SPI bus data to manage a collection of SPI transfers and
> > > flush them to the SPI platform driver with the sync() operation. This
> > > allows for faster handling of multiple channel DAC writes, avoiding kernel
> > > overhead per spi_sync() call, which will be helpful when enabling
> > > triggered buffer support.
> >
> > Why spi_message_alloc() can't be used instead of manual handling?
> > (Seems no current users, so you even can modify it for your needs.)
>
> I need to manually call spi_message_add_tail() to append messages.
> I suppose that such function is a bit weird and no wonder why it
> is not being used. struct spi_message_with_transfers might need
> to be properly declared so that users can populate the transfer
> array without manually moving pointers or having to redefine the type.
>
Agreed it would be significant surgery. Perhaps worth it as a follow up
if you can find a couple of drivers open coding the equivalent.
Otherwise perhaps send a patch removing spi_message_alloc()
J
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus
2026-06-02 16:33 ` [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus Rodrigo Alencar via B4 Relay
2026-06-02 19:14 ` Andy Shevchenko
@ 2026-06-03 12:24 ` Jonathan Cameron
1 sibling, 0 replies; 30+ messages in thread
From: Jonathan Cameron @ 2026-06-03 12:24 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, 02 Jun 2026 17:33:56 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Use of local SPI bus data to manage a collection of SPI transfers and
> flush them to the SPI platform driver with the sync() operation. This
> allows for faster handling of multiple channel DAC writes, avoiding kernel
> overhead per spi_sync() call, which will be helpful when enabling
> triggered buffer support.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
Some minor stuff inline. Maybe it does make sense to adapt for a generic
solution in the spi core, maybe not but most of these comments apply anyway!
Jonathan
> ---
> drivers/iio/dac/ad5686-spi.c | 109 +++++++++++++++++++++++++++++++------------
> drivers/iio/dac/ad5686.c | 4 +-
> drivers/iio/dac/ad5686.h | 8 +++-
> drivers/iio/dac/ad5696-i2c.c | 2 +-
> 4 files changed, 88 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
> index 6b6ef1d7071f..66a5a2164395 100644
> --- a/drivers/iio/dac/ad5686-spi.c
> +++ b/drivers/iio/dac/ad5686-spi.c
> @@ -12,59 +12,81 @@
> #include <linux/errno.h>
> #include <linux/mod_devicetable.h>
> #include <linux/module.h>
> +#include <linux/overflow.h>
> #include <linux/spi/spi.h>
>
> #include <asm/byteorder.h>
>
> #include "ad5686.h"
>
> +struct ad5686_spi_data {
> + struct spi_message msg;
> + unsigned int size;
Not obvious to me what size is from the naming. Maybe this
structure needs some documentation.
> + unsigned int capacity;
> + struct spi_transfer xfers[] __counted_by(capacity);
> +};
> +
> static int ad5686_spi_write(struct ad5686_state *st,
> u8 cmd, u8 addr, u16 val)
> {
> - struct spi_device *spi = to_spi_device(st->dev);
> - u8 tx_len, *buf;
> + struct ad5686_spi_data *bus_data = st->bus_data;
> + struct spi_transfer *xfer;
> +
> + if (bus_data->size >= bus_data->capacity)
> + return -E2BIG;
> +
> + if (bus_data->size)
> + bus_data->xfers[bus_data->size - 1].cs_change = 1;
> + else
> + spi_message_init(&bus_data->msg);
> +
> + xfer = &bus_data->xfers[bus_data->size];
> + xfer->rx_buf = NULL;
> + xfer->cs_change = 0;
These are both 'resets' to defaults and you are heavily relying
on other fields in that rather complex structure never being set.
Maybe initializing all the fields is simpler?
>
> switch (st->chip_info->regmap_type) {
> case AD5310_REGMAP:
> - st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) |
> - val);
> - buf = &st->data[0].d8[0];
(why not use d16 here? Obviously this is original code, but applies
below)
> - tx_len = 2;
> + st->data[bus_data->size].d16 =
> + cpu_to_be16(AD5310_CMD(cmd) | val);
> + xfer->tx_buf = &st->data[bus_data->size].d8[0];
> + xfer->len = 2;
Following on from above on resetting xfer fields.
*xfer = (struct spi_transfers) {
.tx_buf = &st->data[bus_data->size].d16,
.len = sizeof(&st->data[bus_data->size.d16),
};
> break;
> case AD5683_REGMAP:
> - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
> - AD5683_DATA(val));
> - buf = &st->data[0].d8[1];
> - tx_len = 3;
> + st->data[bus_data->size].d32 =
> + cpu_to_be32(AD5686_CMD(cmd) | AD5683_DATA(val));
> + xfer->tx_buf = &st->data[bus_data->size].d8[1];
> + xfer->len = 3;
Similar use of designated initializer at small cost of clearing stuff
that is clear. I doubt that matters.
> break;
> case AD5686_REGMAP:
> - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
> - AD5686_ADDR(addr) |
> - val);
> - buf = &st->data[0].d8[1];
> - tx_len = 3;
> + st->data[bus_data->size].d32 =
> + cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) | val);
> + xfer->tx_buf = &st->data[bus_data->size].d8[1];
> + xfer->len = 3;
same again.
> break;
> default:
> return -EINVAL;
> }
>
> - return spi_write(spi, buf, tx_len);
> + spi_message_add_tail(xfer, &bus_data->msg);
> + bus_data->size++;
> +
> + return 0;
> +}
> +
> +static int ad5686_spi_sync(struct ad5686_state *st)
> +{
> + struct spi_device *spi = to_spi_device(st->dev);
> + struct ad5686_spi_data *bus_data = st->bus_data;
> +
> + bus_data->size = 0; /* always reset, even on sync failure */
> + return spi_sync(spi, &bus_data->msg);
> }
>
> static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
> {
> - struct spi_transfer t[] = {
> - {
> - .tx_buf = &st->data[0].d8[1],
> - .len = 3,
> - .cs_change = 1,
> - }, {
> - .tx_buf = &st->data[1].d8[1],
> - .rx_buf = &st->data[2].d8[1],
> - .len = 3,
> - },
> - };
> struct spi_device *spi = to_spi_device(st->dev);
> + struct ad5686_spi_data *bus_data = st->bus_data;
> + struct spi_transfer *xfer = &bus_data->xfers[0];
> u8 cmd = 0;
> int ret;
>
> @@ -85,8 +107,18 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
> AD5686_ADDR(addr));
> st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
>
> - ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
> - if (ret < 0)
> + xfer[0].tx_buf = &st->data[0].d8[1];
> + xfer[0].len = 3;
> + xfer[0].cs_change = 1;
> + xfer[1].tx_buf = &st->data[1].d8[1];
> + xfer[1].rx_buf = &st->data[2].d8[1];
> + xfer[1].len = 3;
> + xfer[1].cs_change = 0;
Similar to above - I'd initialize the lot as suggested up there.
Saves on effort thinking about it. Maybe the cost matters but I doubt it.
> +
> + spi_message_init_with_transfers(&bus_data->msg, xfer, 2);
> +
> + ret = spi_sync(spi, &bus_data->msg);
> + if (ret)
> return ret;
>
> return be32_to_cpu(st->data[2].d32);
> @@ -95,12 +127,27 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
> static const struct ad5686_bus_ops ad5686_spi_ops = {
> .write = ad5686_spi_write,
> .read = ad5686_spi_read,
> + .sync = ad5686_spi_sync,
> };
>
> static int ad5686_spi_probe(struct spi_device *spi)
> {
> - return ad5686_probe(&spi->dev, spi_get_device_match_data(spi),
> - spi->modalias, &ad5686_spi_ops);
> + const struct ad5686_chip_info *info = spi_get_device_match_data(spi);
> + struct ad5686_spi_data *bus_data;
> + unsigned int capacity;
> +
> + /* read operation requires at least 2 transfers */
> + capacity = max(info->num_channels, 2);
> + bus_data = devm_kzalloc(&spi->dev,
> + struct_size(bus_data, xfers, capacity),
> + GFP_KERNEL);
> + if (!bus_data)
> + return -ENOMEM;
> +
> + bus_data->capacity = capacity;
> +
> + return ad5686_probe(&spi->dev, info, spi->modalias, &ad5686_spi_ops,
> + bus_data);
> }
>
> static const struct spi_device_id ad5686_spi_id[] = {
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 10/12] iio: dac: ad5686: add triggered buffer support
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (8 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 09/12] iio: dac: ad5686: implement new sync() op for the spi bus Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:18 ` Andy Shevchenko
2026-06-03 12:41 ` Jonathan Cameron
2026-06-02 16:33 ` [PATCH 11/12] iio: dac: ad5686: write_raw: use guard(mutex)() Rodrigo Alencar via B4 Relay
2026-06-02 16:33 ` [PATCH 12/12] iio: dac: ad5686: add gain control support Rodrigo Alencar via B4 Relay
11 siblings, 2 replies; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Implement trigger handler by leveraging the LDAC gpio to update all DAC
channels at once when it is available. Also, the multiple channel writes
can be flushed at once with the sync() operation.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/Kconfig | 2 ++
drivers/iio/dac/ad5686.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 657c68e75542..5f14fcd780e2 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -240,6 +240,8 @@ config LTC2688
config AD5686
tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
config AD5686_SPI
tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)"
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index a4cc0f86ea54..5052df44ab1c 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -19,7 +19,11 @@
#include <linux/sysfs.h>
#include <linux/wordpart.h>
+#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include "ad5686.h"
@@ -245,6 +249,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
.address = addr, \
+ .scan_index = chan, \
.scan_type = { \
.sign = 'u', \
.realbits = (bits), \
@@ -469,6 +474,53 @@ const struct ad5686_chip_info ad5679r_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686");
+static irqreturn_t ad5686_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct iio_buffer *buffer = indio_dev->buffer;
+ struct ad5686_state *st = iio_priv(indio_dev);
+ u16 val[AD5686_MAX_CHANNELS] = { };
+ int ret, ch, i = 0;
+ bool async_update;
+ u8 cmd;
+
+ ret = iio_pop_from_buffer(buffer, val);
+ if (ret)
+ goto out;
+
+ mutex_lock(&st->lock);
+
+ async_update = st->ldac_gpio && bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev)) > 1;
+ if (async_update) {
+ /* use ldac to update all channels simultaneously */
+ cmd = AD5686_CMD_WRITE_INPUT_N;
+ gpiod_set_value_cansleep(st->ldac_gpio, 0);
+ } else {
+ cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N;
+ }
+
+ iio_for_each_active_channel(indio_dev, ch) {
+ ret = st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++]);
+ if (ret)
+ goto cleanup;
+ }
+
+ if (st->ops->sync)
+ ret = st->ops->sync(st); /* flush all pending transfers */
+
+cleanup:
+ if (async_update)
+ gpiod_set_value_cansleep(st->ldac_gpio, 1);
+
+ mutex_unlock(&st->lock);
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
int ad5686_probe(struct device *dev,
const struct ad5686_chip_info *chip_info,
const char *name, const struct ad5686_bus_ops *ops,
@@ -569,6 +621,13 @@ int ad5686_probe(struct device *dev,
return -EINVAL;
}
+ ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL,
+ &ad5686_trigger_handler,
+ IIO_BUFFER_DIRECTION_OUT,
+ NULL, NULL);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686");
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 10/12] iio: dac: ad5686: add triggered buffer support
2026-06-02 16:33 ` [PATCH 10/12] iio: dac: ad5686: add triggered buffer support Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:18 ` Andy Shevchenko
2026-06-03 12:41 ` Jonathan Cameron
1 sibling, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:18 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:57PM +0100, Rodrigo Alencar via B4 Relay wrote:
> Implement trigger handler by leveraging the LDAC gpio to update all DAC
> channels at once when it is available. Also, the multiple channel writes
> can be flushed at once with the sync() operation.
...
> +static irqreturn_t ad5686_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct iio_buffer *buffer = indio_dev->buffer;
> + struct ad5686_state *st = iio_priv(indio_dev);
> + u16 val[AD5686_MAX_CHANNELS] = { };
> + int ret, ch, i = 0;
> + bool async_update;
> + u8 cmd;
> +
> + ret = iio_pop_from_buffer(buffer, val);
> + if (ret)
> + goto out;
> +
> + mutex_lock(&st->lock);
> +
> + async_update = st->ldac_gpio && bitmap_weight(indio_dev->active_scan_mask,
> + iio_get_masklength(indio_dev)) > 1;
Easier to read on a logical split
async_update = st->ldac_gpio &&
bitmap_weight(indio_dev->active_scan_mask, iio_get_masklength(indio_dev)) > 1;
Or with a temporary variable where you keep the weight of the bitmap.
> + if (async_update) {
> + /* use ldac to update all channels simultaneously */
> + cmd = AD5686_CMD_WRITE_INPUT_N;
> + gpiod_set_value_cansleep(st->ldac_gpio, 0);
> + } else {
> + cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N;
> + }
> +
> + iio_for_each_active_channel(indio_dev, ch) {
> + ret = st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++]);
> + if (ret)
> + goto cleanup;
> + }
> +
> + if (st->ops->sync)
> + ret = st->ops->sync(st); /* flush all pending transfers */
> +cleanup:
Bad label naming. Also may clash in the future with some overflow or wrap traps
(you can find interesting discussion with Linus on the implementation of that
with Kees).
> + if (async_update)
> + gpiod_set_value_cansleep(st->ldac_gpio, 1);
> +
> + mutex_unlock(&st->lock);
> +out:
out_notify_done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread* Re: [PATCH 10/12] iio: dac: ad5686: add triggered buffer support
2026-06-02 16:33 ` [PATCH 10/12] iio: dac: ad5686: add triggered buffer support Rodrigo Alencar via B4 Relay
2026-06-02 19:18 ` Andy Shevchenko
@ 2026-06-03 12:41 ` Jonathan Cameron
1 sibling, 0 replies; 30+ messages in thread
From: Jonathan Cameron @ 2026-06-03 12:41 UTC (permalink / raw)
To: Rodrigo Alencar via B4 Relay
Cc: rodrigo.alencar, Michael Auchter, linux, linux-iio, devicetree,
linux-kernel, linux-hardening, Michael Hennerich, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, 02 Jun 2026 17:33:57 +0100
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Implement trigger handler by leveraging the LDAC gpio to update all DAC
> channels at once when it is available. Also, the multiple channel writes
> can be flushed at once with the sync() operation.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
One passing comment inline. + some musings... I'd love to avoid
the need for lots of drivers to call iio_trigger_notify_done() manually
as it always results in ugly code paths.
> ---
> drivers/iio/dac/Kconfig | 2 ++
> drivers/iio/dac/ad5686.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 61 insertions(+)
>
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index 657c68e75542..5f14fcd780e2 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -240,6 +240,8 @@ config LTC2688
>
> config AD5686
> tristate
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
>
> config AD5686_SPI
> tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)"
> diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
> index a4cc0f86ea54..5052df44ab1c 100644
> --- a/drivers/iio/dac/ad5686.c
> +++ b/drivers/iio/dac/ad5686.c
> @@ -19,7 +19,11 @@
> #include <linux/sysfs.h>
> #include <linux/wordpart.h>
>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
>
> #include "ad5686.h"
>
> @@ -245,6 +249,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> .address = addr, \
> + .scan_index = chan, \
> .scan_type = { \
> .sign = 'u', \
> .realbits = (bits), \
> @@ -469,6 +474,53 @@ const struct ad5686_chip_info ad5679r_chip_info = {
> };
> EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686");
>
> +static irqreturn_t ad5686_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct iio_buffer *buffer = indio_dev->buffer;
> + struct ad5686_state *st = iio_priv(indio_dev);
> + u16 val[AD5686_MAX_CHANNELS] = { };
> + int ret, ch, i = 0;
> + bool async_update;
> + u8 cmd;
> +
> + ret = iio_pop_from_buffer(buffer, val);
> + if (ret)
> + goto out;
> +
> + mutex_lock(&st->lock);
> +
> + async_update = st->ldac_gpio && bitmap_weight(indio_dev->active_scan_mask,
> + iio_get_masklength(indio_dev)) > 1;
> + if (async_update) {
> + /* use ldac to update all channels simultaneously */
> + cmd = AD5686_CMD_WRITE_INPUT_N;
> + gpiod_set_value_cansleep(st->ldac_gpio, 0);
> + } else {
> + cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N;
> + }
> +
> + iio_for_each_active_channel(indio_dev, ch) {
> + ret = st->ops->write(st, cmd, indio_dev->channels[ch].address, val[i++]);
> + if (ret)
> + goto cleanup;
> + }
> +
> + if (st->ops->sync)
> + ret = st->ops->sync(st); /* flush all pending transfers */
> +
> +cleanup:
> + if (async_update)
Error paths are always fun. Do we care about setting ldac_gpio to 1 if
we failed to write the channel values? That will set any that did successfully
update, but not all of them. Note I'm not sure on the right answer for this.
There may not be one!
> + gpiod_set_value_cansleep(st->ldac_gpio, 1);
> +
> + mutex_unlock(&st->lock);
> +out:
> + iio_trigger_notify_done(indio_dev->trig);
We get this pattern so often (though not always). Feels like maybe
we should put some effort into a generic opt in solution for this.
A job for another day but options that come to mind.
1) (hideous) a flag
2) Maybe an alternative callback. thread_always_complete or
something like that. Pain to wire through all the calls though
and injecting the necessary wrapper isn't great either.
Implementation wise would be a case of popping in a wrapper function
in iio_trigger_attach_poll() call to request_threaded_irq().
3) Maybe a helper macro? Bit ugly as we'd need one to generate
the wrapper function and another to use the same name for
the registration function.
Hmm. Those are all ugly (maybe 2 is ok ish). Suggestions welcome!
> +
> + return IRQ_HANDLED;
> +}
> +
> int ad5686_probe(struct device *dev,
> const struct ad5686_chip_info *chip_info,
> const char *name, const struct ad5686_bus_ops *ops,
> @@ -569,6 +621,13 @@ int ad5686_probe(struct device *dev,
> return -EINVAL;
> }
>
> + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL,
> + &ad5686_trigger_handler,
> + IIO_BUFFER_DIRECTION_OUT,
> + NULL, NULL);
> + if (ret)
> + return ret;
> +
> return devm_iio_device_register(dev, indio_dev);
> }
> EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686");
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 11/12] iio: dac: ad5686: write_raw: use guard(mutex)()
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (9 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 10/12] iio: dac: ad5686: add triggered buffer support Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:20 ` Andy Shevchenko
2026-06-02 16:33 ` [PATCH 12/12] iio: dac: ad5686: add gain control support Rodrigo Alencar via B4 Relay
11 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Use guarded mutex lock to facilitate code review when adding new
attributes. This will allow for early returns, avoiding error-prone
locking and unlocking in error paths. Gain-control support will add
the scale attribute.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 5052df44ab1c..54b953018cfa 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -205,23 +205,19 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad5686_state *st = iio_priv(indio_dev);
- int ret;
+
+ guard(mutex)(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (val >= (1 << chan->scan_type.realbits) || val < 0)
return -EINVAL;
- mutex_lock(&st->lock);
- ret = ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
- chan->address, val << chan->scan_type.shift);
- mutex_unlock(&st->lock);
- break;
+ return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val << chan->scan_type.shift);
default:
- ret = -EINVAL;
+ return -EINVAL;
}
-
- return ret;
}
static const struct iio_info ad5686_info = {
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 11/12] iio: dac: ad5686: write_raw: use guard(mutex)()
2026-06-02 16:33 ` [PATCH 11/12] iio: dac: ad5686: write_raw: use guard(mutex)() Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:20 ` Andy Shevchenko
0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:20 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:58PM +0100, Rodrigo Alencar via B4 Relay wrote:
>
> Use guarded mutex lock to facilitate code review when adding new
> attributes. This will allow for early returns, avoiding error-prone
> locking and unlocking in error paths. Gain-control support will add
> the scale attribute.
You just added one in the previous patch.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH 12/12] iio: dac: ad5686: add gain control support
2026-06-02 16:33 [PATCH 00/12] New features for the AD5686 IIO driver Rodrigo Alencar via B4 Relay
` (10 preceding siblings ...)
2026-06-02 16:33 ` [PATCH 11/12] iio: dac: ad5686: write_raw: use guard(mutex)() Rodrigo Alencar via B4 Relay
@ 2026-06-02 16:33 ` Rodrigo Alencar via B4 Relay
2026-06-02 19:25 ` Andy Shevchenko
11 siblings, 1 reply; 30+ messages in thread
From: Rodrigo Alencar via B4 Relay @ 2026-06-02 16:33 UTC (permalink / raw)
To: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening
Cc: Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Philipp Zabel, Kees Cook, Gustavo A. R. Silva, Rodrigo Alencar
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Most of the supported devices rely on a GAIN pin to control a 2x
multiplier applied to the output voltage. Other devices, e.g. the
single-channel ones, provides a gain control through a bit field in the
control register. Some designs might have the GAIN pin hardwired to
VDD/VLOGIC or GND, which would still be fine for this patch, that allows
the scale property to be configurable with two available options.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/dac/ad5686.c | 96 +++++++++++++++++++++++++++++++++++++++++++++---
drivers/iio/dac/ad5686.h | 8 ++++
2 files changed, 99 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 54b953018cfa..fabe967c5225 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -13,10 +13,12 @@
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kstrtox.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/sysfs.h>
+#include <linux/units.h>
#include <linux/wordpart.h>
#include <linux/iio/buffer.h>
@@ -39,7 +41,8 @@ static int ad5310_control_sync(struct ad5686_state *st)
return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0,
FIELD_PREP(AD5310_PD_MSK, pd_val & AD5686_PD_MSK) |
- FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1));
+ FIELD_PREP(AD5310_REF_BIT_MSK, st->use_internal_vref ? 0 : 1) |
+ FIELD_PREP(AD5310_GAIN_BIT_MSK, st->double_scale ? 1 : 0));
}
static int ad5683_control_sync(struct ad5686_state *st)
@@ -48,7 +51,8 @@ static int ad5683_control_sync(struct ad5686_state *st)
return ad5686_write(st, AD5686_CMD_CONTROL_REG, 0,
FIELD_PREP(AD5683_PD_MSK, pd_val & AD5686_PD_MSK) |
- FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1));
+ FIELD_PREP(AD5683_REF_BIT_MSK, st->use_internal_vref ? 0 : 1) |
+ FIELD_PREP(AD5683_GAIN_BIT_MSK, st->double_scale ? 1 : 0));
}
static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan)
@@ -191,9 +195,14 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
GENMASK(chan->scan_type.realbits - 1, 0);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = st->vref_mv;
- *val2 = chan->scan_type.realbits;
- return IIO_VAL_FRACTIONAL_LOG2;
+ if (st->double_scale) {
+ *val = st->scale_avail[2];
+ *val2 = st->scale_avail[3];
+ } else {
+ *val = st->scale_avail[0];
+ *val2 = st->scale_avail[1];
+ }
+ return IIO_VAL_INT_PLUS_NANO;
}
return -EINVAL;
}
@@ -215,6 +224,63 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
return ad5686_write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
chan->address, val << chan->scan_type.shift);
+ case IIO_CHAN_INFO_SCALE:
+ if (val == st->scale_avail[0] && val2 == st->scale_avail[1])
+ st->double_scale = false;
+ else if (val == st->scale_avail[2] && val2 == st->scale_avail[3])
+ st->double_scale = true;
+ else
+ return -EINVAL;
+
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ return ad5310_control_sync(st);
+ case AD5683_REGMAP:
+ return ad5683_control_sync(st);
+ case AD5686_REGMAP:
+ /*
+ * even if the gain pin is hardwired on the board, the
+ * user is able to control the scale such that it
+ * matches the actual gain setting.
+ */
+ gpiod_set_value_cansleep(st->gain_gpio,
+ st->double_scale ? 1 : 0);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5686_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5686_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *vals = st->scale_avail;
+ *length = ARRAY_SIZE(st->scale_avail);
+ return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
@@ -223,6 +289,8 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
static const struct iio_info ad5686_info = {
.read_raw = ad5686_read_raw,
.write_raw = ad5686_write_raw,
+ .write_raw_get_fmt = ad5686_write_raw_get_fmt,
+ .read_avail = ad5686_read_avail,
};
static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
@@ -244,6 +312,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),\
.address = addr, \
.scan_index = chan, \
.scan_type = { \
@@ -470,6 +539,16 @@ const struct ad5686_chip_info ad5679r_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686");
+static void ad5686_init_scale_avail(struct ad5686_state *st)
+{
+ int realbits = st->chip_info->channels[0].scan_type.realbits;
+ s64 tmp;
+
+ tmp = 2ULL * st->vref_mv * NANO >> realbits;
+ st->scale_avail[2] = div_s64_rem(tmp, NANO, &st->scale_avail[3]);
+ st->scale_avail[0] = div_s64_rem(tmp >> 1, NANO, &st->scale_avail[1]);
+}
+
static irqreturn_t ad5686_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@@ -569,6 +648,13 @@ int ad5686_probe(struct device *dev,
return dev_err_probe(dev, PTR_ERR(st->ldac_gpio),
"Failed to get LDAC GPIO\n");
+ st->gain_gpio = devm_gpiod_get_optional(dev, "gain", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gain_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->gain_gpio),
+ "Failed to get GAIN GPIO\n");
+
+ ad5686_init_scale_avail(st);
+
udelay(5); /* power-up time */
reset_control_assert(rstc);
udelay(1); /* reset pulse: comfortably bigger than the spec */
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index bc793179db09..455d6bbefdfe 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -40,9 +40,11 @@
#define AD5686_CMD_CONTROL_REG 0x4
#define AD5686_CMD_READBACK_ENABLE_V2 0x5
+#define AD5310_GAIN_BIT_MSK BIT(7)
#define AD5310_REF_BIT_MSK BIT(8)
#define AD5310_PD_MSK GENMASK(10, 9)
+#define AD5683_GAIN_BIT_MSK BIT(11)
#define AD5683_REF_BIT_MSK BIT(12)
#define AD5683_PD_MSK GENMASK(14, 13)
@@ -124,9 +126,12 @@ extern const struct ad5686_chip_info ad5679r_chip_info;
* @chip_info: chip model specific constants, available modes etc
* @ops: bus specific operations
* @ldac_gpio: LDAC pin GPIO descriptor
+ * @gain_gpio: GAIN pin GPIO descriptor
* @vref_mv: actual reference voltage used
* @pwr_down_mask: power down mask
* @pwr_down_mode: current power down mode
+ * @scale_avail: pre-calculated available scale values
+ * @double_scale: flag to indicate the gain multiplier is applied
* @use_internal_vref: set to true if the internal reference voltage is used
* @lock: lock to protect access to state fields, which includes
* the data buffer during regmap ops
@@ -138,9 +143,12 @@ struct ad5686_state {
const struct ad5686_chip_info *chip_info;
const struct ad5686_bus_ops *ops;
struct gpio_desc *ldac_gpio;
+ struct gpio_desc *gain_gpio;
unsigned short vref_mv;
unsigned int pwr_down_mask;
unsigned int pwr_down_mode;
+ int scale_avail[4];
+ bool double_scale;
bool use_internal_vref;
struct mutex lock;
void *bus_data;
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH 12/12] iio: dac: ad5686: add gain control support
2026-06-02 16:33 ` [PATCH 12/12] iio: dac: ad5686: add gain control support Rodrigo Alencar via B4 Relay
@ 2026-06-02 19:25 ` Andy Shevchenko
0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2026-06-02 19:25 UTC (permalink / raw)
To: rodrigo.alencar
Cc: Michael Auchter, linux, linux-iio, devicetree, linux-kernel,
linux-hardening, Michael Hennerich, Jonathan Cameron,
David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel, Kees Cook, Gustavo A. R. Silva
On Tue, Jun 02, 2026 at 05:33:59PM +0100, Rodrigo Alencar via B4 Relay wrote:
>
> Most of the supported devices rely on a GAIN pin to control a 2x
> multiplier applied to the output voltage. Other devices, e.g. the
> single-channel ones, provides a gain control through a bit field in the
> control register. Some designs might have the GAIN pin hardwired to
> VDD/VLOGIC or GND, which would still be fine for this patch, that allows
> the scale property to be configurable with two available options.
...
> + case IIO_CHAN_INFO_SCALE:
> + if (val == st->scale_avail[0] && val2 == st->scale_avail[1])
> + st->double_scale = false;
> + else if (val == st->scale_avail[2] && val2 == st->scale_avail[3])
> + st->double_scale = true;
> + else
> + return -EINVAL;
> +
> + switch (st->chip_info->regmap_type) {
> + case AD5310_REGMAP:
> + return ad5310_control_sync(st);
> + case AD5683_REGMAP:
> + return ad5683_control_sync(st);
> + case AD5686_REGMAP:
> + /*
> + * even if the gain pin is hardwired on the board, the
Even
> + * user is able to control the scale such that it
> + * matches the actual gain setting.
> + */
> + gpiod_set_value_cansleep(st->gain_gpio,
> + st->double_scale ? 1 : 0);
> + return 0;
> + default:
> + return -EINVAL;
> + }
...
> @@ -138,9 +143,12 @@ struct ad5686_state {
Have you run `pahole`? It seems to me that those bools can be combined with
vref_mv to reduce 4 bytes gap.
> const struct ad5686_chip_info *chip_info;
> const struct ad5686_bus_ops *ops;
> struct gpio_desc *ldac_gpio;
> + struct gpio_desc *gain_gpio;
> unsigned short vref_mv;
> unsigned int pwr_down_mask;
> unsigned int pwr_down_mode;
> + int scale_avail[4];
> + bool double_scale;
> bool use_internal_vref;
> struct mutex lock;
> void *bus_data;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread