diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml index 96feca8fb42b8d..52efd1852bf075 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml @@ -18,8 +18,12 @@ description: | properties: compatible: - enum: - - adi,ad4134 + oneOf: + - const: adi,ad4134 + - items: + - enum: + - adi,ad7134 + - const: adi,ad4134 reg: maxItems: 1 @@ -30,6 +34,12 @@ properties: reset-gpios: maxItems: 1 + cs-gpios: + description: + Used to synchronize devices when two or more ADCs are used for + synchronized data sampling. + maxItems: 1 + clocks: maxItems: 1 @@ -56,6 +66,15 @@ properties: iovdd-supply: true refin-supply: true + adi,adc-frame: + $ref: /schemas/types.yaml#/definitions/string + enum: [ 16-bit, 16-bit+CRC, 24-bit, 24-bit+CRC ] + default: 24-bit + description: + Describes how the ADC data packet should be configured. The data packet + size is 16-bit when "16-bit" is selected, 24-bit when "16-bit+CRC" or + "24-bit" are selected, and 32-bit when "24-bit+CRC" is set. + adi,spi-engine: $ref: /schemas/types.yaml#/definitions/phandle description: phandle of the SPI slave connected to the SPI engine master @@ -104,6 +123,7 @@ examples: iovdd-supply = <&iovdd>; refin-supply = <&refin>; + adi,adc-frame = "24-bit"; adi,spi-engine = <&ad4134_spi_engine>; }; }; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad3552r-hs.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad3552r-hs.dts index f77f4cf1ccc6d6..92f816ccc7b190 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad3552r-hs.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad3552r-hs.dts @@ -50,7 +50,7 @@ }; &fpga_axi { - ref_clk: clock-controller@44B00000 { + ref_clk: spi-clock-controller@44B00000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44B00000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4000.dtsi b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4000.dtsi index 5c6e341d340780..8bc40b83301121 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4000.dtsi +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4000.dtsi @@ -94,7 +94,7 @@ clocks = <&clkc 17>; }; - spi_clk: clock-controller@0x44a70000 { + spi_clk: spi-clock-controller@0x44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4030-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4030-24.dts index c70017f98fde00..59165fdb9b3a7b 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4030-24.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4030-24.dts @@ -58,7 +58,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4032-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4032-24.dts index fc1ed92fe352db..144f48f6d097a4 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4032-24.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4032-24.dts @@ -58,7 +58,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-16.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-16.dts new file mode 100644 index 00000000000000..3b0d1b2f8031fc --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-16.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include "zynq-zed-adv7511-ad4134-24.dts" + +&spi0 { + status = "okay"; + + ad4134: adc@0 { + adi,adc-frame = "16-bit"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-16CRC.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-16CRC.dts new file mode 100644 index 00000000000000..d9227436e9204d --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-16CRC.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include "zynq-zed-adv7511-ad4134-24.dts" + +&spi0 { + status = "okay"; + + ad4134: adc@0 { + adi,adc-frame = "16-bit+CRC"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-24.dts new file mode 100644 index 00000000000000..ae638149074085 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-24.dts @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2022 Analog Devices, Inc. + */ +/dts-v1/; + +#include + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + avdd5: regulator-avdd5 { + compatible = "regulator-fixed"; + regulator-name = "avdd5"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + avdd1v8: regulator-avdd1v8 { + compatible = "regulator-fixed"; + regulator-name = "avdd1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + iovdd: regulator-iovdd { + compatible = "regulator-fixed"; + regulator-name = "iovdd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + refin: regulator-refin { + compatible = "regulator-fixed"; + regulator-name = "refin"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + regulator-always-on; + }; + + trigger_pwm: adc-pwm-trigger { + compatible = "pwm-trigger"; + #trigger-source-cells = <0>; + pwms = <&ad4134_odr_generator 0 1000000 0>; + }; + + clocks { + cnv_ext_clk: ext-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <48000000>; + clock-output-names = "cnv_ext_clk"; + }; + }; +}; + +&fpga_axi { + rx_dma: dma-controller@44a30000 { + 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 15>; + }; + + axi_spi_engine_0: spi@44a00000 { + compatible = "adi,axi-spi-engine-1.00.a"; + reg = <0x44a00000 0x10000>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15 &spi_clk>; + clock-names = "s_axi_aclk", "spi_clk"; + dmas = <&rx_dma 0>; + dma-names = "offload0-rx"; + trigger-sources = <&trigger_pwm>; + #address-cells = <0x1>; + #size-cells = <0x0>; + + ad4134_spi_engine: spi_engine@0 { + compatible = "adi,ad4134-spi-engine"; + reg = <0>; + + spi-max-frequency = <50000000>; + + spi-cpha; + spi-rx-bus-width = <1>, <1>, <1>, <1>; /* 4 lanes of 1 bit each */ + }; + }; + + ad4134_odr_generator: pwm@44b00000 { + compatible = "adi,axi-pwmgen-2.00.a"; + reg = <0x44b00000 0x10000>; + #pwm-cells = <3>; + clocks = <&clkc 15>; + clock-names = "axi"; + }; + + spi_clk: spi-clock-controller@44b10000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44b10000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 15>; + clock-names = "clkin1", "s_axi_aclk"; + clock-output-names = "clkin1", "spi_clk"; + }; +}; + +&spi0 { + status = "okay"; + + ad4134: adc@0 { + compatible = "adi,ad4134"; + reg = <0>; + + spi-max-frequency = <10000000>; + + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + + clocks = <&cnv_ext_clk>; + clock-names = "cnv_ext_clk"; + + pwm-names = "odr"; + pwms = <&ad4134_odr_generator 1 1000000 0>; + + avdd5-supply = <&avdd5>; + avdd1v8-supply = <&avdd1v8>; + iovdd-supply = <&iovdd>; + refin-supply = <&refin>; + + adi,spi-engine = <&ad4134_spi_engine>; + adi,adc-frame = "24-bit"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-24CRC.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-24CRC.dts new file mode 100644 index 00000000000000..d5321e12b9d10f --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134-24CRC.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include "zynq-zed-adv7511-ad4134-24.dts" + +&spi0 { + status = "okay"; + + ad4134: adc@0 { + adi,adc-frame = "24-bit+CRC"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134.dts index 8bb11765d3076a..756d746ad4f054 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4134.dts @@ -48,15 +48,38 @@ regulator-always-on; }; - sys_clk: sys-clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <48000000>; + trigger_pwm: adc-pwm-trigger { + compatible = "pwm-trigger"; + #trigger-source-cells = <0>; + pwms = <&ad4134_odr_generator 0 1000000 0>; + }; + + clocks { + ext_clk: ext-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + clock-output-names = "clkin"; + }; }; }; &fpga_axi { - rx_dma: dma-controller@44a30000 { + ad4134_odr_generator: odr_generator@44b00000 { + //compatible = "adi,axi-pwmgen"; + //reg = <0x44b00000 0x10000>; + //#pwm-cells = <2>; + //clocks = <&ext_clk>; + + compatible = "adi,axi-pwmgen-2.00.a"; + reg = <0x44b00000 0x1000>; + label = "ad4134_pwm"; + #pwm-cells = <3>; + clocks = <&clkc 15>, <&ext_clk>; + clock-names = "axi", "clkin"; + }; + + rx_dma: rx-dmac@44a30000 { compatible = "adi,axi-dmac-1.00.a"; reg = <0x44a30000 0x1000>; #dma-cells = <1>; @@ -65,70 +88,48 @@ clocks = <&clkc 15>; }; + spi_clk: axi_clkgen@44b10000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44b10000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 15>; + clock-names = "s_axi_aclk", "clkin1"; + clock-output-names = "spi_clk"; + }; + axi_spi_engine_0: spi@44a00000 { - compatible = "adi,legacy-axi-spi-engine-1.00.a"; + compatible = "adi,axi-spi-engine-1.00.a"; reg = <0x44a00000 0x10000>; interrupt-parent = <&intc>; interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clkc 15 &spi_clk>; clock-names = "s_axi_aclk", "spi_clk"; - num-cs = <1>; - + dmas = <&rx_dma 0>; + dma-names = "offload0-rx"; + trigger-sources = <&trigger_pwm>; #address-cells = <0x1>; #size-cells = <0x0>; - ad4134_spi_engine: spi_engine@0 { - compatible = "adi,ad4134-spi-engine"; + ad4134: adc@0 { + compatible = "adi,ad4134"; reg = <0>; - - spi-max-frequency = <48000000>; - - spi-cpha; + spi-max-frequency = <50000000>; + avdd5-supply = <&avdd5>; + dvdd5-supply = <&avdd5>; + iovdd-supply = <&iovdd>; + refin-supply = <&refin>; + avdd1v8-supply = <&avdd1v8>; + dvdd1v8-supply = <&avdd1v8>; + clkvdd-supply = <&avdd1v8>; + clocks = <&ext_clk>; + clock-names = "clkin"; + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + //pwms = <&ad4134_odr_generator 0 0 + // &ad4134_odr_generator 1 0>; + //pwm-names = "trigger_pwm","odr_pwm"; + pwm-names = "odr"; + pwms = <&ad4134_odr_generator 1 1000000 0>; }; }; - ad4134_odr_generator: pwm@44b00000 { - compatible = "adi,axi-pwmgen-2.00.a"; - reg = <0x44b00000 0x10000>; - #pwm-cells = <2>; - clocks = <&clkc 15>, <&spi_clk>; - clock-names = "axi", "ext"; - }; - - spi_clk: clock-controller@44b10000 { - compatible = "adi,axi-clkgen-2.00.a"; - reg = <0x44b10000 0x10000>; - #clock-cells = <0>; - clocks = <&clkc 15>, <&clkc 15>; - clock-names = "clkin1", "s_axi_aclk"; - }; -}; - -&spi0 { - status = "okay"; - - ad4134: adc@0 { - compatible = "adi,ad4134"; - reg = <0>; - - spi-max-frequency = <1000000>; - - reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; - - clocks = <&sys_clk>; - clock-names = "sys_clk"; - - dmas = <&rx_dma 0>; - dma-names = "rx"; - - pwms = <&ad4134_odr_generator 0 0>; - pwm-names = "odr_pwm"; - - avdd5-supply = <&avdd5>; - avdd1v8-supply = <&avdd1v8>; - iovdd-supply = <&iovdd>; - refin-supply = <&refin>; - - adi,spi-engine = <&ad4134_spi_engine>; - }; }; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-16.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-16.dts index e9d4f05f0ab6e7..ed523d44daefe6 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-16.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-16.dts @@ -58,7 +58,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts index 8a883184f982e0..dc5ec464cf185a 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4630-24.dts @@ -72,7 +72,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4696.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4696.dts index 733e469e820a9b..7f98f78df446f8 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4696.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad4696.dts @@ -74,7 +74,7 @@ clock-names = "axi", "ext"; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-16.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-16.dts new file mode 100644 index 00000000000000..92ce2e66134e6e --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-16.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include "zynq-zed-adv7511-ad7134-24.dts" + +&spi0 { + status = "okay"; + + ad4134_0: adc_0@0 { + adi,adc-frame = "16-bit"; + }; + + ad4134_1: adc_1@1 { + adi,adc-frame = "16-bit"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-16CRC.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-16CRC.dts new file mode 100644 index 00000000000000..98279e5de2b8a0 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-16CRC.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include "zynq-zed-adv7511-ad7134-24.dts" + +&spi0 { + status = "okay"; + + ad4134_0: adc_0@0 { + adi,adc-frame = "16-bit+CRC"; + }; + + ad4134_1: adc_1@1 { + adi,adc-frame = "16-bit+CRC"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-24.dts new file mode 100644 index 00000000000000..4df7ae38ec4bc4 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-24.dts @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + avdd5: regulator-avdd5 { + compatible = "regulator-fixed"; + regulator-name = "avdd5"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + avdd1v8: regulator-avdd1v8 { + compatible = "regulator-fixed"; + regulator-name = "avdd1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + iovdd: regulator-iovdd { + compatible = "regulator-fixed"; + regulator-name = "iovdd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + refin: regulator-refin { + compatible = "regulator-fixed"; + regulator-name = "refin"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + regulator-always-on; + }; + + trigger_pwm: adc-pwm-trigger { + compatible = "pwm-trigger"; + #trigger-source-cells = <0>; + pwms = <&ad4134_odr_generator 0 1000000 0>; + }; + + clocks { + cnv_ext_clk: ext-clk { + #clock-cells = <0x0>; + compatible = "fixed-clock"; + clock-frequency = <48000000>; + clock-output-names = "cnv_ext_clk"; + }; + }; +}; + +&fpga_axi { + rx_dma: rx-dmac@44a30000 { + 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 15>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <128>; + adi,source-bus-type = <1>; + adi,destination-bus-width = <64>; + adi,destination-bus-type = <0>; + }; + }; + }; + + axi_spi_engine_0: spi@44a00000 { + compatible = "adi,axi-spi-engine-1.00.a"; + reg = <0x44a00000 0x10000>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15 &spi_clk>; + clock-names = "s_axi_aclk", "spi_clk"; + dmas = <&rx_dma 0>; + dma-names = "offload0-rx"; + trigger-sources = <&trigger_pwm>; + #address-cells = <0x1>; + #size-cells = <0x0>; + + ad4134_spi_engine: spi_engine@0 { + compatible = "adi,ad4134-spi-engine"; + reg = <0>; + + spi-max-frequency = <50000000>; + spi-rx-bus-width = <1>, <1>, <1>, <1>, <1>, <1>, <1>, <1>; /* 8 lanes of 1 bit each */ + + }; + }; + + ad4134_odr_generator: odr_generator@44b00000 { + compatible = "adi,axi-pwmgen-2.00.a"; + reg = <0x44b00000 0x10000>; + #pwm-cells = <3>; + clocks = <&clkc 15>; + clock-names = "axi"; + }; + + spi_clk: axi_clkgen@44b10000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44b10000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 15>; + clock-names = "clkin1", "s_axi_aclk"; + clock-output-names = "clkin1", "spi_clk"; + }; +}; + +&spi0 { + status = "okay"; + + ad4134_0: adc_0@0 { + compatible = "adi,ad4134"; + reg = <0>; + + spi-max-frequency = <10000000>; + + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + cs-gpios = <&gpio0 104 GPIO_ACTIVE_HIGH>; + + clocks = <&cnv_ext_clk>; + clock-names = "cnv_ext_clk"; + + pwm-names = "odr"; + pwms = <&ad4134_odr_generator 1 1000000 0>; + + avdd5-supply = <&avdd5>; + avdd1v8-supply = <&avdd1v8>; + iovdd-supply = <&iovdd>; + refin-supply = <&refin>; + + adi,spi-engine = <&ad4134_spi_engine>; + adi,adc-frame = "24-bit"; + }; + + ad4134_1: adc_1@1 { + compatible = "adi,ad4134"; + + reg = <1>; + + spi-max-frequency = <10000000>; + + reset-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>; + + clocks = <&cnv_ext_clk>; + clock-names = "cnv_ext_clk"; + + avdd5-supply = <&avdd5>; + avdd1v8-supply = <&avdd1v8>; + iovdd-supply = <&iovdd>; + refin-supply = <&refin>; + adi,adc-frame = "24-bit"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-24CRC.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-24CRC.dts new file mode 100644 index 00000000000000..c7758208861ede --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7134-24CRC.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include "zynq-zed-adv7511-ad7134-24.dts" + +&spi0 { + status = "okay"; + + ad4134_0: adc_0@0 { + adi,adc-frame = "24-bit+CRC"; + }; + + ad4134_1: adc_1@1 { + adi,adc-frame = "24-bit+CRC"; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7625.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7625.dts index 3296844e6a27c5..2f0b90c916523d 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7625.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7625.dts @@ -67,7 +67,7 @@ clocks = <&clkc 15>; }; - ref_clk: clock-controller@44a80000 { + ref_clk: spi-clock-controller@44a80000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a80000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7960.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7960.dts index f77ff83b6c86ab..fc78c0c01a8cc8 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7960.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ad7960.dts @@ -69,7 +69,7 @@ clocks = <&clkc 15>; }; - ref_clk: clock-controller@44a80000 { + ref_clk: spi-clock-controller@44a80000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a80000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4216.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4216.dts index 734c61d899d2f7..c0bced262a4f19 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4216.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4216.dts @@ -74,7 +74,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4220.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4220.dts index cc71b571b30c70..af311f52129c88 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4220.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4220.dts @@ -85,7 +85,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24.dts index 847d7872b8804e..981645ef5ae0db 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24.dts @@ -85,7 +85,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24_cm0_sdi4_cz2.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24_cm0_sdi4_cz2.dts index 35a0ae473d4e1d..cf6b47fa86fc5d 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24_cm0_sdi4_cz2.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-adaq4224-24_cm0_sdi4_cz2.dts @@ -72,7 +72,7 @@ clocks = <&clkc 15>; }; - spi_clk: clock-controller@44a70000 { + spi_clk: spi-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-cn0561-24.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-cn0561-24.dts new file mode 100644 index 00000000000000..3e1f67b4dd3598 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-cn0561-24.dts @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD7134 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad4134 + * https://wiki.analog.com/resources/eval/user-guides/ad4134 + * + * hdl_project: + * + * Copyright (C) 2024 Analog Devices, Inc. + */ +/dts-v1/; + +#include + +#include "zynq-zed.dtsi" +#include "zynq-zed-adv7511.dtsi" + +/ { + avdd5: regulator-avdd5 { + compatible = "regulator-fixed"; + regulator-name = "avdd5"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + avdd1v8: regulator-avdd1v8 { + compatible = "regulator-fixed"; + regulator-name = "avdd1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + iovdd: regulator-iovdd { + compatible = "regulator-fixed"; + regulator-name = "iovdd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + refin: regulator-refin { + compatible = "regulator-fixed"; + regulator-name = "refin"; + regulator-min-microvolt = <4096000>; + regulator-max-microvolt = <4096000>; + regulator-always-on; + }; + + trigger_pwm: adc-pwm-trigger { + compatible = "pwm-trigger"; + #trigger-source-cells = <0>; + pwms = <&ad4134_odr_generator 0 1000000 0>; + }; + + clocks { + cnv_ext_clk: ext-clk { + #clock-cells = <0x0>; + compatible = "fixed-clock"; + clock-frequency = <100000000>; + clock-output-names = "cnv_ext_clk"; + }; + }; + + ad4134_control@0 { + compatible = "adi,one-bit-adc-dac"; + #address-cells = <1>; + #size-cells = <0>; + + out-gpios = <&ad4134_0 0 0>, <&ad4134_0 1 0>, <&ad4134_0 2 0>, <&ad4134_0 3 0>, + <&ad4134_0 4 0>, <&ad4134_0 5 0>, <&ad4134_0 6 0>, <&ad4134_0 7 0>; + + label = "ad4134"; + + channel@0 { + reg = <0>; + label = "GPIO0"; + }; + + channel@1 { + reg = <1>; + label = "GPIO1"; + }; + + channel@2 { + reg = <2>; + label = "GPIO2"; + }; + + channel@3 { + reg = <3>; + label = "GPIO3"; + }; + + channel@4 { + reg = <4>; + label = "GPIO4"; + }; + + channel@5 { + reg = <5>; + label = "GPIO5"; + }; + + channel@6 { + reg = <6>; + label = "GPIO6"; + }; + + channel@7 { + reg = <7>; + label = "GPIO7"; + }; + }; +}; + +&fpga_axi { + rx_dma: rx-dmac@44a30000 { + 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 15>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <128>; + adi,source-bus-type = <1>; + adi,destination-bus-width = <64>; + adi,destination-bus-type = <0>; + }; + }; + }; + + axi_spi_engine_0: spi@44a00000 { + compatible = "adi,axi-spi-engine-1.00.a"; + reg = <0x44a00000 0x10000>; + interrupt-parent = <&intc>; + interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 15 &spi_clk>; + clock-names = "s_axi_aclk", "spi_clk"; + dmas = <&rx_dma 0>; + dma-names = "offload0-rx"; + trigger-sources = <&trigger_pwm>; + #address-cells = <0x1>; + #size-cells = <0x0>; + + ad4134_spi_engine: spi_engine@0 { + compatible = "adi,ad4134-spi-engine"; + reg = <0>; + + spi-max-frequency = <50000000>; + + spi-cpha; + }; + }; + + ad4134_odr_generator: odr_generator@44b00000 { + compatible = "adi,axi-pwmgen-2.00.a"; + reg = <0x44b00000 0x10000>; + #pwm-cells = <2>; + clocks = <&clkc 15>, <&cnv_ext_clk>; + clock-names = "axi", "ext"; + }; + + spi_clk: axi_clkgen@44b10000 { + compatible = "adi,axi-clkgen-2.00.a"; + reg = <0x44b10000 0x10000>; + #clock-cells = <0>; + clocks = <&clkc 15>, <&clkc 15>; + clock-names = "clkin1", "s_axi_aclk"; + clock-output-names = "clkin1", "spi_clk"; + }; +}; + +&spi0 { + status = "okay"; + + ad4134_0: adc_0@0 { + compatible = "adi,ad4134"; + reg = <0>; + + spi-max-frequency = <10000000>; + + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + gpio-cs-gpios = <&gpio0 104 GPIO_ACTIVE_HIGH>; + + clocks = <&cnv_ext_clk>; + clock-names = "cnv_ext_clk"; + + pwm-names = "odr"; + pwms = <&ad4134_odr_generator 1 1000000 0>; + + avdd5-supply = <&avdd5>; + avdd1v8-supply = <&avdd1v8>; + iovdd-supply = <&iovdd>; + refin-supply = <&refin>; + + adi,spi-engine = <&ad4134_spi_engine>; + adi,adc-frame = "24-bit"; + + gpio-controller; + #gpio-cells = <2>; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ltc2387.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ltc2387.dts index d3859eb41fdc1c..e74edbda6be3b3 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ltc2387.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-ltc2387.dts @@ -34,7 +34,7 @@ clocks = <&clkc 15>; }; - ref_clk: clock-controller@44a70000 { + ref_clk: ref-clock-controller@44a70000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x44a70000 0x10000>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-m2k-revb.dts b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-m2k-revb.dts index f0654ea295ba42..1845a3e5abb572 100644 --- a/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-m2k-revb.dts +++ b/arch/arm/boot/dts/xilinx/zynq-zed-adv7511-m2k-revb.dts @@ -200,7 +200,7 @@ adi,axi-dds-default-frequency = <1000000>; }; - logic_analyzer_clkgen: clock-controller@7000000 { + logic_analyzer_clkgen: m2k-clock-controller@7000000 { compatible = "adi,axi-clkgen-2.00.a"; reg = <0x70000000 0x10000>; clocks = <&clkc 16>, <&clkc 15>; diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c index 72a008b2ca309c..2641e015adf5d6 100644 --- a/drivers/iio/adc/ad4134.c +++ b/drivers/iio/adc/ad4134.c @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -17,36 +19,77 @@ #include #include #include +#include #include #include #include #include +#include + +#include +#include +#include +#include + +#define AD4134_DCLK_RISING_OFFSET_NS 8 +#define AD4134_MIN_ODR_FREQ_HZ 10 +#define AD4134_MAX_ODR_FREQ_HZ (1496 * HZ_PER_KHZ) + +#define AD4134_SPI_MAX_XFER_LEN 3 +#define AD4134_NUM_CHANNELS 4 +#define AD4134_CHAN_PRECISION_BITS 24 + #define AD4134_NAME "ad4134" +#define AD4134_IF_CONFIG_A_REG 0x0 +#define AD4134_IF_CONFIG_A_RESET_MASK (BIT(7) | BIT(0)) + +#define AD4134_IF_CONFIG_B_REG 0x01 +#define AD4134_IF_CONFIG_B_SINGLE_INSTR BIT(7) +#define AD4134_IF_CONFIG_B_MASTER_SLAVE_RD_CTRL BIT(5) +#define AD4134_IF_CONFIG_B_RESET BIT(1) + #define AD4134_DEVICE_CONFIG_REG 0x02 #define AD4134_DEVICE_CONFIG_POWER_MODE_MASK BIT(0) #define AD4134_POWER_MODE_HIGH_PERF 0b1 #define AD4134_DATA_PACKET_CONFIG_REG 0x11 #define AD4134_DATA_PACKET_CONFIG_FRAME_MASK GENMASK(5, 4) -#define AD4134_DATA_FRAME_24BIT_CRC 0b11 +#define AD4134_DATA_PACKET_16BIT_FRAME 0x0 +#define AD4134_DATA_PACKET_16BIT_CRC6_FRAME 0x1 +#define AD4134_DATA_PACKET_24BIT_FRAME 0x2 +#define AD4134_DATA_PACKET_24BIT_CRC6_FRAME 0x3 #define AD4134_DIG_IF_CFG_REG 0x12 #define AD4134_DIF_IF_CFG_FORMAT_MASK GENMASK(1, 0) #define AD4134_DATA_FORMAT_QUAD_CH_PARALLEL 0b10 +#define AD4134_CHAN_DIG_FILTER_SEL_REG 0x1E +#define AD4134_CHAN_DIG_FILTER_SEL_MASK GENMASK(7, 0) +#define AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH0 GENMASK(1, 0) +#define AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH1 GENMASK(3, 2) +#define AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH2 GENMASK(5, 4) +#define AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH3 GENMASK(7, 6) + +#define AD4134_GPIO_INPUT(x) 0x00 +#define AD4134_GPIO_OUTPUT(x) BIT(x) +#define AD4134_GPIO_DIR_CONTROL 0x20 +#define AD4134_GPIO_DATA 0x21 + +#define AD4134_SINC6_FILTER 0b01010101 + #define AD4134_ODR_MIN 10 #define AD4134_ODR_MAX 1496000 -#define AD4134_ODR_DEFAULT 300000 - -#define AD4134_NUM_CHANNELS 4 -#define AD4134_REAL_BITS 24 -#define AD4134_WORD_BITS 32 +#define AD4134_ODR_DEFAULT 1496000 #define AD4134_RESET_TIME_US 10000000 +enum { + ODR_SET_FREQ, +}; + enum ad4134_regulators { AD4134_AVDD5_REGULATOR, AD4134_AVDD1V8_REGULATOR, @@ -55,12 +98,132 @@ enum ad4134_regulators { AD4134_NUM_REGULATORS }; +/* maps adi,adc-frame property value to enum */ +static const char * const ad4134_frame_config[] = { + [AD4134_DATA_PACKET_16BIT_FRAME] = "16-bit", + [AD4134_DATA_PACKET_16BIT_CRC6_FRAME] = "16-bit+CRC", + [AD4134_DATA_PACKET_24BIT_FRAME] = "24-bit", + [AD4134_DATA_PACKET_24BIT_CRC6_FRAME] = "24-bit+CRC", +}; + +enum ad7134_flt_type { + WIDEBAND, + SINC6, + SINC3, + SINC3_REJECTION +}; + +static const char * const ad7134_filter_enum[] = { + [WIDEBAND] = "WIDEBAND", + [SINC6] = "SINC6", + [SINC3] = "SINC3", + [SINC3_REJECTION] = "SINC3_REJECTION", +}; + +#define AD4134_CHANNEL(_index, _realbits, _storebits, _ext_info) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (_realbits), \ + .storagebits = 32, \ + .shift = 0, \ + .endianness = IIO_CPU, \ + }, \ + .ext_info = _ext_info, \ +} + +static ssize_t ad7134_set_sync(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len); +static ssize_t ad7134_get_sync(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf); +static int ad7134_set_dig_fil(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int filter); +static int ad7134_get_dig_fil(struct iio_dev *dev, + const struct iio_chan_spec *chan); + +static const struct iio_enum ad7134_flt_type_iio_enum = { + .items = ad7134_filter_enum, + .num_items = ARRAY_SIZE(ad7134_filter_enum), + .set = ad7134_set_dig_fil, + .get = ad7134_get_dig_fil, +}; + +static struct iio_chan_spec_ext_info ad7134_ext_info[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7134_flt_type_iio_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ad7134_flt_type_iio_enum), + { + .name = "ad4134_sync", + .write = ad7134_set_sync, + .read = ad7134_get_sync, + .shared = IIO_SHARED_BY_ALL, + }, + { }, +}; + +#define AD4134_CHAN_SET(_realbits, _storebits) { \ + AD4134_CHANNEL(0, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(1, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(2, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(3, _realbits, _storebits, ad7134_ext_info), \ +} + +#define AD4134_DUO_CHAN_SET(_realbits, _storebits) { \ + AD4134_CHANNEL(0, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(1, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(2, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(3, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(4, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(5, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(6, _realbits, _storebits, ad7134_ext_info), \ + AD4134_CHANNEL(7, _realbits, _storebits, ad7134_ext_info), \ +} + +static const struct iio_chan_spec ad4134_16_chan_set[] = AD4134_CHAN_SET(16, 16); +static const struct iio_chan_spec ad4134_16CRC_chan_set[] = AD4134_CHAN_SET(16, 24); +static const struct iio_chan_spec ad4134_24_chan_set[] = AD4134_CHAN_SET(24, 24); +static const struct iio_chan_spec ad4134_24CRC_chan_set[] = AD4134_CHAN_SET(24, 32); + +static const struct iio_chan_spec ad4134_16_duo_chan_set[] = AD4134_DUO_CHAN_SET(16, 16); +static const struct iio_chan_spec ad4134_16CRC_duo_chan_set[] = AD4134_DUO_CHAN_SET(16, 24); +static const struct iio_chan_spec ad4134_24_duo_chan_set[] = AD4134_DUO_CHAN_SET(24, 24); +static const struct iio_chan_spec ad4134_24CRC_duo_chan_set[] = AD4134_DUO_CHAN_SET(24, 32); + +static const unsigned long ad4134_channel_masks[] = { + GENMASK(ARRAY_SIZE(ad4134_16_chan_set) - 1, 0), + 0, +}; + +static const unsigned long ad4134_duo_channel_masks[] = { + GENMASK(ARRAY_SIZE(ad4134_16_duo_chan_set) - 1, 0), + 0, +}; + struct ad4134_state { struct fwnode_handle *spi_engine_fwnode; struct regmap *regmap; struct spi_device *spi; struct spi_device *spi_engine; - struct pwm_device *odr_pwm; + + unsigned long sys_clk_hz; + + struct spi_transfer xfers; + struct spi_message msg; + + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_offload_trigger_config offload_trigger_config; + struct pwm_device *odr_trigger; + struct pwm_waveform odr_wf; + unsigned int odr_hz; + struct regulator_bulk_data regulators[AD4134_NUM_REGULATORS]; /* @@ -70,63 +233,602 @@ struct ad4134_state { struct mutex lock; struct spi_message buf_read_msg; - struct spi_transfer buf_read_xfer; + struct gpio_desc *cs_gpio; + struct gpio_chip gpiochip; unsigned int odr; + unsigned int filter_type; unsigned long sys_clk_rate; int refin_mv; + int output_frame; + u8 num_dout_lines; + bool ad4134_duo; + }; +static ssize_t ad7134_get_sync(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + return sprintf(buf, "enable\n"); +} + +static int ad7134_sync(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + int ret; + + gpiod_set_value_cansleep(st->cs_gpio, 1); + ret = regmap_update_bits(st->regmap, AD4134_IF_CONFIG_B_REG, + (AD4134_IF_CONFIG_B_RESET | AD4134_IF_CONFIG_B_SINGLE_INSTR), + (AD4134_IF_CONFIG_B_RESET | AD4134_IF_CONFIG_B_SINGLE_INSTR)); + if (ret) + return ret; + + gpiod_set_value_cansleep(st->cs_gpio, 0); + + return 0; +} + +static ssize_t ad7134_set_sync(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + int ret; + + ret = ad7134_sync(indio_dev); + + return ret ? ret : len; +} + +static int ad7134_set_dig_fil(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int filter) +{ + struct ad4134_state *st = iio_priv(dev); + int ret; + + st->filter_type = filter; + gpiod_set_value_cansleep(st->cs_gpio, 1); + + ret = regmap_update_bits(st->regmap, AD4134_CHAN_DIG_FILTER_SEL_REG, + AD4134_CHAN_DIG_FILTER_SEL_MASK, + FIELD_PREP(AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH0, filter) | + FIELD_PREP(AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH1, filter) | + FIELD_PREP(AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH2, filter) | + FIELD_PREP(AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH3, filter)); + + gpiod_set_value_cansleep(st->cs_gpio, 0); + + if (ret) + return ret; + return 0; +} + +static int ad7134_get_dig_fil(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4134_state *st = iio_priv(dev); + int ret; + unsigned int readval; + + ret = regmap_read(st->regmap, AD4134_CHAN_DIG_FILTER_SEL_REG, &readval); + if (ret) + return ret; + + return FIELD_GET(AD4134_CHAN_DIG_FILTER_SEL_FRAME_MASK_CH0, readval); +} + static int ad4134_samp_freq_avail[] = { AD4134_ODR_MIN, 1, AD4134_ODR_MAX }; -static int _ad4134_set_odr(struct ad4134_state *st, unsigned int odr) +/* + * Hardcoded 32-bit storagebits because the currently available HDL only + * supports that. + */ +#define AD4134_OFFLOAD_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 's', \ + .storagebits = 32, \ + .realbits = AD4134_CHAN_PRECISION_BITS, \ + .endianness = IIO_CPU, \ + }, \ +} + +/* + * It's not possible for software to record when offloaded SPI transfers run so + * no additional timestamp channel is added. + */ +static const struct iio_chan_spec ad4134_offload_chan_set[] = { + AD4134_OFFLOAD_CHANNEL(0), + AD4134_OFFLOAD_CHANNEL(1), + AD4134_OFFLOAD_CHANNEL(2), + AD4134_OFFLOAD_CHANNEL(3), +}; + +/* The chip converts and outputs all 4 channels on each sample request */ +static const unsigned long ad4134_offload_scan_masks[] = { + GENMASK(3, 0), + 0 +}; + +static int ad4134_setup_odr(struct ad4134_state *st, unsigned int freq_hz) { - struct pwm_state state; + struct pwm_waveform odr_wf = { }; + u64 odr_high_time_ns; + unsigned int odr_hz; + u64 target = 10; int ret; - if (odr < AD4134_ODR_MIN || odr > AD4134_ODR_MAX) + dev_info(&st->spi->dev, "%s: Updating conversion rate to %u Hz\n", + __func__, freq_hz); + + /* + * Every ODR pulse causes each of the 4 ADCs within the AD4134 chip to + * take a sample simultaneously. The peripheral then outputs the data + * from all those channels over one, two, or four data output lanes. If + * the controller can fetch data from multiple lanes, the throughput is + * increased proportionally to the number of data lanes in use. + * Conversely, when multiple data lanes are enabled, the requested + * sampling frequency can be reached with slower ODR frequencies. ? + */ + //odr_hz = freq_hz / st->num_dout_lines; + odr_hz = freq_hz; + dev_info(&st->spi->dev, "ODR frequency: %u Hz (DOUT lines: %u)\n", + odr_hz, st->num_dout_lines); + if (odr_hz < AD4134_MIN_ODR_FREQ_HZ || odr_hz > AD4134_MAX_ODR_FREQ_HZ) { + dev_err(&st->spi->dev, "ODR %u Hz out of range [%d, %d]\n", + odr_hz, AD4134_MIN_ODR_FREQ_HZ, AD4134_MAX_ODR_FREQ_HZ); return -EINVAL; + } - pwm_get_state(st->odr_pwm, &state); + odr_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, odr_hz); + /* + * For an arbitrary system clock (fSYSCLK), we have a minimum ODR high + * time of 6/fSYSCLK derived from the device clock and data interface + * timing (with Gated DCLK) specifications. Set the PWM duty cycle to + * keep ODR up for at least that long. If the rounded PWM's value is + * less than the minimum required, increase the target value by 10 and + * attempt to round the waveform again, until the minimum is reached. + */ + odr_high_time_ns = div64_ul(6ULL * NANO, st->sys_clk_hz); + do { + odr_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->odr_trigger, &odr_wf); + if (ret) + return ret; + target += 10; + } while (odr_wf.duty_length_ns < odr_high_time_ns); /* - * fDIGCLK = fSYSCLK / 2 - * tDIGCLK = 1s / fDIGCLK - * tODR_HIGH_TIME = 3 * tDIGCLK - * See datasheet page 10, Table 3. Data Interface Timing with Gated DCLK. + * PWM waveform rounding might also change the wave period. Double check + * the resulting ODR PWM period is valid. */ - state.duty_cycle = DIV_ROUND_CLOSEST_ULL(6ULL * NSEC_PER_SEC, st->sys_clk_rate); - state.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, odr); + if (odr_wf.period_length_ns < 2 * odr_high_time_ns) + return -EINVAL; + + ret = pwm_set_waveform_might_sleep(st->odr_trigger, &odr_wf, false); + + return 0; +} - ret = pwm_apply_might_sleep(st->odr_pwm, &state); +static const struct spi_offload_config ad4134_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad4134_update_conversion_rate(struct ad4134_state *st, + unsigned int freq_hz) +{ + struct spi_offload_trigger_config *config = &st->offload_trigger_config; + struct pwm_waveform odr_wf = { }; + u64 offload_period_ns; + u64 offload_offset_ns; + u64 odr_high_time_ns; + unsigned int odr_hz; + u64 target = 10; + int ret; + + /* + * Every ODR pulse causes each of the 4 ADCs within the AD4134 chip to + * take a sample simultaneously. The peripheral then outputs the data + * from all those channels over one, two, or four data output lanes. If + * the controller can fetch data from multiple lanes, the throughput is + * increased proportionally to the number of data lanes in use. + * Conversely, when multiple data lanes are enabled, the requested + * sampling frequency can be reached with slower ODR frequencies. + */ + //odr_hz = freq_hz / st->num_dout_lines; + odr_hz = freq_hz; + if (odr_hz < AD4134_MIN_ODR_FREQ_HZ || odr_hz > AD4134_MAX_ODR_FREQ_HZ) + return -EINVAL; + + odr_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, odr_hz); + /* + * For an arbitrary system clock (fSYSCLK), we have a minimum ODR high + * time of 6/fSYSCLK derived from the device clock and data interface + * timing (with Gated DCLK) specifications. Set the PWM duty cycle to + * keep ODR up for at least that long. If the rounded PWM's value is + * less than the minimum required, increase the target value by 10 and + * attempt to round the waveform again, until the minimum is reached. + */ + odr_high_time_ns = div64_ul(6ULL * NANO, st->sys_clk_hz); + do { + odr_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->odr_trigger, &odr_wf); + if (ret) + return ret; + target += 10; + } while (odr_wf.duty_length_ns < odr_high_time_ns); + + ret = pwm_set_waveform_might_sleep(st->odr_trigger, &odr_wf, false); if (ret) - return ret; + return -EINVAL; + + /* + * PWM waveform rounding might also change the wave period. Double check + * the resulting ODR PWM period is valid. + */ + if (odr_wf.period_length_ns < 2 * odr_high_time_ns) + return -EINVAL; - st->odr = odr; + /* + * The controller fetches one sample per active lane each time the + * offload module is triggered. If multiple data lanes are enabled, the + * offload trigger frequency can be proportionally slower. ? + */ + offload_period_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, +// odr_hz * st->num_dout_lines); + odr_hz); + + config->periodic.frequency_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, + offload_period_ns); + + /* + * For gated DCLK, the minimum required time between ODR rising edge + * and DCLK rising edge is the sum of ODR high time and ODR falling + * edge to DCLK rising edge time. Delay the offload trigger for at least + * that amount of time so the ADC sample data will be available when the + * SPI transfer begin. + */ + offload_offset_ns = odr_high_time_ns + AD4134_DCLK_RISING_OFFSET_NS; + do { + config->periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, config); + if (ret) + return ret; + + offload_offset_ns += 10; + } while (config->periodic.offset_ns < odr_high_time_ns + + AD4134_DCLK_RISING_OFFSET_NS); + + st->odr_wf = odr_wf; + st->odr_hz = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, odr_wf.period_length_ns); return 0; } -static int ad4134_set_odr(struct iio_dev *indio_dev, unsigned int odr) +static ssize_t sampling_frequency_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct ad4134_state *st = iio_priv(dev_to_iio_dev(dev)); + + /* + * If the controller can fetch data from multiple lanes, the throughput + * is increased proportionally to the number of data lanes in use. + */ + //return sysfs_emit(buf, "%u\n", st->odr_hz * st->num_dout_lines); + return sysfs_emit(buf, "%u\n", st->odr_hz); +} + +static ssize_t sampling_frequency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad4134_state *st = iio_priv(indio_dev); + unsigned int val; int ret; - ret = iio_device_claim_direct_mode(indio_dev); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = kstrtouint(buf, 10, &val); + if (ret) + goto out_store; + + ret = ad4134_update_conversion_rate(st, val); + +out_store: + iio_device_release_direct(indio_dev); + return ret ?: len; +} + +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0); + +static ssize_t sampling_frequency_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad4134_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "[%u %u %lu]\n", + AD4134_MIN_ODR_FREQ_HZ * st->num_dout_lines, 1, + AD4134_MAX_ODR_FREQ_HZ * st->num_dout_lines); +} + +static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0); + +static ssize_t soft_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad4134_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + gpiod_set_value_cansleep(st->cs_gpio, 1); + ret = regmap_update_bits(st->regmap, AD4134_IF_CONFIG_A_REG, + AD4134_IF_CONFIG_A_RESET_MASK, + AD4134_IF_CONFIG_A_RESET_MASK); + + gpiod_set_value_cansleep(st->cs_gpio, 0); + +out_store: + iio_device_release_direct(indio_dev); + return ret ?: len; +} + +static IIO_DEVICE_ATTR_WO(soft_reset, 0); + +static struct attribute *ad4134_offload_attributes[] = { + &iio_dev_attr_soft_reset.dev_attr.attr, /* Can be provided without offload */ + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +const struct attribute_group ad4134_offload_attribute_group = { + .attrs = ad4134_offload_attributes, +}; + +static void ad4134_prepare_offload_msg(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + unsigned int base_len = roundup_pow_of_two(BITS_TO_BYTES(AD4134_CHAN_PRECISION_BITS)); + unsigned int num_devices; + unsigned int bpw; + + switch (st->output_frame) { + case 0: + bpw = 16; + break; + case 1: + case 2: + bpw = 24; + break; + case 3: + bpw = 32; + break; + default: + dev_err(&st->spi->dev, "invalid adi,adc-frame: %d\n", st->output_frame); + return; + } + num_devices = st->ad4134_duo ? 2 : 1; + + st->xfers.bits_per_word = bpw; + st->xfers.len = base_len * st->num_dout_lines * num_devices; + if (st->num_dout_lines > 1) + st->xfers.multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + + st->xfers.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->msg, &st->xfers, 1); +} + +static int ad4134_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + int ret; + + ad4134_prepare_offload_msg(indio_dev); + st->msg.offload = st->offload; + ret = spi_optimize_message(st->spi_engine, &st->msg); if (ret) return ret; + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_unoptimize; + + return 0; + +out_unoptimize: + spi_unoptimize_message(&st->msg); + + return 0; +} + +static int ad4134_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + + spi_unoptimize_message(&st->msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4134_offload_buffer_setup_ops = { + .postenable = &ad4134_offload_buffer_postenable, + .predisable = &ad4134_offload_buffer_predisable, +}; + +static int ad4134_spi_offload_setup(struct iio_dev *indio_dev, + struct ad4134_state *st) +{ + struct device *offload_dev = &st->spi_engine->dev; + struct device *dev = &st->spi->dev; + struct dma_chan *rx_dma; + + + st->offload_trigger = devm_spi_offload_trigger_get(offload_dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(offload_dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(offload_dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + return devm_iio_dmaengine_buffer_setup_with_handle(offload_dev, + indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + +static int ad4134_pwm_get(struct ad4134_state *st) +{ + struct device *dev = &st->spi->dev; + + st->odr_trigger = devm_pwm_get(dev, NULL); + if (IS_ERR(st->odr_trigger)) + return dev_err_probe(dev, PTR_ERR(st->odr_trigger), + "failed to get ODR PWM\n"); + + + return 0; +} + +static int ad4134_offload_buffer_setup(struct iio_dev *indio_dev, struct spi_device *spi) +{ + struct ad4134_state *st = iio_priv(indio_dev); + struct device *offload_dev = &st->spi_engine->dev; + struct device *dev = &spi->dev; + int ret; + + st->offload = devm_spi_offload_get(offload_dev, st->spi_engine, &ad4134_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret) + return dev_err_probe(dev, ret, "failed to get offload\n"); + + + ret = ad4134_spi_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup SPI offload\n"); + + + return 0; +} + +static int ad4134_input_gpio(struct gpio_chip *chip, unsigned int offset) +{ + struct ad4134_state *st = gpiochip_get_data(chip); + int ret; + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, AD4134_GPIO_DIR_CONTROL, + BIT(offset), AD4134_GPIO_INPUT(offset)); + + mutex_unlock(&st->lock); + + return ret; +} + +static int ad4134_output_gpio(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct ad4134_state *st = gpiochip_get_data(chip); + int ret; + mutex_lock(&st->lock); - ret = _ad4134_set_odr(st, odr); + ret = regmap_update_bits(st->regmap, AD4134_GPIO_DIR_CONTROL, + BIT(offset), AD4134_GPIO_OUTPUT(offset)); + if (ret < 0) + goto out; + ret = regmap_update_bits(st->regmap, AD4134_GPIO_DATA, BIT(offset), + (value << offset)); +out: mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + return ret; +} + +static int ad4134_get_gpio(struct gpio_chip *chip, unsigned int offset) +{ + struct ad4134_state *st = gpiochip_get_data(chip); + unsigned int val; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD4134_GPIO_DIR_CONTROL, &val); + if (ret < 0) + goto out; + + ret = regmap_read(st->regmap, AD4134_GPIO_DATA, &val); + if (ret < 0) + goto out; + + ret = !!(val & BIT(offset)); + +out: + mutex_unlock(&st->lock); return ret; } +static void ad4134_set_gpio(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct ad4134_state *st = gpiochip_get_data(chip); + unsigned int val; + int ret; + + mutex_lock(&st->lock); + ret = regmap_read(st->regmap, AD4134_GPIO_DIR_CONTROL, &val); + if (ret < 0) + goto out; + + if (val & BIT(offset)) + regmap_update_bits(st->regmap, AD4134_GPIO_DATA, BIT(offset), + (value << offset)); + +out: + mutex_unlock(&st->lock); +} + +static int ad4134_gpio_setup(struct ad4134_state *st) +{ + st->gpiochip.label = "ad4134"; + st->gpiochip.base = -1; + st->gpiochip.ngpio = 8; + st->gpiochip.parent = &st->spi->dev; + st->gpiochip.can_sleep = true; + st->gpiochip.direction_input = ad4134_input_gpio; + st->gpiochip.direction_output = ad4134_output_gpio; + st->gpiochip.get = ad4134_get_gpio; + st->gpiochip.set = ad4134_set_gpio; + + return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, st); +} + static int ad4134_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -141,7 +843,7 @@ static int ad4134_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&st->lock); - *val = st->odr; + *val = st->odr_hz; mutex_unlock(&st->lock); return IIO_VAL_INT; @@ -170,9 +872,22 @@ static int ad4134_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { + struct ad4134_state *st = iio_priv(indio_dev); + int ret; + switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - return ad4134_set_odr(indio_dev, val); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + ret = ad4134_update_conversion_rate(st, val); + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; default: return -EINVAL; } @@ -193,66 +908,26 @@ static const struct iio_info ad4134_info = { .read_raw = ad4134_read_raw, .read_avail = ad4134_read_avail, .write_raw = ad4134_write_raw, + .attrs = &ad4134_offload_attribute_group, .debugfs_reg_access = ad4134_reg_access, }; -#define AD4134_CHANNEL(index) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = (index), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_type_available = \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .scan_index = (index), \ - .scan_type = { \ - .sign = 's', \ - .realbits = AD4134_REAL_BITS, \ - .storagebits = 32, \ - .shift = AD4134_WORD_BITS - AD4134_REAL_BITS \ - }, \ -} - -static const struct iio_chan_spec ad4134_channels[] = { - AD4134_CHANNEL(0), - AD4134_CHANNEL(1), - AD4134_CHANNEL(2), - AD4134_CHANNEL(3), -}; - -static const unsigned long ad4134_channel_masks[] = { - GENMASK(AD4134_NUM_CHANNELS - 1, 0), - 0, -}; - -static int ad4134_buffer_postenable(struct iio_dev *indio_dev) +static int ad4134_get_ADC_count(struct ad4134_state *st) { - struct ad4134_state *st = iio_priv(indio_dev); - int ret; - - ret = legacy_spi_engine_offload_load_msg(st->spi_engine, &st->buf_read_msg); - if (ret) - return ret; - - legacy_spi_engine_offload_enable(st->spi_engine, true); - - return 0; -} - -static int ad4134_buffer_predisable(struct iio_dev *indio_dev) -{ - struct ad4134_state *st = iio_priv(indio_dev); - - legacy_spi_engine_offload_enable(st->spi_engine, false); - - return 0; + struct device *controller_dev = &st->spi->controller->dev; + unsigned int adc_count = 0; + + device_for_each_child_node_scoped(controller_dev, child) { + if (fwnode_property_match_string(child, "compatible", + "adi,ad4134") >= 0) + adc_count++; + if (fwnode_property_match_string(child, "compatible", + "adi,ad7134") >= 0) + adc_count++; + } + return adc_count; } -static const struct iio_buffer_setup_ops ad4134_buffer_ops = { - .postenable = ad4134_buffer_postenable, - .predisable = ad4134_buffer_predisable, -}; - static void ad4134_disable_regulators(void *data) { struct ad4134_state *st = data; @@ -260,10 +935,6 @@ static void ad4134_disable_regulators(void *data) regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); } -static void ad4134_disable_pwm(void *data) -{ - pwm_disable(data); -} static int ad4134_setup(struct ad4134_state *st) { @@ -272,13 +943,14 @@ static int ad4134_setup(struct ad4134_state *st) struct clk *clk; int ret; - clk = devm_clk_get_enabled(dev, "sys_clk"); + clk = devm_clk_get_enabled(dev, "cnv_ext_clk"); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "Failed to find SYS clock\n"); st->sys_clk_rate = clk_get_rate(clk); if (!st->sys_clk_rate) return dev_err_probe(dev, -EINVAL, "Failed to get SYS clock rate\n"); + st->sys_clk_hz = st->sys_clk_rate; ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators), st->regulators); @@ -305,32 +977,43 @@ static int ad4134_setup(struct ad4134_state *st) return dev_err_probe(dev, PTR_ERR(reset_gpio), "Failed to find reset GPIO\n"); + st->cs_gpio = devm_gpiod_get_optional(dev, "cs", GPIOD_OUT_LOW); + if (IS_ERR(st->cs_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cs_gpio), + "Failed to find cs-gpio\n"); + fsleep(AD4134_RESET_TIME_US); gpiod_set_value_cansleep(reset_gpio, 0); - st->odr_pwm = devm_pwm_get(dev, "odr_pwm"); - if (IS_ERR(st->odr_pwm)) - return dev_err_probe(dev, PTR_ERR(st->odr_pwm), - "Failed to find ODR PWM\n"); - - ret = _ad4134_set_odr(st, AD4134_ODR_DEFAULT); - if (ret) - return dev_err_probe(dev, ret, "Failed to initialize ODR\n"); - - ret = pwm_enable(st->odr_pwm); - if (ret) - return ret; + if (device_property_present(&st->spi->dev, "pwms")) { + ret = ad4134_pwm_get(st); + if (ret) + return dev_err_probe(dev, ret, "failed to get PWM: %d\n", ret); + + dev_info(dev, "PWM obtained successfully\n"); + + /* + * Start with a slower sampling rate so there is some room for + * adjusting the sampling frequency without hitting the maximum + * conversion rate. + */ + st->odr_hz = AD4134_MAX_ODR_FREQ_HZ >> 4; + dev_info(dev, "Setting initial ODR frequency to %u Hz\n", st->odr_hz); + //ret = ad4134_update_conversion_rate(st, st->odr_hz); + ret = ad4134_setup_odr(st, st->odr_hz); + if (ret) + return dev_err_probe(dev, ret, "failed to set odr freq\n"); + + dev_info(&st->spi->dev, "wait\n"); + fsleep(MEGA); /* 10^6 microsecs == 1s settling time so PPL lock */ + } - ret = devm_add_action_or_reset(dev, ad4134_disable_pwm, st->odr_pwm); - if (ret) - return dev_err_probe(dev, ret, - "Failed to add ODR PWM disable action\n"); ret = regmap_update_bits(st->regmap, AD4134_DATA_PACKET_CONFIG_REG, AD4134_DATA_PACKET_CONFIG_FRAME_MASK, FIELD_PREP(AD4134_DATA_PACKET_CONFIG_FRAME_MASK, - AD4134_DATA_FRAME_24BIT_CRC)); + st->output_frame)); if (ret) return ret; @@ -341,10 +1024,22 @@ static int ad4134_setup(struct ad4134_state *st) if (ret) return ret; - return regmap_update_bits(st->regmap, AD4134_DEVICE_CONFIG_REG, + ret = regmap_update_bits(st->regmap, AD4134_DEVICE_CONFIG_REG, AD4134_DEVICE_CONFIG_POWER_MODE_MASK, FIELD_PREP(AD4134_DEVICE_CONFIG_POWER_MODE_MASK, AD4134_POWER_MODE_HIGH_PERF)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4134_CHAN_DIG_FILTER_SEL_REG, + AD4134_CHAN_DIG_FILTER_SEL_MASK, + FIELD_PREP(AD4134_CHAN_DIG_FILTER_SEL_MASK, + AD4134_SINC6_FILTER)); + + if (ret) + return ret; + + return 0; } static const struct regmap_config ad4134_regmap_config = { @@ -376,6 +1071,14 @@ static int ad4134_bind(struct device *dev) if (ret) return ret; + ret = ad4134_offload_buffer_setup(indio_dev, st->spi); + if (ret) + return ret; + + ret = ad4134_update_conversion_rate(st, st->odr_hz); + if (ret) + return dev_err_probe(dev, ret, "failed to set sampling freq\n"); + return iio_device_register(indio_dev); } @@ -400,6 +1103,7 @@ static int ad4134_probe(struct spi_device *spi) struct fwnode_handle *fwnode = dev_fwnode(dev); struct iio_dev *indio_dev; struct ad4134_state *st; + bool ad4134_duo; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -418,36 +1122,98 @@ static int ad4134_probe(struct spi_device *spi) st->regulators[AD4134_IOVDD_REGULATOR].supply = "iovdd"; st->regulators[AD4134_REFIN_REGULATOR].supply = "refin"; - /* - * Receive buffer needs to be non-zero for the SPI engine master - * to mark the transfer as a read. - */ - st->buf_read_xfer.rx_buf = (void *)-1; - st->buf_read_xfer.len = 1; - st->buf_read_xfer.bits_per_word = AD4134_WORD_BITS; - spi_message_init_with_transfers(&st->buf_read_msg, - &st->buf_read_xfer, 1); + ad4134_duo = ad4134_get_ADC_count(st) == 2; + st->ad4134_duo = ad4134_duo; + + st->output_frame = AD4134_DATA_PACKET_24BIT_FRAME; + ret = device_property_match_property_string(dev, "adi,adc-frame", + ad4134_frame_config, + ARRAY_SIZE(ad4134_frame_config)); + if (ret < 0) + dev_warn(dev, "Failed to get adi,adc-frame property: %d\n", ret); + else + st->output_frame = ret; + + switch (st->output_frame) { + case AD4134_DATA_PACKET_16BIT_FRAME: + if (ad4134_duo) { + indio_dev->channels = ad4134_16_duo_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_16_duo_chan_set); + indio_dev->available_scan_masks = ad4134_duo_channel_masks; + } else { + indio_dev->channels = ad4134_16_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_16_chan_set); + indio_dev->available_scan_masks = ad4134_channel_masks; + } + break; + case AD4134_DATA_PACKET_16BIT_CRC6_FRAME: + if (ad4134_duo) { + indio_dev->channels = ad4134_16CRC_duo_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_16CRC_duo_chan_set); + indio_dev->available_scan_masks = ad4134_duo_channel_masks; + } else { + indio_dev->channels = ad4134_16CRC_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_16CRC_chan_set); + indio_dev->available_scan_masks = ad4134_channel_masks; + } + break; + case AD4134_DATA_PACKET_24BIT_FRAME: + if (ad4134_duo) { + indio_dev->channels = ad4134_24_duo_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_24_duo_chan_set); + indio_dev->available_scan_masks = ad4134_duo_channel_masks; + } else { + indio_dev->channels = ad4134_24_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_24_chan_set); + indio_dev->available_scan_masks = ad4134_channel_masks; + } + break; + case AD4134_DATA_PACKET_24BIT_CRC6_FRAME: + if (ad4134_duo) { + indio_dev->channels = ad4134_24CRC_duo_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_24CRC_duo_chan_set); + indio_dev->available_scan_masks = ad4134_duo_channel_masks; + } else { + indio_dev->channels = ad4134_24CRC_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_24CRC_chan_set); + indio_dev->available_scan_masks = ad4134_channel_masks; + } + break; + default: + return dev_err_probe(dev, -EINVAL, + "Failed to config ADC frame\n"); + } st->regmap = devm_regmap_init_spi(spi, &ad4134_regmap_config); if (IS_ERR(st->regmap)) return PTR_ERR(st->regmap); - indio_dev->channels = ad4134_channels; - indio_dev->num_channels = ARRAY_SIZE(ad4134_channels); - indio_dev->available_scan_masks = ad4134_channel_masks; - indio_dev->name = AD4134_NAME; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_HARDWARE; - indio_dev->setup_ops = &ad4134_buffer_ops; - indio_dev->info = &ad4134_info; + /* The HDL is hardconded/configured to read from all 4 DOUT lines. */ + st->num_dout_lines = 4; ret = ad4134_setup(st); if (ret) return ret; - ret = devm_iio_dmaengine_buffer_setup(dev, indio_dev, "rx"); - if (ret) - return dev_err_probe(dev, ret, - "Failed to allocate IIO DMA buffer\n"); + if (device_property_present(&st->spi->dev, "gpio-controller")) { + ret = ad4134_gpio_setup(st); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "Failed to setup GPIOs\n"); + } + + indio_dev->name = spi->dev.of_node->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad4134_info; + + if (!device_property_present(&st->spi->dev, "adi,spi-engine")) { + indio_dev->channels = 0; + indio_dev->num_channels = 0; + indio_dev->available_scan_masks = 0; + return devm_iio_device_register(dev, indio_dev); + } + + indio_dev->setup_ops = &ad4134_offload_buffer_setup_ops; st->spi_engine_fwnode = fwnode_find_reference(fwnode, "adi,spi-engine", 0); if (IS_ERR(st->spi_engine_fwnode)) @@ -564,6 +1330,7 @@ static void __exit ad4134_exit(void) module_exit(ad4134_exit); MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_AUTHOR("Marcelo Schmitt "); MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);