* [PATCH v7 1/8] dt-bindings: iio: dac: ad3552r: add iio backend support
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant Angelo Dureghello
` (6 subsequent siblings)
7 siblings, 0 replies; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello, Conor Dooley
From: Angelo Dureghello <adureghello@baylibre.com>
There is a version of AXI DAC IP block (for FPGAs) that provides
a physical QSPI bus for AD3552R and similar chips, so supporting
spi-controller functionalities.
For this case, the binding is modified to include some additional
properties.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml
index 41fe00034742..2d2561a52683 100644
--- a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml
@@ -60,6 +60,12 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
+ io-backends:
+ description: The iio backend reference.
+ Device can be optionally connected to the "axi-ad3552r IP" fpga-based
+ QSPI + DDR (Double Data Rate) controller to reach high speed transfers.
+ maxItems: 1
+
'#address-cells':
const: 1
@@ -128,6 +134,7 @@ patternProperties:
- custom-output-range-config
allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
properties:
compatible:
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 1/8] dt-bindings: iio: dac: ad3552r: add iio backend support Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-22 17:22 ` Conor Dooley
2024-10-21 12:40 ` [PATCH v7 3/8] iio: backend: extend features Angelo Dureghello
` (5 subsequent siblings)
7 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Add a new compatible and related bindigns for the fpga-based
"ad3552r" AXI IP core, a variant of the generic AXI DAC IP.
The AXI "ad3552r" IP is a very similar HDL (fpga) variant of the
generic AXI "DAC" IP, intended to control ad3552r and similar chips,
mainly to reach high speed transfer rates using a QSPI DDR
(dobule-data-rate) interface.
The ad3552r device is defined as a child of the AXI DAC, that in
this case is acting as an SPI controller.
Note, #io-backend is present because it is possible (in theory anyway)
to use a separate controller for the control path than that used
for the datapath.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
.../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 69 +++++++++++++++++++++-
1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
index a55e9bfc66d7..0aabb210f26d 100644
--- a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
@@ -19,11 +19,13 @@ description: |
memory via DMA into the DAC.
https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ https://analogdevicesinc.github.io/hdl/library/axi_ad3552r/index.html
properties:
compatible:
enum:
- adi,axi-dac-9.1.b
+ - adi,axi-ad3552r
reg:
maxItems: 1
@@ -36,7 +38,12 @@ properties:
- const: tx
clocks:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
+
+ clock-names:
+ minItems: 1
+ maxItems: 2
'#io-backend-cells':
const: 0
@@ -47,7 +54,31 @@ required:
- reg
- clocks
-additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,axi-ad3552r
+ then:
+ $ref: /schemas/spi/spi-controller.yaml#
+ properties:
+ clocks:
+ minItems: 2
+ maxItems: 2
+ clock-names:
+ items:
+ - const: s_axi_aclk
+ - const: dac_clk
+ else:
+ properties:
+ clocks:
+ maxItems: 1
+ clock-names:
+ items:
+ - const: s_axi_aclk
+
+unevaluatedProperties: false
examples:
- |
@@ -57,6 +88,38 @@ examples:
dmas = <&tx_dma 0>;
dma-names = "tx";
#io-backend-cells = <0>;
- clocks = <&axi_clk>;
+ clocks = <&clkc 15>;
+ clock-names = "s_axi_aclk";
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ axi_dac: spi@44a70000 {
+ compatible = "adi,axi-ad3552r";
+ reg = <0x44a70000 0x1000>;
+ dmas = <&dac_tx_dma 0>;
+ dma-names = "tx";
+ #io-backend-cells = <0>;
+ clocks = <&clkc 15>, <&ref_clk>;
+ clock-names = "s_axi_aclk", "dac_clk";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,ad3552r";
+ reg = <0>;
+ reset-gpios = <&gpio0 92 GPIO_ACTIVE_HIGH>;
+ io-backends = <&axi_dac>;
+ spi-max-frequency = <20000000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ adi,output-range-microvolt = <(-10000000) (10000000)>;
+ };
+ };
};
...
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant
2024-10-21 12:40 ` [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant Angelo Dureghello
@ 2024-10-22 17:22 ` Conor Dooley
2024-10-24 9:28 ` Angelo Dureghello
0 siblings, 1 reply; 29+ messages in thread
From: Conor Dooley @ 2024-10-22 17:22 UTC (permalink / raw)
To: Angelo Dureghello
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
[-- Attachment #1: Type: text/plain, Size: 4079 bytes --]
On Mon, Oct 21, 2024 at 02:40:12PM +0200, Angelo Dureghello wrote:
> From: Angelo Dureghello <adureghello@baylibre.com>
>
> Add a new compatible and related bindigns for the fpga-based
> "ad3552r" AXI IP core, a variant of the generic AXI DAC IP.
>
> The AXI "ad3552r" IP is a very similar HDL (fpga) variant of the
> generic AXI "DAC" IP, intended to control ad3552r and similar chips,
> mainly to reach high speed transfer rates using a QSPI DDR
> (dobule-data-rate) interface.
>
> The ad3552r device is defined as a child of the AXI DAC, that in
> this case is acting as an SPI controller.
>
> Note, #io-backend is present because it is possible (in theory anyway)
> to use a separate controller for the control path than that used
> for the datapath.
>
> Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> ---
> .../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 69 +++++++++++++++++++++-
> 1 file changed, 66 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> index a55e9bfc66d7..0aabb210f26d 100644
> --- a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> @@ -19,11 +19,13 @@ description: |
> memory via DMA into the DAC.
>
> https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
> + https://analogdevicesinc.github.io/hdl/library/axi_ad3552r/index.html
>
> properties:
> compatible:
> enum:
> - adi,axi-dac-9.1.b
> + - adi,axi-ad3552r
>
> reg:
> maxItems: 1
> @@ -36,7 +38,12 @@ properties:
> - const: tx
>
> clocks:
> - maxItems: 1
> + minItems: 1
> + maxItems: 2
> +
> + clock-names:
> + minItems: 1
> + maxItems: 2
>
> '#io-backend-cells':
> const: 0
> @@ -47,7 +54,31 @@ required:
> - reg
> - clocks
>
> -additionalProperties: false
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: adi,axi-ad3552r
> + then:
> + $ref: /schemas/spi/spi-controller.yaml#
> + properties:
> + clocks:
> + minItems: 2
> + maxItems: 2
Is this maxItems required? It matches the outer maximum.
> + clock-names:
> + items:
> + - const: s_axi_aclk
> + - const: dac_clk
The names are the same in both cases, you can move the definitions
outside of the if/then/else stuff and only constrain it here.
> + else:
> + properties:
> + clocks:
> + maxItems: 1
> + clock-names:
> + items:
> + - const: s_axi_aclk
> +
> +unevaluatedProperties: false
>
> examples:
> - |
> @@ -57,6 +88,38 @@ examples:
> dmas = <&tx_dma 0>;
> dma-names = "tx";
> #io-backend-cells = <0>;
> - clocks = <&axi_clk>;
> + clocks = <&clkc 15>;
> + clock-names = "s_axi_aclk";
> + };
> +
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + axi_dac: spi@44a70000 {
> + compatible = "adi,axi-ad3552r";
> + reg = <0x44a70000 0x1000>;
> + dmas = <&dac_tx_dma 0>;
> + dma-names = "tx";
> + #io-backend-cells = <0>;
> + clocks = <&clkc 15>, <&ref_clk>;
> + clock-names = "s_axi_aclk", "dac_clk";
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dac@0 {
> + compatible = "adi,ad3552r";
> + reg = <0>;
> + reset-gpios = <&gpio0 92 GPIO_ACTIVE_HIGH>;
> + io-backends = <&axi_dac>;
> + spi-max-frequency = <20000000>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + channel@0 {
> + reg = <0>;
> + adi,output-range-microvolt = <(-10000000) (10000000)>;
> + };
> + };
> };
> ...
>
> --
> 2.45.0.rc1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant
2024-10-22 17:22 ` Conor Dooley
@ 2024-10-24 9:28 ` Angelo Dureghello
2024-10-24 12:37 ` Nuno Sá
0 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-24 9:28 UTC (permalink / raw)
To: Conor Dooley
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
Hi Conor,
On 22.10.2024 18:22, Conor Dooley wrote:
> On Mon, Oct 21, 2024 at 02:40:12PM +0200, Angelo Dureghello wrote:
> > From: Angelo Dureghello <adureghello@baylibre.com>
> >
> > Add a new compatible and related bindigns for the fpga-based
> > "ad3552r" AXI IP core, a variant of the generic AXI DAC IP.
> >
> > The AXI "ad3552r" IP is a very similar HDL (fpga) variant of the
> > generic AXI "DAC" IP, intended to control ad3552r and similar chips,
> > mainly to reach high speed transfer rates using a QSPI DDR
> > (dobule-data-rate) interface.
> >
> > The ad3552r device is defined as a child of the AXI DAC, that in
> > this case is acting as an SPI controller.
> >
> > Note, #io-backend is present because it is possible (in theory anyway)
> > to use a separate controller for the control path than that used
> > for the datapath.
> >
> > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > ---
> > .../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 69 +++++++++++++++++++++-
> > 1 file changed, 66 insertions(+), 3 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > index a55e9bfc66d7..0aabb210f26d 100644
> > --- a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > @@ -19,11 +19,13 @@ description: |
> > memory via DMA into the DAC.
> >
> > https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
> > + https://analogdevicesinc.github.io/hdl/library/axi_ad3552r/index.html
> >
> > properties:
> > compatible:
> > enum:
> > - adi,axi-dac-9.1.b
> > + - adi,axi-ad3552r
> >
> > reg:
> > maxItems: 1
> > @@ -36,7 +38,12 @@ properties:
> > - const: tx
> >
> > clocks:
> > - maxItems: 1
> > + minItems: 1
> > + maxItems: 2
> > +
> > + clock-names:
> > + minItems: 1
> > + maxItems: 2
> >
> > '#io-backend-cells':
> > const: 0
> > @@ -47,7 +54,31 @@ required:
> > - reg
> > - clocks
> >
> > -additionalProperties: false
> > +allOf:
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + const: adi,axi-ad3552r
> > + then:
> > + $ref: /schemas/spi/spi-controller.yaml#
> > + properties:
> > + clocks:
> > + minItems: 2
> > + maxItems: 2
>
> Is this maxItems required? It matches the outer maximum.
>
> > + clock-names:
> > + items:
> > + - const: s_axi_aclk
> > + - const: dac_clk
>
> The names are the same in both cases, you can move the definitions
> outside of the if/then/else stuff and only constrain it here.
>
thanks, could you maybe have a look if it's ok now ?
(maxItems not needed for a const list)
clocks:
minItems: 1
maxItems: 2
clock-names:
items:
- const: s_axi_aclk
- const: dac_clk
minItems: 1
'#io-backend-cells':
const: 0
required:
- compatible
- dmas
- reg
- clocks
allOf:
- if:
properties:
compatible:
contains:
const: adi,axi-ad3552r
then:
$ref: /schemas/spi/spi-controller.yaml#
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
else:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
unevaluatedProperties: false
...
> > + else:
> > + properties:
> > + clocks:
> > + maxItems: 1
> > + clock-names:
> > + items:
> > + - const: s_axi_aclk
> > +
> > +unevaluatedProperties: false
> >
> > examples:
> > - |
> > @@ -57,6 +88,38 @@ examples:
> > dmas = <&tx_dma 0>;
> > dma-names = "tx";
> > #io-backend-cells = <0>;
> > - clocks = <&axi_clk>;
> > + clocks = <&clkc 15>;
> > + clock-names = "s_axi_aclk";
> > + };
> > +
> > + - |
> > + #include <dt-bindings/gpio/gpio.h>
> > + axi_dac: spi@44a70000 {
> > + compatible = "adi,axi-ad3552r";
> > + reg = <0x44a70000 0x1000>;
> > + dmas = <&dac_tx_dma 0>;
> > + dma-names = "tx";
> > + #io-backend-cells = <0>;
> > + clocks = <&clkc 15>, <&ref_clk>;
> > + clock-names = "s_axi_aclk", "dac_clk";
> > +
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + dac@0 {
> > + compatible = "adi,ad3552r";
> > + reg = <0>;
> > + reset-gpios = <&gpio0 92 GPIO_ACTIVE_HIGH>;
> > + io-backends = <&axi_dac>;
> > + spi-max-frequency = <20000000>;
> > +
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + channel@0 {
> > + reg = <0>;
> > + adi,output-range-microvolt = <(-10000000) (10000000)>;
> > + };
> > + };
> > };
> > ...
> >
> > --
> > 2.45.0.rc1
> >
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant
2024-10-24 9:28 ` Angelo Dureghello
@ 2024-10-24 12:37 ` Nuno Sá
2024-10-24 14:43 ` David Lechner
0 siblings, 1 reply; 29+ messages in thread
From: Nuno Sá @ 2024-10-24 12:37 UTC (permalink / raw)
To: Angelo Dureghello, Conor Dooley
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
On Thu, 2024-10-24 at 11:28 +0200, Angelo Dureghello wrote:
> Hi Conor,
>
> On 22.10.2024 18:22, Conor Dooley wrote:
> > On Mon, Oct 21, 2024 at 02:40:12PM +0200, Angelo Dureghello wrote:
> > > From: Angelo Dureghello <adureghello@baylibre.com>
> > >
> > > Add a new compatible and related bindigns for the fpga-based
> > > "ad3552r" AXI IP core, a variant of the generic AXI DAC IP.
> > >
> > > The AXI "ad3552r" IP is a very similar HDL (fpga) variant of the
> > > generic AXI "DAC" IP, intended to control ad3552r and similar chips,
> > > mainly to reach high speed transfer rates using a QSPI DDR
> > > (dobule-data-rate) interface.
> > >
> > > The ad3552r device is defined as a child of the AXI DAC, that in
> > > this case is acting as an SPI controller.
> > >
> > > Note, #io-backend is present because it is possible (in theory anyway)
> > > to use a separate controller for the control path than that used
> > > for the datapath.
> > >
> > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > ---
> > > .../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 69 +++++++++++++++++++++-
> > > 1 file changed, 66 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > > b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > > index a55e9bfc66d7..0aabb210f26d 100644
> > > --- a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > > +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > > @@ -19,11 +19,13 @@ description: |
> > > memory via DMA into the DAC.
> > >
> > > https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
> > > + https://analogdevicesinc.github.io/hdl/library/axi_ad3552r/index.html
> > >
> > > properties:
> > > compatible:
> > > enum:
> > > - adi,axi-dac-9.1.b
> > > + - adi,axi-ad3552r
> > >
> > > reg:
> > > maxItems: 1
> > > @@ -36,7 +38,12 @@ properties:
> > > - const: tx
> > >
> > > clocks:
> > > - maxItems: 1
> > > + minItems: 1
> > > + maxItems: 2
> > > +
> > > + clock-names:
> > > + minItems: 1
> > > + maxItems: 2
> > >
> > > '#io-backend-cells':
> > > const: 0
> > > @@ -47,7 +54,31 @@ required:
> > > - reg
> > > - clocks
> > >
> > > -additionalProperties: false
> > > +allOf:
> > > + - if:
> > > + properties:
> > > + compatible:
> > > + contains:
> > > + const: adi,axi-ad3552r
> > > + then:
> > > + $ref: /schemas/spi/spi-controller.yaml#
> > > + properties:
> > > + clocks:
> > > + minItems: 2
> > > + maxItems: 2
> >
> > Is this maxItems required? It matches the outer maximum.
> >
> > > + clock-names:
> > > + items:
> > > + - const: s_axi_aclk
> > > + - const: dac_clk
> >
> > The names are the same in both cases, you can move the definitions
> > outside of the if/then/else stuff and only constrain it here.
> >
> thanks, could you maybe have a look if it's ok now ?
> (maxItems not needed for a const list)
>
> clocks:
> minItems: 1
> maxItems: 2
>
> clock-names:
> items:
> - const: s_axi_aclk
> - const: dac_clk
> minItems: 1
>
> '#io-backend-cells':
> const: 0
>
> required:
> - compatible
> - dmas
> - reg
> - clocks
>
> allOf:
> - if:
> properties:
> compatible:
> contains:
> const: adi,axi-ad3552r
> then:
> $ref: /schemas/spi/spi-controller.yaml#
> properties:
> clocks:
> minItems: 2
> clock-names:
> minItems: 2
> else:
> properties:
> clocks:
> maxItems: 1
> clock-names:
> maxItems: 1
I guess in this case it could even be clock-names: false. One does not make much
sense.
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant
2024-10-24 12:37 ` Nuno Sá
@ 2024-10-24 14:43 ` David Lechner
2024-10-24 16:45 ` Conor Dooley
0 siblings, 1 reply; 29+ messages in thread
From: David Lechner @ 2024-10-24 14:43 UTC (permalink / raw)
To: Nuno Sá, Angelo Dureghello, Conor Dooley
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, Mark Brown
On 10/24/24 7:37 AM, Nuno Sá wrote:
> On Thu, 2024-10-24 at 11:28 +0200, Angelo Dureghello wrote:
>> Hi Conor,
>>
>> On 22.10.2024 18:22, Conor Dooley wrote:
>>> On Mon, Oct 21, 2024 at 02:40:12PM +0200, Angelo Dureghello wrote:
>>>> From: Angelo Dureghello <adureghello@baylibre.com>
>>>>
>>>> Add a new compatible and related bindigns for the fpga-based
>>>> "ad3552r" AXI IP core, a variant of the generic AXI DAC IP.
>>>>
>>>> The AXI "ad3552r" IP is a very similar HDL (fpga) variant of the
>>>> generic AXI "DAC" IP, intended to control ad3552r and similar chips,
>>>> mainly to reach high speed transfer rates using a QSPI DDR
>>>> (dobule-data-rate) interface.
>>>>
>>>> The ad3552r device is defined as a child of the AXI DAC, that in
>>>> this case is acting as an SPI controller.
>>>>
>>>> Note, #io-backend is present because it is possible (in theory anyway)
>>>> to use a separate controller for the control path than that used
>>>> for the datapath.
>>>>
>>>> Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
>>>> ---
>>>> .../devicetree/bindings/iio/dac/adi,axi-dac.yaml | 69 +++++++++++++++++++++-
>>>> 1 file changed, 66 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
>>>> b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
>>>> index a55e9bfc66d7..0aabb210f26d 100644
>>>> --- a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
>>>> +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
>>>> @@ -19,11 +19,13 @@ description: |
>>>> memory via DMA into the DAC.
>>>>
>>>> https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
>>>> + https://analogdevicesinc.github.io/hdl/library/axi_ad3552r/index.html
>>>>
>>>> properties:
>>>> compatible:
>>>> enum:
>>>> - adi,axi-dac-9.1.b
>>>> + - adi,axi-ad3552r
>>>>
>>>> reg:
>>>> maxItems: 1
>>>> @@ -36,7 +38,12 @@ properties:
>>>> - const: tx
>>>>
>>>> clocks:
>>>> - maxItems: 1
>>>> + minItems: 1
>>>> + maxItems: 2
>>>> +
>>>> + clock-names:
>>>> + minItems: 1
>>>> + maxItems: 2
>>>>
>>>> '#io-backend-cells':
>>>> const: 0
>>>> @@ -47,7 +54,31 @@ required:
>>>> - reg
>>>> - clocks
>>>>
>>>> -additionalProperties: false
>>>> +allOf:
>>>> + - if:
>>>> + properties:
>>>> + compatible:
>>>> + contains:
>>>> + const: adi,axi-ad3552r
>>>> + then:
>>>> + $ref: /schemas/spi/spi-controller.yaml#
>>>> + properties:
>>>> + clocks:
>>>> + minItems: 2
>>>> + maxItems: 2
>>>
>>> Is this maxItems required? It matches the outer maximum.
>>>
>>>> + clock-names:
>>>> + items:
>>>> + - const: s_axi_aclk
>>>> + - const: dac_clk
>>>
>>> The names are the same in both cases, you can move the definitions
>>> outside of the if/then/else stuff and only constrain it here.
>>>
>> thanks, could you maybe have a look if it's ok now ?
>> (maxItems not needed for a const list)
>>
>> clocks:
>> minItems: 1
>> maxItems: 2
>>
>> clock-names:
>> items:
>> - const: s_axi_aclk
>> - const: dac_clk
>> minItems: 1
>>
>> '#io-backend-cells':
>> const: 0
>>
>> required:
>> - compatible
>> - dmas
>> - reg
>> - clocks
>>
>> allOf:
>> - if:
>> properties:
>> compatible:
>> contains:
>> const: adi,axi-ad3552r
>> then:
>> $ref: /schemas/spi/spi-controller.yaml#
>> properties:
>> clocks:
>> minItems: 2
>> clock-names:
>> minItems: 2
For this one, I think we also need:
required:
- clock-names
>> else:
>> properties:
>> clocks:
>> maxItems: 1
>> clock-names:
>> maxItems: 1
>
> I guess in this case it could even be clock-names: false. One does not make much
> sense.
>
> - Nuno Sá
>
>
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant
2024-10-24 14:43 ` David Lechner
@ 2024-10-24 16:45 ` Conor Dooley
0 siblings, 0 replies; 29+ messages in thread
From: Conor Dooley @ 2024-10-24 16:45 UTC (permalink / raw)
To: David Lechner
Cc: Nuno Sá, Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel, Mark Brown
[-- Attachment #1: Type: text/plain, Size: 1365 bytes --]
On Thu, Oct 24, 2024 at 09:43:02AM -0500, David Lechner wrote:
> >>>
> >> thanks, could you maybe have a look if it's ok now ?
> >> (maxItems not needed for a const list)
> >>
> >> clocks:
> >> minItems: 1
> >> maxItems: 2
> >>
> >> clock-names:
> >> items:
> >> - const: s_axi_aclk
> >> - const: dac_clk
> >> minItems: 1
> >>
> >> '#io-backend-cells':
> >> const: 0
> >>
> >> required:
> >> - compatible
> >> - dmas
> >> - reg
> >> - clocks
> >>
> >> allOf:
> >> - if:
> >> properties:
> >> compatible:
> >> contains:
> >> const: adi,axi-ad3552r
> >> then:
> >> $ref: /schemas/spi/spi-controller.yaml#
> >> properties:
> >> clocks:
> >> minItems: 2
> >> clock-names:
> >> minItems: 2
>
>
> For this one, I think we also need:
>
> required:
> - clock-names
Ye, Angelo had that in the version posted in response to the driver
patch.
This looks ~correct.
>
> >> else:
> >> properties:
> >> clocks:
> >> maxItems: 1
> >> clock-names:
> >> maxItems: 1
> >
> > I guess in this case it could even be clock-names: false. One does not make much
> > sense.
And since it is not mandatory, doubly useless.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v7 3/8] iio: backend: extend features
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 1/8] dt-bindings: iio: dac: ad3552r: add iio backend support Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 2/8] dt-bindings: iio: dac: adi-axi-dac: add ad3552r axi variant Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 4/8] iio: dac: adi-axi-dac: " Angelo Dureghello
` (4 subsequent siblings)
7 siblings, 0 replies; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Extend backend features with new calls needed later on this
patchset from axi version of ad3552r.
The follwoing calls are added:
iio_backend_ddr_enable()
enable ddr bus transfer
iio_backend_ddr_disable()
disable ddr bus transfer
iio_backend_data_stream_enable()
enable data stream over bus interface
iio_backend_data_stream_disable()
disable data stream over bus interface
iio_backend_data_transfer_addr()
define the target register address where the DAC sample
will be written.
Reviewed-by: Nuno Sa <nuno.sa@analog.com>
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
drivers/iio/industrialio-backend.c | 78 ++++++++++++++++++++++++++++++++++++++
include/linux/iio/backend.h | 17 +++++++++
2 files changed, 95 insertions(+)
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 20b3b5212da7..81f3d24f0c50 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -718,6 +718,84 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
return 0;
}
+/**
+ * iio_backend_ddr_enable - Enable interface DDR (Double Data Rate) mode
+ * @back: Backend device
+ *
+ * Enable DDR, data is generated by the IP at each front (raising and falling)
+ * of the bus clock signal.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_ddr_enable(struct iio_backend *back)
+{
+ return iio_backend_op_call(back, ddr_enable);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ddr_enable, IIO_BACKEND);
+
+/**
+ * iio_backend_ddr_disable - Disable interface DDR (Double Data Rate) mode
+ * @back: Backend device
+ *
+ * Disable DDR, setting into SDR mode (Single Data Rate).
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_ddr_disable(struct iio_backend *back)
+{
+ return iio_backend_op_call(back, ddr_disable);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ddr_disable, IIO_BACKEND);
+
+/**
+ * iio_backend_data_stream_enable - Enable data stream
+ * @back: Backend device
+ *
+ * Enable data stream over the bus interface.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_stream_enable(struct iio_backend *back)
+{
+ return iio_backend_op_call(back, data_stream_enable);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_stream_enable, IIO_BACKEND);
+
+/**
+ * iio_backend_data_stream_disable - Disable data stream
+ * @back: Backend device
+ *
+ * Disable data stream over the bus interface.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_stream_disable(struct iio_backend *back)
+{
+ return iio_backend_op_call(back, data_stream_disable);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_stream_disable, IIO_BACKEND);
+
+/**
+ * iio_backend_data_transfer_addr - Set data address.
+ * @back: Backend device
+ * @address: Data register address
+ *
+ * Some devices may need to inform the backend about an address
+ * where to read or write the data.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_transfer_addr(struct iio_backend *back, u32 address)
+{
+ return iio_backend_op_call(back, data_transfer_addr, address);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_transfer_addr, IIO_BACKEND);
+
static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name,
struct fwnode_handle *fwnode)
{
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index 37d56914d485..10be00f3b120 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -14,12 +14,14 @@ struct iio_dev;
enum iio_backend_data_type {
IIO_BACKEND_TWOS_COMPLEMENT,
IIO_BACKEND_OFFSET_BINARY,
+ IIO_BACKEND_DATA_UNSIGNED,
IIO_BACKEND_DATA_TYPE_MAX
};
enum iio_backend_data_source {
IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE,
IIO_BACKEND_EXTERNAL,
+ IIO_BACKEND_INTERNAL_RAMP_16BIT,
IIO_BACKEND_DATA_SOURCE_MAX
};
@@ -89,6 +91,11 @@ enum iio_backend_sample_trigger {
* @read_raw: Read a channel attribute from a backend device
* @debugfs_print_chan_status: Print channel status into a buffer.
* @debugfs_reg_access: Read or write register value of backend.
+ * @ddr_enable: Enable interface DDR (Double Data Rate) mode.
+ * @ddr_disable: Disable interface DDR (Double Data Rate) mode.
+ * @data_stream_enable: Enable data stream.
+ * @data_stream_disable: Disable data stream.
+ * @data_transfer_addr: Set data address.
**/
struct iio_backend_ops {
int (*enable)(struct iio_backend *back);
@@ -129,6 +136,11 @@ struct iio_backend_ops {
size_t len);
int (*debugfs_reg_access)(struct iio_backend *back, unsigned int reg,
unsigned int writeval, unsigned int *readval);
+ int (*ddr_enable)(struct iio_backend *back);
+ int (*ddr_disable)(struct iio_backend *back);
+ int (*data_stream_enable)(struct iio_backend *back);
+ int (*data_stream_disable)(struct iio_backend *back);
+ int (*data_transfer_addr)(struct iio_backend *back, u32 address);
};
/**
@@ -164,6 +176,11 @@ int iio_backend_data_sample_trigger(struct iio_backend *back,
int devm_iio_backend_request_buffer(struct device *dev,
struct iio_backend *back,
struct iio_dev *indio_dev);
+int iio_backend_ddr_enable(struct iio_backend *back);
+int iio_backend_ddr_disable(struct iio_backend *back);
+int iio_backend_data_stream_enable(struct iio_backend *back);
+int iio_backend_data_stream_disable(struct iio_backend *back);
+int iio_backend_data_transfer_addr(struct iio_backend *back, u32 address);
ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len);
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
` (2 preceding siblings ...)
2024-10-21 12:40 ` [PATCH v7 3/8] iio: backend: extend features Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-22 12:36 ` Nuno Sá
2024-10-21 12:40 ` [PATCH v7 5/8] iio: dac: ad3552r: changes to use FIELD_PREP Angelo Dureghello
` (3 subsequent siblings)
7 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Extend AXI-DAC backend with new features required to interface
to the ad3552r DAC. Mainly, a new compatible string is added to
support the ad3552r-axi DAC IP, very similar to the generic DAC
IP but with some customizations to work with the ad3552r.
Then, a series of generic functions has been added to match with
ad3552r needs. Function names has been kept generic as much as
possible, to allow re-utilization from other frontend drivers.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
drivers/iio/dac/adi-axi-dac.c | 269 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 255 insertions(+), 14 deletions(-)
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
index 04193a98616e..9d6809fe7a67 100644
--- a/drivers/iio/dac/adi-axi-dac.c
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -46,9 +46,28 @@
#define AXI_DAC_CNTRL_1_REG 0x0044
#define AXI_DAC_CNTRL_1_SYNC BIT(0)
#define AXI_DAC_CNTRL_2_REG 0x0048
+#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
+#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
#define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
+#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
+#define AXI_DAC_STATUS_1_REG 0x0054
+#define AXI_DAC_STATUS_2_REG 0x0058
#define AXI_DAC_DRP_STATUS_REG 0x0074
#define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
+#define AXI_DAC_CUSTOM_RD_REG 0x0080
+#define AXI_DAC_CUSTOM_WR_REG 0x0084
+#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
+#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
+#define AXI_DAC_UI_STATUS_REG 0x0088
+#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
+#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
+#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
+#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
+#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
+#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
+
+#define AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE (AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA | \
+ AXI_DAC_CUSTOM_CTRL_STREAM)
/* DAC Channel controls */
#define AXI_DAC_CHAN_CNTRL_1_REG(c) (0x0400 + (c) * 0x40)
@@ -63,12 +82,21 @@
#define AXI_DAC_CHAN_CNTRL_7_REG(c) (0x0418 + (c) * 0x40)
#define AXI_DAC_CHAN_CNTRL_7_DATA_SEL GENMASK(3, 0)
+#define AXI_DAC_RD_ADDR(x) (BIT(7) | (x))
+
/* 360 degrees in rad */
#define AXI_DAC_2_PI_MEGA 6283190
enum {
AXI_DAC_DATA_INTERNAL_TONE,
AXI_DAC_DATA_DMA = 2,
+ AXI_DAC_DATA_INTERNAL_RAMP_16BIT = 11,
+};
+
+struct axi_dac_info {
+ unsigned int version;
+ const struct iio_backend_info *backend_info;
+ bool has_dac_clk;
};
struct axi_dac_state {
@@ -79,9 +107,11 @@ struct axi_dac_state {
* data/variables.
*/
struct mutex lock;
+ const struct axi_dac_info *info;
u64 dac_clk;
u32 reg_config;
bool int_tone;
+ int dac_clk_rate;
};
static int axi_dac_enable(struct iio_backend *back)
@@ -471,6 +501,11 @@ static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
AXI_DAC_CHAN_CNTRL_7_REG(chan),
AXI_DAC_CHAN_CNTRL_7_DATA_SEL,
AXI_DAC_DATA_DMA);
+ case IIO_BACKEND_INTERNAL_RAMP_16BIT:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_CHAN_CNTRL_7_REG(chan),
+ AXI_DAC_CHAN_CNTRL_7_DATA_SEL,
+ AXI_DAC_DATA_INTERNAL_RAMP_16BIT);
default:
return -EINVAL;
}
@@ -528,6 +563,166 @@ static int axi_dac_reg_access(struct iio_backend *back, unsigned int reg,
return regmap_write(st->regmap, reg, writeval);
}
+static int axi_dac_ddr_enable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ return regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG,
+ AXI_DAC_CNTRL_2_SDR_DDR_N);
+}
+
+static int axi_dac_ddr_disable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ return regmap_set_bits(st->regmap, AXI_DAC_CNTRL_2_REG,
+ AXI_DAC_CNTRL_2_SDR_DDR_N);
+}
+
+static int axi_dac_data_stream_enable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ return regmap_set_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
+ AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE);
+}
+
+static int axi_dac_data_stream_disable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ return regmap_clear_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
+ AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE);
+}
+
+static int axi_dac_data_transfer_addr(struct iio_backend *back, u32 address)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ if (address > FIELD_MAX(AXI_DAC_CUSTOM_CTRL_ADDRESS))
+ return -EINVAL;
+
+ /*
+ * Sample register address, when the DAC is configured, or stream
+ * start address when the FSM is in stream state.
+ */
+ return regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
+ AXI_DAC_CUSTOM_CTRL_ADDRESS,
+ FIELD_PREP(AXI_DAC_CUSTOM_CTRL_ADDRESS,
+ address));
+}
+
+static int axi_dac_data_format_set(struct iio_backend *back, unsigned int ch,
+ const struct iio_backend_data_fmt *data)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (data->type) {
+ case IIO_BACKEND_DATA_UNSIGNED:
+ return regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG,
+ AXI_DAC_CNTRL_2_UNSIGNED_DATA);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_dac_read_raw(struct iio_backend *back,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_FREQUENCY:
+
+ if (!st->info->has_dac_clk)
+ return -EOPNOTSUPP;
+
+ /*
+ * Returning here always the maximum (buffering mode)
+ * clock rate.
+ */
+ *val = st->dac_clk_rate;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_dac_bus_reg_write(struct iio_backend *back, u32 reg, u32 val,
+ size_t data_size)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ int ret;
+ u32 ival;
+
+ /*
+ * Both AXI_DAC_CNTRL_2_REG and AXI_DAC_CUSTOM_WR_REG need to know
+ * the data size. So keeping data size control here only,
+ * since data size is mandatory for the current transfer.
+ * DDR state handled separately by specific backend calls,
+ * generally all raw register writes are SDR.
+ */
+ if (data_size == sizeof(u16))
+ ival = FIELD_PREP(AXI_DAC_CUSTOM_WR_DATA_16, val);
+ else
+ ival = FIELD_PREP(AXI_DAC_CUSTOM_WR_DATA_8, val);
+
+ ret = regmap_write(st->regmap, AXI_DAC_CUSTOM_WR_REG, ival);
+ if (ret)
+ return ret;
+
+ if (data_size == sizeof(u8))
+ ret = regmap_set_bits(st->regmap, AXI_DAC_CNTRL_2_REG,
+ AXI_DAC_CNTRL_2_SYMB_8B);
+ else
+ ret = regmap_clear_bits(st->regmap, AXI_DAC_CNTRL_2_REG,
+ AXI_DAC_CNTRL_2_SYMB_8B);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
+ AXI_DAC_CUSTOM_CTRL_ADDRESS,
+ FIELD_PREP(AXI_DAC_CUSTOM_CTRL_ADDRESS, reg));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
+ AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA,
+ AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(st->regmap,
+ AXI_DAC_UI_STATUS_REG, ival,
+ FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0,
+ 10, 100 * KILO);
+ if (ret == -ETIMEDOUT)
+ dev_err(st->dev, "AXI read timeout\n");
+
+ /* Cleaning always AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA */
+ return regmap_clear_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
+ AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA);
+}
+
+static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val,
+ size_t data_size)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ int ret;
+
+ /*
+ * SPI, we write with read flag, then we read just at the AXI
+ * io address space to get data read.
+ */
+ ret = axi_dac_bus_reg_write(back, AXI_DAC_RD_ADDR(reg), 0, data_size);
+ if (ret)
+ return ret;
+
+ return regmap_read(st->regmap, AXI_DAC_CUSTOM_RD_REG, val);
+}
+
static const struct iio_backend_ops axi_dac_generic_ops = {
.enable = axi_dac_enable,
.disable = axi_dac_disable,
@@ -541,11 +736,31 @@ static const struct iio_backend_ops axi_dac_generic_ops = {
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_dac_reg_access),
};
+static const struct iio_backend_ops axi_ad3552r_ops = {
+ .enable = axi_dac_enable,
+ .disable = axi_dac_disable,
+ .read_raw = axi_dac_read_raw,
+ .request_buffer = axi_dac_request_buffer,
+ .free_buffer = axi_dac_free_buffer,
+ .data_source_set = axi_dac_data_source_set,
+ .ddr_enable = axi_dac_ddr_enable,
+ .ddr_disable = axi_dac_ddr_disable,
+ .data_stream_enable = axi_dac_data_stream_enable,
+ .data_stream_disable = axi_dac_data_stream_disable,
+ .data_format_set = axi_dac_data_format_set,
+ .data_transfer_addr = axi_dac_data_transfer_addr,
+};
+
static const struct iio_backend_info axi_dac_generic = {
.name = "axi-dac",
.ops = &axi_dac_generic_ops,
};
+static const struct iio_backend_info axi_ad3552r = {
+ .name = "axi-ad3552r",
+ .ops = &axi_ad3552r_ops,
+};
+
static const struct regmap_config axi_dac_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
@@ -555,7 +770,6 @@ static const struct regmap_config axi_dac_regmap_config = {
static int axi_dac_probe(struct platform_device *pdev)
{
- const unsigned int *expected_ver;
struct axi_dac_state *st;
void __iomem *base;
unsigned int ver;
@@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device *pdev)
if (!st)
return -ENOMEM;
- expected_ver = device_get_match_data(&pdev->dev);
- if (!expected_ver)
+ st->info = device_get_match_data(&pdev->dev);
+ if (!st->info)
return -ENODEV;
+ clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
+ if (IS_ERR(clk)) {
+ /* Backward compat., old fdt versions without clock-names. */
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "failed to get clock\n");
+ }
+
+ if (st->info->has_dac_clk) {
+ struct clk *dac_clk;
- clk = devm_clk_get_enabled(&pdev->dev, NULL);
- if (IS_ERR(clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(clk),
- "failed to get clock\n");
+ dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
+ if (IS_ERR(dac_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
+ "failed to get dac_clk clock\n");
+
+ /* We only care about the streaming mode rate */
+ st->dac_clk_rate = clk_get_rate(dac_clk) / 2;
+ }
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -598,12 +827,13 @@ static int axi_dac_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
dev_err(&pdev->dev,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
- ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
- ADI_AXI_PCORE_VER_MINOR(*expected_ver),
- ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version),
+ ADI_AXI_PCORE_VER_MINOR(st->info->version),
+ ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
@@ -629,7 +859,8 @@ static int axi_dac_probe(struct platform_device *pdev)
return ret;
mutex_init(&st->lock);
- ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+
+ ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to register iio backend\n");
@@ -642,10 +873,20 @@ static int axi_dac_probe(struct platform_device *pdev)
return 0;
}
-static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+static const struct axi_dac_info dac_generic = {
+ .version = ADI_AXI_PCORE_VER(9, 1, 'b'),
+ .backend_info = &axi_dac_generic,
+};
+
+static const struct axi_dac_info dac_ad3552r = {
+ .version = ADI_AXI_PCORE_VER(9, 1, 'b'),
+ .backend_info = &axi_ad3552r,
+ .has_dac_clk = true,
+};
static const struct of_device_id axi_dac_of_match[] = {
- { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+ { .compatible = "adi,axi-dac-9.1.b", .data = &dac_generic },
+ { .compatible = "adi,axi-ad3552r", .data = &dac_ad3552r },
{}
};
MODULE_DEVICE_TABLE(of, axi_dac_of_match);
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-21 12:40 ` [PATCH v7 4/8] iio: dac: adi-axi-dac: " Angelo Dureghello
@ 2024-10-22 12:36 ` Nuno Sá
2024-10-22 17:21 ` Conor Dooley
0 siblings, 1 reply; 29+ messages in thread
From: Nuno Sá @ 2024-10-22 12:36 UTC (permalink / raw)
To: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown
On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> From: Angelo Dureghello <adureghello@baylibre.com>
>
> Extend AXI-DAC backend with new features required to interface
> to the ad3552r DAC. Mainly, a new compatible string is added to
> support the ad3552r-axi DAC IP, very similar to the generic DAC
> IP but with some customizations to work with the ad3552r.
>
> Then, a series of generic functions has been added to match with
> ad3552r needs. Function names has been kept generic as much as
> possible, to allow re-utilization from other frontend drivers.
>
> Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> ---
Looks mostly good,
one minor thing that (I think) could be improved
> drivers/iio/dac/adi-axi-dac.c | 269 +++++++++++++++++++++++++++++++++++++++--
> -
> 1 file changed, 255 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> index 04193a98616e..9d6809fe7a67 100644
> --- a/drivers/iio/dac/adi-axi-dac.c
> +++ b/drivers/iio/dac/adi-axi-dac.c
> @@ -46,9 +46,28 @@
> #define AXI_DAC_CNTRL_1_REG 0x0044
> #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> #define AXI_DAC_CNTRL_2_REG 0x0048
> +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> +#define AXI_DAC_STATUS_1_REG 0x0054
> +#define AXI_DAC_STATUS_2_REG 0x0058
> #define AXI_DAC_DRP_STATUS_REG 0x0074
> #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> +#define AXI_DAC_UI_STATUS_REG 0x0088
> +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
...
> static int axi_dac_probe(struct platform_device *pdev)
> {
> - const unsigned int *expected_ver;
> struct axi_dac_state *st;
> void __iomem *base;
> unsigned int ver;
> @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device *pdev)
> if (!st)
> return -ENOMEM;
>
> - expected_ver = device_get_match_data(&pdev->dev);
> - if (!expected_ver)
> + st->info = device_get_match_data(&pdev->dev);
> + if (!st->info)
> return -ENODEV;
> + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> + if (IS_ERR(clk)) {
If clock-names is not given, then we'll get -EINVAL. Hence we could assume that:
if (PTR_ERR(clk) != -EINVAL)
return dev_err_probe();
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-22 12:36 ` Nuno Sá
@ 2024-10-22 17:21 ` Conor Dooley
2024-10-23 14:56 ` Nuno Sá
0 siblings, 1 reply; 29+ messages in thread
From: Conor Dooley @ 2024-10-22 17:21 UTC (permalink / raw)
To: Nuno Sá
Cc: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel, dlechner, Mark Brown
[-- Attachment #1: Type: text/plain, Size: 3260 bytes --]
On Tue, Oct 22, 2024 at 02:36:44PM +0200, Nuno Sá wrote:
> On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > From: Angelo Dureghello <adureghello@baylibre.com>
> >
> > Extend AXI-DAC backend with new features required to interface
> > to the ad3552r DAC. Mainly, a new compatible string is added to
> > support the ad3552r-axi DAC IP, very similar to the generic DAC
> > IP but with some customizations to work with the ad3552r.
> >
> > Then, a series of generic functions has been added to match with
> > ad3552r needs. Function names has been kept generic as much as
> > possible, to allow re-utilization from other frontend drivers.
> >
> > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > ---
>
> Looks mostly good,
>
> one minor thing that (I think) could be improved
> > drivers/iio/dac/adi-axi-dac.c | 269 +++++++++++++++++++++++++++++++++++++++--
> > -
> > 1 file changed, 255 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > index 04193a98616e..9d6809fe7a67 100644
> > --- a/drivers/iio/dac/adi-axi-dac.c
> > +++ b/drivers/iio/dac/adi-axi-dac.c
> > @@ -46,9 +46,28 @@
> > #define AXI_DAC_CNTRL_1_REG 0x0044
> > #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> > #define AXI_DAC_CNTRL_2_REG 0x0048
> > +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> > +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> > #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> > +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> > +#define AXI_DAC_STATUS_1_REG 0x0054
> > +#define AXI_DAC_STATUS_2_REG 0x0058
> > #define AXI_DAC_DRP_STATUS_REG 0x0074
> > #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> > +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> > +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> > +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> > +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> > +#define AXI_DAC_UI_STATUS_REG 0x0088
> > +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> > +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> > +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> > +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> > +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> > +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
>
> ...
>
> > static int axi_dac_probe(struct platform_device *pdev)
> > {
> > - const unsigned int *expected_ver;
> > struct axi_dac_state *st;
> > void __iomem *base;
> > unsigned int ver;
> > @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device *pdev)
> > if (!st)
> > return -ENOMEM;
> >
> > - expected_ver = device_get_match_data(&pdev->dev);
> > - if (!expected_ver)
> > + st->info = device_get_match_data(&pdev->dev);
> > + if (!st->info)
> > return -ENODEV;
> > + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> > + if (IS_ERR(clk)) {
>
> If clock-names is not given, then we'll get -EINVAL. Hence we could assume that:
>
> if (PTR_ERR(clk) != -EINVAL)
> return dev_err_probe();
clock-names isn't a required property, but the driver code effectively
makes it one. Doesn't this lookup need to be by index, unless
clock-names is made required for this variant?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-22 17:21 ` Conor Dooley
@ 2024-10-23 14:56 ` Nuno Sá
2024-10-23 15:22 ` Conor Dooley
0 siblings, 1 reply; 29+ messages in thread
From: Nuno Sá @ 2024-10-23 14:56 UTC (permalink / raw)
To: Conor Dooley
Cc: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel, dlechner, Mark Brown
On Tue, 2024-10-22 at 18:21 +0100, Conor Dooley wrote:
> On Tue, Oct 22, 2024 at 02:36:44PM +0200, Nuno Sá wrote:
> > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > From: Angelo Dureghello <adureghello@baylibre.com>
> > >
> > > Extend AXI-DAC backend with new features required to interface
> > > to the ad3552r DAC. Mainly, a new compatible string is added to
> > > support the ad3552r-axi DAC IP, very similar to the generic DAC
> > > IP but with some customizations to work with the ad3552r.
> > >
> > > Then, a series of generic functions has been added to match with
> > > ad3552r needs. Function names has been kept generic as much as
> > > possible, to allow re-utilization from other frontend drivers.
> > >
> > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > ---
> >
> > Looks mostly good,
> >
> > one minor thing that (I think) could be improved
> > > drivers/iio/dac/adi-axi-dac.c | 269
> > > +++++++++++++++++++++++++++++++++++++++--
> > > -
> > > 1 file changed, 255 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > > index 04193a98616e..9d6809fe7a67 100644
> > > --- a/drivers/iio/dac/adi-axi-dac.c
> > > +++ b/drivers/iio/dac/adi-axi-dac.c
> > > @@ -46,9 +46,28 @@
> > > #define AXI_DAC_CNTRL_1_REG 0x0044
> > > #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> > > #define AXI_DAC_CNTRL_2_REG 0x0048
> > > +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> > > +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> > > #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> > > +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> > > +#define AXI_DAC_STATUS_1_REG 0x0054
> > > +#define AXI_DAC_STATUS_2_REG 0x0058
> > > #define AXI_DAC_DRP_STATUS_REG 0x0074
> > > #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> > > +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> > > +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> > > +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> > > +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> > > +#define AXI_DAC_UI_STATUS_REG 0x0088
> > > +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> > > +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> > > +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> > > +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> > > +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> > > +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
> >
> > ...
> >
> > > static int axi_dac_probe(struct platform_device *pdev)
> > > {
> > > - const unsigned int *expected_ver;
> > > struct axi_dac_state *st;
> > > void __iomem *base;
> > > unsigned int ver;
> > > @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device
> > > *pdev)
> > > if (!st)
> > > return -ENOMEM;
> > >
> > > - expected_ver = device_get_match_data(&pdev->dev);
> > > - if (!expected_ver)
> > > + st->info = device_get_match_data(&pdev->dev);
> > > + if (!st->info)
> > > return -ENODEV;
> > > + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> > > + if (IS_ERR(clk)) {
> >
> > If clock-names is not given, then we'll get -EINVAL. Hence we could assume
> > that:
> >
> > if (PTR_ERR(clk) != -EINVAL)
> > return dev_err_probe();
>
> clock-names isn't a required property, but the driver code effectively
> makes it one. Doesn't this lookup need to be by index, unless
> clock-names is made required for this variant?
Likely I'm missing something but the driver is not making clock-names mandatory,
is it?
At least for the s_axi_aclk, we first try to get it using clock-names and if
that fails we backup to what we're doing which is passing NULL (which
effectively get's the first clock in the array).
The reasoning is that on the generic variant we only need the AXI clk and we
can't now enforce clock-names on it. But to keep things flexible, this was
purposed.
Another alternative that might have more lines of code (but simpler to
understand the intent) is to have (for example) a callback get_clocks function
that we set depending on the variant. And this also makes me realize that we
could improve the bindings. I mean, for the generic dac variant we do not need
clock-names but for this new variant, clock-names is mandatory and I'm fairly
sure we can express that in the bindings.
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-23 14:56 ` Nuno Sá
@ 2024-10-23 15:22 ` Conor Dooley
2024-10-24 7:04 ` Nuno Sá
0 siblings, 1 reply; 29+ messages in thread
From: Conor Dooley @ 2024-10-23 15:22 UTC (permalink / raw)
To: Nuno Sá
Cc: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel, dlechner, Mark Brown
[-- Attachment #1: Type: text/plain, Size: 5396 bytes --]
On Wed, Oct 23, 2024 at 04:56:39PM +0200, Nuno Sá wrote:
> On Tue, 2024-10-22 at 18:21 +0100, Conor Dooley wrote:
> > On Tue, Oct 22, 2024 at 02:36:44PM +0200, Nuno Sá wrote:
> > > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > > From: Angelo Dureghello <adureghello@baylibre.com>
> > > >
> > > > Extend AXI-DAC backend with new features required to interface
> > > > to the ad3552r DAC. Mainly, a new compatible string is added to
> > > > support the ad3552r-axi DAC IP, very similar to the generic DAC
> > > > IP but with some customizations to work with the ad3552r.
> > > >
> > > > Then, a series of generic functions has been added to match with
> > > > ad3552r needs. Function names has been kept generic as much as
> > > > possible, to allow re-utilization from other frontend drivers.
> > > >
> > > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > > ---
> > >
> > > Looks mostly good,
> > >
> > > one minor thing that (I think) could be improved
> > > > drivers/iio/dac/adi-axi-dac.c | 269
> > > > +++++++++++++++++++++++++++++++++++++++--
> > > > -
> > > > 1 file changed, 255 insertions(+), 14 deletions(-)
> > > >
> > > > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > > > index 04193a98616e..9d6809fe7a67 100644
> > > > --- a/drivers/iio/dac/adi-axi-dac.c
> > > > +++ b/drivers/iio/dac/adi-axi-dac.c
> > > > @@ -46,9 +46,28 @@
> > > > #define AXI_DAC_CNTRL_1_REG 0x0044
> > > > #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> > > > #define AXI_DAC_CNTRL_2_REG 0x0048
> > > > +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> > > > +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> > > > #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> > > > +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> > > > +#define AXI_DAC_STATUS_1_REG 0x0054
> > > > +#define AXI_DAC_STATUS_2_REG 0x0058
> > > > #define AXI_DAC_DRP_STATUS_REG 0x0074
> > > > #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> > > > +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> > > > +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> > > > +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> > > > +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> > > > +#define AXI_DAC_UI_STATUS_REG 0x0088
> > > > +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> > > > +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> > > > +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> > > > +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> > > > +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> > > > +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
> > >
> > > ...
> > >
> > > > static int axi_dac_probe(struct platform_device *pdev)
> > > > {
> > > > - const unsigned int *expected_ver;
> > > > struct axi_dac_state *st;
> > > > void __iomem *base;
> > > > unsigned int ver;
> > > > @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device
> > > > *pdev)
> > > > if (!st)
> > > > return -ENOMEM;
> > > >
> > > > - expected_ver = device_get_match_data(&pdev->dev);
> > > > - if (!expected_ver)
> > > > + st->info = device_get_match_data(&pdev->dev);
> > > > + if (!st->info)
> > > > return -ENODEV;
> > > > + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> > > > + if (IS_ERR(clk)) {
> > >
> > > If clock-names is not given, then we'll get -EINVAL. Hence we could assume
> > > that:
> > >
> > > if (PTR_ERR(clk) != -EINVAL)
> > > return dev_err_probe();
> >
> > clock-names isn't a required property, but the driver code effectively
> > makes it one. Doesn't this lookup need to be by index, unless
> > clock-names is made required for this variant?
>
> Likely I'm missing something but the driver is not making clock-names mandatory,
> is it?
Did you miss the "for this variant"? Maybe I left the comment in not
exactly the right place, but I don't think the code works correctly for
the new variant if clock-names aren't provided:
+ if (st->info->has_dac_clk) {
+ struct clk *dac_clk;
+ dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
+ if (IS_ERR(dac_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
+ "failed to get dac_clk clock\n");
+
+ /* We only care about the streaming mode rate */
+ st->dac_clk_rate = clk_get_rate(dac_clk) / 2;
Isn't this going to cause a probe failure?
> At least for the s_axi_aclk, we first try to get it using clock-names and if
> that fails we backup to what we're doing which is passing NULL (which
> effectively get's the first clock in the array).
>
> The reasoning is that on the generic variant we only need the AXI clk and we
> can't now enforce clock-names on it. But to keep things flexible, this was
> purposed.
Why not always just get the first clock by index and avoid the
complexity?
> Another alternative that might have more lines of code (but simpler to
> understand the intent) is to have (for example) a callback get_clocks function
> that we set depending on the variant. And this also makes me realize that we
> could improve the bindings. I mean, for the generic dac variant we do not need
> clock-names but for this new variant, clock-names is mandatory and I'm fairly
> sure we can express that in the bindings.
Right. You can "edit" required in the if/then/else branch for the new
variant.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-23 15:22 ` Conor Dooley
@ 2024-10-24 7:04 ` Nuno Sá
2024-10-24 10:29 ` Angelo Dureghello
0 siblings, 1 reply; 29+ messages in thread
From: Nuno Sá @ 2024-10-24 7:04 UTC (permalink / raw)
To: Conor Dooley
Cc: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel, dlechner, Mark Brown
On Wed, 2024-10-23 at 16:22 +0100, Conor Dooley wrote:
> On Wed, Oct 23, 2024 at 04:56:39PM +0200, Nuno Sá wrote:
> > On Tue, 2024-10-22 at 18:21 +0100, Conor Dooley wrote:
> > > On Tue, Oct 22, 2024 at 02:36:44PM +0200, Nuno Sá wrote:
> > > > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > > > From: Angelo Dureghello <adureghello@baylibre.com>
> > > > >
> > > > > Extend AXI-DAC backend with new features required to interface
> > > > > to the ad3552r DAC. Mainly, a new compatible string is added to
> > > > > support the ad3552r-axi DAC IP, very similar to the generic DAC
> > > > > IP but with some customizations to work with the ad3552r.
> > > > >
> > > > > Then, a series of generic functions has been added to match with
> > > > > ad3552r needs. Function names has been kept generic as much as
> > > > > possible, to allow re-utilization from other frontend drivers.
> > > > >
> > > > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > > > ---
> > > >
> > > > Looks mostly good,
> > > >
> > > > one minor thing that (I think) could be improved
> > > > > drivers/iio/dac/adi-axi-dac.c | 269
> > > > > +++++++++++++++++++++++++++++++++++++++--
> > > > > -
> > > > > 1 file changed, 255 insertions(+), 14 deletions(-)
> > > > >
> > > > > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > > > > index 04193a98616e..9d6809fe7a67 100644
> > > > > --- a/drivers/iio/dac/adi-axi-dac.c
> > > > > +++ b/drivers/iio/dac/adi-axi-dac.c
> > > > > @@ -46,9 +46,28 @@
> > > > > #define AXI_DAC_CNTRL_1_REG 0x0044
> > > > > #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> > > > > #define AXI_DAC_CNTRL_2_REG 0x0048
> > > > > +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> > > > > +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> > > > > #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> > > > > +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> > > > > +#define AXI_DAC_STATUS_1_REG 0x0054
> > > > > +#define AXI_DAC_STATUS_2_REG 0x0058
> > > > > #define AXI_DAC_DRP_STATUS_REG 0x0074
> > > > > #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> > > > > +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> > > > > +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> > > > > +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> > > > > +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> > > > > +#define AXI_DAC_UI_STATUS_REG 0x0088
> > > > > +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> > > > > +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> > > > > +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> > > > > +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> > > > > +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> > > > > +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
> > > >
> > > > ...
> > > >
> > > > > static int axi_dac_probe(struct platform_device *pdev)
> > > > > {
> > > > > - const unsigned int *expected_ver;
> > > > > struct axi_dac_state *st;
> > > > > void __iomem *base;
> > > > > unsigned int ver;
> > > > > @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device
> > > > > *pdev)
> > > > > if (!st)
> > > > > return -ENOMEM;
> > > > >
> > > > > - expected_ver = device_get_match_data(&pdev->dev);
> > > > > - if (!expected_ver)
> > > > > + st->info = device_get_match_data(&pdev->dev);
> > > > > + if (!st->info)
> > > > > return -ENODEV;
> > > > > + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> > > > > + if (IS_ERR(clk)) {
> > > >
> > > > If clock-names is not given, then we'll get -EINVAL. Hence we could assume
> > > > that:
> > > >
> > > > if (PTR_ERR(clk) != -EINVAL)
> > > > return dev_err_probe();
> > >
> > > clock-names isn't a required property, but the driver code effectively
> > > makes it one. Doesn't this lookup need to be by index, unless
> > > clock-names is made required for this variant?
> >
> > Likely I'm missing something but the driver is not making clock-names mandatory,
> > is it?
>
> Did you miss the "for this variant"? Maybe I left the comment in not
I guess so :)
> exactly the right place, but I don't think the code works correctly for
> the new variant if clock-names aren't provided:
>
> + if (st->info->has_dac_clk) {
> + struct clk *dac_clk;
> + dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
> + if (IS_ERR(dac_clk))
> + return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
> + "failed to get dac_clk clock\n");
> +
> + /* We only care about the streaming mode rate */
> + st->dac_clk_rate = clk_get_rate(dac_clk) / 2;
>
> Isn't this going to cause a probe failure?
Exactly. And that goes in line with what I wrote about the bindings not describing
(currently) this. So yes, for the new variant (which has 'has_dac_clk' set to true)
clock-names is indeed mandatory and probe will fail if it's not given.
>
> > At least for the s_axi_aclk, we first try to get it using clock-names and if
> > that fails we backup to what we're doing which is passing NULL (which
> > effectively get's the first clock in the array).
> >
> > The reasoning is that on the generic variant we only need the AXI clk and we
> > can't now enforce clock-names on it. But to keep things flexible, this was
> > purposed.
>
> Why not always just get the first clock by index and avoid the
> complexity?
And that was also suggested in the previous version but then Jonathan suggested this
[1]. I agree things now are a bit confusing because we expect clock-names to be
optional for the generic but mandatory for this new variant and the code is not being
that explicit about it.
>
> > Another alternative that might have more lines of code (but simpler to
> > understand the intent) is to have (for example) a callback get_clocks function
> > that we set depending on the variant. And this also makes me realize that we
> > could improve the bindings. I mean, for the generic dac variant we do not need
> > clock-names but for this new variant, clock-names is mandatory and I'm fairly
> > sure we can express that in the bindings.
>
> Right. You can "edit" required in the if/then/else branch for the new
> variant.
Yeah, and IMO that should be set in the bindings (it would help understanding what
the driver is actually doinfg.
[1]: https://lore.kernel.org/linux-iio/20241019160817.10c3a2bf@jic23-huawei/
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-24 7:04 ` Nuno Sá
@ 2024-10-24 10:29 ` Angelo Dureghello
2024-10-26 17:31 ` Jonathan Cameron
0 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-24 10:29 UTC (permalink / raw)
To: Nuno Sá
Cc: Conor Dooley, Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
On 24.10.2024 09:04, Nuno Sá wrote:
> On Wed, 2024-10-23 at 16:22 +0100, Conor Dooley wrote:
> > On Wed, Oct 23, 2024 at 04:56:39PM +0200, Nuno Sá wrote:
> > > On Tue, 2024-10-22 at 18:21 +0100, Conor Dooley wrote:
> > > > On Tue, Oct 22, 2024 at 02:36:44PM +0200, Nuno Sá wrote:
> > > > > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > > > > From: Angelo Dureghello <adureghello@baylibre.com>
> > > > > >
> > > > > > Extend AXI-DAC backend with new features required to interface
> > > > > > to the ad3552r DAC. Mainly, a new compatible string is added to
> > > > > > support the ad3552r-axi DAC IP, very similar to the generic DAC
> > > > > > IP but with some customizations to work with the ad3552r.
> > > > > >
> > > > > > Then, a series of generic functions has been added to match with
> > > > > > ad3552r needs. Function names has been kept generic as much as
> > > > > > possible, to allow re-utilization from other frontend drivers.
> > > > > >
> > > > > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > > > > ---
> > > > >
> > > > > Looks mostly good,
> > > > >
> > > > > one minor thing that (I think) could be improved
> > > > > > drivers/iio/dac/adi-axi-dac.c | 269
> > > > > > +++++++++++++++++++++++++++++++++++++++--
> > > > > > -
> > > > > > 1 file changed, 255 insertions(+), 14 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > > > > > index 04193a98616e..9d6809fe7a67 100644
> > > > > > --- a/drivers/iio/dac/adi-axi-dac.c
> > > > > > +++ b/drivers/iio/dac/adi-axi-dac.c
> > > > > > @@ -46,9 +46,28 @@
> > > > > > #define AXI_DAC_CNTRL_1_REG 0x0044
> > > > > > #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> > > > > > #define AXI_DAC_CNTRL_2_REG 0x0048
> > > > > > +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> > > > > > +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> > > > > > #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> > > > > > +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> > > > > > +#define AXI_DAC_STATUS_1_REG 0x0054
> > > > > > +#define AXI_DAC_STATUS_2_REG 0x0058
> > > > > > #define AXI_DAC_DRP_STATUS_REG 0x0074
> > > > > > #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> > > > > > +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> > > > > > +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> > > > > > +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> > > > > > +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> > > > > > +#define AXI_DAC_UI_STATUS_REG 0x0088
> > > > > > +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> > > > > > +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> > > > > > +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> > > > > > +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> > > > > > +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> > > > > > +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
> > > > >
> > > > > ...
> > > > >
> > > > > > static int axi_dac_probe(struct platform_device *pdev)
> > > > > > {
> > > > > > - const unsigned int *expected_ver;
> > > > > > struct axi_dac_state *st;
> > > > > > void __iomem *base;
> > > > > > unsigned int ver;
> > > > > > @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device
> > > > > > *pdev)
> > > > > > if (!st)
> > > > > > return -ENOMEM;
> > > > > >
> > > > > > - expected_ver = device_get_match_data(&pdev->dev);
> > > > > > - if (!expected_ver)
> > > > > > + st->info = device_get_match_data(&pdev->dev);
> > > > > > + if (!st->info)
> > > > > > return -ENODEV;
> > > > > > + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> > > > > > + if (IS_ERR(clk)) {
> > > > >
> > > > > If clock-names is not given, then we'll get -EINVAL. Hence we could assume
> > > > > that:
> > > > >
> > > > > if (PTR_ERR(clk) != -EINVAL)
> > > > > return dev_err_probe();
> > > >
> > > > clock-names isn't a required property, but the driver code effectively
> > > > makes it one. Doesn't this lookup need to be by index, unless
> > > > clock-names is made required for this variant?
> > >
> > > Likely I'm missing something but the driver is not making clock-names mandatory,
> > > is it?
> >
> > Did you miss the "for this variant"? Maybe I left the comment in not
>
> I guess so :)
>
> > exactly the right place, but I don't think the code works correctly for
> > the new variant if clock-names aren't provided:
> >
> > + if (st->info->has_dac_clk) {
> > + struct clk *dac_clk;
> > + dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
> > + if (IS_ERR(dac_clk))
> > + return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
> > + "failed to get dac_clk clock\n");
> > +
> > + /* We only care about the streaming mode rate */
> > + st->dac_clk_rate = clk_get_rate(dac_clk) / 2;
> >
> > Isn't this going to cause a probe failure?
>
> Exactly. And that goes in line with what I wrote about the bindings not describing
> (currently) this. So yes, for the new variant (which has 'has_dac_clk' set to true)
> clock-names is indeed mandatory and probe will fail if it's not given.
>
> >
> > > At least for the s_axi_aclk, we first try to get it using clock-names and if
> > > that fails we backup to what we're doing which is passing NULL (which
> > > effectively get's the first clock in the array).
> > >
> > > The reasoning is that on the generic variant we only need the AXI clk and we
> > > can't now enforce clock-names on it. But to keep things flexible, this was
> > > purposed.
> >
> > Why not always just get the first clock by index and avoid the
> > complexity?
>
> And that was also suggested in the previous version but then Jonathan suggested this
> [1]. I agree things now are a bit confusing because we expect clock-names to be
> optional for the generic but mandatory for this new variant and the code is not being
> that explicit about it.
>
> >
> > > Another alternative that might have more lines of code (but simpler to
> > > understand the intent) is to have (for example) a callback get_clocks function
> > > that we set depending on the variant. And this also makes me realize that we
> > > could improve the bindings. I mean, for the generic dac variant we do not need
> > > clock-names but for this new variant, clock-names is mandatory and I'm fairly
> > > sure we can express that in the bindings.
> >
> > Right. You can "edit" required in the if/then/else branch for the new
> > variant.
>
> Yeah, and IMO that should be set in the bindings (it would help understanding what
> the driver is actually doinfg.
>
ok, thanks, so
so modified yaml in this way:
clocks:
minItems: 1
maxItems: 2
clock-names:
items:
- const: s_axi_aclk
- const: dac_clk
minItems: 1
'#io-backend-cells':
const: 0
required:
- compatible
- dmas
- reg
- clocks
allOf:
- if:
properties:
compatible:
contains:
const: adi,axi-ad3552r
then:
$ref: /schemas/spi/spi-controller.yaml#
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
required:
- clock-names
else:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
> [1]: https://lore.kernel.org/linux-iio/20241019160817.10c3a2bf@jic23-huawei/
>
> - Nuno Sá
>
Regards,
angelo
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 4/8] iio: dac: adi-axi-dac: extend features
2024-10-24 10:29 ` Angelo Dureghello
@ 2024-10-26 17:31 ` Jonathan Cameron
0 siblings, 0 replies; 29+ messages in thread
From: Jonathan Cameron @ 2024-10-26 17:31 UTC (permalink / raw)
To: Angelo Dureghello
Cc: Nuno Sá, Conor Dooley, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
On Thu, 24 Oct 2024 12:29:29 +0200
Angelo Dureghello <adureghello@baylibre.com> wrote:
> On 24.10.2024 09:04, Nuno Sá wrote:
> > On Wed, 2024-10-23 at 16:22 +0100, Conor Dooley wrote:
> > > On Wed, Oct 23, 2024 at 04:56:39PM +0200, Nuno Sá wrote:
> > > > On Tue, 2024-10-22 at 18:21 +0100, Conor Dooley wrote:
> > > > > On Tue, Oct 22, 2024 at 02:36:44PM +0200, Nuno Sá wrote:
> > > > > > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > > > > > From: Angelo Dureghello <adureghello@baylibre.com>
> > > > > > >
> > > > > > > Extend AXI-DAC backend with new features required to interface
> > > > > > > to the ad3552r DAC. Mainly, a new compatible string is added to
> > > > > > > support the ad3552r-axi DAC IP, very similar to the generic DAC
> > > > > > > IP but with some customizations to work with the ad3552r.
> > > > > > >
> > > > > > > Then, a series of generic functions has been added to match with
> > > > > > > ad3552r needs. Function names has been kept generic as much as
> > > > > > > possible, to allow re-utilization from other frontend drivers.
> > > > > > >
> > > > > > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > > > > > ---
> > > > > >
> > > > > > Looks mostly good,
> > > > > >
> > > > > > one minor thing that (I think) could be improved
> > > > > > > drivers/iio/dac/adi-axi-dac.c | 269
> > > > > > > +++++++++++++++++++++++++++++++++++++++--
> > > > > > > -
> > > > > > > 1 file changed, 255 insertions(+), 14 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > > > > > > index 04193a98616e..9d6809fe7a67 100644
> > > > > > > --- a/drivers/iio/dac/adi-axi-dac.c
> > > > > > > +++ b/drivers/iio/dac/adi-axi-dac.c
> > > > > > > @@ -46,9 +46,28 @@
> > > > > > > #define AXI_DAC_CNTRL_1_REG 0x0044
> > > > > > > #define AXI_DAC_CNTRL_1_SYNC BIT(0)
> > > > > > > #define AXI_DAC_CNTRL_2_REG 0x0048
> > > > > > > +#define AXI_DAC_CNTRL_2_SDR_DDR_N BIT(16)
> > > > > > > +#define AXI_DAC_CNTRL_2_SYMB_8B BIT(14)
> > > > > > > #define ADI_DAC_CNTRL_2_R1_MODE BIT(5)
> > > > > > > +#define AXI_DAC_CNTRL_2_UNSIGNED_DATA BIT(4)
> > > > > > > +#define AXI_DAC_STATUS_1_REG 0x0054
> > > > > > > +#define AXI_DAC_STATUS_2_REG 0x0058
> > > > > > > #define AXI_DAC_DRP_STATUS_REG 0x0074
> > > > > > > #define AXI_DAC_DRP_STATUS_DRP_LOCKED BIT(17)
> > > > > > > +#define AXI_DAC_CUSTOM_RD_REG 0x0080
> > > > > > > +#define AXI_DAC_CUSTOM_WR_REG 0x0084
> > > > > > > +#define AXI_DAC_CUSTOM_WR_DATA_8 GENMASK(23, 16)
> > > > > > > +#define AXI_DAC_CUSTOM_WR_DATA_16 GENMASK(23, 8)
> > > > > > > +#define AXI_DAC_UI_STATUS_REG 0x0088
> > > > > > > +#define AXI_DAC_UI_STATUS_IF_BUSY BIT(4)
> > > > > > > +#define AXI_DAC_CUSTOM_CTRL_REG 0x008C
> > > > > > > +#define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24)
> > > > > > > +#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2)
> > > > > > > +#define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1)
> > > > > > > +#define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0)
> > > > > >
> > > > > > ...
> > > > > >
> > > > > > > static int axi_dac_probe(struct platform_device *pdev)
> > > > > > > {
> > > > > > > - const unsigned int *expected_ver;
> > > > > > > struct axi_dac_state *st;
> > > > > > > void __iomem *base;
> > > > > > > unsigned int ver;
> > > > > > > @@ -566,14 +780,29 @@ static int axi_dac_probe(struct platform_device
> > > > > > > *pdev)
> > > > > > > if (!st)
> > > > > > > return -ENOMEM;
> > > > > > >
> > > > > > > - expected_ver = device_get_match_data(&pdev->dev);
> > > > > > > - if (!expected_ver)
> > > > > > > + st->info = device_get_match_data(&pdev->dev);
> > > > > > > + if (!st->info)
> > > > > > > return -ENODEV;
> > > > > > > + clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
> > > > > > > + if (IS_ERR(clk)) {
> > > > > >
> > > > > > If clock-names is not given, then we'll get -EINVAL. Hence we could assume
> > > > > > that:
> > > > > >
> > > > > > if (PTR_ERR(clk) != -EINVAL)
> > > > > > return dev_err_probe();
> > > > >
> > > > > clock-names isn't a required property, but the driver code effectively
> > > > > makes it one. Doesn't this lookup need to be by index, unless
> > > > > clock-names is made required for this variant?
> > > >
> > > > Likely I'm missing something but the driver is not making clock-names mandatory,
> > > > is it?
> > >
> > > Did you miss the "for this variant"? Maybe I left the comment in not
> >
> > I guess so :)
> >
> > > exactly the right place, but I don't think the code works correctly for
> > > the new variant if clock-names aren't provided:
> > >
> > > + if (st->info->has_dac_clk) {
> > > + struct clk *dac_clk;
> > > + dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
> > > + if (IS_ERR(dac_clk))
> > > + return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
> > > + "failed to get dac_clk clock\n");
> > > +
> > > + /* We only care about the streaming mode rate */
> > > + st->dac_clk_rate = clk_get_rate(dac_clk) / 2;
> > >
> > > Isn't this going to cause a probe failure?
> >
> > Exactly. And that goes in line with what I wrote about the bindings not describing
> > (currently) this. So yes, for the new variant (which has 'has_dac_clk' set to true)
> > clock-names is indeed mandatory and probe will fail if it's not given.
> >
> > >
> > > > At least for the s_axi_aclk, we first try to get it using clock-names and if
> > > > that fails we backup to what we're doing which is passing NULL (which
> > > > effectively get's the first clock in the array).
> > > >
> > > > The reasoning is that on the generic variant we only need the AXI clk and we
> > > > can't now enforce clock-names on it. But to keep things flexible, this was
> > > > purposed.
> > >
> > > Why not always just get the first clock by index and avoid the
> > > complexity?
> >
> > And that was also suggested in the previous version but then Jonathan suggested this
> > [1]. I agree things now are a bit confusing because we expect clock-names to be
> > optional for the generic but mandatory for this new variant and the code is not being
> > that explicit about it.
> >
If we need all the clocks for a variant to work, then can instead constrain the clock
names if present. So basically enforce the indexing.
I think just requiring clock-names for this variant is cleaner though!
Jonathan
> > >
> > > > Another alternative that might have more lines of code (but simpler to
> > > > understand the intent) is to have (for example) a callback get_clocks function
> > > > that we set depending on the variant. And this also makes me realize that we
> > > > could improve the bindings. I mean, for the generic dac variant we do not need
> > > > clock-names but for this new variant, clock-names is mandatory and I'm fairly
> > > > sure we can express that in the bindings.
> > >
> > > Right. You can "edit" required in the if/then/else branch for the new
> > > variant.
> >
> > Yeah, and IMO that should be set in the bindings (it would help understanding what
> > the driver is actually doinfg.
> >
>
> ok, thanks, so
>
> so modified yaml in this way:
>
> clocks:
> minItems: 1
> maxItems: 2
>
> clock-names:
> items:
> - const: s_axi_aclk
> - const: dac_clk
> minItems: 1
>
> '#io-backend-cells':
> const: 0
>
> required:
> - compatible
> - dmas
> - reg
> - clocks
>
> allOf:
> - if:
> properties:
> compatible:
> contains:
> const: adi,axi-ad3552r
> then:
> $ref: /schemas/spi/spi-controller.yaml#
> properties:
> clocks:
> minItems: 2
> clock-names:
> minItems: 2
> required:
> - clock-names
> else:
> properties:
> clocks:
> maxItems: 1
> clock-names:
> maxItems: 1
>
>
> > [1]: https://lore.kernel.org/linux-iio/20241019160817.10c3a2bf@jic23-huawei/
> >
> > - Nuno Sá
> >
>
> Regards,
> angelo
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v7 5/8] iio: dac: ad3552r: changes to use FIELD_PREP
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
` (3 preceding siblings ...)
2024-10-21 12:40 ` [PATCH v7 4/8] iio: dac: adi-axi-dac: " Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 6/8] iio: dac: ad3552r: extract common code (no changes in behavior intended) Angelo Dureghello
` (2 subsequent siblings)
7 siblings, 0 replies; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Changes to use FIELD_PREP, so that driver-specific ad3552r_field_prep
is removed. Variables (arrays) that was used to call ad3552r_field_prep
are removed too.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
drivers/iio/dac/ad3552r.c | 166 ++++++++++++++--------------------------------
1 file changed, 49 insertions(+), 117 deletions(-)
diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c
index 7d61b2fe6624..75825d6a398b 100644
--- a/drivers/iio/dac/ad3552r.c
+++ b/drivers/iio/dac/ad3552r.c
@@ -210,46 +210,6 @@ static const s32 gains_scaling_table[] = {
[AD3552R_CH_GAIN_SCALING_0_125] = 125
};
-enum ad3552r_dev_attributes {
- /* - Direct register values */
- /* From 0-3 */
- AD3552R_SDO_DRIVE_STRENGTH,
- /*
- * 0 -> Internal Vref, vref_io pin floating (default)
- * 1 -> Internal Vref, vref_io driven by internal vref
- * 2 or 3 -> External Vref
- */
- AD3552R_VREF_SELECT,
- /* Read registers in ascending order if set. Else descending */
- AD3552R_ADDR_ASCENSION,
-};
-
-enum ad3552r_ch_attributes {
- /* DAC powerdown */
- AD3552R_CH_DAC_POWERDOWN,
- /* DAC amplifier powerdown */
- AD3552R_CH_AMPLIFIER_POWERDOWN,
- /* Select the output range. Select from enum ad3552r_ch_output_range */
- AD3552R_CH_OUTPUT_RANGE_SEL,
- /*
- * Over-rider the range selector in order to manually set the output
- * voltage range
- */
- AD3552R_CH_RANGE_OVERRIDE,
- /* Manually set the offset voltage */
- AD3552R_CH_GAIN_OFFSET,
- /* Sets the polarity of the offset. */
- AD3552R_CH_GAIN_OFFSET_POLARITY,
- /* PDAC gain scaling */
- AD3552R_CH_GAIN_SCALING_P,
- /* NDAC gain scaling */
- AD3552R_CH_GAIN_SCALING_N,
- /* Rfb value */
- AD3552R_CH_RFB,
- /* Channel select. When set allow Input -> DAC and Mask -> DAC */
- AD3552R_CH_SELECT,
-};
-
struct ad3552r_ch_data {
s32 scale_int;
s32 scale_dec;
@@ -285,45 +245,6 @@ struct ad3552r_desc {
unsigned int num_ch;
};
-static const u16 addr_mask_map[][2] = {
- [AD3552R_ADDR_ASCENSION] = {
- AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
- AD3552R_MASK_ADDR_ASCENSION
- },
- [AD3552R_SDO_DRIVE_STRENGTH] = {
- AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
- AD3552R_MASK_SDO_DRIVE_STRENGTH
- },
- [AD3552R_VREF_SELECT] = {
- AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
- AD3552R_MASK_REFERENCE_VOLTAGE_SEL
- },
-};
-
-/* 0 -> reg addr, 1->ch0 mask, 2->ch1 mask */
-static const u16 addr_mask_map_ch[][3] = {
- [AD3552R_CH_DAC_POWERDOWN] = {
- AD3552R_REG_ADDR_POWERDOWN_CONFIG,
- AD3552R_MASK_CH_DAC_POWERDOWN(0),
- AD3552R_MASK_CH_DAC_POWERDOWN(1)
- },
- [AD3552R_CH_AMPLIFIER_POWERDOWN] = {
- AD3552R_REG_ADDR_POWERDOWN_CONFIG,
- AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0),
- AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1)
- },
- [AD3552R_CH_OUTPUT_RANGE_SEL] = {
- AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
- AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0),
- AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1)
- },
- [AD3552R_CH_SELECT] = {
- AD3552R_REG_ADDR_CH_SELECT_16B,
- AD3552R_MASK_CH(0),
- AD3552R_MASK_CH(1)
- }
-};
-
static u8 _ad3552r_reg_len(u8 addr)
{
switch (addr) {
@@ -399,11 +320,6 @@ static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val)
return 0;
}
-static u16 ad3552r_field_prep(u16 val, u16 mask)
-{
- return (val << __ffs(mask)) & mask;
-}
-
/* Update field of a register, shift val if needed */
static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask,
u16 val)
@@ -416,21 +332,11 @@ static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask,
return ret;
reg &= ~mask;
- reg |= ad3552r_field_prep(val, mask);
+ reg |= val;
return ad3552r_write_reg(dac, addr, reg);
}
-static int ad3552r_set_ch_value(struct ad3552r_desc *dac,
- enum ad3552r_ch_attributes attr,
- u8 ch,
- u16 val)
-{
- /* Update register related to attributes in chip */
- return ad3552r_update_reg_field(dac, addr_mask_map_ch[attr][0],
- addr_mask_map_ch[attr][ch + 1], val);
-}
-
#define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) { \
.type = IIO_VOLTAGE, \
.output = true, \
@@ -510,8 +416,14 @@ static int ad3552r_write_raw(struct iio_dev *indio_dev,
val);
break;
case IIO_CHAN_INFO_ENABLE:
- err = ad3552r_set_ch_value(dac, AD3552R_CH_DAC_POWERDOWN,
- chan->channel, !val);
+ if (chan->channel == 0)
+ val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(0), !val);
+ else
+ val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(1), !val);
+
+ err = ad3552r_update_reg_field(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG,
+ AD3552R_MASK_CH_DAC_POWERDOWN(chan->channel),
+ val);
break;
default:
err = -EINVAL;
@@ -715,9 +627,9 @@ static int ad3552r_reset(struct ad3552r_desc *dac)
}
return ad3552r_update_reg_field(dac,
- addr_mask_map[AD3552R_ADDR_ASCENSION][0],
- addr_mask_map[AD3552R_ADDR_ASCENSION][1],
- val);
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
+ AD3552R_MASK_ADDR_ASCENSION,
+ FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val));
}
static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min,
@@ -812,20 +724,20 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
"mandatory custom-output-range-config property missing\n");
dac->ch_data[ch].range_override = 1;
- reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE);
+ reg |= FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1);
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
if (err)
return dev_err_probe(dev, err,
"mandatory adi,gain-scaling-p property missing\n");
- reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P);
+ reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, val);
dac->ch_data[ch].p = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
if (err)
return dev_err_probe(dev, err,
"mandatory adi,gain-scaling-n property missing\n");
- reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N);
+ reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, val);
dac->ch_data[ch].n = val;
err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
@@ -841,9 +753,9 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
dac->ch_data[ch].gain_offset = val;
offset = abs((s32)val);
- reg |= ad3552r_field_prep((offset >> 8), AD3552R_MASK_CH_OFFSET_BIT_8);
+ reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, (offset >> 8));
- reg |= ad3552r_field_prep((s32)val < 0, AD3552R_MASK_CH_OFFSET_POLARITY);
+ reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, (s32)val < 0);
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
err = ad3552r_write_reg(dac, addr,
offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
@@ -886,9 +798,9 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
}
err = ad3552r_update_reg_field(dac,
- addr_mask_map[AD3552R_VREF_SELECT][0],
- addr_mask_map[AD3552R_VREF_SELECT][1],
- val);
+ AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
+ AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
+ FIELD_PREP(AD3552R_MASK_REFERENCE_VOLTAGE_SEL, val));
if (err)
return err;
@@ -900,9 +812,9 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
}
err = ad3552r_update_reg_field(dac,
- addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][0],
- addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][1],
- val);
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+ AD3552R_MASK_SDO_DRIVE_STRENGTH,
+ FIELD_PREP(AD3552R_MASK_SDO_DRIVE_STRENGTH, val));
if (err)
return err;
}
@@ -938,9 +850,15 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
"Invalid adi,output-range-microvolt value\n");
val = err;
- err = ad3552r_set_ch_value(dac,
- AD3552R_CH_OUTPUT_RANGE_SEL,
- ch, val);
+ if (ch == 0)
+ val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val);
+ else
+ val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1), val);
+
+ err = ad3552r_update_reg_field(dac,
+ AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
+ AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch),
+ val);
if (err)
return err;
@@ -958,7 +876,14 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
ad3552r_calc_gain_and_offset(dac, ch);
dac->enabled_ch |= BIT(ch);
- err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1);
+ if (ch == 0)
+ val = FIELD_PREP(AD3552R_MASK_CH(0), 1);
+ else
+ val = FIELD_PREP(AD3552R_MASK_CH(1), 1);
+
+ err = ad3552r_update_reg_field(dac,
+ AD3552R_REG_ADDR_CH_SELECT_16B,
+ AD3552R_MASK_CH(ch), val);
if (err < 0)
return err;
@@ -970,8 +895,15 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
/* Disable unused channels */
for_each_clear_bit(ch, &dac->enabled_ch,
dac->model_data->num_hw_channels) {
- err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN,
- ch, 1);
+ if (ch == 0)
+ val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), 1);
+ else
+ val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1), 1);
+
+ err = ad3552r_update_reg_field(dac,
+ AD3552R_REG_ADDR_POWERDOWN_CONFIG,
+ AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch),
+ val);
if (err)
return err;
}
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v7 6/8] iio: dac: ad3552r: extract common code (no changes in behavior intended)
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
` (4 preceding siblings ...)
2024-10-21 12:40 ` [PATCH v7 5/8] iio: dac: ad3552r: changes to use FIELD_PREP Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver Angelo Dureghello
2024-10-21 12:40 ` [PATCH v7 8/8] iio: dac: adi-axi-dac: add registering of child fdt node Angelo Dureghello
7 siblings, 0 replies; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Extracting common code, to share common code to be used later
by the AXI driver version (ad3552r-axi.c).
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
drivers/iio/dac/Makefile | 2 +-
drivers/iio/dac/ad3552r-common.c | 248 ++++++++++++++++++++++++
drivers/iio/dac/ad3552r.c | 398 +++------------------------------------
drivers/iio/dac/ad3552r.h | 226 ++++++++++++++++++++++
4 files changed, 502 insertions(+), 372 deletions(-)
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 621d553bd6e3..c92de0366238 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -4,7 +4,7 @@
#
# When adding new entries keep the list in alphabetical order
-obj-$(CONFIG_AD3552R) += ad3552r.o
+obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
obj-$(CONFIG_AD5360) += ad5360.o
obj-$(CONFIG_AD5380) += ad5380.o
obj-$(CONFIG_AD5421) += ad5421.o
diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c
new file mode 100644
index 000000000000..672f0284bc05
--- /dev/null
+++ b/drivers/iio/dac/ad3552r-common.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2010-2024 Analog Devices Inc.
+// Copyright (c) 2024 Baylibre, SAS
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include "ad3552r.h"
+
+const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2] = {
+ [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 },
+ [AD3552R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 },
+ [AD3552R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 },
+ [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 },
+ [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = { -10000, 10000 }
+};
+EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, IIO_AD3552R);
+
+const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = {
+ [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 },
+ [AD3542R_CH_OUTPUT_RANGE_0__3V] = { 0, 3000 },
+ [AD3542R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 },
+ [AD3542R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 },
+ [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 },
+ [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 }
+};
+EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, IIO_AD3552R);
+
+/* Gain * AD3552R_GAIN_SCALE */
+static const s32 gains_scaling_table[] = {
+ [AD3552R_CH_GAIN_SCALING_1] = 1000,
+ [AD3552R_CH_GAIN_SCALING_0_5] = 500,
+ [AD3552R_CH_GAIN_SCALING_0_25] = 250,
+ [AD3552R_CH_GAIN_SCALING_0_125] = 125
+};
+
+u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs)
+{
+ return FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1) |
+ FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, p) |
+ FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, n) |
+ FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, abs(goffs)) |
+ FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, goffs < 0);
+}
+EXPORT_SYMBOL_NS_GPL(ad3552r_calc_custom_gain, IIO_AD3552R);
+
+static void ad3552r_get_custom_range(struct ad3552r_ch_data *ch_data,
+ s32 *v_min, s32 *v_max)
+{
+ s64 vref, tmp, common, offset, gn, gp;
+ /*
+ * From datasheet formula (In Volts):
+ * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03]
+ * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03]
+ * Calculus are converted to milivolts
+ */
+ vref = 2500;
+ /* 2.5 * 1.03 * 1000 (To mV) */
+ common = 2575 * ch_data->rfb;
+ offset = ch_data->gain_offset;
+
+ gn = gains_scaling_table[ch_data->n];
+ tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common;
+ tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
+ *v_max = vref + tmp;
+
+ gp = gains_scaling_table[ch_data->p];
+ tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common;
+ tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
+ *v_min = vref - tmp;
+}
+
+void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data,
+ const struct ad3552r_model_data *model_data)
+{
+ s32 idx, v_max, v_min, span, rem;
+ s64 tmp;
+
+ if (ch_data->range_override) {
+ ad3552r_get_custom_range(ch_data, &v_min, &v_max);
+ } else {
+ /* Normal range */
+ idx = ch_data->range;
+ v_min = model_data->ranges_table[idx][0];
+ v_max = model_data->ranges_table[idx][1];
+ }
+
+ /*
+ * From datasheet formula:
+ * Vout = Span * (D / 65536) + Vmin
+ * Converted to scale and offset:
+ * Scale = Span / 65536
+ * Offset = 65536 * Vmin / Span
+ *
+ * Reminders are in micros in order to be printed as
+ * IIO_VAL_INT_PLUS_MICRO
+ */
+ span = v_max - v_min;
+ ch_data->scale_int = div_s64_rem(span, 65536, &rem);
+ /* Do operations in microvolts */
+ ch_data->scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000, 65536);
+
+ ch_data->offset_int = div_s64_rem(v_min * 65536, span, &rem);
+ tmp = (s64)rem * 1000000;
+ ch_data->offset_dec = div_s64(tmp, span);
+}
+EXPORT_SYMBOL_NS_GPL(ad3552r_calc_gain_and_offset, IIO_AD3552R);
+
+int ad3552r_get_ref_voltage(struct device *dev, u32 *val)
+{
+ int voltage;
+ int delta = 100000;
+
+ voltage = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (voltage < 0 && voltage != -ENODEV)
+ return dev_err_probe(dev, voltage,
+ "Error getting vref voltage\n");
+
+ if (voltage == -ENODEV) {
+ if (device_property_read_bool(dev, "adi,vref-out-en"))
+ *val = AD3552R_INTERNAL_VREF_PIN_2P5V;
+ else
+ *val = AD3552R_INTERNAL_VREF_PIN_FLOATING;
+
+ return 0;
+ }
+
+ if (voltage > 2500000 + delta || voltage < 2500000 - delta) {
+ dev_warn(dev, "vref-supply must be 2.5V");
+ return -EINVAL;
+ }
+
+ *val = AD3552R_EXTERNAL_VREF_PIN_INPUT;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad3552r_get_ref_voltage, IIO_AD3552R);
+
+int ad3552r_get_drive_strength(struct device *dev, u32 *val)
+{
+ int err;
+ u32 drive_strength;
+
+ err = device_property_read_u32(dev, "adi,sdo-drive-strength",
+ &drive_strength);
+ if (err)
+ return err;
+
+ if (drive_strength > 3) {
+ dev_err_probe(dev, -EINVAL,
+ "adi,sdo-drive-strength must be less than 4\n");
+ return -EINVAL;
+ }
+
+ *val = drive_strength;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad3552r_get_drive_strength, IIO_AD3552R);
+
+int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child,
+ u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs)
+{
+ int err;
+ u32 val;
+ struct fwnode_handle *gain_child __free(fwnode_handle) =
+ fwnode_get_named_child_node(child,
+ "custom-output-range-config");
+
+ if (!gain_child)
+ return dev_err_probe(dev, -EINVAL,
+ "custom-output-range-config mandatory\n");
+
+ err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
+ if (err)
+ return dev_err_probe(dev, err,
+ "adi,gain-scaling-p mandatory\n");
+ *gs_p = val;
+
+ err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
+ if (err)
+ return dev_err_probe(dev, err,
+ "adi,gain-scaling-n property mandatory\n");
+ *gs_n = val;
+
+ err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
+ if (err)
+ return dev_err_probe(dev, err,
+ "adi,rfb-ohms mandatory\n");
+ *rfb = val;
+
+ err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
+ if (err)
+ return dev_err_probe(dev, err,
+ "adi,gain-offset mandatory\n");
+ *goffs = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad3552r_get_custom_gain, IIO_AD3552R);
+
+static int ad3552r_find_range(const struct ad3552r_model_data *model_info,
+ s32 *vals)
+{
+ int i;
+
+ for (i = 0; i < model_info->num_ranges; i++)
+ if (vals[0] == model_info->ranges_table[i][0] * 1000 &&
+ vals[1] == model_info->ranges_table[i][1] * 1000)
+ return i;
+
+ return -EINVAL;
+}
+
+int ad3552r_get_output_range(struct device *dev,
+ const struct ad3552r_model_data *model_info,
+ struct fwnode_handle *child, u32 *val)
+{
+ int ret;
+ s32 vals[2];
+
+ /* This property is optional, so returning -ENOENT if missing */
+ if (!fwnode_property_present(child, "adi,output-range-microvolt"))
+ return -ENOENT;
+
+ ret = fwnode_property_read_u32_array(child,
+ "adi,output-range-microvolt",
+ vals, 2);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "invalid adi,output-range-microvolt\n");
+
+ ret = ad3552r_find_range(model_info, vals);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "invalid adi,output-range-microvolt value\n");
+
+ *val = ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad3552r_get_output_range, IIO_AD3552R);
+
+MODULE_DESCRIPTION("ad3552r common functions");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c
index 75825d6a398b..bbfb451a5da1 100644
--- a/drivers/iio/dac/ad3552r.c
+++ b/drivers/iio/dac/ad3552r.c
@@ -11,226 +11,9 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
-#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
-/* Register addresses */
-/* Primary address space */
-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00
-#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0))
-#define AD3552R_MASK_ADDR_ASCENSION BIT(5)
-#define AD3552R_MASK_SDO_ACTIVE BIT(4)
-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01
-#define AD3552R_MASK_SINGLE_INST BIT(7)
-#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3)
-#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02
-#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n))
-#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2)
-#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0)
-#define AD3552R_REG_ADDR_CHIP_TYPE 0x03
-#define AD3552R_MASK_CLASS GENMASK(7, 0)
-#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04
-#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05
-#define AD3552R_REG_ADDR_CHIP_GRADE 0x06
-#define AD3552R_MASK_GRADE GENMASK(7, 4)
-#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0)
-#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A
-#define AD3552R_REG_ADDR_SPI_REVISION 0x0B
-#define AD3552R_REG_ADDR_VENDOR_L 0x0C
-#define AD3552R_REG_ADDR_VENDOR_H 0x0D
-#define AD3552R_REG_ADDR_STREAM_MODE 0x0E
-#define AD3552R_MASK_LENGTH GENMASK(7, 0)
-#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F
-#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6)
-#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2)
-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10
-#define AD3552R_MASK_CRC_ENABLE (GENMASK(7, 6) |\
- GENMASK(1, 0))
-#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5)
-#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11
-#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7)
-#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5)
-#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3)
-#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2)
-#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1)
-#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0)
-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14
-#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6)
-#define AD3552R_MASK_MEM_CRC_EN BIT(4)
-#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2)
-#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1)
-#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0)
-#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15
-#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6)
-#define AD3552R_MASK_SAMPLE_HOLD_DIFFERENTIAL_USER_EN BIT(5)
-#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3)
-#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2)
-#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0)
-#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16
-#define AD3552R_MASK_REF_RANGE_ALARM BIT(6)
-#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5)
-#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4)
-#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3)
-#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2)
-#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1)
-#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0)
-#define AD3552R_REG_ADDR_ERR_STATUS 0x17
-#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6)
-#define AD3552R_MASK_DUAL_SPI_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5)
-#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4)
-#define AD3552R_MASK_RESET_STATUS BIT(0)
-#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18
-#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch))
-#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch)
-#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19
-#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) ((ch) ? GENMASK(7, 4) :\
- GENMASK(3, 0))
-#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2)
-#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0)
-#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2)
-#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7)
-#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5)
-#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3)
-#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2)
-#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(0)
-/*
- * Secondary region
- * For multibyte registers specify the highest address because the access is
- * done in descending order
- */
-#define AD3552R_SECONDARY_REGION_START 0x28
-#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28
-#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - ch) * 2)
-#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E
-#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F
-#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31
-#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32
-#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - ch) * 2)
-/* 3 bytes registers */
-#define AD3552R_REG_START_24B 0x37
-#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37
-#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - ch) * 3)
-#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40
-#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41
-#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44
-#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45
-#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - ch) * 3)
-
-/* Useful defines */
-#define AD3552R_MAX_CH 2
-#define AD3552R_MASK_CH(ch) BIT(ch)
-#define AD3552R_MASK_ALL_CH GENMASK(1, 0)
-#define AD3552R_MAX_REG_SIZE 3
-#define AD3552R_READ_BIT BIT(7)
-#define AD3552R_ADDR_MASK GENMASK(6, 0)
-#define AD3552R_MASK_DAC_12B 0xFFF0
-#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8
-#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34
-#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2
-#define AD3552R_GAIN_SCALE 1000
-#define AD3552R_LDAC_PULSE_US 100
-
-enum ad3552r_ch_vref_select {
- /* Internal source with Vref I/O floating */
- AD3552R_INTERNAL_VREF_PIN_FLOATING,
- /* Internal source with Vref I/O at 2.5V */
- AD3552R_INTERNAL_VREF_PIN_2P5V,
- /* External source with Vref I/O as input */
- AD3552R_EXTERNAL_VREF_PIN_INPUT
-};
-
-enum ad3552r_id {
- AD3541R_ID = 0x400b,
- AD3542R_ID = 0x4009,
- AD3551R_ID = 0x400a,
- AD3552R_ID = 0x4008,
-};
-
-enum ad3552r_ch_output_range {
- /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
- AD3552R_CH_OUTPUT_RANGE_0__2P5V,
- /* Range from 0 V to 5 V. Requires Rfb1x connection */
- AD3552R_CH_OUTPUT_RANGE_0__5V,
- /* Range from 0 V to 10 V. Requires Rfb2x connection */
- AD3552R_CH_OUTPUT_RANGE_0__10V,
- /* Range from -5 V to 5 V. Requires Rfb2x connection */
- AD3552R_CH_OUTPUT_RANGE_NEG_5__5V,
- /* Range from -10 V to 10 V. Requires Rfb4x connection */
- AD3552R_CH_OUTPUT_RANGE_NEG_10__10V,
-};
-
-static const s32 ad3552r_ch_ranges[][2] = {
- [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500},
- [AD3552R_CH_OUTPUT_RANGE_0__5V] = {0, 5000},
- [AD3552R_CH_OUTPUT_RANGE_0__10V] = {0, 10000},
- [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000},
- [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = {-10000, 10000}
-};
-
-enum ad3542r_ch_output_range {
- /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
- AD3542R_CH_OUTPUT_RANGE_0__2P5V,
- /* Range from 0 V to 3 V. Requires Rfb1x connection */
- AD3542R_CH_OUTPUT_RANGE_0__3V,
- /* Range from 0 V to 5 V. Requires Rfb1x connection */
- AD3542R_CH_OUTPUT_RANGE_0__5V,
- /* Range from 0 V to 10 V. Requires Rfb2x connection */
- AD3542R_CH_OUTPUT_RANGE_0__10V,
- /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */
- AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
- /* Range from -5 V to 5 V. Requires Rfb2x connection */
- AD3542R_CH_OUTPUT_RANGE_NEG_5__5V,
-};
-
-static const s32 ad3542r_ch_ranges[][2] = {
- [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500},
- [AD3542R_CH_OUTPUT_RANGE_0__3V] = {0, 3000},
- [AD3542R_CH_OUTPUT_RANGE_0__5V] = {0, 5000},
- [AD3542R_CH_OUTPUT_RANGE_0__10V] = {0, 10000},
- [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = {-2500, 7500},
- [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000}
-};
-
-enum ad3552r_ch_gain_scaling {
- /* Gain scaling of 1 */
- AD3552R_CH_GAIN_SCALING_1,
- /* Gain scaling of 0.5 */
- AD3552R_CH_GAIN_SCALING_0_5,
- /* Gain scaling of 0.25 */
- AD3552R_CH_GAIN_SCALING_0_25,
- /* Gain scaling of 0.125 */
- AD3552R_CH_GAIN_SCALING_0_125,
-};
-
-/* Gain * AD3552R_GAIN_SCALE */
-static const s32 gains_scaling_table[] = {
- [AD3552R_CH_GAIN_SCALING_1] = 1000,
- [AD3552R_CH_GAIN_SCALING_0_5] = 500,
- [AD3552R_CH_GAIN_SCALING_0_25] = 250,
- [AD3552R_CH_GAIN_SCALING_0_125] = 125
-};
-
-struct ad3552r_ch_data {
- s32 scale_int;
- s32 scale_dec;
- s32 offset_int;
- s32 offset_dec;
- s16 gain_offset;
- u16 rfb;
- u8 n;
- u8 p;
- u8 range;
- bool range_override;
-};
-
-struct ad3552r_model_data {
- const char *model_name;
- enum ad3552r_id chip_id;
- unsigned int num_hw_channels;
- const s32 (*ranges_table)[2];
- int num_ranges;
- bool requires_output_range;
-};
+#include "ad3552r.h"
struct ad3552r_desc {
const struct ad3552r_model_data *model_data;
@@ -632,136 +415,35 @@ static int ad3552r_reset(struct ad3552r_desc *dac)
FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val));
}
-static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min,
- s32 *v_max)
-{
- s64 vref, tmp, common, offset, gn, gp;
- /*
- * From datasheet formula (In Volts):
- * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03]
- * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03]
- * Calculus are converted to milivolts
- */
- vref = 2500;
- /* 2.5 * 1.03 * 1000 (To mV) */
- common = 2575 * dac->ch_data[i].rfb;
- offset = dac->ch_data[i].gain_offset;
-
- gn = gains_scaling_table[dac->ch_data[i].n];
- tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common;
- tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
- *v_max = vref + tmp;
-
- gp = gains_scaling_table[dac->ch_data[i].p];
- tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common;
- tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
- *v_min = vref - tmp;
-}
-
-static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch)
-{
- s32 idx, v_max, v_min, span, rem;
- s64 tmp;
-
- if (dac->ch_data[ch].range_override) {
- ad3552r_get_custom_range(dac, ch, &v_min, &v_max);
- } else {
- /* Normal range */
- idx = dac->ch_data[ch].range;
- v_min = dac->model_data->ranges_table[idx][0];
- v_max = dac->model_data->ranges_table[idx][1];
- }
-
- /*
- * From datasheet formula:
- * Vout = Span * (D / 65536) + Vmin
- * Converted to scale and offset:
- * Scale = Span / 65536
- * Offset = 65536 * Vmin / Span
- *
- * Reminders are in micros in order to be printed as
- * IIO_VAL_INT_PLUS_MICRO
- */
- span = v_max - v_min;
- dac->ch_data[ch].scale_int = div_s64_rem(span, 65536, &rem);
- /* Do operations in microvolts */
- dac->ch_data[ch].scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000,
- 65536);
-
- dac->ch_data[ch].offset_int = div_s64_rem(v_min * 65536, span, &rem);
- tmp = (s64)rem * 1000000;
- dac->ch_data[ch].offset_dec = div_s64(tmp, span);
-}
-
-static int ad3552r_find_range(const struct ad3552r_model_data *model_data,
- s32 *vals)
-{
- int i;
-
- for (i = 0; i < model_data->num_ranges; i++)
- if (vals[0] == model_data->ranges_table[i][0] * 1000 &&
- vals[1] == model_data->ranges_table[i][1] * 1000)
- return i;
-
- return -EINVAL;
-}
-
static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
struct fwnode_handle *child,
u32 ch)
{
struct device *dev = &dac->spi->dev;
- u32 val;
int err;
u8 addr;
- u16 reg = 0, offset;
-
- struct fwnode_handle *gain_child __free(fwnode_handle)
- = fwnode_get_named_child_node(child,
- "custom-output-range-config");
- if (!gain_child)
- return dev_err_probe(dev, -EINVAL,
- "mandatory custom-output-range-config property missing\n");
-
- dac->ch_data[ch].range_override = 1;
- reg |= FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1);
-
- err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
- if (err)
- return dev_err_probe(dev, err,
- "mandatory adi,gain-scaling-p property missing\n");
- reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, val);
- dac->ch_data[ch].p = val;
-
- err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
- if (err)
- return dev_err_probe(dev, err,
- "mandatory adi,gain-scaling-n property missing\n");
- reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, val);
- dac->ch_data[ch].n = val;
-
- err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
- if (err)
- return dev_err_probe(dev, err,
- "mandatory adi,rfb-ohms property missing\n");
- dac->ch_data[ch].rfb = val;
+ u16 reg;
- err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
+ err = ad3552r_get_custom_gain(dev, child,
+ &dac->ch_data[ch].p,
+ &dac->ch_data[ch].n,
+ &dac->ch_data[ch].rfb,
+ &dac->ch_data[ch].gain_offset);
if (err)
- return dev_err_probe(dev, err,
- "mandatory adi,gain-offset property missing\n");
- dac->ch_data[ch].gain_offset = val;
+ return err;
- offset = abs((s32)val);
- reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, (offset >> 8));
+ dac->ch_data[ch].range_override = 1;
- reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, (s32)val < 0);
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
err = ad3552r_write_reg(dac, addr,
- offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
+ abs((s32)dac->ch_data[ch].gain_offset) &
+ AD3552R_MASK_CH_OFFSET_BITS_0_7);
if (err)
return dev_err_probe(dev, err, "Error writing register\n");
+ reg = ad3552r_calc_custom_gain(dac->ch_data[ch].p, dac->ch_data[ch].n,
+ dac->ch_data[ch].gain_offset);
+
err = ad3552r_write_reg(dac, addr, reg);
if (err)
return dev_err_probe(dev, err, "Error writing register\n");
@@ -772,30 +454,17 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
static int ad3552r_configure_device(struct ad3552r_desc *dac)
{
struct device *dev = &dac->spi->dev;
- int err, cnt = 0, voltage, delta = 100000;
- u32 vals[2], val, ch;
+ int err, cnt = 0;
+ u32 val, ch;
dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH);
if (IS_ERR(dac->gpio_ldac))
return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac),
"Error getting gpio ldac");
- voltage = devm_regulator_get_enable_read_voltage(dev, "vref");
- if (voltage < 0 && voltage != -ENODEV)
- return dev_err_probe(dev, voltage, "Error getting vref voltage\n");
-
- if (voltage == -ENODEV) {
- if (device_property_read_bool(dev, "adi,vref-out-en"))
- val = AD3552R_INTERNAL_VREF_PIN_2P5V;
- else
- val = AD3552R_INTERNAL_VREF_PIN_FLOATING;
- } else {
- if (voltage > 2500000 + delta || voltage < 2500000 - delta) {
- dev_warn(dev, "vref-supply must be 2.5V");
- return -EINVAL;
- }
- val = AD3552R_EXTERNAL_VREF_PIN_INPUT;
- }
+ err = ad3552r_get_ref_voltage(dev, &val);
+ if (err < 0)
+ return err;
err = ad3552r_update_reg_field(dac,
AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
@@ -804,13 +473,8 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
if (err)
return err;
- err = device_property_read_u32(dev, "adi,sdo-drive-strength", &val);
+ err = ad3552r_get_drive_strength(dev, &val);
if (!err) {
- if (val > 3) {
- dev_err(dev, "adi,sdo-drive-strength must be less than 4\n");
- return -EINVAL;
- }
-
err = ad3552r_update_reg_field(dac,
AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
AD3552R_MASK_SDO_DRIVE_STRENGTH,
@@ -835,21 +499,12 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
"reg must be less than %d\n",
dac->model_data->num_hw_channels);
- if (fwnode_property_present(child, "adi,output-range-microvolt")) {
- err = fwnode_property_read_u32_array(child,
- "adi,output-range-microvolt",
- vals,
- 2);
- if (err)
- return dev_err_probe(dev, err,
- "adi,output-range-microvolt property could not be parsed\n");
-
- err = ad3552r_find_range(dac->model_data, vals);
- if (err < 0)
- return dev_err_probe(dev, err,
- "Invalid adi,output-range-microvolt value\n");
+ err = ad3552r_get_output_range(dev, dac->model_data,
+ child, &val);
+ if (err && err != -ENOENT)
+ return err;
- val = err;
+ if (!err) {
if (ch == 0)
val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val);
else
@@ -873,7 +528,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
return err;
}
- ad3552r_calc_gain_and_offset(dac, ch);
+ ad3552r_calc_gain_and_offset(&dac->ch_data[ch], dac->model_data);
dac->enabled_ch |= BIT(ch);
if (ch == 0)
@@ -1072,3 +727,4 @@ module_spi_driver(ad3552r_driver);
MODULE_AUTHOR("Mihail Chindris <mihail.chindris@analog.com>");
MODULE_DESCRIPTION("Analog Device AD3552R DAC");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD3552R);
diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h
new file mode 100644
index 000000000000..22bd9ad27c65
--- /dev/null
+++ b/drivers/iio/dac/ad3552r.h
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AD3552R Digital <-> Analog converters common header
+ *
+ * Copyright 2021-2024 Analog Devices Inc.
+ * Author: Angelo Dureghello <adureghello@baylibre.com>
+ */
+
+#ifndef __DRIVERS_IIO_DAC_AD3552R_H__
+#define __DRIVERS_IIO_DAC_AD3552R_H__
+
+/* Register addresses */
+/* Primary address space */
+#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00
+#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0))
+#define AD3552R_MASK_ADDR_ASCENSION BIT(5)
+#define AD3552R_MASK_SDO_ACTIVE BIT(4)
+#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01
+#define AD3552R_MASK_SINGLE_INST BIT(7)
+#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3)
+#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02
+#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n))
+#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2)
+#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0)
+#define AD3552R_REG_ADDR_CHIP_TYPE 0x03
+#define AD3552R_MASK_CLASS GENMASK(7, 0)
+#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04
+#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05
+#define AD3552R_REG_ADDR_CHIP_GRADE 0x06
+#define AD3552R_MASK_GRADE GENMASK(7, 4)
+#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0)
+#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A
+#define AD3552R_REG_ADDR_SPI_REVISION 0x0B
+#define AD3552R_REG_ADDR_VENDOR_L 0x0C
+#define AD3552R_REG_ADDR_VENDOR_H 0x0D
+#define AD3552R_REG_ADDR_STREAM_MODE 0x0E
+#define AD3552R_MASK_LENGTH GENMASK(7, 0)
+#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F
+#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6)
+#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2)
+#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10
+#define AD3552R_MASK_CRC_ENABLE (GENMASK(7, 6) |\
+ GENMASK(1, 0))
+#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5)
+#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11
+#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7)
+#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5)
+#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3)
+#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2)
+#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1)
+#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0)
+#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14
+#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6)
+#define AD3552R_MASK_MEM_CRC_EN BIT(4)
+#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2)
+#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1)
+#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0)
+#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15
+#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6)
+#define AD3552R_MASK_SAMPLE_HOLD_DIFF_USER_EN BIT(5)
+#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3)
+#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2)
+#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0)
+#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16
+#define AD3552R_MASK_REF_RANGE_ALARM BIT(6)
+#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5)
+#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4)
+#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3)
+#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2)
+#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1)
+#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0)
+#define AD3552R_REG_ADDR_ERR_STATUS 0x17
+#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6)
+#define AD3552R_MASK_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5)
+#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4)
+#define AD3552R_MASK_RESET_STATUS BIT(0)
+#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18
+#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch))
+#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch)
+#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19
+#define AD3552R_MASK_CH0_RANGE GENMASK(2, 0)
+#define AD3552R_MASK_CH1_RANGE GENMASK(6, 4)
+#define AD3552R_MASK_CH_OUTPUT_RANGE GENMASK(7, 0)
+#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) ((ch) ? \
+ GENMASK(7, 4) : \
+ GENMASK(3, 0))
+#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2)
+#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0)
+#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2)
+#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7)
+#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5)
+#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3)
+#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2)
+#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(8)
+/*
+ * Secondary region
+ * For multibyte registers specify the highest address because the access is
+ * done in descending order
+ */
+#define AD3552R_SECONDARY_REGION_START 0x28
+#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28
+#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - (ch)) * 2)
+#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E
+#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F
+#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31
+#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32
+#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - (ch)) * 2)
+/* 3 bytes registers */
+#define AD3552R_REG_START_24B 0x37
+#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37
+#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - (ch)) * 3)
+#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40
+#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41
+#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44
+#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45
+#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - (ch)) * 3)
+
+/* Useful defines */
+#define AD3552R_MAX_CH 2
+#define AD3552R_MASK_CH(ch) BIT(ch)
+#define AD3552R_MASK_ALL_CH GENMASK(1, 0)
+#define AD3552R_MAX_REG_SIZE 3
+#define AD3552R_READ_BIT BIT(7)
+#define AD3552R_ADDR_MASK GENMASK(6, 0)
+#define AD3552R_MASK_DAC_12B GENMASK(15, 4)
+#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8
+#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34
+#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2
+#define AD3552R_GAIN_SCALE 1000
+#define AD3552R_LDAC_PULSE_US 100
+
+#define AD3552R_MAX_RANGES 5
+#define AD3542R_MAX_RANGES 6
+
+extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2];
+extern const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2];
+
+enum ad3552r_id {
+ AD3541R_ID = 0x400b,
+ AD3542R_ID = 0x4009,
+ AD3551R_ID = 0x400a,
+ AD3552R_ID = 0x4008,
+};
+
+struct ad3552r_model_data {
+ const char *model_name;
+ enum ad3552r_id chip_id;
+ unsigned int num_hw_channels;
+ const s32 (*ranges_table)[2];
+ int num_ranges;
+ bool requires_output_range;
+};
+
+struct ad3552r_ch_data {
+ s32 scale_int;
+ s32 scale_dec;
+ s32 offset_int;
+ s32 offset_dec;
+ s16 gain_offset;
+ u16 rfb;
+ u8 n;
+ u8 p;
+ u8 range;
+ bool range_override;
+};
+
+enum ad3552r_ch_gain_scaling {
+ /* Gain scaling of 1 */
+ AD3552R_CH_GAIN_SCALING_1,
+ /* Gain scaling of 0.5 */
+ AD3552R_CH_GAIN_SCALING_0_5,
+ /* Gain scaling of 0.25 */
+ AD3552R_CH_GAIN_SCALING_0_25,
+ /* Gain scaling of 0.125 */
+ AD3552R_CH_GAIN_SCALING_0_125,
+};
+
+enum ad3552r_ch_vref_select {
+ /* Internal source with Vref I/O floating */
+ AD3552R_INTERNAL_VREF_PIN_FLOATING,
+ /* Internal source with Vref I/O at 2.5V */
+ AD3552R_INTERNAL_VREF_PIN_2P5V,
+ /* External source with Vref I/O as input */
+ AD3552R_EXTERNAL_VREF_PIN_INPUT
+};
+
+enum ad3542r_ch_output_range {
+ /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
+ AD3542R_CH_OUTPUT_RANGE_0__2P5V,
+ /* Range from 0 V to 3 V. Requires Rfb1x connection */
+ AD3542R_CH_OUTPUT_RANGE_0__3V,
+ /* Range from 0 V to 5 V. Requires Rfb1x connection */
+ AD3542R_CH_OUTPUT_RANGE_0__5V,
+ /* Range from 0 V to 10 V. Requires Rfb2x connection */
+ AD3542R_CH_OUTPUT_RANGE_0__10V,
+ /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */
+ AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
+ /* Range from -5 V to 5 V. Requires Rfb2x connection */
+ AD3542R_CH_OUTPUT_RANGE_NEG_5__5V,
+};
+
+enum ad3552r_ch_output_range {
+ /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
+ AD3552R_CH_OUTPUT_RANGE_0__2P5V,
+ /* Range from 0 V to 5 V. Requires Rfb1x connection */
+ AD3552R_CH_OUTPUT_RANGE_0__5V,
+ /* Range from 0 V to 10 V. Requires Rfb2x connection */
+ AD3552R_CH_OUTPUT_RANGE_0__10V,
+ /* Range from -5 V to 5 V. Requires Rfb2x connection */
+ AD3552R_CH_OUTPUT_RANGE_NEG_5__5V,
+ /* Range from -10 V to 10 V. Requires Rfb4x connection */
+ AD3552R_CH_OUTPUT_RANGE_NEG_10__10V,
+};
+
+int ad3552r_get_output_range(struct device *dev,
+ const struct ad3552r_model_data *model_info,
+ struct fwnode_handle *child, u32 *val);
+int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child,
+ u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs);
+u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs);
+int ad3552r_get_ref_voltage(struct device *dev, u32 *val);
+int ad3552r_get_drive_strength(struct device *dev, u32 *val);
+void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data,
+ const struct ad3552r_model_data *model_data);
+
+#endif /* __DRIVERS_IIO_DAC_AD3552R_H__ */
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
` (5 preceding siblings ...)
2024-10-21 12:40 ` [PATCH v7 6/8] iio: dac: ad3552r: extract common code (no changes in behavior intended) Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-22 12:28 ` Nuno Sá
2024-10-21 12:40 ` [PATCH v7 8/8] iio: dac: adi-axi-dac: add registering of child fdt node Angelo Dureghello
7 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Add High Speed ad3552r platform driver.
The ad3552r DAC is controlled by a custom (fpga-based) DAC IP
through the current AXI backend, or similar alternative IIO backend.
Compared to the existing driver (ad3552r.c), that is a simple SPI
driver, this driver is coupled with a DAC IIO backend that finally
controls the ad3552r by a fpga-based "QSPI+DDR" interface, to reach
maximum transfer rate of 33MUPS using dma stream capabilities.
All commands involving QSPI bus read/write are delegated to the backend
through the provided APIs for bus read/write.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
drivers/iio/dac/Kconfig | 14 ++
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/ad3552r-hs.c | 547 +++++++++++++++++++++++++++++++++++++++++++
drivers/iio/dac/ad3552r-hs.h | 18 ++
drivers/iio/dac/ad3552r.h | 4 +
5 files changed, 584 insertions(+)
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index fa091995d002..fc11698e88f2 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -6,6 +6,20 @@
menu "Digital to analog converters"
+config AD3552R_HS
+ tristate "Analog Devices AD3552R DAC High Speed driver"
+ select ADI_AXI_DAC
+ help
+ Say yes here to build support for Analog Devices AD3552R
+ Digital to Analog Converter High Speed driver.
+
+ The driver requires the assistance of an IP core to operate,
+ since data is streamed into target device via DMA, sent over a
+ QSPI + DDR (Double Data Rate) bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad3552r-hs.
+
config AD3552R
tristate "Analog Devices AD3552R DAC driver"
depends on SPI_MASTER
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index c92de0366238..d92e08ca93ca 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -4,6 +4,7 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o ad3552r-common.o
obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
obj-$(CONFIG_AD5360) += ad5360.o
obj-$(CONFIG_AD5380) += ad5380.o
diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
new file mode 100644
index 000000000000..27bdc35fdc29
--- /dev/null
+++ b/drivers/iio/dac/ad3552r-hs.c
@@ -0,0 +1,547 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD3552R
+ * Digital to Analog converter driver, High Speed version
+ *
+ * Copyright 2024 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/units.h>
+
+#include "ad3552r.h"
+#include "ad3552r-hs.h"
+
+struct ad3552r_hs_state {
+ const struct ad3552r_model_data *model_data;
+ struct gpio_desc *reset_gpio;
+ struct device *dev;
+ struct iio_backend *back;
+ bool single_channel;
+ struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
+ struct ad3552r_hs_platform_data *data;
+};
+
+static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st,
+ u32 reg, u32 mask, u32 val,
+ size_t xfer_size)
+{
+ u32 rval;
+ int ret;
+
+ ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size);
+ if (ret)
+ return ret;
+
+ rval = (rval & ~mask) | val;
+
+ return st->data->bus_reg_write(st->back, reg, rval, xfer_size);
+}
+
+static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad3552r_hs_state *st = iio_priv(indio_dev);
+ int ret;
+ int ch = chan->channel;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ int sclk;
+
+ ret = iio_backend_read_raw(st->back, chan, &sclk, 0,
+ IIO_CHAN_INFO_FREQUENCY);
+ if (ret != IIO_VAL_INT)
+ return -EINVAL;
+
+ /*
+ * Using 4 lanes (QSPI), then using 2 as DDR mode is
+ * considered always on (considering buffering mode always).
+ */
+ *val = DIV_ROUND_CLOSEST(sclk * 4 * 2,
+ chan->scan_type.realbits);
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_RAW:
+ ret = st->data->bus_reg_read(st->back,
+ AD3552R_REG_ADDR_CH_DAC_16B(chan->channel),
+ val, 2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->ch_data[ch].scale_int;
+ *val2 = st->ch_data[ch].scale_dec;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = st->ch_data[ch].offset_int;
+ *val2 = st->ch_data[ch].offset_dec;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad3552r_hs_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ad3552r_hs_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ return st->data->bus_reg_write(st->back,
+ AD3552R_REG_ADDR_CH_DAC_16B(chan->channel),
+ val, 2);
+ }
+ unreachable();
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad3552r_hs_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad3552r_hs_state *st = iio_priv(indio_dev);
+ struct iio_backend_data_fmt fmt = {
+ .type = IIO_BACKEND_DATA_UNSIGNED
+ };
+ int loop_len, val, ret;
+
+ switch (*indio_dev->active_scan_mask) {
+ case AD3552R_CH0_ACTIVE:
+ st->single_channel = true;
+ loop_len = 2;
+ val = AD3552R_REG_ADDR_CH_DAC_16B(0);
+ break;
+ case AD3552R_CH1_ACTIVE:
+ st->single_channel = true;
+ loop_len = 2;
+ val = AD3552R_REG_ADDR_CH_DAC_16B(1);
+ break;
+ case AD3552R_CH0_ACTIVE | AD3552R_CH1_ACTIVE:
+ st->single_channel = false;
+ loop_len = 4;
+ val = AD3552R_REG_ADDR_CH_DAC_16B(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_STREAM_MODE,
+ loop_len, 1);
+ if (ret)
+ return ret;
+
+ /* Inform DAC chip to switch into DDR mode */
+ ret = ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+ AD3552R_MASK_SPI_CONFIG_DDR,
+ AD3552R_MASK_SPI_CONFIG_DDR, 1);
+ if (ret)
+ return ret;
+
+ /* Inform DAC IP to go for DDR mode from now on */
+ ret = iio_backend_ddr_enable(st->back);
+ if (ret) {
+ dev_err(st->dev, "could not set DDR mode, not streaming");
+ goto exit_err;
+ }
+
+ ret = iio_backend_data_transfer_addr(st->back, val);
+ if (ret)
+ goto exit_err;
+
+ ret = iio_backend_data_format_set(st->back, 0, &fmt);
+ if (ret)
+ goto exit_err;
+
+ ret = iio_backend_data_stream_enable(st->back);
+ if (ret)
+ goto exit_err;
+
+ return 0;
+
+exit_err:
+ ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+ AD3552R_MASK_SPI_CONFIG_DDR,
+ 0, 1);
+
+ iio_backend_ddr_disable(st->back);
+
+ return ret;
+}
+
+static int ad3552r_hs_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad3552r_hs_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_backend_data_stream_disable(st->back);
+ if (ret)
+ return ret;
+
+ /* Inform DAC to set in SDR mode */
+ ret = ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+ AD3552R_MASK_SPI_CONFIG_DDR,
+ 0, 1);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_ddr_disable(st->back);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static inline int ad3552r_hs_set_output_range(struct ad3552r_hs_state *st,
+ int ch, unsigned int mode)
+{
+ int val;
+
+ if (ch == 0)
+ val = FIELD_PREP(AD3552R_MASK_CH0_RANGE, mode);
+ else
+ val = FIELD_PREP(AD3552R_MASK_CH1_RANGE, mode);
+
+ return ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
+ AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch),
+ val, 1);
+}
+
+static int ad3552r_hs_reset(struct ad3552r_hs_state *st)
+{
+ int ret;
+
+ /*
+ * Using inverted "active-high" logic here, since ad3552r classic-spi
+ * fdt node (and driver) is using the same logic.
+ */
+
+ st->reset_gpio = devm_gpiod_get_optional(st->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(st->reset_gpio))
+ return PTR_ERR(st->reset_gpio);
+
+ if (st->reset_gpio) {
+ fsleep(10);
+ gpiod_set_value_cansleep(st->reset_gpio, 1);
+ } else {
+ ret = ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
+ AD3552R_MASK_SOFTWARE_RESET,
+ AD3552R_MASK_SOFTWARE_RESET, 1);
+ if (ret)
+ return ret;
+ }
+ msleep(100);
+
+ return 0;
+}
+
+static int ad3552r_hs_scratch_pad_test(struct ad3552r_hs_state *st)
+{
+ int ret, val;
+
+ ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
+ AD3552R_SCRATCH_PAD_TEST_VAL1, 1);
+ if (ret)
+ return ret;
+
+ ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
+ &val, 1);
+ if (ret)
+ return ret;
+
+ if (val != AD3552R_SCRATCH_PAD_TEST_VAL1)
+ return dev_err_probe(st->dev, -EIO,
+ "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read 0x%x\n",
+ AD3552R_SCRATCH_PAD_TEST_VAL1, val);
+
+ ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
+ AD3552R_SCRATCH_PAD_TEST_VAL2, 1);
+ if (ret)
+ return ret;
+
+ ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
+ &val, 1);
+ if (ret)
+ return ret;
+
+ if (val != AD3552R_SCRATCH_PAD_TEST_VAL2)
+ return dev_err_probe(st->dev, -EIO,
+ "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read 0x%x\n",
+ AD3552R_SCRATCH_PAD_TEST_VAL2, val);
+
+ return 0;
+}
+
+static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st,
+ int ch, u16 gain, u16 offset)
+{
+ int ret;
+
+ ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_OFFSET(ch),
+ offset, 1);
+ if (ret)
+ return dev_err_probe(st->dev, ret, "Error writing register\n");
+
+ ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch),
+ gain, 1);
+ if (ret)
+ return dev_err_probe(st->dev, ret, "Error writing register\n");
+
+ return 0;
+}
+
+static int ad3552r_hs_setup(struct ad3552r_hs_state *st)
+{
+ s16 goffs;
+ u16 id;
+ u16 gain = 0, offset = 0;
+ u32 ch, val, range;
+ int ret;
+
+ ret = ad3552r_hs_reset(st);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_ddr_disable(st->back);
+ if (ret)
+ return ret;
+
+ ret = ad3552r_hs_scratch_pad_test(st);
+ if (ret)
+ return ret;
+
+ ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L,
+ &val, 1);
+ if (ret)
+ return ret;
+
+ id = val;
+
+ ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H,
+ &val, 1);
+ if (ret)
+ return ret;
+
+ id |= val << 8;
+ if (id != st->model_data->chip_id)
+ dev_info(st->dev, "Chip ID error. Expected 0x%x, Read 0x%x\n",
+ AD3552R_ID, id);
+
+ ret = st->data->bus_reg_write(st->back,
+ AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
+ 0, 1);
+ if (ret)
+ return ret;
+
+ ret = st->data->bus_reg_write(st->back,
+ AD3552R_REG_ADDR_TRANSFER_REGISTER,
+ FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE,
+ AD3552R_QUAD_SPI) |
+ AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL);
+ if (ret)
+ return ret;
+
+ ret = ad3552r_get_ref_voltage(st->dev, &val);
+ if (ret < 0)
+ return ret;
+
+ val = ret;
+
+ ret = ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
+ AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
+ val, 1);
+ if (ret)
+ return ret;
+
+ ret = ad3552r_get_drive_strength(st->dev, &val);
+ if (!ret) {
+ ret = ad3552r_qspi_update_reg_bits(st,
+ AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+ AD3552R_MASK_SDO_DRIVE_STRENGTH,
+ val, 1);
+ if (ret)
+ return ret;
+ }
+
+ device_for_each_child_node_scoped(st->dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &ch);
+ if (ret)
+ return dev_err_probe(st->dev, ret,
+ "reg property missing\n");
+
+ ret = ad3552r_get_output_range(st->dev, st->model_data, child,
+ &range);
+ if (!ret) {
+ st->ch_data[ch].range = range;
+
+ ret = ad3552r_hs_set_output_range(st, ch, range);
+ if (ret)
+ return ret;
+
+ } else if (ret == -ENOENT) {
+ ret = ad3552r_get_custom_gain(st->dev, child,
+ &st->ch_data[ch].p,
+ &st->ch_data[ch].n,
+ &st->ch_data[ch].rfb,
+ &st->ch_data[ch].gain_offset);
+ if (ret)
+ return ret;
+
+ gain = ad3552r_calc_custom_gain(st->ch_data[ch].p,
+ st->ch_data[ch].n,
+ st->ch_data[ch].gain_offset);
+ offset = abs(goffs);
+
+ st->ch_data[ch].range_override = 1;
+
+ ret = ad3552r_hs_setup_custom_gain(st, ch, gain,
+ offset);
+ if (ret)
+ return ret;
+ } else {
+ return ret;
+ }
+
+ ad3552r_calc_gain_and_offset(&st->ch_data[ch], st->model_data);
+ }
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = {
+ .postenable = ad3552r_hs_buffer_postenable,
+ .predisable = ad3552r_hs_buffer_predisable,
+};
+
+#define AD3552R_CHANNEL(ch) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .output = 1, \
+ .indexed = 1, \
+ .channel = (ch), \
+ .scan_index = (ch), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ } \
+}
+
+static const struct iio_chan_spec ad3552r_hs_channels[] = {
+ AD3552R_CHANNEL(0),
+ AD3552R_CHANNEL(1),
+};
+
+static const struct iio_info ad3552r_hs_info = {
+ .read_raw = &ad3552r_hs_read_raw,
+ .write_raw = &ad3552r_hs_write_raw,
+};
+
+static int ad3552r_hs_probe(struct platform_device *pdev)
+{
+ struct ad3552r_hs_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->dev = &pdev->dev;
+
+ st->data = pdev->dev.platform_data;
+ if (!st->data)
+ return dev_err_probe(st->dev, -ENODEV, "No platform data !");
+
+ st->back = devm_iio_backend_get(&pdev->dev, NULL);
+ if (IS_ERR(st->back))
+ return PTR_ERR(st->back);
+
+ ret = devm_iio_backend_enable(&pdev->dev, st->back);
+ if (ret)
+ return ret;
+
+ st->model_data = device_get_match_data(&pdev->dev);
+ if (!st->model_data)
+ return -ENODEV;
+
+ indio_dev->name = "ad3552r";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->setup_ops = &ad3552r_hs_buffer_setup_ops;
+ indio_dev->channels = ad3552r_hs_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad3552r_hs_channels);
+ indio_dev->info = &ad3552r_hs_info;
+
+ ret = devm_iio_backend_request_buffer(&pdev->dev, st->back, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad3552r_hs_setup(st);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct ad3552r_model_data ad3552r_model_data = {
+ .model_name = "ad3552r",
+ .chip_id = AD3552R_ID,
+ .num_hw_channels = 2,
+ .ranges_table = ad3552r_ch_ranges,
+ .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges),
+};
+
+static const struct of_device_id ad3552r_hs_of_id[] = {
+ { .compatible = "adi,ad3552r", .data = &ad3552r_model_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad3552r_hs_of_id);
+
+static struct platform_driver ad3552r_hs_driver = {
+ .driver = {
+ .name = "ad3552r-hs",
+ .of_match_table = ad3552r_hs_of_id,
+ },
+ .probe = ad3552r_hs_probe,
+};
+module_platform_driver(ad3552r_hs_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Angelo Dureghello <adueghello@baylibre.com>");
+MODULE_DESCRIPTION("AD3552R Driver - High Speed version");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BACKEND);
+MODULE_IMPORT_NS(IIO_AD3552R);
diff --git a/drivers/iio/dac/ad3552r-hs.h b/drivers/iio/dac/ad3552r-hs.h
new file mode 100644
index 000000000000..dbf71d5e58c1
--- /dev/null
+++ b/drivers/iio/dac/ad3552r-hs.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2024 Analog Devices Inc.
+ * Copyright (c) 2024 Baylibre, SAS
+ */
+#ifndef __LINUX_PLATFORM_DATA_AD3552R_HS_H__
+#define __LINUX_PLATFORM_DATA_AD3552R_HS_H__
+
+struct iio_backend;
+
+struct ad3552r_hs_platform_data {
+ int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val,
+ size_t data_size);
+ int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val,
+ size_t data_size);
+};
+
+#endif /* __LINUX_PLATFORM_DATA_AD3552R_HS_H__ */
diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h
index 22bd9ad27c65..6120a08e08a4 100644
--- a/drivers/iio/dac/ad3552r.h
+++ b/drivers/iio/dac/ad3552r.h
@@ -129,8 +129,12 @@
#define AD3552R_GAIN_SCALE 1000
#define AD3552R_LDAC_PULSE_US 100
+#define AD3552R_CH0_ACTIVE BIT(0)
+#define AD3552R_CH1_ACTIVE BIT(1)
+
#define AD3552R_MAX_RANGES 5
#define AD3542R_MAX_RANGES 6
+#define AD3552R_QUAD_SPI 2
extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2];
extern const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2];
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-21 12:40 ` [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver Angelo Dureghello
@ 2024-10-22 12:28 ` Nuno Sá
2024-10-22 16:40 ` Angelo Dureghello
0 siblings, 1 reply; 29+ messages in thread
From: Nuno Sá @ 2024-10-22 12:28 UTC (permalink / raw)
To: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown
On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> From: Angelo Dureghello <adureghello@baylibre.com>
>
> Add High Speed ad3552r platform driver.
>
> The ad3552r DAC is controlled by a custom (fpga-based) DAC IP
> through the current AXI backend, or similar alternative IIO backend.
>
> Compared to the existing driver (ad3552r.c), that is a simple SPI
> driver, this driver is coupled with a DAC IIO backend that finally
> controls the ad3552r by a fpga-based "QSPI+DDR" interface, to reach
> maximum transfer rate of 33MUPS using dma stream capabilities.
>
> All commands involving QSPI bus read/write are delegated to the backend
> through the provided APIs for bus read/write.
>
> Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> ---
> drivers/iio/dac/Kconfig | 14 ++
> drivers/iio/dac/Makefile | 1 +
> drivers/iio/dac/ad3552r-hs.c | 547
> +++++++++++++++++++++++++++++++++++++++++++
> drivers/iio/dac/ad3552r-hs.h | 18 ++
> drivers/iio/dac/ad3552r.h | 4 +
> 5 files changed, 584 insertions(+)
>
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index fa091995d002..fc11698e88f2 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -6,6 +6,20 @@
>
> menu "Digital to analog converters"
>
> +config AD3552R_HS
> + tristate "Analog Devices AD3552R DAC High Speed driver"
> + select ADI_AXI_DAC
> + help
> + Say yes here to build support for Analog Devices AD3552R
> + Digital to Analog Converter High Speed driver.
> +
> + The driver requires the assistance of an IP core to operate,
> + since data is streamed into target device via DMA, sent over a
> + QSPI + DDR (Double Data Rate) bus.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called ad3552r-hs.
> +
> config AD3552R
> tristate "Analog Devices AD3552R DAC driver"
> depends on SPI_MASTER
> diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> index c92de0366238..d92e08ca93ca 100644
> --- a/drivers/iio/dac/Makefile
> +++ b/drivers/iio/dac/Makefile
> @@ -4,6 +4,7 @@
> #
>
> # When adding new entries keep the list in alphabetical order
> +obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o ad3552r-common.o
> obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
> obj-$(CONFIG_AD5360) += ad5360.o
> obj-$(CONFIG_AD5380) += ad5380.o
> diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
> new file mode 100644
> index 000000000000..27bdc35fdc29
> --- /dev/null
> +++ b/drivers/iio/dac/ad3552r-hs.c
> @@ -0,0 +1,547 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Analog Devices AD3552R
> + * Digital to Analog converter driver, High Speed version
> + *
> + * Copyright 2024 Analog Devices Inc.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/iio/backend.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/units.h>
> +
> +#include "ad3552r.h"
> +#include "ad3552r-hs.h"
> +
> +struct ad3552r_hs_state {
> + const struct ad3552r_model_data *model_data;
> + struct gpio_desc *reset_gpio;
> + struct device *dev;
> + struct iio_backend *back;
> + bool single_channel;
> + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
> + struct ad3552r_hs_platform_data *data;
> +};
> +
> +static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st,
> + u32 reg, u32 mask, u32 val,
> + size_t xfer_size)
> +{
> + u32 rval;
> + int ret;
> +
> + ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size);
> + if (ret)
> + return ret;
> +
> + rval = (rval & ~mask) | val;
> +
> + return st->data->bus_reg_write(st->back, reg, rval, xfer_size);
> +}
> +
> +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> + int ret;
> + int ch = chan->channel;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ: {
> + int sclk;
> +
> + ret = iio_backend_read_raw(st->back, chan, &sclk, 0,
> + IIO_CHAN_INFO_FREQUENCY);
> + if (ret != IIO_VAL_INT)
> + return -EINVAL;
> +
I just saw you had some questions on v6 that everyone failed to see. See my
reply to David here:
https://lore.kernel.org/linux-iio/61cf3072af74a8b2951c948ddc2383ba1e55954d.camel@gmail.com/
It should be easy and it's something that makes sense (at least to me :))
> + /*
> + * Using 4 lanes (QSPI), then using 2 as DDR mode is
> + * considered always on (considering buffering mode always).
> + */
> + *val = DIV_ROUND_CLOSEST(sclk * 4 * 2,
> + chan->scan_type.realbits);
> +
> + return IIO_VAL_INT;
> + }
> + case IIO_CHAN_INFO_RAW:
> + ret = st->data->bus_reg_read(st->back,
> + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel),
> + val, 2);
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + *val = st->ch_data[ch].scale_int;
> + *val2 = st->ch_data[ch].scale_dec;
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_OFFSET:
> + *val = st->ch_data[ch].offset_int;
> + *val2 = st->ch_data[ch].offset_dec;
> + return IIO_VAL_INT_PLUS_MICRO;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int ad3552r_hs_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> + return st->data->bus_reg_write(st->back,
> + AD3552R_REG_ADDR_CH_DAC_16B(chan-
> >channel),
> + val, 2);
> + }
Maybe we'll get the new stuff in time for this :)
...
> +
> +static int ad3552r_hs_reset(struct ad3552r_hs_state *st)
> +{
> + int ret;
> +
> + /*
> + * Using inverted "active-high" logic here, since ad3552r classic-spi
> + * fdt node (and driver) is using the same logic.
> + */
> +
I don't understand this. This is a new device with a different compatible. Why
keeping the wrong logic? AFAICT, there's nothing in the bindings about the pin
polarity.
> + st->reset_gpio = devm_gpiod_get_optional(st->dev,
> + "reset", GPIOD_OUT_LOW);
> + if (IS_ERR(st->reset_gpio))
> + return PTR_ERR(st->reset_gpio);
> +
> + if (st->reset_gpio) {
> + fsleep(10);
> + gpiod_set_value_cansleep(st->reset_gpio, 1);
> + } else {
> + ret = ad3552r_qspi_update_reg_bits(st,
> + AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
> + AD3552R_MASK_SOFTWARE_RESET,
> + AD3552R_MASK_SOFTWARE_RESET, 1);
> + if (ret)
> + return ret;
> + }
> + msleep(100);
> +
> + return 0;
> +}
> +
> +static int ad3552r_hs_scratch_pad_test(struct ad3552r_hs_state *st)
> +{
> + int ret, val;
> +
> + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> + AD3552R_SCRATCH_PAD_TEST_VAL1, 1);
> + if (ret)
> + return ret;
> +
> + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> + &val, 1);
> + if (ret)
> + return ret;
> +
> + if (val != AD3552R_SCRATCH_PAD_TEST_VAL1)
> + return dev_err_probe(st->dev, -EIO,
> + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> 0x%x\n",
> + AD3552R_SCRATCH_PAD_TEST_VAL1, val);
> +
> + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> + AD3552R_SCRATCH_PAD_TEST_VAL2, 1);
> + if (ret)
> + return ret;
> +
> + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> + &val, 1);
> + if (ret)
> + return ret;
> +
> + if (val != AD3552R_SCRATCH_PAD_TEST_VAL2)
> + return dev_err_probe(st->dev, -EIO,
> + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> 0x%x\n",
> + AD3552R_SCRATCH_PAD_TEST_VAL2, val);
> +
> + return 0;
> +}
> +
> +static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st,
> + int ch, u16 gain, u16 offset)
> +{
> + int ret;
> +
> + ret = st->data->bus_reg_write(st->back,
> AD3552R_REG_ADDR_CH_OFFSET(ch),
> + offset, 1);
> + if (ret)
> + return dev_err_probe(st->dev, ret, "Error writing
> register\n");
> +
> + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch),
> + gain, 1);
> + if (ret)
> + return dev_err_probe(st->dev, ret, "Error writing
> register\n");
> +
> + return 0;
nit: Not a big fan of these logs on read/write registers functions... Also seems
that you're not being consistent (either you have them or not). FWIW, I would
simplify and drop them. That would allow to do
return st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch), gain, 1);
> +}
> +
> +static int ad3552r_hs_setup(struct ad3552r_hs_state *st)
> +{
> + s16 goffs;
> + u16 id;
> + u16 gain = 0, offset = 0;
> + u32 ch, val, range;
> + int ret;
> +
> + ret = ad3552r_hs_reset(st);
> + if (ret)
> + return ret;
> +
> + ret = iio_backend_ddr_disable(st->back);
> + if (ret)
> + return ret;
> +
> + ret = ad3552r_hs_scratch_pad_test(st);
> + if (ret)
> + return ret;
> +
> + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L,
> + &val, 1);
> + if (ret)
> + return ret;
> +
> + id = val;
> +
> + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H,
> + &val, 1);
> + if (ret)
> + return ret;
> +
> + id |= val << 8;
> + if (id != st->model_data->chip_id)
> + dev_info(st->dev, "Chip ID error. Expected 0x%x, Read
> 0x%x\n",
> + AD3552R_ID, id);
> +
> + ret = st->data->bus_reg_write(st->back,
> + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> + 0, 1);
> + if (ret)
> + return ret;
> +
> + ret = st->data->bus_reg_write(st->back,
> + AD3552R_REG_ADDR_TRANSFER_REGISTER,
> + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE,
> + AD3552R_QUAD_SPI) |
> + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1);
> + if (ret)
> + return ret;
> +
> + ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
> + if (ret)
> + return ret;
> +
> + ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL);
> + if (ret)
> + return ret;
> +
> + ret = ad3552r_get_ref_voltage(st->dev, &val);
> + if (ret < 0)
> + return ret;
> +
> + val = ret;
> +
> + ret = ad3552r_qspi_update_reg_bits(st,
> + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> + AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
> + val, 1);
> + if (ret)
> + return ret;
> +
> + ret = ad3552r_get_drive_strength(st->dev, &val);
> + if (!ret) {
> + ret = ad3552r_qspi_update_reg_bits(st,
> + AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
> + AD3552R_MASK_SDO_DRIVE_STRENGTH,
> + val, 1);
> + if (ret)
> + return ret;
> + }
> +
> + device_for_each_child_node_scoped(st->dev, child) {
> + ret = fwnode_property_read_u32(child, "reg", &ch);
> + if (ret)
> + return dev_err_probe(st->dev, ret,
> + "reg property missing\n");
> +
> + ret = ad3552r_get_output_range(st->dev, st->model_data,
> child,
> + &range);
> + if (!ret) {
> + st->ch_data[ch].range = range;
> +
> + ret = ad3552r_hs_set_output_range(st, ch, range);
> + if (ret)
> + return ret;
> +
> + } else if (ret == -ENOENT) {
> + ret = ad3552r_get_custom_gain(st->dev, child,
> + &st->ch_data[ch].p,
> + &st->ch_data[ch].n,
> + &st->ch_data[ch].rfb,
> + &st-
> >ch_data[ch].gain_offset);
> + if (ret)
> + return ret;
> +
> + gain = ad3552r_calc_custom_gain(st->ch_data[ch].p,
> + st->ch_data[ch].n,
> + st->ch_data[ch].gain_offset);
> + offset = abs(goffs);
> +
> + st->ch_data[ch].range_override = 1;
> +
> + ret = ad3552r_hs_setup_custom_gain(st, ch, gain,
> + offset);
> + if (ret)
> + return ret;
> + } else {
> + return ret;
> + }
Just personal preference... I think this would be neater:
if (ret && ret != ENOENT)
return ret;
if (ret == -ENOENT) {
...
} else {
...
}
Advantage is that it also handles errors first (which is the typical pattern)
> +
> + ad3552r_calc_gain_and_offset(&st->ch_data[ch], st-
> >model_data);
> + }
> +
> + return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = {
> + .postenable = ad3552r_hs_buffer_postenable,
> + .predisable = ad3552r_hs_buffer_predisable,
> +};
> +
> +#define AD3552R_CHANNEL(ch) { \
> + .type = IIO_VOLTAGE, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
> + BIT(IIO_CHAN_INFO_SCALE) | \
> + BIT(IIO_CHAN_INFO_OFFSET), \
> + .output = 1, \
> + .indexed = 1, \
> + .channel = (ch), \
> + .scan_index = (ch), \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + .endianness = IIO_BE, \
> + } \
> +}
> +
> +static const struct iio_chan_spec ad3552r_hs_channels[] = {
> + AD3552R_CHANNEL(0),
> + AD3552R_CHANNEL(1),
> +};
> +
> +static const struct iio_info ad3552r_hs_info = {
> + .read_raw = &ad3552r_hs_read_raw,
> + .write_raw = &ad3552r_hs_write_raw,
> +};
> +
> +static int ad3552r_hs_probe(struct platform_device *pdev)
> +{
> + struct ad3552r_hs_state *st;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + st = iio_priv(indio_dev);
> + st->dev = &pdev->dev;
> +
> + st->data = pdev->dev.platform_data;
dev_get_platdata()
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-22 12:28 ` Nuno Sá
@ 2024-10-22 16:40 ` Angelo Dureghello
2024-10-24 13:05 ` Nuno Sá
0 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-22 16:40 UTC (permalink / raw)
To: Nuno Sá
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
Hi Nuno,
On 22.10.2024 14:28, Nuno Sá wrote:
> On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > From: Angelo Dureghello <adureghello@baylibre.com>
> >
> > Add High Speed ad3552r platform driver.
> >
> > The ad3552r DAC is controlled by a custom (fpga-based) DAC IP
> > through the current AXI backend, or similar alternative IIO backend.
> >
> > Compared to the existing driver (ad3552r.c), that is a simple SPI
> > driver, this driver is coupled with a DAC IIO backend that finally
> > controls the ad3552r by a fpga-based "QSPI+DDR" interface, to reach
> > maximum transfer rate of 33MUPS using dma stream capabilities.
> >
> > All commands involving QSPI bus read/write are delegated to the backend
> > through the provided APIs for bus read/write.
> >
> > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > ---
> > drivers/iio/dac/Kconfig | 14 ++
> > drivers/iio/dac/Makefile | 1 +
> > drivers/iio/dac/ad3552r-hs.c | 547
> > +++++++++++++++++++++++++++++++++++++++++++
> > drivers/iio/dac/ad3552r-hs.h | 18 ++
> > drivers/iio/dac/ad3552r.h | 4 +
> > 5 files changed, 584 insertions(+)
> >
> > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > index fa091995d002..fc11698e88f2 100644
> > --- a/drivers/iio/dac/Kconfig
> > +++ b/drivers/iio/dac/Kconfig
> > @@ -6,6 +6,20 @@
> >
> > menu "Digital to analog converters"
> >
> > +config AD3552R_HS
> > + tristate "Analog Devices AD3552R DAC High Speed driver"
> > + select ADI_AXI_DAC
> > + help
> > + Say yes here to build support for Analog Devices AD3552R
> > + Digital to Analog Converter High Speed driver.
> > +
> > + The driver requires the assistance of an IP core to operate,
> > + since data is streamed into target device via DMA, sent over a
> > + QSPI + DDR (Double Data Rate) bus.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called ad3552r-hs.
> > +
> > config AD3552R
> > tristate "Analog Devices AD3552R DAC driver"
> > depends on SPI_MASTER
> > diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> > index c92de0366238..d92e08ca93ca 100644
> > --- a/drivers/iio/dac/Makefile
> > +++ b/drivers/iio/dac/Makefile
> > @@ -4,6 +4,7 @@
> > #
> >
> > # When adding new entries keep the list in alphabetical order
> > +obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o ad3552r-common.o
> > obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
> > obj-$(CONFIG_AD5360) += ad5360.o
> > obj-$(CONFIG_AD5380) += ad5380.o
> > diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
> > new file mode 100644
> > index 000000000000..27bdc35fdc29
> > --- /dev/null
> > +++ b/drivers/iio/dac/ad3552r-hs.c
> > @@ -0,0 +1,547 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Analog Devices AD3552R
> > + * Digital to Analog converter driver, High Speed version
> > + *
> > + * Copyright 2024 Analog Devices Inc.
> > + */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/iio/backend.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/property.h>
> > +#include <linux/units.h>
> > +
> > +#include "ad3552r.h"
> > +#include "ad3552r-hs.h"
> > +
> > +struct ad3552r_hs_state {
> > + const struct ad3552r_model_data *model_data;
> > + struct gpio_desc *reset_gpio;
> > + struct device *dev;
> > + struct iio_backend *back;
> > + bool single_channel;
> > + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
> > + struct ad3552r_hs_platform_data *data;
> > +};
> > +
> > +static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st,
> > + u32 reg, u32 mask, u32 val,
> > + size_t xfer_size)
> > +{
> > + u32 rval;
> > + int ret;
> > +
> > + ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size);
> > + if (ret)
> > + return ret;
> > +
> > + rval = (rval & ~mask) | val;
> > +
> > + return st->data->bus_reg_write(st->back, reg, rval, xfer_size);
> > +}
> > +
> > +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int *val, int *val2, long mask)
> > +{
> > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > + int ret;
> > + int ch = chan->channel;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_SAMP_FREQ: {
> > + int sclk;
> > +
> > + ret = iio_backend_read_raw(st->back, chan, &sclk, 0,
> > + IIO_CHAN_INFO_FREQUENCY);
> > + if (ret != IIO_VAL_INT)
> > + return -EINVAL;
> > +
>
> I just saw you had some questions on v6 that everyone failed to see. See my
> reply to David here:
>
> https://lore.kernel.org/linux-iio/61cf3072af74a8b2951c948ddc2383ba1e55954d.camel@gmail.com/
>
> It should be easy and it's something that makes sense (at least to me :))
>
I understood that we would improve things later in case.
Could we maybe stay with IIO_CHAN_INFO_FREQUENCY ? It doesn't seems to me
so out of scope. Sorry but i am trying to finalize someway this job,
so i am trying to conatain changes now at v7, if code is not really
totally wrong.
> > + /*
> > + * Using 4 lanes (QSPI), then using 2 as DDR mode is
> > + * considered always on (considering buffering mode always).
> > + */
> > + *val = DIV_ROUND_CLOSEST(sclk * 4 * 2,
> > + chan->scan_type.realbits);
> > +
> > + return IIO_VAL_INT;
> > + }
> > + case IIO_CHAN_INFO_RAW:
> > + ret = st->data->bus_reg_read(st->back,
> > + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel),
> > + val, 2);
> > + if (ret)
> > + return ret;
> > +
> > + return IIO_VAL_INT;
> > + case IIO_CHAN_INFO_SCALE:
> > + *val = st->ch_data[ch].scale_int;
> > + *val2 = st->ch_data[ch].scale_dec;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > + case IIO_CHAN_INFO_OFFSET:
> > + *val = st->ch_data[ch].offset_int;
> > + *val2 = st->ch_data[ch].offset_dec;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int ad3552r_hs_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> > + return st->data->bus_reg_write(st->back,
> > + AD3552R_REG_ADDR_CH_DAC_16B(chan-
> > >channel),
> > + val, 2);
> > + }
>
> Maybe we'll get the new stuff in time for this :)
>
> ...
>
> > +
> > +static int ad3552r_hs_reset(struct ad3552r_hs_state *st)
> > +{
> > + int ret;
> > +
> > + /*
> > + * Using inverted "active-high" logic here, since ad3552r classic-spi
> > + * fdt node (and driver) is using the same logic.
> > + */
> > +
>
> I don't understand this. This is a new device with a different compatible. Why
> keeping the wrong logic? AFAICT, there's nothing in the bindings about the pin
> polarity.
>
ad3552r.c uses same compatible (adi,ad3552r), and in the code it implements
this same inverted logic. So i thought to use the same logic.
I can anyway change to the correct active-low logic for this driver,
but would honestly not enter in fixing old code now at v7.
Happy to do such fix on ad3552r.c later on.
> > + st->reset_gpio = devm_gpiod_get_optional(st->dev,
> > + "reset", GPIOD_OUT_LOW);
> > + if (IS_ERR(st->reset_gpio))
> > + return PTR_ERR(st->reset_gpio);
> > +
> > + if (st->reset_gpio) {
> > + fsleep(10);
> > + gpiod_set_value_cansleep(st->reset_gpio, 1);
> > + } else {
> > + ret = ad3552r_qspi_update_reg_bits(st,
> > + AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
> > + AD3552R_MASK_SOFTWARE_RESET,
> > + AD3552R_MASK_SOFTWARE_RESET, 1);
> > + if (ret)
> > + return ret;
> > + }
> > + msleep(100);
> > +
> > + return 0;
> > +}
> > +
> > +static int ad3552r_hs_scratch_pad_test(struct ad3552r_hs_state *st)
> > +{
> > + int ret, val;
> > +
> > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > + AD3552R_SCRATCH_PAD_TEST_VAL1, 1);
> > + if (ret)
> > + return ret;
> > +
> > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > + &val, 1);
> > + if (ret)
> > + return ret;
> > +
> > + if (val != AD3552R_SCRATCH_PAD_TEST_VAL1)
> > + return dev_err_probe(st->dev, -EIO,
> > + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> > 0x%x\n",
> > + AD3552R_SCRATCH_PAD_TEST_VAL1, val);
> > +
> > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > + AD3552R_SCRATCH_PAD_TEST_VAL2, 1);
> > + if (ret)
> > + return ret;
> > +
> > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > + &val, 1);
> > + if (ret)
> > + return ret;
> > +
> > + if (val != AD3552R_SCRATCH_PAD_TEST_VAL2)
> > + return dev_err_probe(st->dev, -EIO,
> > + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> > 0x%x\n",
> > + AD3552R_SCRATCH_PAD_TEST_VAL2, val);
> > +
> > + return 0;
> > +}
> > +
> > +static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st,
> > + int ch, u16 gain, u16 offset)
> > +{
> > + int ret;
> > +
> > + ret = st->data->bus_reg_write(st->back,
> > AD3552R_REG_ADDR_CH_OFFSET(ch),
> > + offset, 1);
> > + if (ret)
> > + return dev_err_probe(st->dev, ret, "Error writing
> > register\n");
> > +
> > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch),
> > + gain, 1);
> > + if (ret)
> > + return dev_err_probe(st->dev, ret, "Error writing
> > register\n");
> > +
> > + return 0;
>
> nit: Not a big fan of these logs on read/write registers functions... Also seems
> that you're not being consistent (either you have them or not). FWIW, I would
> simplify and drop them. That would allow to do
>
> return st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch), gain, 1);
>
Used dev_err_probe on quite all probe functions.
I don't see nothing really wrong on this codem except maybe a more meaningful
message.
> > +}
> > +
> > +static int ad3552r_hs_setup(struct ad3552r_hs_state *st)
> > +{
> > + s16 goffs;
> > + u16 id;
> > + u16 gain = 0, offset = 0;
> > + u32 ch, val, range;
> > + int ret;
> > +
> > + ret = ad3552r_hs_reset(st);
> > + if (ret)
> > + return ret;
> > +
> > + ret = iio_backend_ddr_disable(st->back);
> > + if (ret)
> > + return ret;
> > +
> > + ret = ad3552r_hs_scratch_pad_test(st);
> > + if (ret)
> > + return ret;
> > +
> > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L,
> > + &val, 1);
> > + if (ret)
> > + return ret;
> > +
> > + id = val;
> > +
> > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H,
> > + &val, 1);
> > + if (ret)
> > + return ret;
> > +
> > + id |= val << 8;
> > + if (id != st->model_data->chip_id)
> > + dev_info(st->dev, "Chip ID error. Expected 0x%x, Read
> > 0x%x\n",
> > + AD3552R_ID, id);
> > +
> > + ret = st->data->bus_reg_write(st->back,
> > + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> > + 0, 1);
> > + if (ret)
> > + return ret;
> > +
> > + ret = st->data->bus_reg_write(st->back,
> > + AD3552R_REG_ADDR_TRANSFER_REGISTER,
> > + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE,
> > + AD3552R_QUAD_SPI) |
> > + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1);
> > + if (ret)
> > + return ret;
> > +
> > + ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
> > + if (ret)
> > + return ret;
> > +
> > + ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL);
> > + if (ret)
> > + return ret;
> > +
> > + ret = ad3552r_get_ref_voltage(st->dev, &val);
> > + if (ret < 0)
> > + return ret;
> > +
> > + val = ret;
> > +
> > + ret = ad3552r_qspi_update_reg_bits(st,
> > + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> > + AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
> > + val, 1);
> > + if (ret)
> > + return ret;
> > +
> > + ret = ad3552r_get_drive_strength(st->dev, &val);
> > + if (!ret) {
> > + ret = ad3552r_qspi_update_reg_bits(st,
> > + AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
> > + AD3552R_MASK_SDO_DRIVE_STRENGTH,
> > + val, 1);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + device_for_each_child_node_scoped(st->dev, child) {
> > + ret = fwnode_property_read_u32(child, "reg", &ch);
> > + if (ret)
> > + return dev_err_probe(st->dev, ret,
> > + "reg property missing\n");
> > +
> > + ret = ad3552r_get_output_range(st->dev, st->model_data,
> > child,
> > + &range);
> > + if (!ret) {
> > + st->ch_data[ch].range = range;
> > +
> > + ret = ad3552r_hs_set_output_range(st, ch, range);
> > + if (ret)
> > + return ret;
> > +
> > + } else if (ret == -ENOENT) {
> > + ret = ad3552r_get_custom_gain(st->dev, child,
> > + &st->ch_data[ch].p,
> > + &st->ch_data[ch].n,
> > + &st->ch_data[ch].rfb,
> > + &st-
> > >ch_data[ch].gain_offset);
> > + if (ret)
> > + return ret;
> > +
> > + gain = ad3552r_calc_custom_gain(st->ch_data[ch].p,
> > + st->ch_data[ch].n,
> > + st->ch_data[ch].gain_offset);
> > + offset = abs(goffs);
> > +
> > + st->ch_data[ch].range_override = 1;
> > +
> > + ret = ad3552r_hs_setup_custom_gain(st, ch, gain,
> > + offset);
> > + if (ret)
> > + return ret;
> > + } else {
> > + return ret;
> > + }
>
> Just personal preference... I think this would be neater:
> if (ret && ret != ENOENT)
> return ret;
> if (ret == -ENOENT) {
> ...
> } else {
> ...
> }
>
> Advantage is that it also handles errors first (which is the typical pattern)
I tested this code, would not change possibly now at this stage,
unless another version should be sent.
>
> > +
> > + ad3552r_calc_gain_and_offset(&st->ch_data[ch], st-
> > >model_data);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = {
> > + .postenable = ad3552r_hs_buffer_postenable,
> > + .predisable = ad3552r_hs_buffer_predisable,
> > +};
> > +
> > +#define AD3552R_CHANNEL(ch) { \
> > + .type = IIO_VOLTAGE, \
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
> > + BIT(IIO_CHAN_INFO_SCALE) | \
> > + BIT(IIO_CHAN_INFO_OFFSET), \
> > + .output = 1, \
> > + .indexed = 1, \
> > + .channel = (ch), \
> > + .scan_index = (ch), \
> > + .scan_type = { \
> > + .sign = 'u', \
> > + .realbits = 16, \
> > + .storagebits = 16, \
> > + .endianness = IIO_BE, \
> > + } \
> > +}
> > +
> > +static const struct iio_chan_spec ad3552r_hs_channels[] = {
> > + AD3552R_CHANNEL(0),
> > + AD3552R_CHANNEL(1),
> > +};
> > +
> > +static const struct iio_info ad3552r_hs_info = {
> > + .read_raw = &ad3552r_hs_read_raw,
> > + .write_raw = &ad3552r_hs_write_raw,
> > +};
> > +
> > +static int ad3552r_hs_probe(struct platform_device *pdev)
> > +{
> > + struct ad3552r_hs_state *st;
> > + struct iio_dev *indio_dev;
> > + int ret;
> > +
> > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + st = iio_priv(indio_dev);
> > + st->dev = &pdev->dev;
> > +
> > + st->data = pdev->dev.platform_data;
>
> dev_get_platdata()
>
pdev->dev.platform_data seems correct to me, used in a lot of places
in the driver framework. Can we stay with it ?
> - Nuno Sá
>
>
Regards,
angelo
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-22 16:40 ` Angelo Dureghello
@ 2024-10-24 13:05 ` Nuno Sá
2024-10-24 15:02 ` Angelo Dureghello
0 siblings, 1 reply; 29+ messages in thread
From: Nuno Sá @ 2024-10-24 13:05 UTC (permalink / raw)
To: Angelo Dureghello
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
On Tue, 2024-10-22 at 18:40 +0200, Angelo Dureghello wrote:
> Hi Nuno,
>
> On 22.10.2024 14:28, Nuno Sá wrote:
> > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > From: Angelo Dureghello <adureghello@baylibre.com>
> > >
> > > Add High Speed ad3552r platform driver.
> > >
> > > The ad3552r DAC is controlled by a custom (fpga-based) DAC IP
> > > through the current AXI backend, or similar alternative IIO backend.
> > >
> > > Compared to the existing driver (ad3552r.c), that is a simple SPI
> > > driver, this driver is coupled with a DAC IIO backend that finally
> > > controls the ad3552r by a fpga-based "QSPI+DDR" interface, to reach
> > > maximum transfer rate of 33MUPS using dma stream capabilities.
> > >
> > > All commands involving QSPI bus read/write are delegated to the backend
> > > through the provided APIs for bus read/write.
> > >
> > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > ---
> > > drivers/iio/dac/Kconfig | 14 ++
> > > drivers/iio/dac/Makefile | 1 +
> > > drivers/iio/dac/ad3552r-hs.c | 547
> > > +++++++++++++++++++++++++++++++++++++++++++
> > > drivers/iio/dac/ad3552r-hs.h | 18 ++
> > > drivers/iio/dac/ad3552r.h | 4 +
> > > 5 files changed, 584 insertions(+)
> > >
> > > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > > index fa091995d002..fc11698e88f2 100644
> > > --- a/drivers/iio/dac/Kconfig
> > > +++ b/drivers/iio/dac/Kconfig
> > > @@ -6,6 +6,20 @@
> > >
> > > menu "Digital to analog converters"
> > >
> > > +config AD3552R_HS
> > > + tristate "Analog Devices AD3552R DAC High Speed driver"
> > > + select ADI_AXI_DAC
> > > + help
> > > + Say yes here to build support for Analog Devices AD3552R
> > > + Digital to Analog Converter High Speed driver.
> > > +
> > > + The driver requires the assistance of an IP core to operate,
> > > + since data is streamed into target device via DMA, sent over a
> > > + QSPI + DDR (Double Data Rate) bus.
> > > +
> > > + To compile this driver as a module, choose M here: the
> > > + module will be called ad3552r-hs.
> > > +
> > > config AD3552R
> > > tristate "Analog Devices AD3552R DAC driver"
> > > depends on SPI_MASTER
> > > diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> > > index c92de0366238..d92e08ca93ca 100644
> > > --- a/drivers/iio/dac/Makefile
> > > +++ b/drivers/iio/dac/Makefile
> > > @@ -4,6 +4,7 @@
> > > #
> > >
> > > # When adding new entries keep the list in alphabetical order
> > > +obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o ad3552r-common.o
> > > obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
> > > obj-$(CONFIG_AD5360) += ad5360.o
> > > obj-$(CONFIG_AD5380) += ad5380.o
> > > diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
> > > new file mode 100644
> > > index 000000000000..27bdc35fdc29
> > > --- /dev/null
> > > +++ b/drivers/iio/dac/ad3552r-hs.c
> > > @@ -0,0 +1,547 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Analog Devices AD3552R
> > > + * Digital to Analog converter driver, High Speed version
> > > + *
> > > + * Copyright 2024 Analog Devices Inc.
> > > + */
> > > +
> > > +#include <linux/bitfield.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/gpio/consumer.h>
> > > +#include <linux/iio/backend.h>
> > > +#include <linux/iio/buffer.h>
> > > +#include <linux/mod_devicetable.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/property.h>
> > > +#include <linux/units.h>
> > > +
> > > +#include "ad3552r.h"
> > > +#include "ad3552r-hs.h"
> > > +
> > > +struct ad3552r_hs_state {
> > > + const struct ad3552r_model_data *model_data;
> > > + struct gpio_desc *reset_gpio;
> > > + struct device *dev;
> > > + struct iio_backend *back;
> > > + bool single_channel;
> > > + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
> > > + struct ad3552r_hs_platform_data *data;
> > > +};
> > > +
> > > +static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st,
> > > + u32 reg, u32 mask, u32 val,
> > > + size_t xfer_size)
> > > +{
> > > + u32 rval;
> > > + int ret;
> > > +
> > > + ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + rval = (rval & ~mask) | val;
> > > +
> > > + return st->data->bus_reg_write(st->back, reg, rval, xfer_size);
> > > +}
> > > +
> > > +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
> > > + struct iio_chan_spec const *chan,
> > > + int *val, int *val2, long mask)
> > > +{
> > > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > > + int ret;
> > > + int ch = chan->channel;
> > > +
> > > + switch (mask) {
> > > + case IIO_CHAN_INFO_SAMP_FREQ: {
> > > + int sclk;
> > > +
> > > + ret = iio_backend_read_raw(st->back, chan, &sclk, 0,
> > > + IIO_CHAN_INFO_FREQUENCY);
> > > + if (ret != IIO_VAL_INT)
> > > + return -EINVAL;
> > > +
> >
> > I just saw you had some questions on v6 that everyone failed to see. See my
> > reply to David here:
> >
> > https://lore.kernel.org/linux-iio/61cf3072af74a8b2951c948ddc2383ba1e55954d.camel@gmail.com/
> >
> > It should be easy and it's something that makes sense (at least to me :))
> >
>
> I understood that we would improve things later in case.
>
> Could we maybe stay with IIO_CHAN_INFO_FREQUENCY ? It doesn't seems to me
> so out of scope. Sorry but i am trying to finalize someway this job,
> so i am trying to conatain changes now at v7, if code is not really
> totally wrong.
I think you're trying to rush in the series. I can understand your frustration but
believe me that v7 (or v8) is not so bad :).
David already raised concerns about using IIO_CHAN_INFO_FREQUENCY. I'm also not a fan
of it and gave you another option that should be trivial and makes sense (given that
bus_read and write are already being done through the platform_data interface). So
no, I don't think we're going to accept "is not really totally wrong.". IOW, We want
it to be totally right - if such a thing exists :).
>
> > > + /*
> > > + * Using 4 lanes (QSPI), then using 2 as DDR mode is
> > > + * considered always on (considering buffering mode always).
> > > + */
> > > + *val = DIV_ROUND_CLOSEST(sclk * 4 * 2,
> > > + chan->scan_type.realbits);
> > > +
> > > + return IIO_VAL_INT;
> > > + }
> > > + case IIO_CHAN_INFO_RAW:
> > > + ret = st->data->bus_reg_read(st->back,
> > > + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel),
> > > + val, 2);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return IIO_VAL_INT;
> > > + case IIO_CHAN_INFO_SCALE:
> > > + *val = st->ch_data[ch].scale_int;
> > > + *val2 = st->ch_data[ch].scale_dec;
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > + case IIO_CHAN_INFO_OFFSET:
> > > + *val = st->ch_data[ch].offset_int;
> > > + *val2 = st->ch_data[ch].offset_dec;
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > + default:
> > > + return -EINVAL;
> > > + }
> > > +}
> > > +
> > > +static int ad3552r_hs_write_raw(struct iio_dev *indio_dev,
> > > + struct iio_chan_spec const *chan,
> > > + int val, int val2, long mask)
> > > +{
> > > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > > +
> > > + switch (mask) {
> > > + case IIO_CHAN_INFO_RAW:
> > > + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> > > + return st->data->bus_reg_write(st->back,
> > > + AD3552R_REG_ADDR_CH_DAC_16B(chan-
> > > > channel),
> > > + val, 2);
> > > + }
> >
> > Maybe we'll get the new stuff in time for this :)
> >
> > ...
> >
> > > +
> > > +static int ad3552r_hs_reset(struct ad3552r_hs_state *st)
> > > +{
> > > + int ret;
> > > +
> > > + /*
> > > + * Using inverted "active-high" logic here, since ad3552r classic-spi
> > > + * fdt node (and driver) is using the same logic.
> > > + */
> > > +
> >
> > I don't understand this. This is a new device with a different compatible. Why
> > keeping the wrong logic? AFAICT, there's nothing in the bindings about the pin
> > polarity.
> >
>
> ad3552r.c uses same compatible (adi,ad3552r), and in the code it implements
> this same inverted logic. So i thought to use the same logic.
> I can anyway change to the correct active-low logic for this driver,
> but would honestly not enter in fixing old code now at v7.
> Happy to do such fix on ad3552r.c later on.
Ok, bad example from me with the compatible. The point is this is a different device.
It's a platform device while the other one is a spi device. So why doing it wrong in
here? Not saying to change the other device logic, just not doing it deliberately
wrong in a new device.
For the old device, we can't likely change it as we could break current users who
just adapted their DTs to conform to the driver logic.
>
> > > + st->reset_gpio = devm_gpiod_get_optional(st->dev,
> > > + "reset", GPIOD_OUT_LOW);
> > > + if (IS_ERR(st->reset_gpio))
> > > + return PTR_ERR(st->reset_gpio);
> > > +
> > > + if (st->reset_gpio) {
> > > + fsleep(10);
> > > + gpiod_set_value_cansleep(st->reset_gpio, 1);
> > > + } else {
> > > + ret = ad3552r_qspi_update_reg_bits(st,
> > > + AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
> > > + AD3552R_MASK_SOFTWARE_RESET,
> > > + AD3552R_MASK_SOFTWARE_RESET, 1);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > + msleep(100);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int ad3552r_hs_scratch_pad_test(struct ad3552r_hs_state *st)
> > > +{
> > > + int ret, val;
> > > +
> > > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > + AD3552R_SCRATCH_PAD_TEST_VAL1, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > + &val, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + if (val != AD3552R_SCRATCH_PAD_TEST_VAL1)
> > > + return dev_err_probe(st->dev, -EIO,
> > > + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> > > 0x%x\n",
> > > + AD3552R_SCRATCH_PAD_TEST_VAL1, val);
> > > +
> > > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > + AD3552R_SCRATCH_PAD_TEST_VAL2, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > + &val, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + if (val != AD3552R_SCRATCH_PAD_TEST_VAL2)
> > > + return dev_err_probe(st->dev, -EIO,
> > > + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> > > 0x%x\n",
> > > + AD3552R_SCRATCH_PAD_TEST_VAL2, val);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st,
> > > + int ch, u16 gain, u16 offset)
> > > +{
> > > + int ret;
> > > +
> > > + ret = st->data->bus_reg_write(st->back,
> > > AD3552R_REG_ADDR_CH_OFFSET(ch),
> > > + offset, 1);
> > > + if (ret)
> > > + return dev_err_probe(st->dev, ret, "Error writing
> > > register\n");
> > > +
> > > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch),
> > > + gain, 1);
> > > + if (ret)
> > > + return dev_err_probe(st->dev, ret, "Error writing
> > > register\n");
> > > +
> > > + return 0;
> >
> > nit: Not a big fan of these logs on read/write registers functions... Also seems
> > that you're not being consistent (either you have them or not). FWIW, I would
> > simplify and drop them. That would allow to do
> >
> > return st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch), gain, 1);
> >
>
> Used dev_err_probe on quite all probe functions.
> I don't see nothing really wrong on this codem except maybe a more meaningful
> message.
No, you're not being consistent. You have another calls (example: st->data-
>bus_reg_rea()) where no log is being given.
>
> > > +}
> > > +
> > > +static int ad3552r_hs_setup(struct ad3552r_hs_state *st)
> > > +{
> > > + s16 goffs;
> > > + u16 id;
> > > + u16 gain = 0, offset = 0;
> > > + u32 ch, val, range;
> > > + int ret;
> > > +
> > > + ret = ad3552r_hs_reset(st);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = iio_backend_ddr_disable(st->back);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = ad3552r_hs_scratch_pad_test(st);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L,
> > > + &val, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + id = val;
> > > +
> > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H,
> > > + &val, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + id |= val << 8;
> > > + if (id != st->model_data->chip_id)
> > > + dev_info(st->dev, "Chip ID error. Expected 0x%x, Read
> > > 0x%x\n",
> > > + AD3552R_ID, id);
> > > +
> > > + ret = st->data->bus_reg_write(st->back,
> > > + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> > > + 0, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = st->data->bus_reg_write(st->back,
> > > + AD3552R_REG_ADDR_TRANSFER_REGISTER,
> > > + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE,
> > > + AD3552R_QUAD_SPI) |
> > > + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = ad3552r_get_ref_voltage(st->dev, &val);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + val = ret;
> > > +
> > > + ret = ad3552r_qspi_update_reg_bits(st,
> > > + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> > > + AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
> > > + val, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = ad3552r_get_drive_strength(st->dev, &val);
> > > + if (!ret) {
> > > + ret = ad3552r_qspi_update_reg_bits(st,
> > > + AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
> > > + AD3552R_MASK_SDO_DRIVE_STRENGTH,
> > > + val, 1);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + device_for_each_child_node_scoped(st->dev, child) {
> > > + ret = fwnode_property_read_u32(child, "reg", &ch);
> > > + if (ret)
> > > + return dev_err_probe(st->dev, ret,
> > > + "reg property missing\n");
> > > +
> > > + ret = ad3552r_get_output_range(st->dev, st->model_data,
> > > child,
> > > + &range);
> > > + if (!ret) {
> > > + st->ch_data[ch].range = range;
> > > +
> > > + ret = ad3552r_hs_set_output_range(st, ch, range);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + } else if (ret == -ENOENT) {
> > > + ret = ad3552r_get_custom_gain(st->dev, child,
> > > + &st->ch_data[ch].p,
> > > + &st->ch_data[ch].n,
> > > + &st->ch_data[ch].rfb,
> > > + &st-
> > > > ch_data[ch].gain_offset);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + gain = ad3552r_calc_custom_gain(st->ch_data[ch].p,
> > > + st->ch_data[ch].n,
> > > + st->ch_data[ch].gain_offset);
> > > + offset = abs(goffs);
> > > +
> > > + st->ch_data[ch].range_override = 1;
> > > +
> > > + ret = ad3552r_hs_setup_custom_gain(st, ch, gain,
> > > + offset);
> > > + if (ret)
> > > + return ret;
> > > + } else {
> > > + return ret;
> > > + }
> >
> > Just personal preference... I think this would be neater:
> > if (ret && ret != ENOENT)
> > return ret;
> > if (ret == -ENOENT) {
> > ...
> > } else {
> > ...
> > }
> >
> > Advantage is that it also handles errors first (which is the typical pattern)
>
> I tested this code, would not change possibly now at this stage,
> unless another version should be sent.
Not sure if we'll need another but personally I cannot ack this one as it stands...
sorry.
>
> >
> > > +
> > > + ad3552r_calc_gain_and_offset(&st->ch_data[ch], st-
> > > > model_data);
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = {
> > > + .postenable = ad3552r_hs_buffer_postenable,
> > > + .predisable = ad3552r_hs_buffer_predisable,
> > > +};
> > > +
> > > +#define AD3552R_CHANNEL(ch) { \
> > > + .type = IIO_VOLTAGE, \
> > > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > > + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
> > > + BIT(IIO_CHAN_INFO_SCALE) | \
> > > + BIT(IIO_CHAN_INFO_OFFSET), \
> > > + .output = 1, \
> > > + .indexed = 1, \
> > > + .channel = (ch), \
> > > + .scan_index = (ch), \
> > > + .scan_type = { \
> > > + .sign = 'u', \
> > > + .realbits = 16, \
> > > + .storagebits = 16, \
> > > + .endianness = IIO_BE, \
> > > + } \
> > > +}
> > > +
> > > +static const struct iio_chan_spec ad3552r_hs_channels[] = {
> > > + AD3552R_CHANNEL(0),
> > > + AD3552R_CHANNEL(1),
> > > +};
> > > +
> > > +static const struct iio_info ad3552r_hs_info = {
> > > + .read_raw = &ad3552r_hs_read_raw,
> > > + .write_raw = &ad3552r_hs_write_raw,
> > > +};
> > > +
> > > +static int ad3552r_hs_probe(struct platform_device *pdev)
> > > +{
> > > + struct ad3552r_hs_state *st;
> > > + struct iio_dev *indio_dev;
> > > + int ret;
> > > +
> > > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
> > > + if (!indio_dev)
> > > + return -ENOMEM;
> > > +
> > > + st = iio_priv(indio_dev);
> > > + st->dev = &pdev->dev;
> > > +
> > > + st->data = pdev->dev.platform_data;
> >
> > dev_get_platdata()
> >
>
> pdev->dev.platform_data seems correct to me, used in a lot of places
> in the driver framework. Can we stay with it ?
>
It is correct but if we an helper, why not using it? It may be used in a lot of
places just because the helper was added afterwards...
- Nuno Sá
>
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-24 13:05 ` Nuno Sá
@ 2024-10-24 15:02 ` Angelo Dureghello
2024-10-24 15:13 ` David Lechner
2024-10-25 6:13 ` Nuno Sá
0 siblings, 2 replies; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-24 15:02 UTC (permalink / raw)
To: Nuno Sá
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
Hi Nuno,
On 24.10.2024 15:05, Nuno Sá wrote:
> On Tue, 2024-10-22 at 18:40 +0200, Angelo Dureghello wrote:
> > Hi Nuno,
> >
> > On 22.10.2024 14:28, Nuno Sá wrote:
> > > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > > From: Angelo Dureghello <adureghello@baylibre.com>
> > > >
> > > > Add High Speed ad3552r platform driver.
> > > >
> > > > The ad3552r DAC is controlled by a custom (fpga-based) DAC IP
> > > > through the current AXI backend, or similar alternative IIO backend.
> > > >
> > > > Compared to the existing driver (ad3552r.c), that is a simple SPI
> > > > driver, this driver is coupled with a DAC IIO backend that finally
> > > > controls the ad3552r by a fpga-based "QSPI+DDR" interface, to reach
> > > > maximum transfer rate of 33MUPS using dma stream capabilities.
> > > >
> > > > All commands involving QSPI bus read/write are delegated to the backend
> > > > through the provided APIs for bus read/write.
> > > >
> > > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > > ---
> > > > drivers/iio/dac/Kconfig | 14 ++
> > > > drivers/iio/dac/Makefile | 1 +
> > > > drivers/iio/dac/ad3552r-hs.c | 547
> > > > +++++++++++++++++++++++++++++++++++++++++++
> > > > drivers/iio/dac/ad3552r-hs.h | 18 ++
> > > > drivers/iio/dac/ad3552r.h | 4 +
> > > > 5 files changed, 584 insertions(+)
> > > >
> > > > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > > > index fa091995d002..fc11698e88f2 100644
> > > > --- a/drivers/iio/dac/Kconfig
> > > > +++ b/drivers/iio/dac/Kconfig
> > > > @@ -6,6 +6,20 @@
> > > >
> > > > menu "Digital to analog converters"
> > > >
> > > > +config AD3552R_HS
> > > > + tristate "Analog Devices AD3552R DAC High Speed driver"
> > > > + select ADI_AXI_DAC
> > > > + help
> > > > + Say yes here to build support for Analog Devices AD3552R
> > > > + Digital to Analog Converter High Speed driver.
> > > > +
> > > > + The driver requires the assistance of an IP core to operate,
> > > > + since data is streamed into target device via DMA, sent over a
> > > > + QSPI + DDR (Double Data Rate) bus.
> > > > +
> > > > + To compile this driver as a module, choose M here: the
> > > > + module will be called ad3552r-hs.
> > > > +
> > > > config AD3552R
> > > > tristate "Analog Devices AD3552R DAC driver"
> > > > depends on SPI_MASTER
> > > > diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> > > > index c92de0366238..d92e08ca93ca 100644
> > > > --- a/drivers/iio/dac/Makefile
> > > > +++ b/drivers/iio/dac/Makefile
> > > > @@ -4,6 +4,7 @@
> > > > #
> > > >
> > > > # When adding new entries keep the list in alphabetical order
> > > > +obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o ad3552r-common.o
> > > > obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
> > > > obj-$(CONFIG_AD5360) += ad5360.o
> > > > obj-$(CONFIG_AD5380) += ad5380.o
> > > > diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
> > > > new file mode 100644
> > > > index 000000000000..27bdc35fdc29
> > > > --- /dev/null
> > > > +++ b/drivers/iio/dac/ad3552r-hs.c
> > > > @@ -0,0 +1,547 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-only
> > > > +/*
> > > > + * Analog Devices AD3552R
> > > > + * Digital to Analog converter driver, High Speed version
> > > > + *
> > > > + * Copyright 2024 Analog Devices Inc.
> > > > + */
> > > > +
> > > > +#include <linux/bitfield.h>
> > > > +#include <linux/delay.h>
> > > > +#include <linux/gpio/consumer.h>
> > > > +#include <linux/iio/backend.h>
> > > > +#include <linux/iio/buffer.h>
> > > > +#include <linux/mod_devicetable.h>
> > > > +#include <linux/platform_device.h>
> > > > +#include <linux/property.h>
> > > > +#include <linux/units.h>
> > > > +
> > > > +#include "ad3552r.h"
> > > > +#include "ad3552r-hs.h"
> > > > +
> > > > +struct ad3552r_hs_state {
> > > > + const struct ad3552r_model_data *model_data;
> > > > + struct gpio_desc *reset_gpio;
> > > > + struct device *dev;
> > > > + struct iio_backend *back;
> > > > + bool single_channel;
> > > > + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
> > > > + struct ad3552r_hs_platform_data *data;
> > > > +};
> > > > +
> > > > +static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st,
> > > > + u32 reg, u32 mask, u32 val,
> > > > + size_t xfer_size)
> > > > +{
> > > > + u32 rval;
> > > > + int ret;
> > > > +
> > > > + ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + rval = (rval & ~mask) | val;
> > > > +
> > > > + return st->data->bus_reg_write(st->back, reg, rval, xfer_size);
> > > > +}
> > > > +
> > > > +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
> > > > + struct iio_chan_spec const *chan,
> > > > + int *val, int *val2, long mask)
> > > > +{
> > > > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > > > + int ret;
> > > > + int ch = chan->channel;
> > > > +
> > > > + switch (mask) {
> > > > + case IIO_CHAN_INFO_SAMP_FREQ: {
> > > > + int sclk;
> > > > +
> > > > + ret = iio_backend_read_raw(st->back, chan, &sclk, 0,
> > > > + IIO_CHAN_INFO_FREQUENCY);
> > > > + if (ret != IIO_VAL_INT)
> > > > + return -EINVAL;
> > > > +
> > >
> > > I just saw you had some questions on v6 that everyone failed to see. See my
> > > reply to David here:
> > >
> > > https://lore.kernel.org/linux-iio/61cf3072af74a8b2951c948ddc2383ba1e55954d.camel@gmail.com/
> > >
> > > It should be easy and it's something that makes sense (at least to me :))
> > >
> >
> > I understood that we would improve things later in case.
> >
> > Could we maybe stay with IIO_CHAN_INFO_FREQUENCY ? It doesn't seems to me
> > so out of scope. Sorry but i am trying to finalize someway this job,
> > so i am trying to conatain changes now at v7, if code is not really
> > totally wrong.
>
> I think you're trying to rush in the series. I can understand your frustration but
> believe me that v7 (or v8) is not so bad :).
>
> David already raised concerns about using IIO_CHAN_INFO_FREQUENCY. I'm also not a fan
> of it and gave you another option that should be trivial and makes sense (given that
> bus_read and write are already being done through the platform_data interface). So
> no, I don't think we're going to accept "is not really totally wrong.". IOW, We want
> it to be totally right - if such a thing exists :).
>
> >
i changed this way, using platform_data:
static int axi_dac_bus_clok(struct iio_backend *back)
{
struct axi_dac_state *st = iio_backend_get_priv(back);
/*
* Returning here always the maximum (buffering mode)
* clock rate.
*/
return st->dac_clk_rate;
}
And, on ad3552r-hs.c
static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ad3552r_hs_state *st = iio_priv(indio_dev);
int ret;
int ch = chan->channel;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
/*
* Using 4 lanes (QSPI), then using 2 as DDR mode is
* considered always on (considering buffering mode always).
*/
*val = DIV_ROUND_CLOSEST(st->data->bus_clock(st->back) * 4 * 2,
chan->scan_type.realbits);
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
Let me know if you see any issue.
> > > > + /*
> > > > + * Using 4 lanes (QSPI), then using 2 as DDR mode is
> > > > + * considered always on (considering buffering mode always).
> > > > + */
> > > > + *val = DIV_ROUND_CLOSEST(sclk * 4 * 2,
> > > > + chan->scan_type.realbits);
> > > > +
> > > > + return IIO_VAL_INT;
> > > > + }
> > > > + case IIO_CHAN_INFO_RAW:
> > > > + ret = st->data->bus_reg_read(st->back,
> > > > + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel),
> > > > + val, 2);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + return IIO_VAL_INT;
> > > > + case IIO_CHAN_INFO_SCALE:
> > > > + *val = st->ch_data[ch].scale_int;
> > > > + *val2 = st->ch_data[ch].scale_dec;
> > > > + return IIO_VAL_INT_PLUS_MICRO;
> > > > + case IIO_CHAN_INFO_OFFSET:
> > > > + *val = st->ch_data[ch].offset_int;
> > > > + *val2 = st->ch_data[ch].offset_dec;
> > > > + return IIO_VAL_INT_PLUS_MICRO;
> > > > + default:
> > > > + return -EINVAL;
> > > > + }
> > > > +}
> > > > +
> > > > +static int ad3552r_hs_write_raw(struct iio_dev *indio_dev,
> > > > + struct iio_chan_spec const *chan,
> > > > + int val, int val2, long mask)
> > > > +{
> > > > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > > > +
> > > > + switch (mask) {
> > > > + case IIO_CHAN_INFO_RAW:
> > > > + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> > > > + return st->data->bus_reg_write(st->back,
> > > > + AD3552R_REG_ADDR_CH_DAC_16B(chan-
> > > > > channel),
> > > > + val, 2);
> > > > + }
> > >
> > > Maybe we'll get the new stuff in time for this :)
> > >
This is not clear, sorry.
> > ...
> > >
> > > > +
> > > > +static int ad3552r_hs_reset(struct ad3552r_hs_state *st)
> > > > +{
> > > > + int ret;
> > > > +
> > > > + /*
> > > > + * Using inverted "active-high" logic here, since ad3552r classic-spi
> > > > + * fdt node (and driver) is using the same logic.
> > > > + */
> > > > +
> > >
> > > I don't understand this. This is a new device with a different compatible. Why
> > > keeping the wrong logic? AFAICT, there's nothing in the bindings about the pin
> > > polarity.
> > >
> >
> > ad3552r.c uses same compatible (adi,ad3552r), and in the code it implements
> > this same inverted logic. So i thought to use the same logic.
> > I can anyway change to the correct active-low logic for this driver,
> > but would honestly not enter in fixing old code now at v7.
> > Happy to do such fix on ad3552r.c later on.
>
> Ok, bad example from me with the compatible. The point is this is a different device.
> It's a platform device while the other one is a spi device. So why doing it wrong in
> here? Not saying to change the other device logic, just not doing it deliberately
> wrong in a new device.
Ok, i'll use active-low so.
>
> For the old device, we can't likely change it as we could break current users who
> just adapted their DTs to conform to the driver logic.
>
> >
> > > > + st->reset_gpio = devm_gpiod_get_optional(st->dev,
> > > > + "reset", GPIOD_OUT_LOW);
> > > > + if (IS_ERR(st->reset_gpio))
> > > > + return PTR_ERR(st->reset_gpio);
> > > > +
> > > > + if (st->reset_gpio) {
> > > > + fsleep(10);
> > > > + gpiod_set_value_cansleep(st->reset_gpio, 1);
> > > > + } else {
> > > > + ret = ad3552r_qspi_update_reg_bits(st,
> > > > + AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
> > > > + AD3552R_MASK_SOFTWARE_RESET,
> > > > + AD3552R_MASK_SOFTWARE_RESET, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > + }
> > > > + msleep(100);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int ad3552r_hs_scratch_pad_test(struct ad3552r_hs_state *st)
> > > > +{
> > > > + int ret, val;
> > > > +
> > > > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > > + AD3552R_SCRATCH_PAD_TEST_VAL1, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > > + &val, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + if (val != AD3552R_SCRATCH_PAD_TEST_VAL1)
> > > > + return dev_err_probe(st->dev, -EIO,
> > > > + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> > > > 0x%x\n",
> > > > + AD3552R_SCRATCH_PAD_TEST_VAL1, val);
> > > > +
> > > > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > > + AD3552R_SCRATCH_PAD_TEST_VAL2, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_SCRATCH_PAD,
> > > > + &val, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + if (val != AD3552R_SCRATCH_PAD_TEST_VAL2)
> > > > + return dev_err_probe(st->dev, -EIO,
> > > > + "SCRATCH_PAD_TEST mismatch. Expected 0x%x, Read
> > > > 0x%x\n",
> > > > + AD3552R_SCRATCH_PAD_TEST_VAL2, val);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st,
> > > > + int ch, u16 gain, u16 offset)
> > > > +{
> > > > + int ret;
> > > > +
> > > > + ret = st->data->bus_reg_write(st->back,
> > > > AD3552R_REG_ADDR_CH_OFFSET(ch),
> > > > + offset, 1);
> > > > + if (ret)
> > > > + return dev_err_probe(st->dev, ret, "Error writing
> > > > register\n");
> > > > +
> > > > + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch),
> > > > + gain, 1);
> > > > + if (ret)
> > > > + return dev_err_probe(st->dev, ret, "Error writing
> > > > register\n");
> > > > +
> > > > + return 0;
> > >
> > > nit: Not a big fan of these logs on read/write registers functions... Also seems
> > > that you're not being consistent (either you have them or not). FWIW, I would
> > > simplify and drop them. That would allow to do
> > >
> > > return st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_CH_GAIN(ch), gain, 1);
> > >
> >
> > Used dev_err_probe on quite all probe functions.
> > I don't see nothing really wrong on this codem except maybe a more meaningful
> > message.
>
> No, you're not being consistent. You have another calls (example: st->data-
> >bus_reg_rea()) where no log is being given.
>
> >
> > > > +}
> > > > +
> > > > +static int ad3552r_hs_setup(struct ad3552r_hs_state *st)
> > > > +{
> > > > + s16 goffs;
> > > > + u16 id;
> > > > + u16 gain = 0, offset = 0;
> > > > + u32 ch, val, range;
> > > > + int ret;
> > > > +
> > > > + ret = ad3552r_hs_reset(st);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = iio_backend_ddr_disable(st->back);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = ad3552r_hs_scratch_pad_test(st);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L,
> > > > + &val, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + id = val;
> > > > +
> > > > + ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H,
> > > > + &val, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + id |= val << 8;
> > > > + if (id != st->model_data->chip_id)
> > > > + dev_info(st->dev, "Chip ID error. Expected 0x%x, Read
> > > > 0x%x\n",
> > > > + AD3552R_ID, id);
> > > > +
> > > > + ret = st->data->bus_reg_write(st->back,
> > > > + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> > > > + 0, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = st->data->bus_reg_write(st->back,
> > > > + AD3552R_REG_ADDR_TRANSFER_REGISTER,
> > > > + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE,
> > > > + AD3552R_QUAD_SPI) |
> > > > + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = ad3552r_get_ref_voltage(st->dev, &val);
> > > > + if (ret < 0)
> > > > + return ret;
> > > > +
> > > > + val = ret;
> > > > +
> > > > + ret = ad3552r_qspi_update_reg_bits(st,
> > > > + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
> > > > + AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
> > > > + val, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + ret = ad3552r_get_drive_strength(st->dev, &val);
> > > > + if (!ret) {
> > > > + ret = ad3552r_qspi_update_reg_bits(st,
> > > > + AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
> > > > + AD3552R_MASK_SDO_DRIVE_STRENGTH,
> > > > + val, 1);
> > > > + if (ret)
> > > > + return ret;
> > > > + }
> > > > +
> > > > + device_for_each_child_node_scoped(st->dev, child) {
> > > > + ret = fwnode_property_read_u32(child, "reg", &ch);
> > > > + if (ret)
> > > > + return dev_err_probe(st->dev, ret,
> > > > + "reg property missing\n");
> > > > +
> > > > + ret = ad3552r_get_output_range(st->dev, st->model_data,
> > > > child,
> > > > + &range);
> > > > + if (!ret) {
> > > > + st->ch_data[ch].range = range;
> > > > +
> > > > + ret = ad3552r_hs_set_output_range(st, ch, range);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + } else if (ret == -ENOENT) {
> > > > + ret = ad3552r_get_custom_gain(st->dev, child,
> > > > + &st->ch_data[ch].p,
> > > > + &st->ch_data[ch].n,
> > > > + &st->ch_data[ch].rfb,
> > > > + &st-
> > > > > ch_data[ch].gain_offset);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > + gain = ad3552r_calc_custom_gain(st->ch_data[ch].p,
> > > > + st->ch_data[ch].n,
> > > > + st->ch_data[ch].gain_offset);
> > > > + offset = abs(goffs);
> > > > +
> > > > + st->ch_data[ch].range_override = 1;
> > > > +
> > > > + ret = ad3552r_hs_setup_custom_gain(st, ch, gain,
> > > > + offset);
> > > > + if (ret)
> > > > + return ret;
> > > > + } else {
> > > > + return ret;
> > > > + }
> > >
> > > Just personal preference... I think this would be neater:
> > > if (ret && ret != ENOENT)
> > > return ret;
> > > if (ret == -ENOENT) {
> > > ...
> > > } else {
> > > ...
> > > }
> > >
> > > Advantage is that it also handles errors first (which is the typical pattern)
> >
> > I tested this code, would not change possibly now at this stage,
> > unless another version should be sent.
>
> Not sure if we'll need another but personally I cannot ack this one as it stands...
> sorry.
>
> >
> > >
> > > > +
> > > > + ad3552r_calc_gain_and_offset(&st->ch_data[ch], st-
> > > > > model_data);
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static const struct iio_buffer_setup_ops ad3552r_hs_buffer_setup_ops = {
> > > > + .postenable = ad3552r_hs_buffer_postenable,
> > > > + .predisable = ad3552r_hs_buffer_predisable,
> > > > +};
> > > > +
> > > > +#define AD3552R_CHANNEL(ch) { \
> > > > + .type = IIO_VOLTAGE, \
> > > > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > > > + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
> > > > + BIT(IIO_CHAN_INFO_SCALE) | \
> > > > + BIT(IIO_CHAN_INFO_OFFSET), \
> > > > + .output = 1, \
> > > > + .indexed = 1, \
> > > > + .channel = (ch), \
> > > > + .scan_index = (ch), \
> > > > + .scan_type = { \
> > > > + .sign = 'u', \
> > > > + .realbits = 16, \
> > > > + .storagebits = 16, \
> > > > + .endianness = IIO_BE, \
> > > > + } \
> > > > +}
> > > > +
> > > > +static const struct iio_chan_spec ad3552r_hs_channels[] = {
> > > > + AD3552R_CHANNEL(0),
> > > > + AD3552R_CHANNEL(1),
> > > > +};
> > > > +
> > > > +static const struct iio_info ad3552r_hs_info = {
> > > > + .read_raw = &ad3552r_hs_read_raw,
> > > > + .write_raw = &ad3552r_hs_write_raw,
> > > > +};
> > > > +
> > > > +static int ad3552r_hs_probe(struct platform_device *pdev)
> > > > +{
> > > > + struct ad3552r_hs_state *st;
> > > > + struct iio_dev *indio_dev;
> > > > + int ret;
> > > > +
> > > > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
> > > > + if (!indio_dev)
> > > > + return -ENOMEM;
> > > > +
> > > > + st = iio_priv(indio_dev);
> > > > + st->dev = &pdev->dev;
> > > > +
> > > > + st->data = pdev->dev.platform_data;
> > >
> > > dev_get_platdata()
> > >
> >
> > pdev->dev.platform_data seems correct to me, used in a lot of places
> > in the driver framework. Can we stay with it ?
> >
>
> It is correct but if we an helper, why not using it? It may be used in a lot of
> places just because the helper was added afterwards...
>
> - Nuno Sá
> >
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-24 15:02 ` Angelo Dureghello
@ 2024-10-24 15:13 ` David Lechner
2024-10-26 17:35 ` Jonathan Cameron
2024-10-25 6:13 ` Nuno Sá
1 sibling, 1 reply; 29+ messages in thread
From: David Lechner @ 2024-10-24 15:13 UTC (permalink / raw)
To: Angelo Dureghello, Nuno Sá
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, Mark Brown
On 10/24/24 10:02 AM, Angelo Dureghello wrote:
> Hi Nuno,
>
> On 24.10.2024 15:05, Nuno Sá wrote:
>> On Tue, 2024-10-22 at 18:40 +0200, Angelo Dureghello wrote:
>>> Hi Nuno,
>>>
>>> On 22.10.2024 14:28, Nuno Sá wrote:
>>>> On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
>>>>> From: Angelo Dureghello <adureghello@baylibre.com>
>>>>>
>>>>> Add High Speed ad3552r platform driver.
...
>>>>> + switch (mask) {
>>>>> + case IIO_CHAN_INFO_RAW:
>>>>> + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
>>>>> + return st->data->bus_reg_write(st->back,
>>>>> + AD3552R_REG_ADDR_CH_DAC_16B(chan-
>>>>>> channel),
>>>>> + val, 2);
>>>>> + }
>>>>
>>>> Maybe we'll get the new stuff in time for this :)
>>>>
> This is not clear, sorry.
>
Probably this :-)
https://lore.kernel.org/all/20241023105757.GA9767@noisy.programming.kicks-ass.net/
But it hasn't reached the stable tree yet.
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-24 15:13 ` David Lechner
@ 2024-10-26 17:35 ` Jonathan Cameron
0 siblings, 0 replies; 29+ messages in thread
From: Jonathan Cameron @ 2024-10-26 17:35 UTC (permalink / raw)
To: David Lechner
Cc: Angelo Dureghello, Nuno Sá, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, Mark Brown
On Thu, 24 Oct 2024 10:13:25 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 10/24/24 10:02 AM, Angelo Dureghello wrote:
> > Hi Nuno,
> >
> > On 24.10.2024 15:05, Nuno Sá wrote:
> >> On Tue, 2024-10-22 at 18:40 +0200, Angelo Dureghello wrote:
> >>> Hi Nuno,
> >>>
> >>> On 22.10.2024 14:28, Nuno Sá wrote:
> >>>> On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> >>>>> From: Angelo Dureghello <adureghello@baylibre.com>
> >>>>>
> >>>>> Add High Speed ad3552r platform driver.
>
> ...
>
> >>>>> + switch (mask) {
> >>>>> + case IIO_CHAN_INFO_RAW:
> >>>>> + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> >>>>> + return st->data->bus_reg_write(st->back,
> >>>>> + AD3552R_REG_ADDR_CH_DAC_16B(chan-
> >>>>>> channel),
> >>>>> + val, 2);
> >>>>> + }
> >>>>
> >>>> Maybe we'll get the new stuff in time for this :)
> >>>>
> > This is not clear, sorry.
> >
>
> Probably this :-)
>
> https://lore.kernel.org/all/20241023105757.GA9767@noisy.programming.kicks-ass.net/
>
> But it hasn't reached the stable tree yet.
Agreed. It is going to be nice to get rid of so much ugly code.
I guess mostly next cycle however.
Jonathan
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver
2024-10-24 15:02 ` Angelo Dureghello
2024-10-24 15:13 ` David Lechner
@ 2024-10-25 6:13 ` Nuno Sá
1 sibling, 0 replies; 29+ messages in thread
From: Nuno Sá @ 2024-10-25 6:13 UTC (permalink / raw)
To: Angelo Dureghello
Cc: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, linux-iio, devicetree, linux-kernel, dlechner,
Mark Brown
Hi Angelo,
Just some minor (not that big of a deal comments)
On Thu, 2024-10-24 at 17:02 +0200, Angelo Dureghello wrote:
> Hi Nuno,
>
> On 24.10.2024 15:05, Nuno Sá wrote:
> > On Tue, 2024-10-22 at 18:40 +0200, Angelo Dureghello wrote:
> > > Hi Nuno,
> > >
> > > On 22.10.2024 14:28, Nuno Sá wrote:
> > > > On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> > > > > From: Angelo Dureghello <adureghello@baylibre.com>
> > > > >
> > > > > Add High Speed ad3552r platform driver.
> > > > >
> > > > > The ad3552r DAC is controlled by a custom (fpga-based) DAC IP
> > > > > through the current AXI backend, or similar alternative IIO backend.
> > > > >
> > > > > Compared to the existing driver (ad3552r.c), that is a simple SPI
> > > > > driver, this driver is coupled with a DAC IIO backend that finally
> > > > > controls the ad3552r by a fpga-based "QSPI+DDR" interface, to reach
> > > > > maximum transfer rate of 33MUPS using dma stream capabilities.
> > > > >
> > > > > All commands involving QSPI bus read/write are delegated to the backend
> > > > > through the provided APIs for bus read/write.
> > > > >
> > > > > Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> > > > > ---
> > > > > drivers/iio/dac/Kconfig | 14 ++
> > > > > drivers/iio/dac/Makefile | 1 +
> > > > > drivers/iio/dac/ad3552r-hs.c | 547
> > > > > +++++++++++++++++++++++++++++++++++++++++++
> > > > > drivers/iio/dac/ad3552r-hs.h | 18 ++
> > > > > drivers/iio/dac/ad3552r.h | 4 +
> > > > > 5 files changed, 584 insertions(+)
> > > > >
> > > > > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > > > > index fa091995d002..fc11698e88f2 100644
> > > > > --- a/drivers/iio/dac/Kconfig
> > > > > +++ b/drivers/iio/dac/Kconfig
> > > > > @@ -6,6 +6,20 @@
> > > > >
> > > > > menu "Digital to analog converters"
> > > > >
> > > > > +config AD3552R_HS
> > > > > + tristate "Analog Devices AD3552R DAC High Speed driver"
> > > > > + select ADI_AXI_DAC
> > > > > + help
> > > > > + Say yes here to build support for Analog Devices AD3552R
> > > > > + Digital to Analog Converter High Speed driver.
> > > > > +
> > > > > + The driver requires the assistance of an IP core to operate,
> > > > > + since data is streamed into target device via DMA, sent over a
> > > > > + QSPI + DDR (Double Data Rate) bus.
> > > > > +
> > > > > + To compile this driver as a module, choose M here: the
> > > > > + module will be called ad3552r-hs.
> > > > > +
> > > > > config AD3552R
> > > > > tristate "Analog Devices AD3552R DAC driver"
> > > > > depends on SPI_MASTER
> > > > > diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> > > > > index c92de0366238..d92e08ca93ca 100644
> > > > > --- a/drivers/iio/dac/Makefile
> > > > > +++ b/drivers/iio/dac/Makefile
> > > > > @@ -4,6 +4,7 @@
> > > > > #
> > > > >
> > > > > # When adding new entries keep the list in alphabetical order
> > > > > +obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o ad3552r-common.o
> > > > > obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
> > > > > obj-$(CONFIG_AD5360) += ad5360.o
> > > > > obj-$(CONFIG_AD5380) += ad5380.o
> > > > > diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c
> > > > > new file mode 100644
> > > > > index 000000000000..27bdc35fdc29
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/dac/ad3552r-hs.c
> > > > > @@ -0,0 +1,547 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0-only
> > > > > +/*
> > > > > + * Analog Devices AD3552R
> > > > > + * Digital to Analog converter driver, High Speed version
> > > > > + *
> > > > > + * Copyright 2024 Analog Devices Inc.
> > > > > + */
> > > > > +
> > > > > +#include <linux/bitfield.h>
> > > > > +#include <linux/delay.h>
> > > > > +#include <linux/gpio/consumer.h>
> > > > > +#include <linux/iio/backend.h>
> > > > > +#include <linux/iio/buffer.h>
> > > > > +#include <linux/mod_devicetable.h>
> > > > > +#include <linux/platform_device.h>
> > > > > +#include <linux/property.h>
> > > > > +#include <linux/units.h>
> > > > > +
> > > > > +#include "ad3552r.h"
> > > > > +#include "ad3552r-hs.h"
> > > > > +
> > > > > +struct ad3552r_hs_state {
> > > > > + const struct ad3552r_model_data *model_data;
> > > > > + struct gpio_desc *reset_gpio;
> > > > > + struct device *dev;
> > > > > + struct iio_backend *back;
> > > > > + bool single_channel;
> > > > > + struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
> > > > > + struct ad3552r_hs_platform_data *data;
> > > > > +};
> > > > > +
> > > > > +static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st,
> > > > > + u32 reg, u32 mask, u32 val,
> > > > > + size_t xfer_size)
> > > > > +{
> > > > > + u32 rval;
> > > > > + int ret;
> > > > > +
> > > > > + ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > + rval = (rval & ~mask) | val;
> > > > > +
> > > > > + return st->data->bus_reg_write(st->back, reg, rval, xfer_size);
> > > > > +}
> > > > > +
> > > > > +static int ad3552r_hs_read_raw(struct iio_dev *indio_dev,
> > > > > + struct iio_chan_spec const *chan,
> > > > > + int *val, int *val2, long mask)
> > > > > +{
> > > > > + struct ad3552r_hs_state *st = iio_priv(indio_dev);
> > > > > + int ret;
> > > > > + int ch = chan->channel;
> > > > > +
> > > > > + switch (mask) {
> > > > > + case IIO_CHAN_INFO_SAMP_FREQ: {
> > > > > + int sclk;
> > > > > +
> > > > > + ret = iio_backend_read_raw(st->back, chan, &sclk, 0,
> > > > > + IIO_CHAN_INFO_FREQUENCY);
> > > > > + if (ret != IIO_VAL_INT)
> > > > > + return -EINVAL;
> > > > > +
> > > >
> > > > I just saw you had some questions on v6 that everyone failed to see. See my
> > > > reply to David here:
> > > >
> > > > https://lore.kernel.org/linux-iio/61cf3072af74a8b2951c948ddc2383ba1e55954d.camel@gmail.com/
> > > >
> > > > It should be easy and it's something that makes sense (at least to me :))
> > > >
> > >
> > > I understood that we would improve things later in case.
> > >
> > > Could we maybe stay with IIO_CHAN_INFO_FREQUENCY ? It doesn't seems to me
> > > so out of scope. Sorry but i am trying to finalize someway this job,
> > > so i am trying to conatain changes now at v7, if code is not really
> > > totally wrong.
> >
> > I think you're trying to rush in the series. I can understand your frustration
> > but
> > believe me that v7 (or v8) is not so bad :).
> >
> > David already raised concerns about using IIO_CHAN_INFO_FREQUENCY. I'm also not a
> > fan
> > of it and gave you another option that should be trivial and makes sense (given
> > that
> > bus_read and write are already being done through the platform_data interface).
> > So
> > no, I don't think we're going to accept "is not really totally wrong.". IOW, We
> > want
> > it to be totally right - if such a thing exists :).
> >
> > >
>
> i changed this way, using platform_data:
>
> static int axi_dac_bus_clok(struct iio_backend *back)
If we don't have error I would change it to:
static void axi_dac_bus_clock(struct iio_backend *back, u64 *rate) - or at the very
least return u64 and not int.
But alternatively, if you want to take simplicity one step further, you can just save
a u64 bus_clock variable in your platform_data and access it directly (given that
we're only assuming the streaming rate in which case this is constant). And If we
ever have an usecase where we need more flexibility, it should be fairly staright to
bring this the bus_clock() callback.
I'm fine either way so up to you :)
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v7 8/8] iio: dac: adi-axi-dac: add registering of child fdt node
2024-10-21 12:40 [PATCH v7 0/8] iio: add support for the ad3552r AXI DAC IP Angelo Dureghello
` (6 preceding siblings ...)
2024-10-21 12:40 ` [PATCH v7 7/8] iio: dac: ad3552r: add high-speed platform driver Angelo Dureghello
@ 2024-10-21 12:40 ` Angelo Dureghello
2024-10-22 12:02 ` Nuno Sá
7 siblings, 1 reply; 29+ messages in thread
From: Angelo Dureghello @ 2024-10-21 12:40 UTC (permalink / raw)
To: Nuno Sá, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown,
Angelo Dureghello
From: Angelo Dureghello <adureghello@baylibre.com>
Change to obtain the fdt use case as reported in the
adi,ad3552r.yaml file in this patchset.
The DAC device is defined as a child node of the backend.
Registering the child fdt node as a platform devices.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
drivers/iio/dac/adi-axi-dac.c | 53 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
index 9d6809fe7a67..7f7ef3e219ba 100644
--- a/drivers/iio/dac/adi-axi-dac.c
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -29,6 +29,8 @@
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include "ad3552r-hs.h"
+
/*
* Register definitions:
* https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
@@ -97,6 +99,7 @@ struct axi_dac_info {
unsigned int version;
const struct iio_backend_info *backend_info;
bool has_dac_clk;
+ bool has_child_nodes;
};
struct axi_dac_state {
@@ -723,6 +726,35 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val,
return regmap_read(st->regmap, AXI_DAC_CUSTOM_RD_REG, val);
}
+static void axi_dac_child_remove(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int axi_dac_create_platform_device(struct axi_dac_state *st,
+ struct fwnode_handle *child)
+{
+ struct ad3552r_hs_platform_data pdata = {
+ .bus_reg_read = axi_dac_bus_reg_read,
+ .bus_reg_write = axi_dac_bus_reg_write,
+ };
+ struct platform_device_info pi = {
+ .parent = st->dev,
+ .name = fwnode_get_name(child),
+ .id = PLATFORM_DEVID_AUTO,
+ .fwnode = child,
+ .data = &pdata,
+ .size_data = sizeof(pdata),
+ };
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_full(&pi);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return devm_add_action_or_reset(st->dev, axi_dac_child_remove, pdev);
+}
+
static const struct iio_backend_ops axi_dac_generic_ops = {
.enable = axi_dac_enable,
.disable = axi_dac_disable,
@@ -865,6 +897,26 @@ static int axi_dac_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"failed to register iio backend\n");
+ if (st->info->has_child_nodes) {
+ device_for_each_child_node_scoped(&pdev->dev, child) {
+ int val;
+
+ /* Processing only reg 0 node */
+ ret = fwnode_property_read_u32(child, "reg", &val);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "invalid reg property.");
+ if (val != 0)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "invalid node address.");
+
+ ret = axi_dac_create_platform_device(st, child);
+ if (ret)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "cannot create device.");
+ }
+ }
+
dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
@@ -882,6 +934,7 @@ static const struct axi_dac_info dac_ad3552r = {
.version = ADI_AXI_PCORE_VER(9, 1, 'b'),
.backend_info = &axi_ad3552r,
.has_dac_clk = true,
+ .has_child_nodes = true,
};
static const struct of_device_id axi_dac_of_match[] = {
--
2.45.0.rc1
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v7 8/8] iio: dac: adi-axi-dac: add registering of child fdt node
2024-10-21 12:40 ` [PATCH v7 8/8] iio: dac: adi-axi-dac: add registering of child fdt node Angelo Dureghello
@ 2024-10-22 12:02 ` Nuno Sá
0 siblings, 0 replies; 29+ messages in thread
From: Nuno Sá @ 2024-10-22 12:02 UTC (permalink / raw)
To: Angelo Dureghello, Nuno Sá, Lars-Peter Clausen,
Michael Hennerich, Jonathan Cameron, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan
Cc: linux-iio, devicetree, linux-kernel, dlechner, Mark Brown
On Mon, 2024-10-21 at 14:40 +0200, Angelo Dureghello wrote:
> From: Angelo Dureghello <adureghello@baylibre.com>
>
> Change to obtain the fdt use case as reported in the
> adi,ad3552r.yaml file in this patchset.
>
> The DAC device is defined as a child node of the backend.
> Registering the child fdt node as a platform devices.
>
> Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
> ---
> drivers/iio/dac/adi-axi-dac.c | 53
> +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 53 insertions(+)
>
> diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> index 9d6809fe7a67..7f7ef3e219ba 100644
> --- a/drivers/iio/dac/adi-axi-dac.c
> +++ b/drivers/iio/dac/adi-axi-dac.c
> @@ -29,6 +29,8 @@
> #include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
>
> +#include "ad3552r-hs.h"
> +
> /*
> * Register definitions:
> * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
> @@ -97,6 +99,7 @@ struct axi_dac_info {
> unsigned int version;
> const struct iio_backend_info *backend_info;
> bool has_dac_clk;
> + bool has_child_nodes;
> };
>
> struct axi_dac_state {
> @@ -723,6 +726,35 @@ static int axi_dac_bus_reg_read(struct iio_backend *back,
> u32 reg, u32 *val,
> return regmap_read(st->regmap, AXI_DAC_CUSTOM_RD_REG, val);
> }
>
> +static void axi_dac_child_remove(void *data)
> +{
> + platform_device_unregister(data);
> +}
> +
> +static int axi_dac_create_platform_device(struct axi_dac_state *st,
> + struct fwnode_handle *child)
> +{
> + struct ad3552r_hs_platform_data pdata = {
> + .bus_reg_read = axi_dac_bus_reg_read,
> + .bus_reg_write = axi_dac_bus_reg_write,
> + };
> + struct platform_device_info pi = {
> + .parent = st->dev,
> + .name = fwnode_get_name(child),
> + .id = PLATFORM_DEVID_AUTO,
> + .fwnode = child,
> + .data = &pdata,
> + .size_data = sizeof(pdata),
> + };
> + struct platform_device *pdev;
> +
> + pdev = platform_device_register_full(&pi);
> + if (IS_ERR(pdev))
> + return PTR_ERR(pdev);
> +
> + return devm_add_action_or_reset(st->dev, axi_dac_child_remove, pdev);
> +}
> +
> static const struct iio_backend_ops axi_dac_generic_ops = {
> .enable = axi_dac_enable,
> .disable = axi_dac_disable,
> @@ -865,6 +897,26 @@ static int axi_dac_probe(struct platform_device *pdev)
> return dev_err_probe(&pdev->dev, ret,
> "failed to register iio backend\n");
>
> + if (st->info->has_child_nodes) {
I would prefer to not be silent on possible misconfigurations. IOW, moving the
check inside the loop and error out if (!st->info->has_child_nodes)
- Nuno Sá
^ permalink raw reply [flat|nested] 29+ messages in thread