* [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC
@ 2026-05-08 11:55 Janani Sunil
2026-05-08 11:55 ` [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R Janani Sunil
` (3 more replies)
0 siblings, 4 replies; 12+ messages in thread
From: Janani Sunil @ 2026-05-08 11:55 UTC (permalink / raw)
To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet,
Shuah Khan
Cc: linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil,
Janani Sunil
This patch series adds support for Analog Devices AD5529R, a 16 channel
16 and 12 bit voltage Digital-to-Analog Converter (DAC) with integrated
precision reference. The AD5529R operates from both unipolar and
bipolar supplies. The device communicates via SPI interface.
**Device Overview:**
The AD5529R features 16 independent DAC channels, with 16 or 12 bit
resolution, allowing independently programmable output ranges. The
internal 4.096V precision reference sets the accuracy of the output
voltage.
**Features Implemented:**
- Automatic detection of 12/16 bit variant with product ID read.
- Reset support via GPIO.
- Dual regmap configuration to handle 8 and 16 bit registers.
**Patch Summary:**
1. **dt-bindings**: Binding documentation with channel configuration.
2. **driver**: Implement IIO DAC Driver with regmap support.
3. **documentation**: Add driver documentation with usage examples.
**Testing:**
The driver was compiled and tested on the EVAL-AD5529R-ARDZ using a
coraZ7 with a mainline v7.0 kernel.
**Driver Rationale:**
AD5529R introduces:
1. A unique register layout
2. Mixed 8-bit and 16-bit register accesses
3. Product ID based generic identification
4. Hardware specific features like function generators, multi-die
hotpath registers etc.
The device warrants its own drivers due to these fundamental
architectural differences, that would require substantial changes to
existing drivers without providing reusable benefits. The standalone
driver also allows future extensions for related devices in the same
family.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
Changes in v2:
- Fix IIO scale to use millivolts per ABI requirement
- Fix documentation voltage calculations (2.5V not 2.048V)
- Fix bipolar ranges in documentation (±5V, ±10V, ±15V, ±20V)
- Fix alphabetical ordering in documentation index
- Add missing newline to documentation file
- Fix scale units description (millivolts not microvolts)
- Include a section for driver rationale in the cover letter
- Reword contents in cover letter 12/16 bit generic->variant
- Add dependency array for spi-cpha and spi-cpol properties
- Link to v1: https://lore.kernel.org/r/20260507-ad5529r-driver-v1-0-b4460f3cb44f@analog.com
---
Janani Sunil (3):
dt-bindings: iio: dac: Add AD5529R
iio: dac: Add AD5529R DAC driver support
Documentation: iio: Add AD5529R Documentation
.../devicetree/bindings/iio/dac/adi,ad5529r.yaml | 96 ++++
Documentation/iio/ad5529r.rst | 216 ++++++++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 9 +
drivers/iio/dac/Kconfig | 17 +
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/ad5529r.c | 564 +++++++++++++++++++++
7 files changed, 904 insertions(+)
---
base-commit: 93df88612859e8e19dec93c69d563b4b73e9bd4b
change-id: 20260507-ad5529r-driver-866bbdd864de
Best regards,
--
Janani Sunil <janani.sunil@analog.com>
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R 2026-05-08 11:55 [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Janani Sunil @ 2026-05-08 11:55 ` Janani Sunil 2026-05-08 12:48 ` Jonathan Cameron 2026-05-08 11:55 ` [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support Janani Sunil ` (2 subsequent siblings) 3 siblings, 1 reply; 12+ messages in thread From: Janani Sunil @ 2026-05-08 11:55 UTC (permalink / raw) To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan Cc: linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, Janani Sunil Devicetree bindings for AD5529R 16 channel 12/16 bit high voltage, buffered voltage output digital-to-analog converter (DAC) with an integrated precision reference. Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- .../devicetree/bindings/iio/dac/adi,ad5529r.yaml | 96 ++++++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 103 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml new file mode 100644 index 000000000000..f531b4865b01 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad5529r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5529R 16-Channel 12/16-bit High Voltage DAC + +maintainers: + - Janani Sunil <janani.sunil@analog.com> + +description: | + The AD5529R is a 16-channel, 12-bit or 16-bit, high voltage, buffered voltage output + digital-to-analog converter (DAC) with an integrated precision reference. + The device operates from unipolar and bipolar supplies. It is guaranteed + monotonic and has built-in rail-to-rail output buffers that can source or + sink up to 25mA. + + Specifications: + * 16 independent 12-bit or 16-bit DAC channels + * Independently programmable output ranges: 0V to 5V, 0V to 10V, 0V to 20V, + 0V to 40V, ±5V, ±10V, ±15V, and ±20V + * The device supports SPI communication with Mode 0 and Mode 3. + * 4.096V precision reference, 12ppm/°C maximum + * Built-in function generation: Toggle, Sinusoidal Dither, and Ramp waveforms + * Multiplexer for output voltage, load current sense and die temperature + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad5529r.pdf + +properties: + compatible: + const: adi,ad5529r + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 50000000 + + reset-gpios: + maxItems: 1 + description: + GPIO connected to the RESET pin. Active low. When asserted low, + performs a power-on reset and initializes the device to its default state. + + vdd-supply: + description: Digital power supply (typically 3.3V) + + avdd-supply: + description: Analog power supply (typically 5V) + + hvdd-supply: + description: High voltage positive supply (up to 40V for output range) + + hvss-supply: + description: High voltage negative supply (ground or negative voltage) + +required: + - compatible + - reg + - vdd-supply + - avdd-supply + - hvdd-supply + - hvss-supply + +dependencies: + spi-cpha: [ spi-cpol ] + spi-cpol: [ spi-cpha ] + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "adi,ad5529r"; + reg = <0>; + spi-max-frequency = <25000000>; + + vdd-supply = <&vdd_regulator>; + avdd-supply = <&avdd_regulator>; + hvdd-supply = <&hvdd_regulator>; + hvss-supply = <&hvss_regulator>; + + reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index d6c3c7d22403..320e84765ce6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1507,6 +1507,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml F: drivers/iio/adc/ad4851.c +ANALOG DEVICES INC AD5529R DRIVER +M: Janani Sunil <janani.sunil@analog.com> +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml + ANALOG DEVICES INC AD5706R DRIVER M: Alexis Czezar Torreno <alexisczezar.torreno@analog.com> L: linux-iio@vger.kernel.org -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R 2026-05-08 11:55 ` [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R Janani Sunil @ 2026-05-08 12:48 ` Jonathan Cameron 2026-05-08 13:08 ` Jonathan Cameron ` (2 more replies) 0 siblings, 3 replies; 12+ messages in thread From: Jonathan Cameron @ 2026-05-08 12:48 UTC (permalink / raw) To: Janani Sunil Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, rodrigo.alencar On Fri, 8 May 2026 13:55:47 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > Devicetree bindings for AD5529R 16 channel 12/16 bit high voltage, > buffered voltage output digital-to-analog converter (DAC) with an > integrated precision reference. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > .../devicetree/bindings/iio/dac/adi,ad5529r.yaml | 96 ++++++++++++++++++++++ > MAINTAINERS | 7 ++ > 2 files changed, 103 insertions(+) > > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > new file mode 100644 > index 000000000000..f531b4865b01 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > @@ -0,0 +1,96 @@ > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/iio/dac/adi,ad5529r.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: Analog Devices AD5529R 16-Channel 12/16-bit High Voltage DAC How is one device bother 12 and 16-bit? That sometimes happens for ADCs where it is really reflecting oversampling or for device with hardware FIFOs where storage space is saved by using lower bit rate. I'm not sure either applies here. > + > +maintainers: > + - Janani Sunil <janani.sunil@analog.com> > + > +description: | > + The AD5529R is a 16-channel, 12-bit or 16-bit, high voltage, buffered voltage output > + digital-to-analog converter (DAC) with an integrated precision reference. > + The device operates from unipolar and bipolar supplies. It is guaranteed > + monotonic and has built-in rail-to-rail output buffers that can source or > + sink up to 25mA. > + > + Specifications: > + * 16 independent 12-bit or 16-bit DAC channels > + * Independently programmable output ranges: 0V to 5V, 0V to 10V, 0V to 20V, > + 0V to 40V, ±5V, ±10V, ±15V, and ±20V > + * The device supports SPI communication with Mode 0 and Mode 3. > + * 4.096V precision reference, 12ppm/°C maximum > + * Built-in function generation: Toggle, Sinusoidal Dither, and Ramp waveforms Interesting - so this is a DDS, be it a simple one. +CC Rodrigo who has been wrestling with one of those recently. Rodrigo, can you take a look at this driver and see if it fits in the ABI etc you've been hammering out? Thanks! > + * Multiplexer for output voltage, load current sense and die temperature > + > + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad5529r.pdf > + > +properties: > + compatible: > + const: adi,ad5529r > + > + reg: > + maxItems: 1 > + > + spi-max-frequency: > + maximum: 50000000 > + > + reset-gpios: > + maxItems: 1 > + description: > + GPIO connected to the RESET pin. Active low. When asserted low, > + performs a power-on reset and initializes the device to its default state. > + > + vdd-supply: > + description: Digital power supply (typically 3.3V) > + > + avdd-supply: > + description: Analog power supply (typically 5V) > + > + hvdd-supply: > + description: High voltage positive supply (up to 40V for output range) > + > + hvss-supply: > + description: High voltage negative supply (ground or negative voltage) I don't mind doing it this way but in some similar cases where 0 is something that can be considered the 'default' we've made the supply optional. What was your reasoning for requiring it in this case? dt-bindings should be as complete as we can make them - with that in mind... There are some more interesting corners on this device the binding doesn't currently cover such as mux_out pin. We'd normally do that by making the driver potentially a client of an ADC Easier though is !alarm which smells like an interrupt. !clear probably a gpio. TG0-3 also GPIOs. > + > +required: > + - compatible > + - reg > + - vdd-supply > + - avdd-supply > + - hvdd-supply > + - hvss-supply ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R 2026-05-08 12:48 ` Jonathan Cameron @ 2026-05-08 13:08 ` Jonathan Cameron 2026-05-08 13:50 ` Rodrigo Alencar 2026-05-08 13:57 ` Nuno Sá 2 siblings, 0 replies; 12+ messages in thread From: Jonathan Cameron @ 2026-05-08 13:08 UTC (permalink / raw) To: Janani Sunil Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, rodrigo.alencar On Fri, 8 May 2026 13:48:43 +0100 Jonathan Cameron <jic23@kernel.org> wrote: > On Fri, 8 May 2026 13:55:47 +0200 > Janani Sunil <janani.sunil@analog.com> wrote: > > > Devicetree bindings for AD5529R 16 channel 12/16 bit high voltage, > > buffered voltage output digital-to-analog converter (DAC) with an > > integrated precision reference. > > > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > > --- > > .../devicetree/bindings/iio/dac/adi,ad5529r.yaml | 96 ++++++++++++++++++++++ > > MAINTAINERS | 7 ++ > > 2 files changed, 103 insertions(+) > > > > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > > new file mode 100644 > > index 000000000000..f531b4865b01 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > > @@ -0,0 +1,96 @@ > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) > > +%YAML 1.2 > > +--- > > +$id: http://devicetree.org/schemas/iio/dac/adi,ad5529r.yaml# > > +$schema: http://devicetree.org/meta-schemas/core.yaml# > > + > > +title: Analog Devices AD5529R 16-Channel 12/16-bit High Voltage DAC > > How is one device bother 12 and 16-bit? That sometimes happens for > ADCs where it is really reflecting oversampling or for device with hardware > FIFOs where storage space is saved by using lower bit rate. I'm not sure either > applies here. Having read the driver I now understand. This is supporting two parts and doing device ID based detection. In an unusual step for Analog they have the same base part number with a post fix. Whilst this approach works today it fundamentally breaks fallback dt-compatibles being used in future (the driver fails for any non match of WHOAMI value as it needs them to look up device specific data) As such I think you need to have separate compatibles for the 12 and 16 bit versions. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R 2026-05-08 12:48 ` Jonathan Cameron 2026-05-08 13:08 ` Jonathan Cameron @ 2026-05-08 13:50 ` Rodrigo Alencar 2026-05-08 13:57 ` Nuno Sá 2 siblings, 0 replies; 12+ messages in thread From: Rodrigo Alencar @ 2026-05-08 13:50 UTC (permalink / raw) To: Jonathan Cameron, Janani Sunil Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, rodrigo.alencar On 26/05/08 01:48PM, Jonathan Cameron wrote: > On Fri, 8 May 2026 13:55:47 +0200 > Janani Sunil <janani.sunil@analog.com> wrote: ... > > + Specifications: > > + * 16 independent 12-bit or 16-bit DAC channels > > + * Independently programmable output ranges: 0V to 5V, 0V to 10V, 0V to 20V, > > + 0V to 40V, ±5V, ±10V, ±15V, and ±20V > > + * The device supports SPI communication with Mode 0 and Mode 3. > > + * 4.096V precision reference, 12ppm/°C maximum > > + * Built-in function generation: Toggle, Sinusoidal Dither, and Ramp waveforms > > Interesting - so this is a DDS, be it a simple one. +CC Rodrigo who has been > wrestling with one of those recently. Rodrigo, can you take a look at this > driver and see if it fits in the ABI etc you've been hammering out? Thanks! I am not sure how this is a DDS as it does really generate frequencies, so it does not seem to act as an oscillator. I'd say the minimum for a DDS is an NCO + DAC. The function generation seem to act only on the voltage levels. There is the step size and ramp limits configuration that are similar to a DDS with a digital ramp generator. I suppose that users have some use cases that are often integrated into the products so they can do less (and the overall system becomes more efficient). However, those products end up solving too many problems at once and they get too complex! -- Kind regards, Rodrigo Alencar ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R 2026-05-08 12:48 ` Jonathan Cameron 2026-05-08 13:08 ` Jonathan Cameron 2026-05-08 13:50 ` Rodrigo Alencar @ 2026-05-08 13:57 ` Nuno Sá 2 siblings, 0 replies; 12+ messages in thread From: Nuno Sá @ 2026-05-08 13:57 UTC (permalink / raw) To: Jonathan Cameron Cc: Janani Sunil, Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, rodrigo.alencar On Fri, May 08, 2026 at 01:48:43PM +0100, Jonathan Cameron wrote: > On Fri, 8 May 2026 13:55:47 +0200 > Janani Sunil <janani.sunil@analog.com> wrote: > > > Devicetree bindings for AD5529R 16 channel 12/16 bit high voltage, > > buffered voltage output digital-to-analog converter (DAC) with an > > integrated precision reference. > > > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > > --- > > .../devicetree/bindings/iio/dac/adi,ad5529r.yaml | 96 ++++++++++++++++++++++ > > MAINTAINERS | 7 ++ > > 2 files changed, 103 insertions(+) > > > > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > > new file mode 100644 > > index 000000000000..f531b4865b01 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > > @@ -0,0 +1,96 @@ > > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) > > +%YAML 1.2 > > +--- > > +$id: http://devicetree.org/schemas/iio/dac/adi,ad5529r.yaml# > > +$schema: http://devicetree.org/meta-schemas/core.yaml# > > + > > +title: Analog Devices AD5529R 16-Channel 12/16-bit High Voltage DAC > > How is one device bother 12 and 16-bit? That sometimes happens for > ADCs where it is really reflecting oversampling or for device with hardware > FIFOs where storage space is saved by using lower bit rate. I'm not sure either > applies here. > > > + > > +maintainers: > > + - Janani Sunil <janani.sunil@analog.com> > > + > > +description: | > > + The AD5529R is a 16-channel, 12-bit or 16-bit, high voltage, buffered voltage output > > + digital-to-analog converter (DAC) with an integrated precision reference. > > + The device operates from unipolar and bipolar supplies. It is guaranteed > > + monotonic and has built-in rail-to-rail output buffers that can source or > > + sink up to 25mA. > > + > > + Specifications: > > + * 16 independent 12-bit or 16-bit DAC channels > > + * Independently programmable output ranges: 0V to 5V, 0V to 10V, 0V to 20V, > > + 0V to 40V, ±5V, ±10V, ±15V, and ±20V > > + * The device supports SPI communication with Mode 0 and Mode 3. > > + * 4.096V precision reference, 12ppm/°C maximum > > + * Built-in function generation: Toggle, Sinusoidal Dither, and Ramp waveforms > > Interesting - so this is a DDS, be it a simple one. +CC Rodrigo who has been > wrestling with one of those recently. Rodrigo, can you take a look at this > driver and see if it fits in the ABI etc you've been hammering out? Thanks! Yes, this also crossed my mind. I only briefly looked into the datasheet but this device seems to be similar to the ltc2688 for the toggle and dither modes. Naturally ramp is something new. Not really sure this one is a DDS as that typically means things like NCOs. This device looks like a "plain" DAC. So, devices like the one Rodrigo is working one combine DDS + DAC to do things like synthesizing waveforms (in a very fast way). We would need to do some auditing but I guess we tend to put above devices in dac/ (because they do have a DAC internally) or even maybe in frequency/ but maybe worth thinking about a new directory for those. Having said the above the ABI might still matter for both devices. - Nuno Sá > > > > + * Multiplexer for output voltage, load current sense and die temperature > > + > > + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad5529r.pdf > > + > > +properties: > > + compatible: > > + const: adi,ad5529r > > + > > + reg: > > + maxItems: 1 > > + > > + spi-max-frequency: > > + maximum: 50000000 > > + > > + reset-gpios: > > + maxItems: 1 > > + description: > > + GPIO connected to the RESET pin. Active low. When asserted low, > > + performs a power-on reset and initializes the device to its default state. > > + > > + vdd-supply: > > + description: Digital power supply (typically 3.3V) > > + > > + avdd-supply: > > + description: Analog power supply (typically 5V) > > + > > + hvdd-supply: > > + description: High voltage positive supply (up to 40V for output range) > > + > > + hvss-supply: > > + description: High voltage negative supply (ground or negative voltage) > > I don't mind doing it this way but in some similar cases where 0 is something that > can be considered the 'default' we've made the supply optional. What was > your reasoning for requiring it in this case? > > dt-bindings should be as complete as we can make them - with that in mind... > > There are some more interesting corners on this device the binding doesn't > currently cover such as mux_out pin. We'd normally do that by making the > driver potentially a client of an ADC > > Easier though is !alarm which smells like an interrupt. > !clear probably a gpio. TG0-3 also GPIOs. > > > + > > +required: > > + - compatible > > + - reg > > + - vdd-supply > > + - avdd-supply > > + - hvdd-supply > > + - hvss-supply > ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support 2026-05-08 11:55 [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Janani Sunil 2026-05-08 11:55 ` [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R Janani Sunil @ 2026-05-08 11:55 ` Janani Sunil 2026-05-08 13:30 ` Jonathan Cameron 2026-05-08 20:55 ` sashiko-bot 2026-05-08 11:55 ` [PATCH v2 3/3] Documentation: iio: Add AD5529R Documentation Janani Sunil 2026-05-08 12:36 ` [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Jonathan Cameron 3 siblings, 2 replies; 12+ messages in thread From: Janani Sunil @ 2026-05-08 11:55 UTC (permalink / raw) To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan Cc: linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, Janani Sunil Add support for AD5529R 16-channel, 12/16 bit Digital to Analog Converter Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- MAINTAINERS | 1 + drivers/iio/dac/Kconfig | 17 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5529r.c | 564 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 583 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 320e84765ce6..143714e27d51 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1513,6 +1513,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml +F: drivers/iio/dac/ad5529r.c ANALOG DEVICES INC AD5706R DRIVER M: Alexis Czezar Torreno <alexisczezar.torreno@analog.com> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 657c68e75542..bb1d59889a2a 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -134,6 +134,23 @@ config AD5449 To compile this driver as a module, choose M here: the module will be called ad5449. +config AD5529R + tristate "Analog Devices AD5529R High Voltage DAC driver" + depends on SPI_MASTER + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD5529R + 16-Channel, 12-Bit/16-Bit, 40V High Voltage Precision Digital to Analog + Converter. + + The device features multiple output voltage ranges from -20V to +20V, + built-in 4.096V voltage reference, and digital functions including + toggle, dither, and ramp modes. Supports both 12-bit and 16-bit + resolution variants. + + To compile this driver as a module, choose M here: the + module will be called ad5529r. + config AD5592R_BASE tristate diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 003431798498..f35e060b3643 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_AD5446) += ad5446.o obj-$(CONFIG_AD5446_SPI) += ad5446-spi.o obj-$(CONFIG_AD5446_I2C) += ad5446-i2c.o obj-$(CONFIG_AD5449) += ad5449.o +obj-$(CONFIG_AD5529R) += ad5529r.o obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o obj-$(CONFIG_AD5592R) += ad5592r.o obj-$(CONFIG_AD5593R) += ad5593r.o diff --git a/drivers/iio/dac/ad5529r.c b/drivers/iio/dac/ad5529r.c new file mode 100644 index 000000000000..3676956f6eff --- /dev/null +++ b/drivers/iio/dac/ad5529r.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD5529R Digital-to-Analog Converter Driver + * 16-Channel, 12/16-Bit, 40V High Voltage Precision DAC + * + * Copyright 2026 Analog Devices Inc. + * Author: Janani Sunil <janani.sunil@analog.com> + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/errno.h> +#include <linux/iio/iio.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +/* Register Map */ +#define AD5529R_REG_INTERFACE_CONFIG_A 0x00 +#define AD5529R_REG_INTERFACE_CONFIG_B 0x01 +#define AD5529R_REG_DEVICE_CONFIG 0x02 +#define AD5529R_REG_CHIP_TYPE 0x03 +#define AD5529R_REG_PRODUCT_ID_L 0x04 +#define AD5529R_REG_PRODUCT_ID_H 0x05 +#define AD5529R_REG_CHIP_GRADE 0x06 +#define AD5529R_REG_SCRATCH_PAD 0x0A +#define AD5529R_REG_SPI_REVISION 0x0B +#define AD5529R_REG_VENDOR_L 0x0C +#define AD5529R_REG_VENDOR_H 0x0D +#define AD5529R_REG_STREAM_MODE 0x0E +#define AD5529R_REG_TRANSFER_CONFIG 0x0F +#define AD5529R_REG_INTERFACE_CONFIG_C 0x10 +#define AD5529R_REG_INTERFACE_STATUS_A 0x11 + +/* Configuration registers */ +#define AD5529R_REG_MULTI_DAC_CH_SEL (0x14 + 1) +#define AD5529R_REG_LDAC_SYNC_ASYNC (0x16 + 1) +#define AD5529R_REG_LDAC_HW_SW (0x18 + 1) + +/* Hardware LDAC source and edge select registers (per channel, 16-bit) */ +#define AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE (0x1A + 1) +#define AD5529R_REG_LDAC_HW_SRC_EDGE_SEL(ch) \ + (AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE + (ch) * 2) + +/* Output configuration */ +#define AD5529R_REG_OUT_OPERATING_MODE (0x3A + 1) +#define AD5529R_REG_OUT_RANGE_BASE (0x3C + 1) +#define AD5529R_REG_OUT_RANGE(ch) (AD5529R_REG_OUT_RANGE_BASE + (ch) * 2) + +/* Calibration registers */ +#define AD5529R_REG_CAL_GAIN_BASE (0x5C + 1) +#define AD5529R_REG_CAL_GAIN(ch) (AD5529R_REG_CAL_GAIN_BASE + (ch) * 2) + +#define AD5529R_REG_CAL_OFFSET_BASE (0x7C + 1) +#define AD5529R_REG_CAL_OFFSET(ch) (AD5529R_REG_CAL_OFFSET_BASE + (ch) * 2) + +/* Function generator registers */ +#define AD5529R_REG_FUNC_EN (0x9C + 1) +#define AD5529R_REG_FUNC_MODE_SEL_BASE (0x9E + 1) +#define AD5529R_REG_FUNC_MODE_SEL(ch) \ + (AD5529R_REG_FUNC_MODE_SEL_BASE + (ch) * 2) + +#define AD5529R_REG_FUNC_DAC_INPUT_B_BASE (0xBE + 1) +#define AD5529R_REG_FUNC_DAC_INPUT_B(ch) \ + (AD5529R_REG_FUNC_DAC_INPUT_B_BASE + (ch) * 2) + +#define AD5529R_REG_FUNC_DITHER_PERIOD_BASE (0xDE + 1) +#define AD5529R_REG_FUNC_DITHER_PERIOD(ch) \ + (AD5529R_REG_FUNC_DITHER_PERIOD_BASE + (ch) * 2) + +#define AD5529R_REG_FUNC_DITHER_PHASE_BASE (0xFE + 1) +#define AD5529R_REG_FUNC_DITHER_PHASE(ch) \ + (AD5529R_REG_FUNC_DITHER_PHASE_BASE + (ch) * 2) + +#define AD5529R_REG_FUNC_RAMP_STEP_BASE (0x11E + 1) +#define AD5529R_REG_FUNC_RAMP_STEP(ch) \ + (AD5529R_REG_FUNC_RAMP_STEP_BASE + (ch) * 2) + +#define AD5529R_REG_FUNC_INT_EN (0x13E + 1) + +/* Multiplexer and main DAC registers */ +#define AD5529R_REG_MUX_OUT_SEL (0x140 + 1) +#define AD5529R_REG_MULTI_DAC_SW_LDAC (0x142 + 1) +#define AD5529R_REG_MULTI_DAC_INPUT_A (0x144 + 1) +#define AD5529R_REG_DAC_SW_LDAC (0x146 + 1) + +#define AD5529R_REG_DAC_INPUT_A_BASE (0x148 + 1) +#define AD5529R_REG_DAC_INPUT_A(ch) (AD5529R_REG_DAC_INPUT_A_BASE + (ch) * 2) + +/* Status and readback registers */ +#define AD5529R_REG_FUNC_INT_STAT (0x168 + 1) +#define AD5529R_REG_DAC_DATA_READBACK_BASE (0x16A + 1) +#define AD5529R_REG_DAC_DATA_READBACK(ch) \ + (AD5529R_REG_DAC_DATA_READBACK_BASE + (ch) * 2) + +/* Temperature sensor registers */ +#define AD5529R_REG_TSENS_EN (0x18A + 1) +#define AD5529R_REG_TSENS_ALERT_FLAG (0x18C + 1) +#define AD5529R_REG_TSENS_SHTD_FLAG (0x18E + 1) +#define AD5529R_REG_TSENS_ALERT_STAT (0x190 + 1) +#define AD5529R_REG_TSENS_SHTD_STAT (0x192 + 1) +#define AD5529R_REG_ALARMB_TSENS_EN (0x194 + 1) +#define AD5529R_REG_ALARMB_TSENS_SEL (0x196 + 1) +#define AD5529R_REG_TSENS_SHTD_EN_CH (0x198 + 1) +#define AD5529R_REG_DAC_DIS_DEGLITCH_CH (0x19A + 1) +#define AD5529R_REG_DAC_INT_EN (0x19C + 1) +#define AD5529R_REG_ALL_FUNC_INT_STAT (0x19E + 1) +#define AD5529R_REG_FUNC_BUSY (0x1A0 + 1) +#define AD5529R_REG_REF_SRC_SEL (0x1A2 + 1) +#define AD5529R_REG_INIT_CRC_ERR_STAT (0x1A4 + 1) + +/* Hotpath registers for multi-device support */ +#define AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC (0x1A8 + 1) +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_0 (0x1AA + 1) +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_1 (0x1AC + 1) +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_2 (0x1AE + 1) +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_3 (0x1B0 + 1) +#define AD5529R_REG_DAC_HOTPATH_SW_LDAC (0x1B2 + 1) + +/* Hotpath per-channel DAC input registers for each die */ +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE (0x1B4 + 1) +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0(ch) \ + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE + (ch) * 2) + +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE (0x1D4 + 1) +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1(ch) \ + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE + (ch) * 2) + +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE (0x1F4 + 1) +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2(ch) \ + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE + (ch) * 2) + +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE (0x214 + 1) +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3(ch) \ + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE + (ch) * 2) + +#define AD5529R_INTERFACE_CONFIG_A_SW_RESET (BIT(7) | BIT(0)) +#define AD5529R_INTERFACE_CONFIG_A_ADDR_ASCENSION BIT(5) +#define AD5529R_INTERFACE_CONFIG_A_SDO_ENABLE BIT(4) +#define AD5529R_INTERFACE_CONFIG_A_DEFAULT 0x10 +#define AD5529R_NUM_CHANNELS 16 +#define AD5529R_MAX_CHANNEL_INDEX (AD5529R_NUM_CHANNELS - 1) +#define AD5529R_MAX_REGISTER (0x232 + 1) +#define AD5529R_8BIT_REG_MAX 0x13 +#define AD5529R_ADDR(reg_addr) ((reg_addr) & 0xFFF) +#define AD5529R_RESET_PULSE_US 1000 +#define AD5529R_RESET_DELAY_US 10000 +#define AD5529R_SPI_BUF_SIZE 4 +#define AD5529R_NUM_SUPPLIES 4 +#define AD5529R_SPI_READ_FLAG 0x80 + +/* Device identification values */ +#define AD5529R_PRODUCT_ID_16BIT 0x4A +#define AD5529R_PRODUCT_ID_12BIT 0x49 + +struct ad5529r_model_data { + const char *model_name; + unsigned int resolution; + const struct iio_chan_spec *channels; +}; + +#define AD5529R_DAC_CHANNEL(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + }, \ +} + +static const char * const ad5529r_supply_names[AD5529R_NUM_SUPPLIES] = { + "vdd", + "avdd", + "hvdd", + "hvss", +}; + +static const struct iio_chan_spec ad5529r_channels_16bit[] = { + AD5529R_DAC_CHANNEL(0, 16), + AD5529R_DAC_CHANNEL(1, 16), + AD5529R_DAC_CHANNEL(2, 16), + AD5529R_DAC_CHANNEL(3, 16), + AD5529R_DAC_CHANNEL(4, 16), + AD5529R_DAC_CHANNEL(5, 16), + AD5529R_DAC_CHANNEL(6, 16), + AD5529R_DAC_CHANNEL(7, 16), + AD5529R_DAC_CHANNEL(8, 16), + AD5529R_DAC_CHANNEL(9, 16), + AD5529R_DAC_CHANNEL(10, 16), + AD5529R_DAC_CHANNEL(11, 16), + AD5529R_DAC_CHANNEL(12, 16), + AD5529R_DAC_CHANNEL(13, 16), + AD5529R_DAC_CHANNEL(14, 16), + AD5529R_DAC_CHANNEL(15, 16), +}; + +static const struct iio_chan_spec ad5529r_channels_12bit[] = { + AD5529R_DAC_CHANNEL(0, 12), + AD5529R_DAC_CHANNEL(1, 12), + AD5529R_DAC_CHANNEL(2, 12), + AD5529R_DAC_CHANNEL(3, 12), + AD5529R_DAC_CHANNEL(4, 12), + AD5529R_DAC_CHANNEL(5, 12), + AD5529R_DAC_CHANNEL(6, 12), + AD5529R_DAC_CHANNEL(7, 12), + AD5529R_DAC_CHANNEL(8, 12), + AD5529R_DAC_CHANNEL(9, 12), + AD5529R_DAC_CHANNEL(10, 12), + AD5529R_DAC_CHANNEL(11, 12), + AD5529R_DAC_CHANNEL(12, 12), + AD5529R_DAC_CHANNEL(13, 12), + AD5529R_DAC_CHANNEL(14, 12), + AD5529R_DAC_CHANNEL(15, 12), +}; + +static const struct ad5529r_model_data ad5529r_16bit_model_data = { + .model_name = "ad5529r-16", + .resolution = 16, + .channels = ad5529r_channels_16bit, +}; + +static const struct ad5529r_model_data ad5529r_12bit_model_data = { + .model_name = "ad5529r-12", + .resolution = 12, + .channels = ad5529r_channels_12bit, +}; + +struct ad5529r_state { + struct spi_device *spi; + const struct ad5529r_model_data *model_data; + struct regmap *regmap_8bit; + struct regmap *regmap_16bit; +}; + +static const struct regmap_range ad5529r_8bit_readable_ranges[] = { + regmap_reg_range(AD5529R_REG_INTERFACE_CONFIG_A, AD5529R_REG_CHIP_GRADE), + regmap_reg_range(AD5529R_REG_SCRATCH_PAD, AD5529R_REG_VENDOR_H), + regmap_reg_range(AD5529R_REG_STREAM_MODE, AD5529R_REG_INTERFACE_STATUS_A), +}; + +static const struct regmap_range ad5529r_16bit_readable_ranges[] = { + regmap_reg_range(AD5529R_REG_MULTI_DAC_CH_SEL, AD5529R_REG_LDAC_HW_SW), + regmap_reg_range(AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE, + AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_OUT_OPERATING_MODE, AD5529R_REG_OUT_OPERATING_MODE), + regmap_reg_range(AD5529R_REG_OUT_RANGE_BASE, + AD5529R_REG_OUT_RANGE_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_CAL_GAIN_BASE, + AD5529R_REG_CAL_GAIN_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_CAL_OFFSET_BASE, + AD5529R_REG_CAL_OFFSET_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_EN, AD5529R_REG_FUNC_EN), + regmap_reg_range(AD5529R_REG_FUNC_MODE_SEL_BASE, + AD5529R_REG_FUNC_MODE_SEL_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_DAC_INPUT_B_BASE, + AD5529R_REG_FUNC_DAC_INPUT_B_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_DITHER_PERIOD_BASE, + AD5529R_REG_FUNC_DITHER_PERIOD_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_DITHER_PHASE_BASE, + AD5529R_REG_FUNC_DITHER_PHASE_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_RAMP_STEP_BASE, + AD5529R_REG_FUNC_RAMP_STEP_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_INT_EN, AD5529R_REG_DAC_SW_LDAC), + regmap_reg_range(AD5529R_REG_DAC_INPUT_A_BASE, + AD5529R_REG_DAC_INPUT_A_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_FUNC_INT_STAT, AD5529R_REG_FUNC_INT_STAT), + regmap_reg_range(AD5529R_REG_DAC_DATA_READBACK_BASE, + AD5529R_REG_DAC_DATA_READBACK_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_TSENS_EN, AD5529R_REG_INIT_CRC_ERR_STAT), + regmap_reg_range(AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC, AD5529R_REG_DAC_HOTPATH_SW_LDAC), + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE, + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE + + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE, + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE + + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE, + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE + + AD5529R_MAX_CHANNEL_INDEX * 2), + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE, + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE + + AD5529R_MAX_CHANNEL_INDEX * 2), +}; + +static const struct regmap_access_table ad5529r_8bit_readable_table = { + .yes_ranges = ad5529r_8bit_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad5529r_8bit_readable_ranges), +}; + +static const struct regmap_access_table ad5529r_16bit_readable_table = { + .yes_ranges = ad5529r_16bit_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(ad5529r_16bit_readable_ranges), +}; + +static const struct regmap_range ad5529r_8bit_read_only_ranges[] = { + regmap_reg_range(AD5529R_REG_CHIP_TYPE, AD5529R_REG_CHIP_GRADE), + regmap_reg_range(AD5529R_REG_SPI_REVISION, AD5529R_REG_VENDOR_H), + regmap_reg_range(AD5529R_REG_DEVICE_CONFIG, AD5529R_REG_DEVICE_CONFIG), +}; + +static const struct regmap_range ad5529r_16bit_read_only_ranges[] = { + regmap_reg_range(AD5529R_REG_TSENS_ALERT_FLAG, AD5529R_REG_TSENS_SHTD_STAT), + regmap_reg_range(AD5529R_REG_ALL_FUNC_INT_STAT, AD5529R_REG_FUNC_BUSY), + regmap_reg_range(AD5529R_REG_INIT_CRC_ERR_STAT, AD5529R_REG_INIT_CRC_ERR_STAT), + regmap_reg_range(AD5529R_REG_DAC_DATA_READBACK_BASE, + AD5529R_REG_DAC_DATA_READBACK_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), +}; + +static const struct regmap_access_table ad5529r_8bit_writeable_table = { + .no_ranges = ad5529r_8bit_read_only_ranges, + .n_no_ranges = ARRAY_SIZE(ad5529r_8bit_read_only_ranges), +}; + +static const struct regmap_access_table ad5529r_16bit_writeable_table = { + .no_ranges = ad5529r_16bit_read_only_ranges, + .n_no_ranges = ARRAY_SIZE(ad5529r_16bit_read_only_ranges), +}; + +static const struct regmap_config ad5529r_regmap_8bit_config = { + .name = "ad5529r-8bit", + .reg_bits = 16, + .val_bits = 8, + .max_register = AD5529R_8BIT_REG_MAX, + .read_flag_mask = AD5529R_SPI_READ_FLAG, + .rd_table = &ad5529r_8bit_readable_table, + .wr_table = &ad5529r_8bit_writeable_table, +}; + +static const struct regmap_config ad5529r_regmap_16bit_config = { + .name = "ad5529r-16bit", + .reg_bits = 16, + .val_bits = 16, + .max_register = AD5529R_MAX_REGISTER, + .read_flag_mask = AD5529R_SPI_READ_FLAG, + .rd_table = &ad5529r_16bit_readable_table, + .wr_table = &ad5529r_16bit_writeable_table, +}; + +static struct regmap *ad5529r_get_regmap(struct ad5529r_state *st, unsigned int reg) +{ + if (reg <= AD5529R_8BIT_REG_MAX) + return st->regmap_8bit; + + return st->regmap_16bit; +} + +static int ad5529r_debugfs_reg_read(struct ad5529r_state *st, unsigned int reg, + unsigned int *val) +{ + return regmap_read(ad5529r_get_regmap(st, reg), reg, val); +} + +static int ad5529r_debugfs_reg_write(struct ad5529r_state *st, unsigned int reg, + unsigned int val) +{ + return regmap_write(ad5529r_get_regmap(st, reg), reg, val); +} + +static int ad5529r_detect_device(struct ad5529r_state *st) +{ + unsigned int product_id; + int ret; + + ret = regmap_read(st->regmap_8bit, AD5529R_REG_PRODUCT_ID_L, &product_id); + if (ret) + return ret; + + switch (product_id) { + case AD5529R_PRODUCT_ID_16BIT: + st->model_data = &ad5529r_16bit_model_data; + break; + case AD5529R_PRODUCT_ID_12BIT: + st->model_data = &ad5529r_12bit_model_data; + break; + default: + dev_err(&st->spi->dev, "Unknown product ID: 0x%02X\n", product_id); + return -ENODEV; + } + + dev_dbg(&st->spi->dev, "Detected %s variant (Product ID: 0x%02X)\n", + st->model_data->model_name, product_id); + + return 0; +} + +static int ad5529r_reset(struct ad5529r_state *st) +{ + struct reset_control *rst; + int ret; + + rst = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + if (rst) { + ret = reset_control_deassert(rst); + if (ret) + return ret; + } else { + ret = regmap_write(st->regmap_8bit, AD5529R_REG_INTERFACE_CONFIG_A, + AD5529R_INTERFACE_CONFIG_A_SW_RESET); + if (ret) + return ret; + } + + fsleep(AD5529R_RESET_DELAY_US); + + return regmap_write(st->regmap_8bit, AD5529R_REG_INTERFACE_CONFIG_A, + AD5529R_INTERFACE_CONFIG_A_DEFAULT); +} + +static int ad5529r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad5529r_state *st = iio_priv(indio_dev); + unsigned int reg_addr; + unsigned int reg_val_h; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + reg_addr = AD5529R_REG_DAC_INPUT_A(chan->channel); + ret = regmap_read(st->regmap_16bit, reg_addr, ®_val_h); + if (ret) + return ret; + + *val = reg_val_h; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * Using default 0-5V range: VOUTn = A × D/2^N + B + * where A = 5V, B = 0V, D = digital code, N = resolution + * Scale = 5000mV / 2^resolution + */ + *val = 5000; + *val2 = st->model_data->resolution; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad5529r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad5529r_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val > GENMASK(st->model_data->resolution - 1, 0)) + return -EINVAL; + + return regmap_write(st->regmap_16bit, AD5529R_REG_DAC_INPUT_A(chan->channel), val); + default: + return -EINVAL; + } +} + +static int ad5529r_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad5529r_state *st = iio_priv(indio_dev); + + if (!readval) + return ad5529r_debugfs_reg_write(st, reg, writeval); + + return ad5529r_debugfs_reg_read(st, reg, readval); +} + +static const struct iio_info ad5529r_info = { + .read_raw = ad5529r_read_raw, + .write_raw = ad5529r_write_raw, + .debugfs_reg_access = ad5529r_reg_access, +}; + +static int ad5529r_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad5529r_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->spi = spi; + + ret = devm_regulator_bulk_get_enable(dev, AD5529R_NUM_SUPPLIES, + ad5529r_supply_names); + if (ret) + return dev_err_probe(dev, ret, "Failed to get and enable regulators\n"); + + st->regmap_8bit = devm_regmap_init_spi(spi, &ad5529r_regmap_8bit_config); + if (IS_ERR(st->regmap_8bit)) + return dev_err_probe(dev, PTR_ERR(st->regmap_8bit), + "Failed to initialize 8-bit regmap\n"); + + st->regmap_16bit = devm_regmap_init_spi(spi, &ad5529r_regmap_16bit_config); + if (IS_ERR(st->regmap_16bit)) + return dev_err_probe(dev, PTR_ERR(st->regmap_16bit), + "Failed to initialize 16-bit regmap\n"); + + ret = ad5529r_reset(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to reset device\n"); + + ret = ad5529r_detect_device(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to detect device variant\n"); + + indio_dev->name = st->model_data->model_name; + indio_dev->info = &ad5529r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->model_data->channels; + indio_dev->num_channels = AD5529R_NUM_CHANNELS; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad5529r_of_match[] = { + { .compatible = "adi,ad5529r" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5529r_of_match); + +static const struct spi_device_id ad5529r_id[] = { + { "ad5529r" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5529r_id); + +static struct spi_driver ad5529r_driver = { + .driver = { + .name = "ad5529r", + .of_match_table = ad5529r_of_match, + }, + .probe = ad5529r_probe, + .id_table = ad5529r_id, +}; +module_spi_driver(ad5529r_driver); + +MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD5529R 12/16-bit DAC driver"); +MODULE_LICENSE("GPL"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support 2026-05-08 11:55 ` [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support Janani Sunil @ 2026-05-08 13:30 ` Jonathan Cameron 2026-05-08 20:55 ` sashiko-bot 1 sibling, 0 replies; 12+ messages in thread From: Jonathan Cameron @ 2026-05-08 13:30 UTC (permalink / raw) To: Janani Sunil Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil On Fri, 8 May 2026 13:55:48 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > Add support for AD5529R 16-channel, 12/16 bit Digital to Analog Converter > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> Hi Janani, Various comments inline Thanks, Jonathan > diff --git a/drivers/iio/dac/ad5529r.c b/drivers/iio/dac/ad5529r.c > new file mode 100644 > index 000000000000..3676956f6eff > --- /dev/null > +++ b/drivers/iio/dac/ad5529r.c > @@ -0,0 +1,564 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * AD5529R Digital-to-Analog Converter Driver > + * 16-Channel, 12/16-Bit, 40V High Voltage Precision DAC > + * > + * Copyright 2026 Analog Devices Inc. > + * Author: Janani Sunil <janani.sunil@analog.com> > + */ > + > +#include <linux/array_size.h> > +#include <linux/bits.h> > +#include <linux/device.h> > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/mod_devicetable.h> > +#include <linux/spi/spi.h> > +#include <linux/errno.h> > +#include <linux/iio/iio.h> > +#include <linux/regmap.h> > +#include <linux/reset.h> > +#include <linux/delay.h> > +#include <linux/regulator/consumer.h> Alphabetical order. > + > +/* Register Map */ > +#define AD5529R_REG_INTERFACE_CONFIG_A 0x00 > +#define AD5529R_REG_INTERFACE_CONFIG_B 0x01 > +#define AD5529R_REG_DEVICE_CONFIG 0x02 > +#define AD5529R_REG_CHIP_TYPE 0x03 > +#define AD5529R_REG_PRODUCT_ID_L 0x04 > +#define AD5529R_REG_PRODUCT_ID_H 0x05 > +#define AD5529R_REG_CHIP_GRADE 0x06 > +#define AD5529R_REG_SCRATCH_PAD 0x0A > +#define AD5529R_REG_SPI_REVISION 0x0B > +#define AD5529R_REG_VENDOR_L 0x0C > +#define AD5529R_REG_VENDOR_H 0x0D > +#define AD5529R_REG_STREAM_MODE 0x0E > +#define AD5529R_REG_TRANSFER_CONFIG 0x0F > +#define AD5529R_REG_INTERFACE_CONFIG_C 0x10 > +#define AD5529R_REG_INTERFACE_STATUS_A 0x11 > + > +/* Configuration registers */ > +#define AD5529R_REG_MULTI_DAC_CH_SEL (0x14 + 1) Feels like this would all be simpler if you used autoincrement rather than default value of autdecrement. What breaks if you do that? Superficially feels like all the +1 would go away - though with need for a byte swap? Might be worth that pain for the simpler code. Should just be a regmap_config parameter. > +#define AD5529R_REG_LDAC_SYNC_ASYNC (0x16 + 1) > +#define AD5529R_REG_LDAC_HW_SW (0x18 + 1) > + > +/* Hardware LDAC source and edge select registers (per channel, 16-bit) */ > +#define AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE (0x1A + 1) > +#define AD5529R_REG_LDAC_HW_SRC_EDGE_SEL(ch) \ > + (AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE + (ch) * 2) > + > +/* Output configuration */ > +#define AD5529R_REG_OUT_OPERATING_MODE (0x3A + 1) > +#define AD5529R_REG_OUT_RANGE_BASE (0x3C + 1) > +#define AD5529R_REG_OUT_RANGE(ch) (AD5529R_REG_OUT_RANGE_BASE + (ch) * 2) > + > +/* Calibration registers */ > +#define AD5529R_REG_CAL_GAIN_BASE (0x5C + 1) > +#define AD5529R_REG_CAL_GAIN(ch) (AD5529R_REG_CAL_GAIN_BASE + (ch) * 2) > + > +#define AD5529R_REG_CAL_OFFSET_BASE (0x7C + 1) > +#define AD5529R_REG_CAL_OFFSET(ch) (AD5529R_REG_CAL_OFFSET_BASE + (ch) * 2) > + > +/* Function generator registers */ > +#define AD5529R_REG_FUNC_EN (0x9C + 1) > +#define AD5529R_REG_FUNC_MODE_SEL_BASE (0x9E + 1) > +#define AD5529R_REG_FUNC_MODE_SEL(ch) \ > + (AD5529R_REG_FUNC_MODE_SEL_BASE + (ch) * 2) > + > +#define AD5529R_REG_FUNC_DAC_INPUT_B_BASE (0xBE + 1) > +#define AD5529R_REG_FUNC_DAC_INPUT_B(ch) \ > + (AD5529R_REG_FUNC_DAC_INPUT_B_BASE + (ch) * 2) > + > +#define AD5529R_REG_FUNC_DITHER_PERIOD_BASE (0xDE + 1) > +#define AD5529R_REG_FUNC_DITHER_PERIOD(ch) \ > + (AD5529R_REG_FUNC_DITHER_PERIOD_BASE + (ch) * 2) > + > +#define AD5529R_REG_FUNC_DITHER_PHASE_BASE (0xFE + 1) > +#define AD5529R_REG_FUNC_DITHER_PHASE(ch) \ > + (AD5529R_REG_FUNC_DITHER_PHASE_BASE + (ch) * 2) > + > +#define AD5529R_REG_FUNC_RAMP_STEP_BASE (0x11E + 1) > +#define AD5529R_REG_FUNC_RAMP_STEP(ch) \ > + (AD5529R_REG_FUNC_RAMP_STEP_BASE + (ch) * 2) > + > +#define AD5529R_REG_FUNC_INT_EN (0x13E + 1) > + > +/* Multiplexer and main DAC registers */ > +#define AD5529R_REG_MUX_OUT_SEL (0x140 + 1) > +#define AD5529R_REG_MULTI_DAC_SW_LDAC (0x142 + 1) > +#define AD5529R_REG_MULTI_DAC_INPUT_A (0x144 + 1) > +#define AD5529R_REG_DAC_SW_LDAC (0x146 + 1) > + > +#define AD5529R_REG_DAC_INPUT_A_BASE (0x148 + 1) > +#define AD5529R_REG_DAC_INPUT_A(ch) (AD5529R_REG_DAC_INPUT_A_BASE + (ch) * 2) > + > +/* Status and readback registers */ > +#define AD5529R_REG_FUNC_INT_STAT (0x168 + 1) > +#define AD5529R_REG_DAC_DATA_READBACK_BASE (0x16A + 1) > +#define AD5529R_REG_DAC_DATA_READBACK(ch) \ > + (AD5529R_REG_DAC_DATA_READBACK_BASE + (ch) * 2) > + > +/* Temperature sensor registers */ > +#define AD5529R_REG_TSENS_EN (0x18A + 1) > +#define AD5529R_REG_TSENS_ALERT_FLAG (0x18C + 1) > +#define AD5529R_REG_TSENS_SHTD_FLAG (0x18E + 1) > +#define AD5529R_REG_TSENS_ALERT_STAT (0x190 + 1) > +#define AD5529R_REG_TSENS_SHTD_STAT (0x192 + 1) > +#define AD5529R_REG_ALARMB_TSENS_EN (0x194 + 1) > +#define AD5529R_REG_ALARMB_TSENS_SEL (0x196 + 1) > +#define AD5529R_REG_TSENS_SHTD_EN_CH (0x198 + 1) > +#define AD5529R_REG_DAC_DIS_DEGLITCH_CH (0x19A + 1) > +#define AD5529R_REG_DAC_INT_EN (0x19C + 1) > +#define AD5529R_REG_ALL_FUNC_INT_STAT (0x19E + 1) > +#define AD5529R_REG_FUNC_BUSY (0x1A0 + 1) > +#define AD5529R_REG_REF_SRC_SEL (0x1A2 + 1) > +#define AD5529R_REG_INIT_CRC_ERR_STAT (0x1A4 + 1) > + > +/* Hotpath registers for multi-device support */ > +#define AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC (0x1A8 + 1) > +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_0 (0x1AA + 1) > +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_1 (0x1AC + 1) > +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_2 (0x1AE + 1) > +#define AD5529R_REG_MULTI_DAC_HOTPATH_INPUT_A_DIE_3 (0x1B0 + 1) > +#define AD5529R_REG_DAC_HOTPATH_SW_LDAC (0x1B2 + 1) > + > +/* Hotpath per-channel DAC input registers for each die */ > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE (0x1B4 + 1) > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0(ch) \ > + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE + (ch) * 2) > + > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE (0x1D4 + 1) > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1(ch) \ > + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE + (ch) * 2) > + > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE (0x1F4 + 1) > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2(ch) \ > + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE + (ch) * 2) > + > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE (0x214 + 1) > +#define AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3(ch) \ > + (AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE + (ch) * 2) > + > +#define AD5529R_INTERFACE_CONFIG_A_SW_RESET (BIT(7) | BIT(0)) > +#define AD5529R_INTERFACE_CONFIG_A_ADDR_ASCENSION BIT(5) > +#define AD5529R_INTERFACE_CONFIG_A_SDO_ENABLE BIT(4) > +#define AD5529R_INTERFACE_CONFIG_A_DEFAULT 0x10 I'd put the values it represents inline and get rid of this define. _DEFAULT defines are rarely a good design pattern > +#define AD5529R_NUM_CHANNELS 16 I'd store this along side the channels pointer then you can use ARRAY_SIZE() on the the chan_spec array and drop this constant. > +#define AD5529R_MAX_CHANNEL_INDEX (AD5529R_NUM_CHANNELS - 1) With the above gone, just make this a hard coded 15. > +#define AD5529R_MAX_REGISTER (0x232 + 1) > +#define AD5529R_8BIT_REG_MAX 0x13 > +#define AD5529R_ADDR(reg_addr) ((reg_addr) & 0xFFF) Not used, so drop it. > +#define AD5529R_RESET_PULSE_US 1000 > +#define AD5529R_RESET_DELAY_US 10000 As mentioned below - just put these numbers inline. Defines just make the code harder to read when they are used only once and represent exactly what the value is. > +#define AD5529R_SPI_BUF_SIZE 4 No idea what this is for - not used. > +#define AD5529R_NUM_SUPPLIES 4 No need for a constant for this - use [] to define the names array and ARRAY_SIZE() on that to get the size where needed. > +#define AD5529R_SPI_READ_FLAG 0x80 > + > +static const struct regmap_range ad5529r_8bit_readable_ranges[] = { > + regmap_reg_range(AD5529R_REG_INTERFACE_CONFIG_A, AD5529R_REG_CHIP_GRADE), > + regmap_reg_range(AD5529R_REG_SCRATCH_PAD, AD5529R_REG_VENDOR_H), > + regmap_reg_range(AD5529R_REG_STREAM_MODE, AD5529R_REG_INTERFACE_STATUS_A), > +}; > + > +static const struct regmap_range ad5529r_16bit_readable_ranges[] = { Tricky bit here is you are saying it's a 16 bit regmap but then providing address ranges including the ones we shouldn't use. We need to hide those intermediate addresses. Various things might work depending on the addresses. Can we hide the bottom bit of each address then write it to appropriate value under the hood. That is divide addresses by 2? > + regmap_reg_range(AD5529R_REG_MULTI_DAC_CH_SEL, AD5529R_REG_LDAC_HW_SW), > + regmap_reg_range(AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE, > + AD5529R_REG_LDAC_HW_SRC_EDGE_SEL_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_OUT_OPERATING_MODE, AD5529R_REG_OUT_OPERATING_MODE), > + regmap_reg_range(AD5529R_REG_OUT_RANGE_BASE, > + AD5529R_REG_OUT_RANGE_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_CAL_GAIN_BASE, > + AD5529R_REG_CAL_GAIN_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_CAL_OFFSET_BASE, > + AD5529R_REG_CAL_OFFSET_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_EN, AD5529R_REG_FUNC_EN), > + regmap_reg_range(AD5529R_REG_FUNC_MODE_SEL_BASE, > + AD5529R_REG_FUNC_MODE_SEL_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_DAC_INPUT_B_BASE, > + AD5529R_REG_FUNC_DAC_INPUT_B_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_DITHER_PERIOD_BASE, > + AD5529R_REG_FUNC_DITHER_PERIOD_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_DITHER_PHASE_BASE, > + AD5529R_REG_FUNC_DITHER_PHASE_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_RAMP_STEP_BASE, > + AD5529R_REG_FUNC_RAMP_STEP_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_INT_EN, AD5529R_REG_DAC_SW_LDAC), > + regmap_reg_range(AD5529R_REG_DAC_INPUT_A_BASE, > + AD5529R_REG_DAC_INPUT_A_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_FUNC_INT_STAT, AD5529R_REG_FUNC_INT_STAT), > + regmap_reg_range(AD5529R_REG_DAC_DATA_READBACK_BASE, > + AD5529R_REG_DAC_DATA_READBACK_BASE + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_TSENS_EN, AD5529R_REG_INIT_CRC_ERR_STAT), > + regmap_reg_range(AD5529R_REG_MULTI_DAC_HOTPATH_SW_LDAC, AD5529R_REG_DAC_HOTPATH_SW_LDAC), > + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE, > + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_0_BASE + > + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE, > + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_1_BASE + > + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE, > + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_2_BASE + > + AD5529R_MAX_CHANNEL_INDEX * 2), > + regmap_reg_range(AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE, > + AD5529R_REG_DAC_HOTPATH_INPUT_A_DIE_3_BASE + > + AD5529R_MAX_CHANNEL_INDEX * 2), Yikes. This thing has an ugly register map. > +}; > + > +static int ad5529r_detect_device(struct ad5529r_state *st) > +{ > + unsigned int product_id; > + int ret; > + > + ret = regmap_read(st->regmap_8bit, AD5529R_REG_PRODUCT_ID_L, &product_id); > + if (ret) > + return ret; > + > + switch (product_id) { > + case AD5529R_PRODUCT_ID_16BIT: > + st->model_data = &ad5529r_16bit_model_data; > + break; > + case AD5529R_PRODUCT_ID_12BIT: > + st->model_data = &ad5529r_12bit_model_data; > + break; > + default: > + dev_err(&st->spi->dev, "Unknown product ID: 0x%02X\n", product_id); > + return -ENODEV; See below on why this doesn't extend to fallback compatibles from DT and what to do instead. > + } > + > + dev_dbg(&st->spi->dev, "Detected %s variant (Product ID: 0x%02X)\n", > + st->model_data->model_name, product_id); > + > + return 0; > +} > + > +static int ad5529r_reset(struct ad5529r_state *st) > +{ > + struct reset_control *rst; > + int ret; > + > + rst = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL); > + if (IS_ERR(rst)) > + return PTR_ERR(rst); > + > + if (rst) { > + ret = reset_control_deassert(rst); > + if (ret) > + return ret; > + } else { > + ret = regmap_write(st->regmap_8bit, AD5529R_REG_INTERFACE_CONFIG_A, > + AD5529R_INTERFACE_CONFIG_A_SW_RESET); > + if (ret) > + return ret; > + } > + > + fsleep(AD5529R_RESET_DELAY_US); This define is only used in one place. I'd rather see the value here and a comment on where it comes from - typically a spec reference. > + > + return regmap_write(st->regmap_8bit, AD5529R_REG_INTERFACE_CONFIG_A, > + AD5529R_INTERFACE_CONFIG_A_DEFAULT); > +} > + > +static int ad5529r_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct ad5529r_state *st = iio_priv(indio_dev); > + unsigned int reg_addr; > + unsigned int reg_val_h; Could combine those two on oneline (not that important) > + int ret; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + reg_addr = AD5529R_REG_DAC_INPUT_A(chan->channel); > + ret = regmap_read(st->regmap_16bit, reg_addr, ®_val_h); > + if (ret) > + return ret; > + > + *val = reg_val_h; > + > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + /* > + * Using default 0-5V range: VOUTn = A × D/2^N + B > + * where A = 5V, B = 0V, D = digital code, N = resolution > + * Scale = 5000mV / 2^resolution See the comment on the dt-binding. I think we need support for dt described output ranges from the start. This is a rare multi range device where we could set a safe default but to me it makes little sense and the driver will be doing something unexpected if a newer DT is provided with a different range. > + */ > + *val = 5000; > + *val2 = st->model_data->resolution; > + > + return IIO_VAL_FRACTIONAL_LOG2; > + default: > + return -EINVAL; > + } > +} > + > +static int ad5529r_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct ad5529r_state *st = iio_priv(indio_dev); > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + if (val < 0 || val > GENMASK(st->model_data->resolution - 1, 0)) > + return -EINVAL; > + > + return regmap_write(st->regmap_16bit, AD5529R_REG_DAC_INPUT_A(chan->channel), val); That's a very long line. Break it up as: return regmap_write(st->regmap_16bit, AD5529R_REG_DAC_INPUT_A(chan->channel), val); I don't mind going past 80 for readability but here I don't see it as greatly hurt by breaking the line and it was way past 80. Or use a local reg_addr variable like you have in read_raw() > + default: > + return -EINVAL; > + } > +} > + > +static int ad5529r_probe(struct spi_device *spi) > +{ > + struct device *dev = &spi->dev; > + struct iio_dev *indio_dev; > + struct ad5529r_state *st; > + int ret; > + > + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); > + if (!indio_dev) > + return -ENOMEM; > + > + st = iio_priv(indio_dev); > + > + st->spi = spi; > + > + ret = devm_regulator_bulk_get_enable(dev, AD5529R_NUM_SUPPLIES, > + ad5529r_supply_names); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to get and enable regulators\n"); > + > + st->regmap_8bit = devm_regmap_init_spi(spi, &ad5529r_regmap_8bit_config); > + if (IS_ERR(st->regmap_8bit)) > + return dev_err_probe(dev, PTR_ERR(st->regmap_8bit), > + "Failed to initialize 8-bit regmap\n"); > + > + st->regmap_16bit = devm_regmap_init_spi(spi, &ad5529r_regmap_16bit_config); > + if (IS_ERR(st->regmap_16bit)) > + return dev_err_probe(dev, PTR_ERR(st->regmap_16bit), > + "Failed to initialize 16-bit regmap\n"); > + > + ret = ad5529r_reset(st); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to reset device\n"); > + > + ret = ad5529r_detect_device(st); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to detect device variant\n"); No to this. It breaks the use of fallback device tree compatibles. As such we never fail on an ID missmatch. Instead we just believe firmware when it says whatever is there is compatible with this device. See below on why I think we need to break this into separate compatibles. > + > + indio_dev->name = st->model_data->model_name; > + indio_dev->info = &ad5529r_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = st->model_data->channels; > + indio_dev->num_channels = AD5529R_NUM_CHANNELS; > + > + return devm_iio_device_register(dev, indio_dev); > +} > + > +static const struct of_device_id ad5529r_of_match[] = { > + { .compatible = "adi,ad5529r" }, Hmm. I'm in two minds on this. Is it better to do as you have an detect between the ad5529r-12 and ad5529r-16 based on ID or should we just have them as separate compatibles? If they had different part numbers (which is most common way this is done by ADI and others) then we'd not consider sharing a compatible. As such I think we should split them. That also makes fallback compatibles work. Otherwise how would we know whether a new device ID should be 12 or 16 bit? > + { } > +}; > +MODULE_DEVICE_TABLE(of, ad5529r_of_match); > + > +static const struct spi_device_id ad5529r_id[] = { > + { "ad5529r" }, Same would apply here for including the postfix in the naming. > + { } > +}; > +MODULE_DEVICE_TABLE(spi, ad5529r_id); > + > +static struct spi_driver ad5529r_driver = { > + .driver = { > + .name = "ad5529r", > + .of_match_table = ad5529r_of_match, > + }, > + .probe = ad5529r_probe, > + .id_table = ad5529r_id, > +}; > +module_spi_driver(ad5529r_driver); > + > +MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>"); > +MODULE_DESCRIPTION("Analog Devices AD5529R 12/16-bit DAC driver"); > +MODULE_LICENSE("GPL"); > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support 2026-05-08 11:55 ` [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support Janani Sunil 2026-05-08 13:30 ` Jonathan Cameron @ 2026-05-08 20:55 ` sashiko-bot 1 sibling, 0 replies; 12+ messages in thread From: sashiko-bot @ 2026-05-08 20:55 UTC (permalink / raw) To: Janani Sunil; +Cc: krzk+dt, devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [Critical] Incorrect SPI `read_flag_mask` causes all regmap reads to fail. - [High] Incomplete hardware reset sequence fails to properly reset the device. - [High] Hardcoded output scale ignores device configuration and internal reference. - [High] Missing LDAC (Load DAC) trigger prevents analog output updates. -- commit 02e32fb72beab3fdbff7c500ce586fab5c255e89 Author: Janani Sunil <janani.sunil@analog.com> iio: dac: Add AD5529R DAC driver support Add support for AD5529R 16-channel, 12/16 bit Digital to Analog Converter > diff --git a/drivers/iio/dac/ad5529r.c b/drivers/iio/dac/ad5529r.c > new file mode 100644 > index 0000000000000..3676956f6eff7 > --- /dev/null > +++ b/drivers/iio/dac/ad5529r.c > @@ -0,0 +1,564 @@ [ ... ] > +#define AD5529R_RESET_DELAY_US 10000 > +#define AD5529R_SPI_BUF_SIZE 4 > +#define AD5529R_NUM_SUPPLIES 4 > +#define AD5529R_SPI_READ_FLAG 0x80 [ ... ] > +static const struct regmap_config ad5529r_regmap_8bit_config = { > + .name = "ad5529r-8bit", > + .reg_bits = 16, > + .val_bits = 8, > + .max_register = AD5529R_8BIT_REG_MAX, > + .read_flag_mask = AD5529R_SPI_READ_FLAG, Is this mask correct for a 16-bit register address? Since reg_bits is set to 16 in the regmap configuration, setting read_flag_mask to 0x80 will set bit 7 instead of the most-significant bit (bit 15). Should this be 0x8000 so the device properly interprets read commands? > + .rd_table = &ad5529r_8bit_readable_table, > + .wr_table = &ad5529r_8bit_writeable_table, > +}; [ ... ] > +static int ad5529r_reset(struct ad5529r_state *st) > +{ > + struct reset_control *rst; > + int ret; > + > + rst = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL); > + if (IS_ERR(rst)) > + return PTR_ERR(rst); > + > + if (rst) { > + ret = reset_control_deassert(rst); Will this properly reset the device if the reset line was already left in a deasserted state by the bootloader? There is an unused AD5529R_RESET_PULSE_US macro defined earlier. Does the driver need to call reset_control_assert() and wait before calling reset_control_deassert() to ensure the hardware sees a valid reset pulse? > + if (ret) > + return ret; > + } else { > + ret = regmap_write(st->regmap_8bit, AD5529R_REG_INTERFACE_CONFIG_A, [ ... ] > +static int ad5529r_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ [ ... ] > + case IIO_CHAN_INFO_SCALE: > + /* > + * Using default 0-5V range: VOUTn = A × D/2^N + B > + * where A = 5V, B = 0V, D = digital code, N = resolution > + * Scale = 5000mV / 2^resolution > + */ > + *val = 5000; The commit message mentions the device features multiple output voltage ranges from -20V to +20V and a built-in 4.096V reference. However, the scale is hardcoded to assume a 0-5V range. Will this report incorrect physical voltages to userspace if the device defaults to a different range, uses the internal reference, or was configured for a bipolar range by the bootloader? > + *val2 = st->model_data->resolution; > + > + return IIO_VAL_FRACTIONAL_LOG2; > + default: > + return -EINVAL; > + } > +} > + > +static int ad5529r_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct ad5529r_state *st = iio_priv(indio_dev); > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + if (val < 0 || val > GENMASK(st->model_data->resolution - 1, 0)) > + return -EINVAL; > + > + return regmap_write(st->regmap_16bit, AD5529R_REG_DAC_INPUT_A(chan->channel), val); Does this code actually trigger an update to the physical analog output? The value is written to the DAC_INPUT_A register, but the driver does not appear to issue a software LDAC command or pulse a hardware LDAC GPIO. If the device doesn't default to a transparent mode, is an explicit LDAC trigger needed here? > + default: > + return -EINVAL; > + } > +} -- Sashiko AI review · https://sashiko.dev/#/patchset/20260508-ad5529r-driver-v2-0-e315441685d7@analog.com?part=2 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 3/3] Documentation: iio: Add AD5529R Documentation 2026-05-08 11:55 [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Janani Sunil 2026-05-08 11:55 ` [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R Janani Sunil 2026-05-08 11:55 ` [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support Janani Sunil @ 2026-05-08 11:55 ` Janani Sunil 2026-05-08 13:00 ` Jonathan Cameron 2026-05-08 12:36 ` [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Jonathan Cameron 3 siblings, 1 reply; 12+ messages in thread From: Janani Sunil @ 2026-05-08 11:55 UTC (permalink / raw) To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan Cc: linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil, Janani Sunil Add documentation for AD5529R high voltage, 16-channel 12/16 bit DAC Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- Documentation/iio/ad5529r.rst | 216 ++++++++++++++++++++++++++++++++++++++++++ Documentation/iio/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 218 insertions(+) diff --git a/Documentation/iio/ad5529r.rst b/Documentation/iio/ad5529r.rst new file mode 100644 index 000000000000..41fea1521790 --- /dev/null +++ b/Documentation/iio/ad5529r.rst @@ -0,0 +1,216 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +============== +AD5529R driver +============== + +Device driver for Analog Devices Inc. AD5529R 16-Channel 12/16-bit High Voltage DAC. +The module name is ``ad5529r``. + +Supported devices +================= + +* `AD5529R <https://www.analog.com/en/products/ad5529r.html>`_ + +Description +=========== + +The AD5529R is a 16-channel, 12-bit or 16-bit, high voltage, buffered voltage output +digital-to-analog converter (DAC) with an integrated precision reference. +The device operates from unipolar and bipolar supplies and is guaranteed +monotonic. It has built-in rail-to-rail output buffers that can source or +sink up to 25mA. + +Hardware Features +================= + +* 16 independent 12-bit or 16-bit DAC channels +* Independently programmable output ranges: + + - 0V to 5V (current driver default) + - 0V to 10V + - 0V to 20V + - 0V to 40V + - ±5V + - ±10V + - ±15V + - ±20V + +* 4.096V precision reference (12ppm/°C maximum) +* Built-in function generation capabilities (hardware support) +* Output voltage and current monitoring (hardware support) +* Temperature monitoring with 8 on-chip sensors (hardware support) +* Over-temperature protection +* SPI interface with CRC error detection support + +Current Driver Implementation +============================= + +The current driver provides basic DAC functionality with the following features: + +* Basic DAC output control for all 16 channels +* Scale attributes for voltage conversion (0-5V default range) +* SPI communication with regmap support +* Reset control framework support +* Automatic hardware variant detection (16-bit vs 12-bit) based on product ID +* Debugfs register access for development + +SPI Configuration: + +* **Mode**: Supports SPI mode 0 and mode 3 (default: mode 0) +* **Frequency**: Up to 50 MHz (typically tested at lower frequencies) +* **Word Size**: 16-bit transactions + +.. note:: + The device default configuration uses address decrement mode (ADDR_ASCENSION=0) + for multi-byte SPI transactions. Therefore, all 16-bit register addresses are + incremented by 1 in the driver to access the last byte first, allowing the + hardware to decrement and access the complete multi-byte register correctly. + +IIO Attributes (Currently Implemented) +====================================== + +Basic DAC Control +----------------- + +For each of the 16 channels (0-15): + +**out_voltageY_raw** + Raw DAC code (12-bit: 0-4095, 16-bit: 0-65535) + + * Read: Returns the current DAC register value + * Write: Sets the DAC output code + +**out_voltageY_scale** + Scale factor for voltage conversion (millivolts per LSB) + + Based on the formula: VOUTn = A × D/2^N + B, where A=5V, B=0V, N=resolution + + * 16-bit: 0.076294 mV/LSB (5V ÷ 2^16 = 5V ÷ 65536 = 0.076294mV) + * 12-bit: 1.220703 mV/LSB (5V ÷ 2^12 = 5V ÷ 4096 = 1.220703mV) + * Read-only attribute + +Debug Interface +=============== + +**Register Access** + +The driver provides debugfs register access for debugging and development: + +``/sys/kernel/debug/iio/iio:deviceX/direct_reg_access`` + Direct register read/write access. Format: + + * Read: ``echo <register_address> > direct_reg_access; cat direct_reg_access`` + * Write: ``echo <register_address> <value> > direct_reg_access`` + +Usage examples +============== + +Basic DAC Output Control +------------------------ + +.. code-block:: bash + + # Set channel 0 to mid-scale (approximately 2.5V with 0V to 5V range) + echo "32768" > /sys/bus/iio/devices/iio:device0/out_voltage0_raw + + # Set channel 15 to full scale + echo "65535" > /sys/bus/iio/devices/iio:device0/out_voltage15_raw + + # Read current value from channel 5 + cat /sys/bus/iio/devices/iio:device0/out_voltage5_raw + +Scale Attributes +---------------- + +.. code-block:: bash + + # Read scale factor (millivolts per LSB) + cat /sys/bus/iio/devices/iio:device0/out_voltage0_scale + # Output: 0.076294 (for 16-bit) or 1.220703 (for 12-bit) + + # Convert raw to voltage: voltage_mv = raw * scale + # Formula: VOUTn = A × D/2^N + B where A=5V, B=0V + +Register Access for Development +------------------------------- + +.. code-block:: bash + + # Navigate to debugfs directory + cd /sys/kernel/debug/iio/iio:device0/ + + # Read device product ID (register 0x04) + echo 4 > direct_reg_access + cat direct_reg_access + + # Write to a 16-bit configuration register (example: LDAC_HW_SW register 0x19) + echo "0x019 0xAA11" > direct_reg_access + cat direct_reg_access + # Output: 0xAA11 + + # Write to DAC channel registers (16-bit values) + echo "0x149 32768" > direct_reg_access # DAC channel 0 mid-scale + echo "0x14B 65535" > direct_reg_access # DAC channel 1 full-scale + + # Read back DAC register values + echo 0x149 > direct_reg_access && cat direct_reg_access # Read channel 0 + echo 0x14B > direct_reg_access && cat direct_reg_access # Read channel 1 + +.. note:: + For 16-bit registers, use hexadecimal format for addresses (0x019, 0x149, etc.). + Values can be decimal (32768) or hexadecimal (0xAA11). Register addresses shown + include the +1 offset required for decrement mode operation. + +Device Tree Configuration +========================= + +Basic configuration example: + +.. code-block:: devicetree + + &spi0 { + status = "okay"; + + ad5529r@0 { + compatible = "adi,ad5529r"; + reg = <0>; + spi-max-frequency = <25000000>; + + vdd-supply = <&vdd_regulator>; + avdd-supply = <&avdd_regulator>; + hvdd-supply = <&hvdd_regulator>; + hvss-supply = <&hvss_regulator>; + + reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + }; + }; + +For complete device tree binding documentation, see: +``Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml`` + +Driver Architecture +=================== + +The driver is structured as follows: + +* **Core**: Basic SPI communication and device initialization +* **IIO Interface**: Standard IIO DAC channel interface with scale attributes +* **Dual Regmap**: Uses standard regmap-spi for both 8-bit and 16-bit register access +* **Reset Framework**: Reset control support + +Development Notes +================= + +* The driver uses standard regmap-spi for both 8-bit and 16-bit register access +* SPI mode 0 (CPOL=0, CPHA=0) is typically used +* Reset control framework support for device initialization +* Register addresses are incremented by 1 for 16-bit registers due to decrement mode addressing +* Scale attributes provide voltage conversion for 0-5V range +* Automatic regmap selection based on register address (≤0x13: 8-bit, >0x13: 16-bit) + +References +========== + +* AD5529R Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad5529r.pdf +* Linux IIO Subsystem: https://www.kernel.org/doc/html/latest/driver-api/iio/index.html diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 007e0a1fcc5a..27f2ab41f05e 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -25,6 +25,7 @@ Industrial I/O Kernel Drivers ad4062 ad4691 ad4695 + ad5529r ad7191 ad7380 ad7606 diff --git a/MAINTAINERS b/MAINTAINERS index 143714e27d51..41f42eb1adf2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1513,6 +1513,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml +F: Documentation/iio/ad5529r.rst F: drivers/iio/dac/ad5529r.c ANALOG DEVICES INC AD5706R DRIVER -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] Documentation: iio: Add AD5529R Documentation 2026-05-08 11:55 ` [PATCH v2 3/3] Documentation: iio: Add AD5529R Documentation Janani Sunil @ 2026-05-08 13:00 ` Jonathan Cameron 0 siblings, 0 replies; 12+ messages in thread From: Jonathan Cameron @ 2026-05-08 13:00 UTC (permalink / raw) To: Janani Sunil Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil On Fri, 8 May 2026 13:55:49 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > Add documentation for AD5529R high voltage, 16-channel 12/16 bit DAC Whilst it is good to have documentation for devices - I've made some comments below on not providing documentation of standard things (too much duplication) and being careful to work out who the document is for. These tend to be for users and board integrators etc so we don't tend to have much about the internals of the driver. For that see driver! Jonathan > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > Documentation/iio/ad5529r.rst | 216 ++++++++++++++++++++++++++++++++++++++++++ > Documentation/iio/index.rst | 1 + > MAINTAINERS | 1 + > 3 files changed, 218 insertions(+) > > diff --git a/Documentation/iio/ad5529r.rst b/Documentation/iio/ad5529r.rst > new file mode 100644 > index 000000000000..41fea1521790 > --- /dev/null > +++ b/Documentation/iio/ad5529r.rst > @@ -0,0 +1,216 @@ > +.. SPDX-License-Identifier: GPL-2.0-only > + > +============== > +AD5529R driver > +============== > + > +Device driver for Analog Devices Inc. AD5529R 16-Channel 12/16-bit High Voltage DAC. > +The module name is ``ad5529r``. > + > +Supported devices > +================= > + > +* `AD5529R <https://www.analog.com/en/products/ad5529r.html>`_ > + > +Description > +=========== > + > +The AD5529R is a 16-channel, 12-bit or 16-bit, high voltage, buffered voltage output Long line. Wrap to 80 chars consistently > +digital-to-analog converter (DAC) with an integrated precision reference. > +The device operates from unipolar and bipolar supplies and is guaranteed > +monotonic. It has built-in rail-to-rail output buffers that can source or > +sink up to 25mA. > + > +Hardware Features > +================= > + > +* 16 independent 12-bit or 16-bit DAC channels > +* Independently programmable output ranges: > + > + - 0V to 5V (current driver default) With such wide ranges we have in some previous drivers made it a device tree constraint. It rarely makes sense to switch between them at runtime as these are really about what circuit is downstream of the DAC. That needs to be there from initial driver otherwise we have a backwards compatibility problem. A 'default' of smallest range should be safe though (gets messier for some device where 'smallest' could be unipolar or bipolar) > + - 0V to 10V > + - 0V to 20V > + - 0V to 40V > + - ±5V > + - ±10V > + - ±15V > + - ±20V > + > +* 4.096V precision reference (12ppm/°C maximum) > +* Built-in function generation capabilities (hardware support) I'd drop the "(hardware support)" Given you are talking about functions of the hardware, bit odd if the hardware didn't support them! > +* Output voltage and current monitoring (hardware support) > +* Temperature monitoring with 8 on-chip sensors (hardware support) > +* Over-temperature protection > +* SPI interface with CRC error detection support > + > +Current Driver Implementation > +============================= > + > +The current driver provides basic DAC functionality with the following features: > + > +* Basic DAC output control for all 16 channels > +* Scale attributes for voltage conversion (0-5V default range) > +* SPI communication with regmap support > +* Reset control framework support > +* Automatic hardware variant detection (16-bit vs 12-bit) based on product ID > +* Debugfs register access for development > + > +SPI Configuration: > + > +* **Mode**: Supports SPI mode 0 and mode 3 (default: mode 0) In what sense is it the default? I'd drop that as just depends on the DT. > +* **Frequency**: Up to 50 MHz (typically tested at lower frequencies) > +* **Word Size**: 16-bit transactions > + > +.. note:: > + The device default configuration uses address decrement mode (ADDR_ASCENSION=0) > + for multi-byte SPI transactions. Therefore, all 16-bit register addresses are > + incremented by 1 in the driver to access the last byte first, allowing the > + hardware to decrement and access the complete multi-byte register correctly. This doc is a slightly odd mix of stuff for users (most of it) and driver details like this. I'd move this to a comment in the code and keep the doc for users. > + > +IIO Attributes (Currently Implemented) > +====================================== > + > +Basic DAC Control > +----------------- > + > +For each of the 16 channels (0-15): > + > +**out_voltageY_raw** > + Raw DAC code (12-bit: 0-4095, 16-bit: 0-65535) > + > + * Read: Returns the current DAC register value > + * Write: Sets the DAC output code > + > +**out_voltageY_scale** > + Scale factor for voltage conversion (millivolts per LSB) > + > + Based on the formula: VOUTn = A × D/2^N + B, where A=5V, B=0V, N=resolution > + > + * 16-bit: 0.076294 mV/LSB (5V ÷ 2^16 = 5V ÷ 65536 = 0.076294mV) > + * 12-bit: 1.220703 mV/LSB (5V ÷ 2^12 = 5V ÷ 4096 = 1.220703mV) > + * Read-only attribute > + > +Debug Interface > +=============== > + > +**Register Access** > + > +The driver provides debugfs register access for debugging and development: > + > +``/sys/kernel/debug/iio/iio:deviceX/direct_reg_access`` > + Direct register read/write access. Format: > + > + * Read: ``echo <register_address> > direct_reg_access; cat direct_reg_access`` > + * Write: ``echo <register_address> <value> > direct_reg_access`` > + > +Usage examples > +============== > + > +Basic DAC Output Control > +------------------------ > + > +.. code-block:: bash > + > + # Set channel 0 to mid-scale (approximately 2.5V with 0V to 5V range) > + echo "32768" > /sys/bus/iio/devices/iio:device0/out_voltage0_raw > + > + # Set channel 15 to full scale > + echo "65535" > /sys/bus/iio/devices/iio:device0/out_voltage15_raw > + > + # Read current value from channel 5 > + cat /sys/bus/iio/devices/iio:device0/out_voltage5_raw > + > +Scale Attributes > +---------------- > + > +.. code-block:: bash > + > + # Read scale factor (millivolts per LSB) > + cat /sys/bus/iio/devices/iio:device0/out_voltage0_scale > + # Output: 0.076294 (for 16-bit) or 1.220703 (for 12-bit) > + > + # Convert raw to voltage: voltage_mv = raw * scale > + # Formula: VOUTn = A × D/2^N + B where A=5V, B=0V This stuff is very standard. Consider if it is worth documenting for this specific part. To me it isn't.. > + > +Register Access for Development > +------------------------------- Likewise this section is very standard so I'd not expect per device docs for it. > + > +.. code-block:: bash > + > + # Navigate to debugfs directory > + cd /sys/kernel/debug/iio/iio:device0/ > + > + # Read device product ID (register 0x04) > + echo 4 > direct_reg_access > + cat direct_reg_access > + > + # Write to a 16-bit configuration register (example: LDAC_HW_SW register 0x19) > + echo "0x019 0xAA11" > direct_reg_access > + cat direct_reg_access > + # Output: 0xAA11 > + > + # Write to DAC channel registers (16-bit values) > + echo "0x149 32768" > direct_reg_access # DAC channel 0 mid-scale > + echo "0x14B 65535" > direct_reg_access # DAC channel 1 full-scale > + > + # Read back DAC register values > + echo 0x149 > direct_reg_access && cat direct_reg_access # Read channel 0 > + echo 0x14B > direct_reg_access && cat direct_reg_access # Read channel 1 > + > +.. note:: > + For 16-bit registers, use hexadecimal format for addresses (0x019, 0x149, etc.). Are there non 16-bit registers where we shouldn't use hexadecimal? Otherwise this note seems odd. > + Values can be decimal (32768) or hexadecimal (0xAA11). Register addresses shown > + include the +1 offset required for decrement mode operation. Ah. This is worth noting as users need to be aware of it so keep this bit. > + > +Device Tree Configuration > +========================= > + > +Basic configuration example: > + > +.. code-block:: devicetree > + > + &spi0 { > + status = "okay"; > + > + ad5529r@0 { > + compatible = "adi,ad5529r"; > + reg = <0>; > + spi-max-frequency = <25000000>; > + > + vdd-supply = <&vdd_regulator>; > + avdd-supply = <&avdd_regulator>; > + hvdd-supply = <&hvdd_regulator>; > + hvss-supply = <&hvss_regulator>; > + > + reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; > + }; > + }; > + > +For complete device tree binding documentation, see: > +``Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml`` Hard no to replicating the device tree example - that is just noise. The cross reference is enough. > + > +Driver Architecture > +=================== > + > +The driver is structured as follows: > + > +* **Core**: Basic SPI communication and device initialization > +* **IIO Interface**: Standard IIO DAC channel interface with scale attributes > +* **Dual Regmap**: Uses standard regmap-spi for both 8-bit and 16-bit register access > +* **Reset Framework**: Reset control support Not seeing value in anything except the dual regmap and again this is mixing user stuff with driver internal details. That stuff probably belongs in the driver. > + > +Development Notes > +================= > + > +* The driver uses standard regmap-spi for both 8-bit and 16-bit register access > +* SPI mode 0 (CPOL=0, CPHA=0) is typically used Drop that. We don't care as long as both the options the dt-binding allows work fine. > +* Reset control framework support for device initialization > +* Register addresses are incremented by 1 for 16-bit registers due to decrement mode addressing > +* Scale attributes provide voltage conversion for 0-5V range > +* Automatic regmap selection based on register address (≤0x13: 8-bit, >0x13: 16-bit) All this is driver internal stuff so I'd drop it. > + > +References > +========== > + > +* AD5529R Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad5529r.pdf > +* Linux IIO Subsystem: https://www.kernel.org/doc/html/latest/driver-api/iio/index.html I'd skip that IIO generic docs reference. Not seeing it as adding much in this file. > diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst > index 007e0a1fcc5a..27f2ab41f05e 100644 > --- a/Documentation/iio/index.rst > +++ b/Documentation/iio/index.rst > @@ -25,6 +25,7 @@ Industrial I/O Kernel Drivers > ad4062 > ad4691 > ad4695 > + ad5529r > ad7191 > ad7380 > ad7606 > diff --git a/MAINTAINERS b/MAINTAINERS > index 143714e27d51..41f42eb1adf2 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1513,6 +1513,7 @@ L: linux-iio@vger.kernel.org > S: Supported > W: https://ez.analog.com/linux-software-drivers > F: Documentation/devicetree/bindings/iio/dac/adi,ad5529r.yaml > +F: Documentation/iio/ad5529r.rst > F: drivers/iio/dac/ad5529r.c > > ANALOG DEVICES INC AD5706R DRIVER > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC 2026-05-08 11:55 [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Janani Sunil ` (2 preceding siblings ...) 2026-05-08 11:55 ` [PATCH v2 3/3] Documentation: iio: Add AD5529R Documentation Janani Sunil @ 2026-05-08 12:36 ` Jonathan Cameron 3 siblings, 0 replies; 12+ messages in thread From: Jonathan Cameron @ 2026-05-08 12:36 UTC (permalink / raw) To: Janani Sunil Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel, linux-doc, Janani Sunil On Fri, 8 May 2026 13:55:46 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > This patch series adds support for Analog Devices AD5529R, a 16 channel > 16 and 12 bit voltage Digital-to-Analog Converter (DAC) with integrated > precision reference. The AD5529R operates from both unipolar and > bipolar supplies. The device communicates via SPI interface. Hi Janani Welcome to IIO. Please slow down. For a new driver convention is to wait at least a week between versions so there is time for more reviews to come in and hopefully reduce the total number of versions needed! If you got feedback from Sashiko or other AI tools after posting then feel free to address them in replies to your own series - they aren't a reason to move faster with a new version. Key for why we don't want too many versions is that those few of us who review a lot of code very rarely manage to hold in our memories what we checked on previous versions so tend to end up doing each version from more or less scratch. That means each version takes a non trivial amount of review time. Anyhow, you've sent this now so I'll review it but for v3 if needed slower please! If you have time and want to increase the chances reviewers get to your patch, use it to review those of others. That is is typically where our bottlenecks lie. I've not been griping about this too much for the many cleanups on the list at the moment because the overhead of those is smaller and also I know some are students trying to get patches upstream in a finite window. Thanks Jonathan > > **Device Overview:** > The AD5529R features 16 independent DAC channels, with 16 or 12 bit > resolution, allowing independently programmable output ranges. The > internal 4.096V precision reference sets the accuracy of the output > voltage. > > **Features Implemented:** > - Automatic detection of 12/16 bit variant with product ID read. > - Reset support via GPIO. > - Dual regmap configuration to handle 8 and 16 bit registers. > > **Patch Summary:** > 1. **dt-bindings**: Binding documentation with channel configuration. > 2. **driver**: Implement IIO DAC Driver with regmap support. > 3. **documentation**: Add driver documentation with usage examples. > > **Testing:** > The driver was compiled and tested on the EVAL-AD5529R-ARDZ using a > coraZ7 with a mainline v7.0 kernel. > > **Driver Rationale:** > AD5529R introduces: > 1. A unique register layout > 2. Mixed 8-bit and 16-bit register accesses > 3. Product ID based generic identification > 4. Hardware specific features like function generators, multi-die > hotpath registers etc. > > The device warrants its own drivers due to these fundamental > architectural differences, that would require substantial changes to > existing drivers without providing reusable benefits. The standalone > driver also allows future extensions for related devices in the same > family. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > Changes in v2: > - Fix IIO scale to use millivolts per ABI requirement > - Fix documentation voltage calculations (2.5V not 2.048V) > - Fix bipolar ranges in documentation (±5V, ±10V, ±15V, ±20V) > - Fix alphabetical ordering in documentation index > - Add missing newline to documentation file > - Fix scale units description (millivolts not microvolts) > - Include a section for driver rationale in the cover letter > - Reword contents in cover letter 12/16 bit generic->variant > - Add dependency array for spi-cpha and spi-cpol properties > - Link to v1: https://lore.kernel.org/r/20260507-ad5529r-driver-v1-0-b4460f3cb44f@analog.com > > --- > Janani Sunil (3): > dt-bindings: iio: dac: Add AD5529R > iio: dac: Add AD5529R DAC driver support > Documentation: iio: Add AD5529R Documentation > > .../devicetree/bindings/iio/dac/adi,ad5529r.yaml | 96 ++++ > Documentation/iio/ad5529r.rst | 216 ++++++++ > Documentation/iio/index.rst | 1 + > MAINTAINERS | 9 + > drivers/iio/dac/Kconfig | 17 + > drivers/iio/dac/Makefile | 1 + > drivers/iio/dac/ad5529r.c | 564 +++++++++++++++++++++ > 7 files changed, 904 insertions(+) > --- > base-commit: 93df88612859e8e19dec93c69d563b4b73e9bd4b > change-id: 20260507-ad5529r-driver-866bbdd864de > > Best regards, ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-05-08 20:55 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-08 11:55 [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Janani Sunil 2026-05-08 11:55 ` [PATCH v2 1/3] dt-bindings: iio: dac: Add AD5529R Janani Sunil 2026-05-08 12:48 ` Jonathan Cameron 2026-05-08 13:08 ` Jonathan Cameron 2026-05-08 13:50 ` Rodrigo Alencar 2026-05-08 13:57 ` Nuno Sá 2026-05-08 11:55 ` [PATCH v2 2/3] iio: dac: Add AD5529R DAC driver support Janani Sunil 2026-05-08 13:30 ` Jonathan Cameron 2026-05-08 20:55 ` sashiko-bot 2026-05-08 11:55 ` [PATCH v2 3/3] Documentation: iio: Add AD5529R Documentation Janani Sunil 2026-05-08 13:00 ` Jonathan Cameron 2026-05-08 12:36 ` [PATCH v2 0/3] iio: dac: Add support for AD5529R DAC Jonathan Cameron
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox