From a6c3dc12f251e875335b7fdd411830dc76c6d8ac Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Thu, 31 Oct 2024 19:20:59 -0300 Subject: [PATCH 01/16] dt-bindings: iio: adc: ad7768-1: add new supported parts Add compatibles for supported parts in the ad7768-1 family: ADAQ7767-1, ADAQ7768-1 and ADAQ7769-1 Add property and checks for AFF gain, supported by ADAQ7767-1 and ADAQ7769-1 parts: adi,aaf-gain Signed-off-by: Jonathan Santos --- .../bindings/iio/adc/adi,ad7768-1.yaml | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml index 3ce59d4d065f5d..e67f219a84fca1 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml @@ -4,18 +4,26 @@ $id: http://devicetree.org/schemas/iio/adc/adi,ad7768-1.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices AD7768-1 ADC device driver +title: Analog Devices AD7768-1 ADC family device driver maintainers: - Michael Hennerich description: | - Datasheet at: - https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf + Analog Devices AD7768-1 24-Bit Single Channel Low Power sigma-delta ADC family + + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7767-1.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7768-1.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7769-1.pdf properties: compatible: - const: adi,ad7768-1 + enum: + - adi,ad7768-1 + - adi,adaq7767-1 + - adi,adaq7768-1 + - adi,adaq7769-1 reg: maxItems: 1 @@ -39,6 +47,23 @@ properties: description: ADC reference voltage supply + adi,aaf-gain: + description: | + Specifies the gain of the Analog Anti-Aliasing Filter (AAF) applied to the + ADC input, measured in milli-units. The AAF provides additional signal + rejection within the frequency range of fs ± f3dB, where fs is the sampling + frequency, and f3dB is the -3dB cutoff frequency. The specific values of + fs and f3dB, as well as the rejection intensity, depend on the digital + filter configuration. + + This parameter is required for the ADAQ7767-1 and ADAQ7769-1 devices. + The gain is determined by the selected input pin: + * For the ADAQ7767-1: The input selection of IN1±, IN2± or IN3±. + * For the ADAQ7769-1: The connections of OUT_PGA to IN1_AAF+, IN2_AAF+, + or IN3_AAF+. + $ref: /schemas/types.yaml#/definitions/uint16 + enum: [143, 364, 1000] + adi,sync-in-gpios: maxItems: 1 description: @@ -89,6 +114,21 @@ patternProperties: allOf: - $ref: /schemas/spi/spi-peripheral-props.yaml# + # AAF Gain property only applies to ADAQ7767-1 and ADAQ7769-1 devices + - if: + properties: + compatible: + contains: + enum: + - adi,adaq7767-1 + - adi,adaq7769-1 + then: + required: + adi,aaf-gain + else: + properties: + adi,aaf-gain: false + unevaluatedProperties: false examples: From 436ae82b4468d3383e4ca560d6a87f20bf6a98f3 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Thu, 31 Oct 2024 19:25:18 -0300 Subject: [PATCH 02/16] dt-bindings: iio: adc: ad7768-1: add synchronization over SPI property Add adi,sync-in-spi property to enable synchronization over SPI. This should be used in the case when the GPIO cannot provide a pulse synchronous with the base MCLK signal. User can choose between SPI, GPIO synchronization or neither of them, but only if a external pulse can be provided, for example, by another device in a multidevice setup. Signed-off-by: Jonathan Santos --- .../bindings/iio/adc/adi,ad7768-1.yaml | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml index e67f219a84fca1..1a8696671cb71a 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml @@ -72,6 +72,15 @@ properties: in any way, for example if the filter decimation rate changes. As the line is active low, it should be marked GPIO_ACTIVE_LOW. + adi,sync-in-spi: + description: + Enables synchronization of multiple devices over SPI. This property is + used when a signal synchronous to the base MCLK signal cannot be provided + via GPIO. It requires the SYNC_OUT pin to be connected to the SYNC_IN pin + on the ADC. In the case of multiple devices, the SYNC_OUT pin of one device + should be routed to the SYNC_IN pins of the other devices. + type: boolean + reset-gpios: maxItems: 1 @@ -90,7 +99,6 @@ required: - vref-supply - spi-cpol - spi-cpha - - adi,sync-in-gpios patternProperties: "^channel@([0-9]|1[0-5])$": @@ -129,6 +137,20 @@ allOf: properties: adi,aaf-gain: false + # adi,sync-in-gpios and adi,sync-in-spi are mutually exclusive (neither is also valid) + - if: + required: + - adi,sync-in-gpios + then: + properties: + adi,sync-in-spi: false + - if: + required: + - adi,sync-in-spi + then: + properties: + adi,sync-in-gpios: false + unevaluatedProperties: false examples: From 5e8edfb7fa63316f5937ed5b5a9396c5679b70d6 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 26 Aug 2024 19:23:06 -0300 Subject: [PATCH 03/16] arm: dts: zynq-zed-adv7511-ad7768-1-evb: add support for new spi-engine Use compatible for new spi-engine implementation. Signed-off-by: Jonathan Santos --- arch/arm/boot/dts/zynq-zed-adv7511-ad7768-1-evb.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/zynq-zed-adv7511-ad7768-1-evb.dts b/arch/arm/boot/dts/zynq-zed-adv7511-ad7768-1-evb.dts index 4a9b5b9a7bc9e7..8581afbe8ae20a 100644 --- a/arch/arm/boot/dts/zynq-zed-adv7511-ad7768-1-evb.dts +++ b/arch/arm/boot/dts/zynq-zed-adv7511-ad7768-1-evb.dts @@ -52,7 +52,7 @@ }; axi_spi_engine_0: spi@0x44a00000 { - compatible = "adi,axi-spi-engine-1.00.a"; + compatible = "adi-ex,axi-spi-engine-1.00.a"; reg = <0x44a00000 0x1000>; interrupt-parent = <&intc>; interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; From 8066022e3f501e90930a5e839d559f0ad03d293c Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 26 Aug 2024 19:26:37 -0300 Subject: [PATCH 04/16] arm: dts: Add device tree for ADAQ7767-1 on ZedBoard Enables using the ADAQ7767-1 device on ZedBoard with FMC connector. Signed-off-by: Jonathan Santos --- .../dts/zynq-zed-adv7511-adaq7767-1-evb.dts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 arch/arm/boot/dts/zynq-zed-adv7511-adaq7767-1-evb.dts diff --git a/arch/arm/boot/dts/zynq-zed-adv7511-adaq7767-1-evb.dts b/arch/arm/boot/dts/zynq-zed-adv7511-adaq7767-1-evb.dts new file mode 100644 index 00000000000000..9e488b9edd734e --- /dev/null +++ b/arch/arm/boot/dts/zynq-zed-adv7511-adaq7767-1-evb.dts @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices ADAQ7767-1 + * https://wiki.analog.com/resources/eval/user-guides/ad7768-1 + * + * hdl_project: + * board_revision: + * + * Copyright (C) 2024 Analog Devices Inc. + */ +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" +#include +#include + +/ { + vref: regulator-vref { + compatible = "regulator-fixed"; + regulator-name = "fixed-supply"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + regulator-always-on; + }; + + clocks { + ad7768_1_mclk: clock@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <16384000>; + }; + }; +}; + +&fpga_axi { + rx_dma: rx-dmac@0x44a30000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a30000 0x1000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 16>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <1>; + adi,destination-bus-width = <64>; + adi,destination-bus-type = <0>; + }; + }; + }; + + spi_clock: spieng-axi-clkgen@44a70000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44a70000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 16>; + clock-names = "s_axi_aclk", "clkin1"; + }; + + axi_spi_engine_0: spi@0x44a00000 { + compatible = "adi-ex,axi-spi-engine-1.00.a"; + reg = <0x44a00000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15 &spi_clock>; + clock-names = "s_axi_aclk", "spi_clk"; + num-cs = <1>; + + #address-cells = <0x1>; + #size-cells = <0x0>; + + adaq7767_1: adc@0 { + compatible = "adi,adaq7767-1"; + reg = <0>; + spi-max-frequency = <40000000>; + spi-cpol; + spi-cpha; + vref-supply = <&vref>; + adi,sync-in-spi; + adi,aaf-gain = /bits/ 16 <143>; + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + clocks = <&ad7768_1_mclk>; + clock-names = "mclk"; + dmas = <&rx_dma 0>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + label = "channel_0"; + }; + }; + }; +}; From bcab3c175ba084d6c8929a957a6e0c4a5304f336 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 26 Aug 2024 19:30:37 -0300 Subject: [PATCH 05/16] arm: dts: Add device tree for ADAQ7768-1 on ZedBoard Enables using the ADAQ7768-1 device on ZedBoard with FMC connector. Signed-off-by: Jonathan Santos --- .../dts/zynq-zed-adv7511-adaq7768-1-evb.dts | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 arch/arm/boot/dts/zynq-zed-adv7511-adaq7768-1-evb.dts diff --git a/arch/arm/boot/dts/zynq-zed-adv7511-adaq7768-1-evb.dts b/arch/arm/boot/dts/zynq-zed-adv7511-adaq7768-1-evb.dts new file mode 100644 index 00000000000000..4cd0735c59e1d5 --- /dev/null +++ b/arch/arm/boot/dts/zynq-zed-adv7511-adaq7768-1-evb.dts @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices ADAQ7768-1 + * https://wiki.analog.com/resources/eval/user-guides/ad7768-1 + * + * hdl_project: + * board_revision: + * + * Copyright (C) 2024 Analog Devices Inc. + */ +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" +#include +#include + +/ { + vref: regulator-vref { + compatible = "regulator-fixed"; + regulator-name = "fixed-supply"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + regulator-always-on; + }; + + clocks { + ad7768_1_mclk: clock@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <16384000>; + }; + }; +}; + +&fpga_axi { + rx_dma: rx-dmac@0x44a30000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a30000 0x1000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 16>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <1>; + adi,destination-bus-width = <64>; + adi,destination-bus-type = <0>; + }; + }; + }; + + spi_clock: spieng-axi-clkgen@44a70000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44a70000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 16>; + clock-names = "s_axi_aclk", "clkin1"; + }; + + axi_spi_engine_0: spi@0x44a00000 { + compatible = "adi-ex,axi-spi-engine-1.00.a"; + reg = <0x44a00000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15 &spi_clock>; + clock-names = "s_axi_aclk", "spi_clk"; + num-cs = <1>; + + #address-cells = <0x1>; + #size-cells = <0x0>; + + adaq7768_1: adc@0 { + compatible = "adi,adaq7768-1"; + reg = <0>; + spi-max-frequency = <40000000>; + spi-cpol; + spi-cpha; + vref-supply = <&vref>; + adi,sync-in-spi; + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + clocks = <&ad7768_1_mclk>; + clock-names = "mclk"; + dmas = <&rx_dma 0>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + label = "channel_0"; + }; + }; + }; +}; From a5547a765056c06abe36a40e078559bdb5e5eaa4 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 26 Aug 2024 19:31:43 -0300 Subject: [PATCH 06/16] arm: dts: Add device tree for ADAQ7769-1 on ZedBoard Enables using the ADAQ7769-1 device on ZedBoard with FMC connector. Signed-off-by: Jonathan Santos --- .../dts/zynq-zed-adv7511-adaq7769-1-evb.dts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 arch/arm/boot/dts/zynq-zed-adv7511-adaq7769-1-evb.dts diff --git a/arch/arm/boot/dts/zynq-zed-adv7511-adaq7769-1-evb.dts b/arch/arm/boot/dts/zynq-zed-adv7511-adaq7769-1-evb.dts new file mode 100644 index 00000000000000..4dfb9af447ce91 --- /dev/null +++ b/arch/arm/boot/dts/zynq-zed-adv7511-adaq7769-1-evb.dts @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices ADAQ7769-1 + * https://wiki.analog.com/resources/eval/user-guides/ad7768-1 + * + * hdl_project: + * board_revision: + * + * Copyright (C) 2024 Analog Devices Inc. + */ +/dts-v1/; + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" +#include +#include + +/ { + vref: regulator-vref { + compatible = "regulator-fixed"; + regulator-name = "fixed-supply"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + regulator-always-on; + }; + + clocks { + ad7768_1_mclk: clock@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <16384000>; + }; + }; +}; + +&fpga_axi { + rx_dma: rx-dmac@0x44a30000 { + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x44a30000 0x1000>; + #dma-cells = <1>; + interrupt-parent = <&intc>; + interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 16>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <32>; + adi,source-bus-type = <1>; + adi,destination-bus-width = <64>; + adi,destination-bus-type = <0>; + }; + }; + }; + + spi_clock: spieng-axi-clkgen@44a70000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44a70000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 16>; + clock-names = "s_axi_aclk", "clkin1"; + }; + + axi_spi_engine_0: spi@0x44a00000 { + compatible = "adi-ex,axi-spi-engine-1.00.a"; + reg = <0x44a00000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15 &spi_clock>; + clock-names = "s_axi_aclk", "spi_clk"; + num-cs = <1>; + + #address-cells = <0x1>; + #size-cells = <0x0>; + + adaq7769_1: adc@0 { + compatible = "adi,adaq7769-1"; + reg = <0>; + spi-max-frequency = <40000000>; + spi-cpol; + spi-cpha; + vref-supply = <&vref>; + adi,sync-in-spi; + adi,aaf-gain = /bits/ 16 <143>; + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + clocks = <&ad7768_1_mclk>; + clock-names = "mclk"; + dmas = <&rx_dma 0>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + label = "channel_0"; + }; + }; + }; +}; From 1a5d9bdfa45ccc7746f680037a59065aacebdac8 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Mon, 2 Dec 2024 14:29:57 -0300 Subject: [PATCH 07/16] iio: adc: ad7768-1: use guard(mutex) to simplify code Use guard(mutex) from cleanup.h to remove most of the gotos and to make the code simpler and less likely to fail due to forgetting to unlock the resources. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 56 +++++++++++--------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index f573b4a9ddfd3c..bca0de0944abef 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -5,6 +5,7 @@ * Copyright 2017 Analog Devices Inc. */ #include +#include #include #include #include @@ -340,20 +341,12 @@ static int ad7768_reg_access(struct iio_dev *indio_dev, unsigned int *readval) { struct ad7768_state *st = iio_priv(indio_dev); - int ret; - mutex_lock(&st->lock); - if (readval) { - ret = ad7768_spi_reg_read(st, reg, readval, 1); - if (ret < 0) - goto err_unlock; - } else { - ret = ad7768_spi_reg_write(st, reg, writeval); - } -err_unlock: - mutex_unlock(&st->lock); + guard(mutex)(&st->lock); + if (readval) + return ad7768_spi_reg_read(st, reg, readval, 1); - return ret; + return ad7768_spi_reg_write(st, reg, writeval); } static int ad7768_set_dig_fil(struct ad7768_state *st, @@ -381,32 +374,24 @@ static int ad7768_set_dig_fil(struct ad7768_state *st, int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct ad7768_state *st = gpiochip_get_data(chip); - int ret; - mutex_lock(&st->lock); - ret = ad7768_spi_reg_write_masked(st, + guard(mutex)(&st->lock); + return ad7768_spi_reg_write_masked(st, AD7768_REG_GPIO_CONTROL, BIT(offset), AD7768_GPIO_INPUT(offset)); - mutex_unlock(&st->lock); - - return ret; } int ad7768_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct ad7768_state *st = gpiochip_get_data(chip); - int ret; - mutex_lock(&st->lock); - ret = ad7768_spi_reg_write_masked(st, + guard(mutex)(&st->lock); + return ad7768_spi_reg_write_masked(st, AD7768_REG_GPIO_CONTROL, BIT(offset), AD7768_GPIO_OUTPUT(offset)); - mutex_unlock(&st->lock); - - return ret; } int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -415,24 +400,19 @@ int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset) unsigned int val; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = ad7768_spi_reg_read(st, AD7768_REG_GPIO_CONTROL, &val, 1); if (ret < 0) - goto gpio_get_err; + return ret; if (val & BIT(offset)) ret = ad7768_spi_reg_read(st, AD7768_REG_GPIO_WRITE, &val, 1); else ret = ad7768_spi_reg_read(st, AD7768_REG_GPIO_READ, &val, 1); if (ret < 0) - goto gpio_get_err; - - ret = !!(val & BIT(offset)); - -gpio_get_err: - mutex_unlock(&st->lock); + return ret; - return ret; + return !!(val & BIT(offset)); } void ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) @@ -441,19 +421,16 @@ void ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) unsigned int val; int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = ad7768_spi_reg_read(st, AD7768_REG_GPIO_CONTROL, &val, 1); if (ret < 0) - goto gpio_set_err; + return; if (val & BIT(offset)) ad7768_spi_reg_write_masked(st, AD7768_REG_GPIO_WRITE, BIT(offset), (value << offset)); - -gpio_set_err: - mutex_unlock(&st->lock); } int ad7768_gpio_request(struct gpio_chip *chip, unsigned int offset) @@ -712,7 +689,7 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p) struct ad7768_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = spi_read(st->spi, &st->data.scan.chan, 3); if (ret < 0) @@ -723,7 +700,6 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p) err_unlock: iio_trigger_notify_done(indio_dev->trig); - mutex_unlock(&st->lock); return IRQ_HANDLED; } From 246024d76609ee91dc46e072fc106f2dea03d1d3 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Thu, 8 Aug 2024 17:58:20 -0300 Subject: [PATCH 08/16] iio: adc: ad7768-1: set MOSI idle state to high All supported parts require that the MOSI line stays high while in idle. Configure SPI controller to set MOSI idle state to high. Fixes: 8a15c73a9bcf ("iio: adc: Add AD7768-1 ADC basic support") Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index bca0de0944abef..323b6188e29ee4 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -866,6 +866,15 @@ static int ad7768_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); + /* + * The ADC SDI line must be kept high when + * data is not being clocked out of the controller. + * Request the SPI controller to make MOSI idle high. + */ + spi->mode |= SPI_MOSI_IDLE_HIGH; + ret = spi_setup(spi); + if (ret < 0) + return ret; st->spi = spi; st->vref = devm_regulator_get(&spi->dev, "vref"); From 91424158d92f9daeeeb9b271db1bc626042acdec Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Thu, 31 Oct 2024 18:08:32 -0300 Subject: [PATCH 09/16] iio: adc: ad7768-1: remove tx_buf from IIO buffer read transfer The address byte is not needed in the SPI transfer if continuous read mode is enabled. In this case, data readback occurs on the application of SCLK. Remove tx_buf from IIO buffer read transfer to reduce the duration of each transfer and enable the sample rate of 1.024 MHz at 16-bit resolution, given the maximum supported SCLK rate of 20 MHz: - With address byte: (16-bit data + 8-bit address) * 1.024 MHz > 20 MHZ. Insufficient SCLK cycles to drive out the output. - Without address byte: (16-bit data) * 1.024 MHz < 20 MHz. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 323b6188e29ee4..50685dc6b54c36 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -725,7 +725,6 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev) .bits_per_word = 32 }; unsigned int rx_data[2]; - unsigned int tx_data[2]; struct spi_message msg; int ret; @@ -741,8 +740,6 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev) if (st->spi_is_dma_mapped) { spi_bus_lock(st->spi->master); - tx_data[0] = AD7768_RD_FLAG_MSK(AD7768_REG_ADC_DATA) << 24; - xfer.tx_buf = tx_data; xfer.rx_buf = rx_data; spi_message_init_with_transfers(&msg, &xfer, 1); ret = spi_engine_offload_load_msg(st->spi, &msg); From 97bfb174d71a6ab10364df58abea09c8f8a76b05 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Thu, 31 Oct 2024 18:04:05 -0300 Subject: [PATCH 10/16] iio: adc: ad7768-1: add multiple scan types to support 16-bits mode When the device is configured to Sinc5 filter and decimation x8, output data is reduced to 16-bits in order to support 1 MHz of sampling frequency due to clock limitation. Use multiple scan types feature to enable the driver to switch scan type in runtime, making possible to support both 24-bit and 16-bit resolution. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 90 ++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 50685dc6b54c36..e4650013e177d6 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -140,6 +140,13 @@ struct ad7768_clk_configuration { enum ad7768_pwrmode pwrmode; }; +enum ad7768_scan_type { + AD7768_SCAN_TYPE_DMA_NORMAL, + AD7768_SCAN_TYPE_DMA_HIGH_SPEED, + AD7768_SCAN_TYPE_NORMAL, + AD7768_SCAN_TYPE_HIGH_SPEED, +}; + static const char * const ad7768_vcm_modes[] = { "(AVDD1-AVSS)/2", "2V5", @@ -151,6 +158,10 @@ static const char * const ad7768_vcm_modes[] = { "OFF", }; +static const int ad7768_mclk_div_rates[4] = { + 16, 8, 4, 2, +}; + static const struct ad7768_clk_configuration ad7768_clk_config[] = { { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE }, { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE }, @@ -163,6 +174,33 @@ static const struct ad7768_clk_configuration ad7768_clk_config[] = { { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE }, }; +static const struct iio_scan_type ad7768_scan_type[] = { + [AD7768_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + [AD7768_SCAN_TYPE_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_BE, + }, + [AD7768_SCAN_TYPE_DMA_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7768_SCAN_TYPE_DMA_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + static int ad7768_get_vcm(struct iio_dev *dev, const struct iio_chan_spec *chan); static int ad7768_set_vcm(struct iio_dev *dev, const struct iio_chan_spec *chan, unsigned int mode); @@ -194,12 +232,9 @@ static const struct iio_chan_spec ad7768_channels[] = { .indexed = 1, .channel = 0, .scan_index = 0, - .scan_type = { - .sign = 's', - .realbits = 24, - .storagebits = 32, - .shift = 8, - }, + .has_ext_scan_type = 1, + .ext_scan_type = ad7768_scan_type, + .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), }, }; @@ -211,6 +246,7 @@ struct ad7768_state { struct gpio_chip gpiochip; unsigned int gpio_avail_map; unsigned int mclk_freq; + unsigned int dec_rate; unsigned int samp_freq; unsigned int common_mode_voltage; struct completion completion; @@ -324,6 +360,15 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, &readval, 3); if (ret < 0) return ret; + + /* + * When the decimation rate is set to x8, the ADC data precision is reduced + * from 24 bits to 16 bits. Since the AD7768_REG_ADC_DATA register provides + * 24-bit data, the precision is reduced by right-shifting the read value + * by 8 bits. + */ + if (st->dec_rate == 8) + readval = readval >> 8; /* * Any SPI configuration of the AD7768-1 can only be * performed in continuous conversion mode. @@ -505,6 +550,8 @@ static int ad7768_set_freq(struct ad7768_state *st, if (ret < 0) return ret; + st->dec_rate = ad7768_clk_config[idx].clk_div / + ad7768_mclk_div_rates[ad7768_clk_config[idx].mclk_div]; st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq, ad7768_clk_config[idx].clk_div); @@ -560,8 +607,13 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long info) { struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; int scale_uv, ret; + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + switch (info) { case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); @@ -570,7 +622,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, ret = ad7768_scan_direct(indio_dev); if (ret >= 0) - *val = sign_extend32(ret, chan->scan_type.realbits - 1); + *val = sign_extend32(ret, scan_type->realbits - 1); iio_device_release_direct_mode(indio_dev); if (ret < 0) @@ -584,7 +636,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, return scale_uv; *val = (scale_uv * 2) / 1000; - *val2 = chan->scan_type.realbits; + *val2 = scan_type->realbits; return IIO_VAL_FRACTIONAL_LOG2; @@ -628,11 +680,25 @@ static const struct attribute_group ad7768_group = { .attrs = ad7768_attributes, }; +static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + if (st->spi_is_dma_mapped) + return st->dec_rate == 8 ? AD7768_SCAN_TYPE_DMA_HIGH_SPEED : + AD7768_SCAN_TYPE_DMA_NORMAL; + + return st->dec_rate == 8 ? AD7768_SCAN_TYPE_HIGH_SPEED : + AD7768_SCAN_TYPE_NORMAL; +} + static const struct iio_info ad7768_info = { .attrs = &ad7768_group, .read_raw = &ad7768_read_raw, .write_raw = &ad7768_write_raw, .read_label = ad7768_read_label, + .get_current_scan_type = &ad7768_get_current_scan_type, .debugfs_reg_access = &ad7768_reg_access, }; @@ -720,14 +786,20 @@ static irqreturn_t ad7768_interrupt(int irq, void *dev_id) static int ad7768_buffer_postenable(struct iio_dev *indio_dev) { struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; struct spi_transfer xfer = { .len = 1, - .bits_per_word = 32 }; unsigned int rx_data[2]; struct spi_message msg; int ret; + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + xfer.bits_per_word = scan_type->realbits; + /* * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter * continuous read mode. Subsequent data reads do not require an From 0d9409fcc008bb9ed3486ebe8b4d83db08092762 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Tue, 22 Oct 2024 19:29:58 -0300 Subject: [PATCH 11/16] iio: adc: ad7768-1: add support for Synchronization over SPI The synchronization method using GPIO requires the generated pulse to be truly synchronous with the base MCLK signal. When it is not possible to do that in hardware, the datasheet recommends using synchronization over SPI, where the generated pulse is already synchronous with MCLK. This requires the SYNC_OUT pin to be connected to SYNC_IN pin. Add the option to handle device synchronization over SPI. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index e4650013e177d6..169d53eb1246c8 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -255,6 +255,7 @@ struct ad7768_state { const char *labels[ARRAY_SIZE(ad7768_channels)]; struct gpio_desc *gpio_reset; bool spi_is_dma_mapped; + bool en_spi_sync; int irq; /* * DMA (thus cache coherency maintenance) may require the @@ -323,6 +324,19 @@ static int ad7768_spi_reg_write_masked(struct ad7768_state *st, return ad7768_spi_reg_write(st, addr, (reg_val & ~mask) | val); } +static int ad7768_send_sync_pulse(struct ad7768_state *st) +{ + if (st->en_spi_sync) + return ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x00); + + if (st->gpio_sync_in) { + gpiod_set_value_cansleep(st->gpio_sync_in, 1); + gpiod_set_value_cansleep(st->gpio_sync_in, 0); + } + + return 0; +} + static int ad7768_set_mode(struct ad7768_state *st, enum ad7768_conv_mode mode) { @@ -410,10 +424,7 @@ static int ad7768_set_dig_fil(struct ad7768_state *st, return ret; /* A sync-in pulse is required every time the filter dec rate changes */ - gpiod_set_value(st->gpio_sync_in, 1); - gpiod_set_value(st->gpio_sync_in, 0); - - return 0; + return ad7768_send_sync_pulse(st); } int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -732,11 +743,21 @@ static int ad7768_setup(struct ad7768_state *st) if (ret) return ret; - st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in", + st->gpio_sync_in = devm_gpiod_get_optional(&st->spi->dev, "adi,sync-in", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_sync_in)) return PTR_ERR(st->gpio_sync_in); + if (device_property_present(&st->spi->dev, "adi,sync-in-spi")) + st->en_spi_sync = true; + + /* + * GPIO and SPI Synchronization are mutually exclusive. + * Return error if both are enabled + */ + if (st->gpio_sync_in && st->en_spi_sync) + return -EINVAL; + ret = ad7768_gpio_init(st); if (ret < 0) return ret; From 855f0e15d26fa99308793cd3c979cdc6991877b0 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Tue, 5 Nov 2024 18:23:02 -0300 Subject: [PATCH 12/16] iio: adc: ad7768-1: add filter mode and decimation rate attributes Separate filter mode and decimation rate from the sampling frequency attribute. The new filter mode attribute enables SINC3 and WIDEBAND filters, which were previously unavailable. Previously, combining decimation and MCLK divider in the sampling frequency obscured performance trade-offs. Lower MCLK divider settings increase power usage, while lower decimation rates reduce precision by decreasing averaging. By creating a decimation attribute, users gain finer control over performance. The addition of those attributes allows a wider range of sampling frequencies and more access to the device features. Co-developed-by: PopPaul2021 Signed-off-by: PopPaul2021 Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 403 ++++++++++++++++++++++++++++++++----- 1 file changed, 351 insertions(+), 52 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 169d53eb1246c8..8f6f704f7e46ed 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -20,6 +20,7 @@ #include #include #include +#include "linux/util_macros.h" #include #include @@ -81,6 +82,10 @@ #define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0) #define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x) +/* AD7768_SINC3_DEC_RATE */ +#define AD7768_SINC3_DEC_RATE_MSB_MSK GENMASK(12, 8) +#define AD7768_SINC3_DEC_RATE_LSB_MSK GENMASK(7, 0) + /* AD7768_REG_CONVERSION */ #define AD7768_CONV_MODE_MSK GENMASK(2, 0) #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) @@ -101,6 +106,18 @@ #define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F)) #define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F) +/* Decimation Rate Limits */ +#define SINC5_DEC_RATE_MIN 8 +#define SINC5_DEC_RATE_MAX 1024 +#define SINC3_DEC_RATE_MIN 32 +#define SINC3_DEC_RATE_MAX 163840 +#define WIDEBAND_DEC_RATE_MIN 32 +#define WIDEBAND_DEC_RATE_MAX 1024 + +enum { + DEC_RATE, +}; + enum ad7768_conv_mode { AD7768_CONTINUOUS, AD7768_ONE_SHOT, @@ -122,6 +139,14 @@ enum ad7768_mclk_div { AD7768_MCLK_DIV_2 }; +enum ad7768_flt_mode { + SINC5, + SINC5_DEC_X8, + SINC5_DEC_X16, + SINC3, + WIDEBAND +}; + enum ad7768_dec_rate { AD7768_DEC_RATE_32 = 0, AD7768_DEC_RATE_64 = 1, @@ -133,13 +158,6 @@ enum ad7768_dec_rate { AD7768_DEC_RATE_16 = 10 }; -struct ad7768_clk_configuration { - enum ad7768_mclk_div mclk_div; - enum ad7768_dec_rate dec_rate; - unsigned int clk_div; - enum ad7768_pwrmode pwrmode; -}; - enum ad7768_scan_type { AD7768_SCAN_TYPE_DMA_NORMAL, AD7768_SCAN_TYPE_DMA_HIGH_SPEED, @@ -162,16 +180,18 @@ static const int ad7768_mclk_div_rates[4] = { 16, 8, 4, 2, }; -static const struct ad7768_clk_configuration ad7768_clk_config[] = { - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE }, +static const int dec_rate_values[6] = { + 32, 64, 128, 256, 512, 1024, +}; + +static const int sinc3_dec_rate_max_values[4] = { + 20480, 40960, 81920, 163840, +}; + +static const char * const ad7768_filter_enum[] = { + [SINC5] = "sinc5", + [SINC3] = "sinc3", + [WIDEBAND] = "wideband", }; static const struct iio_scan_type ad7768_scan_type[] = { @@ -204,6 +224,22 @@ static const struct iio_scan_type ad7768_scan_type[] = { static int ad7768_get_vcm(struct iio_dev *dev, const struct iio_chan_spec *chan); static int ad7768_set_vcm(struct iio_dev *dev, const struct iio_chan_spec *chan, unsigned int mode); +static int ad7768_get_dig_fil_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan); +static int ad7768_set_dig_fil_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan, unsigned int filter); +static ssize_t ad7768_ext_info_write(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len); +static ssize_t ad7768_ext_info_read(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf); + +static const struct iio_enum ad7768_flt_type_iio_enum = { + .items = ad7768_filter_enum, + .num_items = ARRAY_SIZE(ad7768_filter_enum), + .set = ad7768_set_dig_fil_attr, + .get = ad7768_get_dig_fil_attr, +}; static const struct iio_enum ad7768_vcm_mode_enum = { .items = ad7768_vcm_modes, @@ -219,6 +255,15 @@ static struct iio_chan_spec_ext_info ad7768_ext_info[] = { IIO_ENUM_AVAILABLE("common_mode_voltage", IIO_SHARED_BY_ALL, &ad7768_vcm_mode_enum), + IIO_ENUM("filter_mode", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum), + IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_ALL, &ad7768_flt_type_iio_enum), + { + .name = "decimation_rate", + .read = ad7768_ext_info_read, + .write = ad7768_ext_info_write, + .shared = IIO_SHARED_BY_ALL, + .private = DEC_RATE, + }, { }, }; @@ -246,7 +291,9 @@ struct ad7768_state { struct gpio_chip gpiochip; unsigned int gpio_avail_map; unsigned int mclk_freq; + unsigned int mclk_div; unsigned int dec_rate; + enum ad7768_flt_mode filter_mode; unsigned int samp_freq; unsigned int common_mode_voltage; struct completion completion; @@ -271,6 +318,9 @@ struct ad7768_state { } data __aligned(IIO_DMA_MINALIGN); }; +static int ad7768_set_freq(struct ad7768_state *st, + unsigned int freq); + static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr, unsigned int *data, unsigned int len) { @@ -337,6 +387,31 @@ static int ad7768_send_sync_pulse(struct ad7768_state *st) return 0; } +static int ad7768_set_mclk_div(struct ad7768_state *st, unsigned int mclk_div) +{ + unsigned int mclk_div_value; + int ret; + + guard(mutex)(&st->lock); + ret = ad7768_spi_reg_read(st, AD7768_REG_POWER_CLOCK, &mclk_div_value, 1); + if (ret) + return ret; + + mclk_div_value &= ~(AD7768_PWR_MCLK_DIV_MSK | AD7768_PWR_PWRMODE_MSK); + /* Set mclk_div value */ + mclk_div_value |= AD7768_PWR_MCLK_DIV(mclk_div); + /* + * Set power mode based on mclk_div value. + * ECO_MODE is only recommended for MCLK_DIV 16 + */ + if (mclk_div > AD7768_MCLK_DIV_16) + mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_FAST_MODE); + else + mclk_div_value |= AD7768_PWR_PWRMODE(AD7768_ECO_MODE); + + return ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, mclk_div_value); +} + static int ad7768_set_mode(struct ad7768_state *st, enum ad7768_conv_mode mode) { @@ -408,23 +483,168 @@ static int ad7768_reg_access(struct iio_dev *indio_dev, return ad7768_spi_reg_write(st, reg, writeval); } -static int ad7768_set_dig_fil(struct ad7768_state *st, - enum ad7768_dec_rate dec_rate) +static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st, + unsigned int dec_rate) { + unsigned int dec_rate_msb, dec_rate_lsb, max_dec_rate; + int ret; + + guard(mutex)(&st->lock); + /* + * Maximum dec_rate is limited by the MCLK_DIV value + * and by the ODR. The edge case is for MCLK_DIV = 2 + * ODR = 50 SPS. + * max_dec_rate <= MCLK / (2 * 50) + */ + max_dec_rate = st->mclk_freq / 100; + dec_rate = clamp_t(unsigned int, dec_rate, 32, max_dec_rate); + /* + * Calculate the equivalent value to sinc3 decimation ratio + * to be written on the SINC3_DECIMATION_RATE register: + * Value = (DEC_RATE / 32) -1 + */ + dec_rate = DIV_ROUND_UP(dec_rate, 32) - 1; + dec_rate_msb = FIELD_GET(AD7768_SINC3_DEC_RATE_MSB_MSK, dec_rate); + dec_rate_lsb = FIELD_GET(AD7768_SINC3_DEC_RATE_LSB_MSK, dec_rate); + + ret = ad7768_spi_reg_write(st, AD7768_REG_SINC3_DEC_RATE_MSB, dec_rate_msb); + if (ret) + return ret; + + ret = ad7768_spi_reg_write(st, AD7768_REG_SINC3_DEC_RATE_LSB, dec_rate_lsb); + if (ret) + return ret; + + st->dec_rate = (dec_rate + 1) * 32; + + return 0; +} + +static int ad7768_set_dec_rate(struct ad7768_state *st, unsigned int dec_rate) +{ + unsigned int mode, dec_rate_reg; + int ret; + + dec_rate_reg = find_closest(dec_rate, dec_rate_values, + ARRAY_SIZE(dec_rate_values)); + + guard(mutex)(&st->lock); + ret = ad7768_spi_reg_read(st, AD7768_REG_DIGITAL_FILTER, &mode, 1); + if (ret) + return ret; + + mode &= ~AD7768_DIG_FIL_DEC_MSK; + mode |= AD7768_DIG_FIL_DEC_RATE(dec_rate); + ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode); + if (ret) + return ret; + + st->dec_rate = dec_rate_values[dec_rate_reg]; + + return 0; +} + +static int ad7768_set_dig_fil(struct iio_dev *dev, + enum ad7768_flt_mode filter_mode) +{ + struct ad7768_state *st = iio_priv(dev); unsigned int mode; int ret; - if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16) - mode = AD7768_DIG_FIL_FIL(dec_rate); - else - mode = AD7768_DIG_FIL_DEC_RATE(dec_rate); + guard(mutex)(&st->lock); + ret = ad7768_spi_reg_read(st, AD7768_REG_DIGITAL_FILTER, &mode, 1); + if (ret) + return ret; + + mode &= ~AD7768_DIG_FIL_FIL_MSK; + mode |= AD7768_DIG_FIL_FIL(filter_mode); ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode); if (ret < 0) return ret; - /* A sync-in pulse is required every time the filter dec rate changes */ - return ad7768_send_sync_pulse(st); + st->filter_mode = filter_mode; + + return 0; +} + +static int ad7768_configure_fil_dec(struct iio_dev *dev, + enum ad7768_flt_mode filter_mode, + unsigned int dec_rate) +{ + struct ad7768_state *st = iio_priv(dev); + int ret; + + if (filter_mode == SINC3) { + ret = ad7768_set_dig_fil(dev, SINC3); + if (ret) + return ret; + + /* recalculate the decimation for this filter mode */ + ret = ad7768_set_sinc3_dec_rate(st, dec_rate); + } else if (filter_mode == WIDEBAND) { + ret = ad7768_set_dig_fil(dev, filter_mode); + if (ret) + return ret; + + /* recalculate the decimation rate */ + ret = ad7768_set_dec_rate(st, dec_rate); + } else { + /* For SINC5 filter */ + /* Decimation 8 and 16 are set in the digital filter field */ + if (dec_rate <= 8) { + ret = ad7768_set_dig_fil(dev, SINC5_DEC_X8); + if (ret) + return ret; + + st->dec_rate = 8; + } else if (dec_rate <= 16) { + ret = ad7768_set_dig_fil(dev, SINC5_DEC_X16); + if (ret) + return ret; + + st->dec_rate = 16; + } else { + ret = ad7768_set_dig_fil(dev, SINC5); + if (ret) + return ret; + + ret = ad7768_set_dec_rate(st, dec_rate); + } + } + if (ret) + return ret; + + /* Update sampling frequency */ + return ad7768_set_freq(st, st->samp_freq); +} + +static int ad7768_set_dig_fil_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int filter) +{ + struct ad7768_state *st = iio_priv(dev); + + return ad7768_configure_fil_dec(dev, filter, st->dec_rate); +} + +static int ad7768_get_dig_fil_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(dev); + int ret; + unsigned int mode; + + ret = ad7768_spi_reg_read(st, AD7768_REG_DIGITAL_FILTER, &mode, 1); + if (ret) + return ret; + + mode = FIELD_GET(AD7768_DIG_FIL_FIL_MSK, mode); + /* Filter modes from 0 to 2 are represented as SINC5 */ + if (mode < SINC3) + return SINC5; + + return mode; } int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -527,46 +747,79 @@ int ad7768_gpio_init(struct ad7768_state *st) return gpiochip_add_data(&st->gpiochip, st); } +static ssize_t ad7768_ext_info_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + switch (private) { + case DEC_RATE: + return sysfs_emit(buf, "%d\n", st->dec_rate); + default: + return -EINVAL; + } +} + +static ssize_t ad7768_ext_info_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + int ret = -EINVAL; + long long dec_rate; + struct ad7768_state *st = iio_priv(indio_dev); + + switch (private) { + case DEC_RATE: + ret = kstrtoll(buf, 10, &dec_rate); + if (ret) + return ret; + + ret = ad7768_configure_fil_dec(indio_dev, st->filter_mode, dec_rate); + if (ret) + return ret; + + return len; + default: + return -EINVAL; + } +} + static int ad7768_set_freq(struct ad7768_state *st, unsigned int freq) { - unsigned int diff_new, diff_old, pwr_mode, i, idx; + unsigned int diff_new, diff_old, i, idx; int res, ret; + freq = clamp_t(unsigned int, freq, 50, 1024000); diff_old = U32_MAX; idx = 0; - res = DIV_ROUND_CLOSEST(st->mclk_freq, freq); + if (freq == 0) + return -EINVAL; + + res = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->dec_rate); /* Find the closest match for the desired sampling frequency */ - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { - diff_new = abs(res - ad7768_clk_config[i].clk_div); + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) { + diff_new = abs(res - ad7768_mclk_div_rates[i]); if (diff_new < diff_old) { diff_old = diff_new; idx = i; } } - /* - * Set both the mclk_div and pwrmode with a single write to the - * POWER_CLOCK register - */ - pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) | - AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode); - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode); - if (ret < 0) - return ret; - - ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate); - if (ret < 0) + /* Set both the mclk_div and pwrmode */ + ret = ad7768_set_mclk_div(st, idx); + if (ret) return ret; - st->dec_rate = ad7768_clk_config[idx].clk_div / - ad7768_mclk_div_rates[ad7768_clk_config[idx].mclk_div]; st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq, - ad7768_clk_config[idx].clk_div); + ad7768_mclk_div_rates[idx] * st->dec_rate); - return 0; + /* A sync-in pulse is required every time the filter dec rate changes */ + return ad7768_send_sync_pulse(st); } static int ad7768_get_vcm(struct iio_dev *dev, const struct iio_chan_spec *chan) @@ -597,13 +850,16 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7768_state *st = iio_priv(indio_dev); - unsigned int freq; + unsigned int freq, freq_filtered; int i, len = 0; - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { - freq = DIV_ROUND_CLOSEST(st->mclk_freq, - ad7768_clk_config[i].clk_div); - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq); + freq_filtered = DIV_ROUND_CLOSEST(st->mclk_freq, st->dec_rate); + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) { + freq = DIV_ROUND_CLOSEST(freq_filtered, + ad7768_mclk_div_rates[i]); + /* Sampling frequency cannot be lower than the minimum of 50 SPS */ + if (freq >= 50) + len += sysfs_emit_at(buf, len, "%d ", freq); } buf[len - 1] = '\n'; @@ -613,6 +869,38 @@ static ssize_t ad7768_sampling_freq_avail(struct device *dev, static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail); +static ssize_t decimation_rate_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7768_state *st = iio_priv(indio_dev); + int len = 0; + + /* Return decimation rate available in range format */ + buf[len++] = '['; + if (st->filter_mode == SINC3) { + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN); + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MIN); + len += sysfs_emit_at(buf, len, "%d ", SINC3_DEC_RATE_MAX); + } else if (st->filter_mode == WIDEBAND) { + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN); + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MIN); + len += sysfs_emit_at(buf, len, "%d ", WIDEBAND_DEC_RATE_MAX); + } else { + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN); + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MIN); + len += sysfs_emit_at(buf, len, "%d ", SINC5_DEC_RATE_MAX); + } + + buf[len - 1] = ']'; + buf[len++] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR_RO(decimation_rate_available, 0); + static int ad7768_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -684,6 +972,7 @@ static int ad7768_read_label(struct iio_dev *indio_dev, static struct attribute *ad7768_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_decimation_rate_available.dev_attr.attr, NULL }; @@ -713,7 +1002,7 @@ static const struct iio_info ad7768_info = { .debugfs_reg_access = &ad7768_reg_access, }; -static int ad7768_setup(struct ad7768_state *st) +static int ad7768_setup(struct ad7768_state *st, struct iio_dev *indio_dev) { int ret; @@ -762,6 +1051,16 @@ static int ad7768_setup(struct ad7768_state *st) if (ret < 0) return ret; + /* Set Default Filter mode */ + ret = ad7768_set_dig_fil(indio_dev, SINC5); + if (ret < 0) + return ret; + + /* Set Default Decimation rate */ + ret = ad7768_set_dec_rate(st, 32); + if (ret < 0) + return ret; + /** * Set the default sampling frequency to 256 kSPS for hardware buffer, * or 32 kSPS for triggered buffer @@ -997,7 +1296,7 @@ static int ad7768_probe(struct spi_device *spi) indio_dev->info = &ad7768_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = ad7768_setup(st); + ret = ad7768_setup(st, indio_dev); if (ret < 0) { dev_err(&spi->dev, "AD7768 setup failed\n"); return ret; From dc084dee433ed79f85b8d0f190764c23a096e32a Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 9 Oct 2024 14:04:06 -0300 Subject: [PATCH 13/16] iio: adc: ad7768-1: use new spi-engine implementation Add support for new spi-engine implementation on ad7768-1 driver, optimizing offload transfers by reducing CS delay and minimizing the number of commands in the offload FIFO. Without the otimization, there's not enough time between the end of a transfer and the start of the next one, reducing the actual sample rate. Adjust 'tx_data' buffer handling in SPI register read and write functions to ensure endianness consistency with the new spi-engine. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 45 +++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 8f6f704f7e46ed..33e5121e152558 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -22,7 +22,7 @@ #include #include "linux/util_macros.h" #include -#include +#include #include #include @@ -289,6 +289,8 @@ struct ad7768_state { struct mutex lock; struct clk *mclk; struct gpio_chip gpiochip; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; unsigned int gpio_avail_map; unsigned int mclk_freq; unsigned int mclk_div; @@ -329,11 +331,11 @@ static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr, .len = len + 1, .bits_per_word = (len == 3 ? 32 : 16), }; - unsigned char tx_data[4]; + u32 tx_data; int ret; - tx_data[0] = AD7768_RD_FLAG_MSK(addr); - xfer.tx_buf = tx_data; + tx_data = AD7768_RD_FLAG_MSK(addr) << (len * 8); + xfer.tx_buf = &tx_data; ret = spi_sync_transfer(st->spi, &xfer, 1); if (ret < 0) return ret; @@ -351,11 +353,11 @@ static int ad7768_spi_reg_write(struct ad7768_state *st, .len = 2, .bits_per_word = 16, }; - unsigned char tx_data[2]; + u16 tx_data; - tx_data[0] = AD7768_WR_FLAG_MSK(addr); - tx_data[1] = val & 0xFF; - xfer.tx_buf = tx_data; + tx_data = AD7768_WR_FLAG_MSK(addr) << 8; + tx_data |= val & 0xFF; + xfer.tx_buf = &tx_data; return spi_sync_transfer(st->spi, &xfer, 1); } @@ -1107,18 +1109,14 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev) { struct ad7768_state *st = iio_priv(indio_dev); const struct iio_scan_type *scan_type; - struct spi_transfer xfer = { - .len = 1, - }; - unsigned int rx_data[2]; - struct spi_message msg; int ret; scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); if (IS_ERR(scan_type)) return PTR_ERR(scan_type); - xfer.bits_per_word = scan_type->realbits; + st->offload_xfer.len = roundup_pow_of_two(BITS_TO_BYTES(scan_type->realbits)); + st->offload_xfer.bits_per_word = scan_type->realbits; /* * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter @@ -1130,14 +1128,20 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev) return ret; if (st->spi_is_dma_mapped) { + /* HACK: invalid pointer indicates read data from SPI offload DMA */ + st->offload_xfer.rx_buf = (void *)(-1); + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret < 0) + return ret; + spi_bus_lock(st->spi->master); - xfer.rx_buf = rx_data; - spi_message_init_with_transfers(&msg, &xfer, 1); - ret = spi_engine_offload_load_msg(st->spi, &msg); + ret = spi_engine_ex_offload_load_msg(st->spi, &st->offload_msg); if (ret < 0) return ret; - spi_engine_offload_enable(st->spi, true); + spi_engine_ex_offload_enable(st->spi, true); } return ret; @@ -1149,9 +1153,10 @@ static int ad7768_buffer_predisable(struct iio_dev *indio_dev) unsigned int regval; if (st->spi_is_dma_mapped) { - spi_engine_offload_enable(st->spi, false); + spi_engine_ex_offload_enable(st->spi, false); spi_bus_unlock(st->spi->master); } + spi_unoptimize_message(&st->offload_msg); /* * To exit continuous read mode, perform a single read of the ADC_DATA @@ -1285,7 +1290,7 @@ static int ad7768_probe(struct spi_device *spi) return PTR_ERR(st->mclk); st->mclk_freq = clk_get_rate(st->mclk); - st->spi_is_dma_mapped = spi_engine_offload_supported(spi); + st->spi_is_dma_mapped = spi_engine_ex_offload_supported(spi); st->irq = spi->irq; mutex_init(&st->lock); From a9722763cd7a3abfbb8afebea5429c8bb8cccfcd Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 9 Oct 2024 15:53:40 -0300 Subject: [PATCH 14/16] iio: adc: ad7768-1: introduce chip info for future multidevice support Add Chip info struct in SPI device to store channel information for each supported part. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 69 +++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 33e5121e152558..0e73a1ee87e93c 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -114,6 +114,8 @@ #define WIDEBAND_DEC_RATE_MIN 32 #define WIDEBAND_DEC_RATE_MAX 1024 +#define AD7768_CHAN_INFO_NONE 0 + enum { DEC_RATE, }; @@ -267,23 +269,34 @@ static struct iio_chan_spec_ext_info ad7768_ext_info[] = { { }, }; +#define AD7768_CHAN(_idx, _msk_avail) { \ + .type = IIO_VOLTAGE,\ + .info_mask_separate_available = _msk_avail,\ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),\ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .ext_info = ad7768_ext_info,\ + .indexed = 1,\ + .channel = _idx,\ + .scan_index = _idx,\ + .has_ext_scan_type = 1,\ + .ext_scan_type = ad7768_scan_type,\ + .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type),\ +} + static const struct iio_chan_spec ad7768_channels[] = { - { - .type = IIO_VOLTAGE, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .ext_info = ad7768_ext_info, - .indexed = 1, - .channel = 0, - .scan_index = 0, - .has_ext_scan_type = 1, - .ext_scan_type = ad7768_scan_type, - .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), - }, + AD7768_CHAN(0, AD7768_CHAN_INFO_NONE), +}; + +struct ad7768_chip_info { + const char *name; + const struct iio_chan_spec *channel_spec; + const unsigned long *available_masks; + int num_channels; }; struct ad7768_state { + const struct ad7768_chip_info *chip; struct spi_device *spi; struct regulator *vref; struct mutex lock; @@ -1249,6 +1262,18 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev, return 0; } +static const unsigned long ad7768_channel_masks[] = { + BIT(0), + 0, +}; + +static const struct ad7768_chip_info ad7768_chip_info = { + .name = "ad7768-1", + .channel_spec = ad7768_channels, + .num_channels = ARRAY_SIZE(ad7768_channels), + .available_masks = ad7768_channel_masks, +}; + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; @@ -1293,11 +1318,17 @@ static int ad7768_probe(struct spi_device *spi) st->spi_is_dma_mapped = spi_engine_ex_offload_supported(spi); st->irq = spi->irq; + st->chip = spi_get_device_match_data(spi); + if (!st->chip) + return dev_err_probe(&spi->dev, -ENODEV, + "Could not find chip info data\n"); + mutex_init(&st->lock); - indio_dev->channels = ad7768_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7768_channels); - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->channels = st->chip->channel_spec; + indio_dev->num_channels = st->chip->num_channels; + indio_dev->available_scan_masks = st->chip->available_masks; + indio_dev->name = st->chip->name; indio_dev->info = &ad7768_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1307,7 +1338,7 @@ static int ad7768_probe(struct spi_device *spi) return ret; } - ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels)); + ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels); if (ret) return ret; @@ -1322,13 +1353,13 @@ static int ad7768_probe(struct spi_device *spi) } static const struct spi_device_id ad7768_id_table[] = { - { "ad7768-1", 0 }, + { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info }, {} }; MODULE_DEVICE_TABLE(spi, ad7768_id_table); static const struct of_device_id ad7768_of_match[] = { - { .compatible = "adi,ad7768-1" }, + { .compatible = "adi,ad7768-1", .data = &ad7768_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, ad7768_of_match); From 7889248a15d9207c792ed497769d615d7b348fc9 Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 6 Nov 2024 17:06:34 -0300 Subject: [PATCH 15/16] iio: adc: ad7768-1: use devm_regulator_get_enable_read_voltage Use devm_regulator_get_enable_read_voltage function as a standard and concise way of reading the voltage from the regulator and keep the regulator enabled. Replace the regulator descriptor with the direct voltage value in the device struct. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 0e73a1ee87e93c..88a6d5281cd5c9 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -298,7 +298,7 @@ struct ad7768_chip_info { struct ad7768_state { const struct ad7768_chip_info *chip; struct spi_device *spi; - struct regulator *vref; + int vref_uv; struct mutex lock; struct clk *mclk; struct gpio_chip gpiochip; @@ -945,7 +945,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - scale_uv = regulator_get_voltage(st->vref); + scale_uv = st->vref_uv; if (scale_uv < 0) return scale_uv; @@ -1187,13 +1187,6 @@ static const struct iio_trigger_ops ad7768_trigger_ops = { .validate_device = iio_trigger_validate_own_device, }; -static void ad7768_regulator_disable(void *data) -{ - struct ad7768_state *st = data; - - regulator_disable(st->vref); -} - static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev) { struct ad7768_state *st = iio_priv(indio_dev); @@ -1296,19 +1289,11 @@ static int ad7768_probe(struct spi_device *spi) return ret; st->spi = spi; - st->vref = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(st->vref)) - return PTR_ERR(st->vref); - - ret = regulator_enable(st->vref); - if (ret) { - dev_err(&spi->dev, "Failed to enable specified vref supply\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st); - if (ret) - return ret; + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "Failed to get VREF voltage\n"); + st->vref_uv = ret; st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) From c245faabe16b231fadc65484826167ece2adc11f Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Thu, 31 Oct 2024 11:58:14 -0300 Subject: [PATCH 16/16] iio: adc: ad7768-1: add support for ADAQ776x-1 ADC Family Add support for ADAQ7767/68/69-1 series, which includes PGIA and Anti-aliasing filter (AAF) gains. The PGA gain is configured in run-time through the scale attribute, if supported by the device. The scale options are updated according to the output data width. The AAF gain is configured in the devicetree and it should correspond to the AAF channel selected in hardware. Signed-off-by: Jonathan Santos --- drivers/iio/adc/ad7768-1.c | 309 ++++++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 88a6d5281cd5c9..4604d18cf6ea8a 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -23,6 +23,8 @@ #include "linux/util_macros.h" #include #include +#include +#include #include #include @@ -93,12 +95,19 @@ /* AD7768_REG_GPIO_CONTROL */ #define AD7768_GPIO_CONTROL_MSK GENMASK(3, 0) #define AD7768_GPIO_UNIVERSAL_EN BIT(7) +#define AD7768_GPIO_PGIA_EN (AD7768_GPIO_UNIVERSAL_EN | GENMASK(2, 0)) /* AD7768_REG_GPIO_WRITE */ #define AD7768_GPIO_WRITE_MSK GENMASK(3, 0) +#define AD7768_GPIO_WRITE(x) FIELD_PREP(AD7768_GPIO_WRITE_MSK, x) /* AD7768_REG_GPIO_READ */ #define AD7768_GPIO_READ_MSK GENMASK(3, 0) +#define AD7768_GPIO_READ(x) FIELD_PREP(AD7768_GPIO_READ_MSK, x) + +/* AD7768_REG_CONVLEN */ +#define AD7768_REG_CONVLEN_MSK GENMASK(3, 3) +#define AD7768_REG_CONVLEN(x) FIELD_PREP(AD7768_REG_CONVLEN_MSK, x) #define AD7768_GPIO_INPUT(x) 0x00 #define AD7768_GPIO_OUTPUT(x) BIT(x) @@ -116,6 +125,9 @@ #define AD7768_CHAN_INFO_NONE 0 +#define ADAQ776X_GAIN_MAX_NANO (128 * NANO) +#define ADAQ776X_MAX_GAIN_MODES 8 + enum { DEC_RATE, }; @@ -167,6 +179,54 @@ enum ad7768_scan_type { AD7768_SCAN_TYPE_HIGH_SPEED, }; +enum { + AD7768_PGA_GAIN_0, + AD7768_PGA_GAIN_1, + AD7768_PGA_GAIN_2, + AD7768_PGA_GAIN_3, + AD7768_PGA_GAIN_4, + AD7768_PGA_GAIN_5, + AD7768_PGA_GAIN_6, + AD7768_PGA_GAIN_7, + AD7768_MAX_PGA_GAIN, +}; + +enum { + AD7768_AAF_IN1, + AD7768_AAF_IN2, + AD7768_AAF_IN3, +}; + +/* + * Gains computed as fractions of 1000 so they can be expressed by integers. + */ +static const int adaq7768_gains[7] = { + [AD7768_PGA_GAIN_0] = 325, + [AD7768_PGA_GAIN_1] = 650, + [AD7768_PGA_GAIN_2] = 1300, + [AD7768_PGA_GAIN_3] = 2600, + [AD7768_PGA_GAIN_4] = 5200, + [AD7768_PGA_GAIN_5] = 10400, + [AD7768_PGA_GAIN_6] = 20800 +}; + +static const int adaq7769_gains[8] = { + [AD7768_PGA_GAIN_0] = 1000, + [AD7768_PGA_GAIN_1] = 2000, + [AD7768_PGA_GAIN_2] = 4000, + [AD7768_PGA_GAIN_3] = 8000, + [AD7768_PGA_GAIN_4] = 16000, + [AD7768_PGA_GAIN_5] = 32000, + [AD7768_PGA_GAIN_6] = 64000, + [AD7768_PGA_GAIN_7] = 128000 +}; + +static const int ad7768_aaf_gains[3] = { + [AD7768_AAF_IN1] = 1000, + [AD7768_AAF_IN2] = 364, + [AD7768_AAF_IN3] = 143 +}; + static const char * const ad7768_vcm_modes[] = { "(AVDD1-AVSS)/2", "2V5", @@ -288,8 +348,18 @@ static const struct iio_chan_spec ad7768_channels[] = { AD7768_CHAN(0, AD7768_CHAN_INFO_NONE), }; +static const struct iio_chan_spec adaq776x_channels[] = { + AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)), +}; + struct ad7768_chip_info { const char *name; + bool has_variable_aaf; + bool has_pga; + int num_pga_modes; + int default_pga_mode; + int pgia_mode2pin_offset; + const int *pga_gains; const struct iio_chan_spec *channel_spec; const unsigned long *available_masks; int num_channels; @@ -299,6 +369,8 @@ struct ad7768_state { const struct ad7768_chip_info *chip; struct spi_device *spi; int vref_uv; + int pga_gain_mode; + int aaf_gain; struct mutex lock; struct clk *mclk; struct gpio_chip gpiochip; @@ -315,6 +387,7 @@ struct ad7768_state { struct iio_trigger *trig; struct gpio_desc *gpio_sync_in; const char *labels[ARRAY_SIZE(ad7768_channels)]; + int scale_tbl[ADAQ776X_MAX_GAIN_MODES][2]; struct gpio_desc *gpio_reset; bool spi_is_dma_mapped; bool en_spi_sync; @@ -559,6 +632,43 @@ static int ad7768_set_dec_rate(struct ad7768_state *st, unsigned int dec_rate) return 0; } +static void ad7768_fill_scale_tbl(struct iio_dev *dev) +{ + struct ad7768_state *st = iio_priv(dev); + const struct iio_scan_type *scan_type; + int val, val2, tmp0, tmp1, i; + unsigned long denominator, numerator; + u64 tmp2; + + scan_type = iio_get_current_scan_type(dev, &dev->channels[0]); + if (scan_type->sign == 's') + val2 = scan_type->realbits - 1; + else + val2 = scan_type->realbits; + + for (i = 0; i < st->chip->num_pga_modes; i++) { + /* Convert gain to a fraction format */ + numerator = st->chip->pga_gains[i]; + denominator = MILLI; + if (st->chip->has_variable_aaf) { + numerator *= ad7768_aaf_gains[st->aaf_gain]; + denominator *= MILLI; + } + + rational_best_approximation(numerator, denominator, __INT_MAX__, __INT_MAX__, + &numerator, &denominator); + + val = st->vref_uv / 1000; + /* Multiply by MILLI here to avoid losing precision */ + val = mult_frac(val, denominator * MILLI, numerator); + /* Would multiply by NANO here but we already multiplied by MILLI */ + tmp2 = shift_right((u64)val * MICRO, val2); + tmp0 = (int)div_s64_rem(tmp2, NANO, &tmp1); + st->scale_tbl[i][0] = tmp0; /* Integer part */ + st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */ + } +} + static int ad7768_set_dig_fil(struct iio_dev *dev, enum ad7768_flt_mode filter_mode) { @@ -580,6 +690,9 @@ static int ad7768_set_dig_fil(struct iio_dev *dev, st->filter_mode = filter_mode; + /* Update scale table: scale values vary according to the precision */ + ad7768_fill_scale_tbl(dev); + return 0; } @@ -662,6 +775,61 @@ static int ad7768_get_dig_fil_attr(struct iio_dev *dev, return mode; } +static int ad7768_calc_pga_gain(struct ad7768_state *st, int gain_int, + int gain_fract, + int precision) +{ + u64 gain_nano, tmp; + int gain_idx; + + precision--; + gain_nano = gain_int * NANO + gain_fract; + if (gain_nano < 0 || gain_nano > ADAQ776X_GAIN_MAX_NANO) + return -EINVAL; + + tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << precision, NANO); + gain_nano = DIV_ROUND_CLOSEST_ULL(st->vref_uv, tmp); + if (st->chip->has_variable_aaf) + /* remove the AAF gain from the gain */ + gain_nano = DIV_ROUND_CLOSEST_ULL(gain_nano * MILLI, + ad7768_aaf_gains[st->aaf_gain]); + tmp = st->chip->num_pga_modes; + gain_idx = find_closest(gain_nano, st->chip->pga_gains, tmp); + + return gain_idx; +} + +static int ad7768_set_pga_gain(struct ad7768_state *st, + int gain_mode) +{ + int ret; + int check_val; + int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset); + + guard(mutex)(&st->lock); + /* Check GPIO control register */ + ret = ad7768_spi_reg_read(st, AD7768_REG_GPIO_CONTROL, &check_val, 1); + if (ret < 0) + return ret; + + if ((check_val & AD7768_GPIO_PGIA_EN) != AD7768_GPIO_PGIA_EN) { + /* Enable PGIA GPIOs and set them as output */ + ret = ad7768_spi_reg_write(st, AD7768_REG_GPIO_CONTROL, AD7768_GPIO_PGIA_EN); + if (ret < 0) + return ret; + } + + /* Write the respective gain values to GPIOs 0, 1, 2 */ + ret = ad7768_spi_reg_write(st, AD7768_REG_GPIO_WRITE, + AD7768_GPIO_WRITE(pgia_pins_value)); + if (ret < 0) + return ret; + + st->pga_gain_mode = gain_mode; + + return 0; +} + int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct ad7768_state *st = gpiochip_get_data(chip); @@ -922,7 +1090,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, { struct ad7768_state *st = iio_priv(indio_dev); const struct iio_scan_type *scan_type; - int scale_uv, ret; + int ret; scan_type = iio_get_current_scan_type(indio_dev, chan); if (IS_ERR(scan_type)) @@ -945,12 +1113,19 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - scale_uv = st->vref_uv; - if (scale_uv < 0) - return scale_uv; - - *val = (scale_uv * 2) / 1000; - *val2 = scan_type->realbits; + if (st->chip->has_pga) { + *val = st->scale_tbl[st->pga_gain_mode][0]; + *val2 = st->scale_tbl[st->pga_gain_mode][1]; + return IIO_VAL_INT_PLUS_NANO; + } + *val = st->vref_uv / 1000; + if (st->chip->has_variable_aaf) + *val = (*val * MILLI) / ad7768_aaf_gains[st->aaf_gain]; + /* + * ADC output code is two's complement so only (realbits - 1) + * bits express voltage magnitude + */ + *val2 = scan_type->realbits - 1; return IIO_VAL_FRACTIONAL_LOG2; @@ -963,15 +1138,59 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int ad7768_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_tbl; + *length = st->chip->num_pga_modes * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + static int ad7768_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + int gain_mode; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: return ad7768_set_freq(st, val); + case IIO_CHAN_INFO_SCALE: + if (!st->chip->has_pga) + return -EOPNOTSUPP; + + gain_mode = ad7768_calc_pga_gain(st, val, val2, + scan_type->realbits); + return ad7768_set_pga_gain(st, gain_mode); default: return -EINVAL; } @@ -1011,7 +1230,9 @@ static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev, static const struct iio_info ad7768_info = { .attrs = &ad7768_group, .read_raw = &ad7768_read_raw, + .read_avail = &ad7768_read_avail, .write_raw = &ad7768_write_raw, + .write_raw_get_fmt = &ad7768_write_raw_get_fmt, .read_label = ad7768_read_label, .get_current_scan_type = &ad7768_get_current_scan_type, .debugfs_reg_access = &ad7768_reg_access, @@ -1267,10 +1488,46 @@ static const struct ad7768_chip_info ad7768_chip_info = { .available_masks = ad7768_channel_masks, }; +static const struct ad7768_chip_info adaq7767_chip_info = { + .name = "adaq7767-1", + .channel_spec = ad7768_channels, + .num_channels = ARRAY_SIZE(ad7768_channels), + .available_masks = ad7768_channel_masks, + .has_pga = false, + .has_variable_aaf = true +}; + +static const struct ad7768_chip_info adaq7768_chip_info = { + .name = "adaq7768-1", + .channel_spec = adaq776x_channels, + .num_channels = ARRAY_SIZE(adaq776x_channels), + .available_masks = ad7768_channel_masks, + .pga_gains = adaq7768_gains, + .default_pga_mode = AD7768_PGA_GAIN_2, + .num_pga_modes = ARRAY_SIZE(adaq7768_gains), + .pgia_mode2pin_offset = 6, + .has_pga = true, + .has_variable_aaf = false +}; + +static const struct ad7768_chip_info adaq7769_chip_info = { + .name = "adaq7769-1", + .channel_spec = adaq776x_channels, + .num_channels = ARRAY_SIZE(adaq776x_channels), + .available_masks = ad7768_channel_masks, + .pga_gains = adaq7769_gains, + .default_pga_mode = AD7768_PGA_GAIN_0, + .num_pga_modes = ARRAY_SIZE(adaq7769_gains), + .pgia_mode2pin_offset = 0, + .has_pga = true, + .has_variable_aaf = true +}; + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; struct iio_dev *indio_dev; + u32 val; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -1317,12 +1574,44 @@ static int ad7768_probe(struct spi_device *spi) indio_dev->info = &ad7768_info; indio_dev->modes = INDIO_DIRECT_MODE; + st->aaf_gain = AD7768_AAF_IN1; + ret = device_property_read_u32(&spi->dev, "adi,aaf-gain", &val); + if (ret) { + /* AAF gain required, but not specified */ + if (st->chip->has_variable_aaf) + return dev_err_probe(&spi->dev, -EINVAL, "AAF gain not specified\n"); + + } else if (!st->chip->has_variable_aaf) { + /* AAF gain provided, but not supported */ + return dev_err_probe(&spi->dev, -EINVAL, "AAF gain not supported for %s\n", + st->chip->name); + } else { + /* Device supports variable AAF gain, validate and set the gain */ + switch (val) { + case 1000: + st->aaf_gain = AD7768_AAF_IN1; + break; + case 364: + st->aaf_gain = AD7768_AAF_IN2; + break; + case 143: + st->aaf_gain = AD7768_AAF_IN3; + break; + default: + return dev_err_probe(&spi->dev, -EINVAL, + "Invalid firmware provided gain\n"); + } + } + ret = ad7768_setup(st, indio_dev); if (ret < 0) { dev_err(&spi->dev, "AD7768 setup failed\n"); return ret; } + if (st->chip->has_pga) + ad7768_set_pga_gain(st, st->chip->default_pga_mode); + ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels); if (ret) return ret; @@ -1339,12 +1628,18 @@ static int ad7768_probe(struct spi_device *spi) static const struct spi_device_id ad7768_id_table[] = { { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info }, + { "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info }, + { "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info }, + { "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info }, {} }; MODULE_DEVICE_TABLE(spi, ad7768_id_table); static const struct of_device_id ad7768_of_match[] = { { .compatible = "adi,ad7768-1", .data = &ad7768_chip_info }, + { .compatible = "adi,adaq7767-1", .data = &adaq7767_chip_info }, + { .compatible = "adi,adaq7768-1", .data = &adaq7768_chip_info }, + { .compatible = "adi,adaq7769-1", .data = &adaq7769_chip_info }, { }, }; MODULE_DEVICE_TABLE(of, ad7768_of_match);