2023-12-21 11:39:09

by Ceclan, Dumitru

[permalink] [raw]
Subject: [PATCH 0/3] Add support for LTC6373

This patch series adds support for the LTC6373 Instrumentation Amplifier within
the existing HMC425A driver.

The LTC6373 is a silicon, 3-bit Fully-Differential digital instrumentation
amplifier that supports the following programmable gains (Vout/Vin):
G = 0.25, 0.5, 1, 2, 4, 8, 16 + Shutdown.
The programmable interface consists of 3 digitally controled inputs.

Dumitru Ceclan (3):
dt-bindings: iio: hmc425a: add entry for LTC6373
iio: amplifiers: hmc425a: move conversion logic
iio: amplifiers: hmc425a: add support for LTC6373 Instrumentation
Amplifier

.../bindings/iio/amplifiers/adi,hmc425a.yaml | 16 ++
drivers/iio/amplifiers/hmc425a.c | 178 +++++++++++++-----
2 files changed, 149 insertions(+), 45 deletions(-)

--
2.42.0



2023-12-21 11:39:25

by Ceclan, Dumitru

[permalink] [raw]
Subject: [PATCH 1/3] dt-bindings: iio: hmc425a: add entry for LTC6373

The LTC6373 is a silicon, 3-bit Fully-Differential digital instrumentation
amplifier that supports the following programmable gains (Vout/Vin):
G = 0.25, 0.5, 1, 2, 4, 8, 16 + Shutdown.

Signed-off-by: Dumitru Ceclan <[email protected]>
---
.../bindings/iio/amplifiers/adi,hmc425a.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml
index 67de9d4e3a1d..8e7d99823059 100644
--- a/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml
+++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml
@@ -21,6 +21,8 @@ description: |
HMC540S 1 dB LSB Silicon MMIC 4-Bit Digital Positive Control Attenuator, 0.1 - 8 GHz
https://www.analog.com/media/en/technical-documentation/data-sheets/hmc540s.pdf

+ LTC6373 is a 3-Bit precision instrumentation amplifier with fully differential outputs
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ltc6373.pdf

properties:
compatible:
@@ -28,6 +30,7 @@ properties:
- adi,adrf5740
- adi,hmc425a
- adi,hmc540s
+ - adi,ltc6373

vcc-supply: true

@@ -38,6 +41,19 @@ properties:
minItems: 6
maxItems: 6

+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,ltc6373
+
+ then:
+ properties:
+ ctrl-gpios:
+ minItems: 3
+ maxItems: 3
+
required:
- compatible
- ctrl-gpios
--
2.42.0


2023-12-21 11:39:43

by Ceclan, Dumitru

[permalink] [raw]
Subject: [PATCH 2/3] iio: amplifiers: hmc425a: move conversion logic

Move gain-dB<->code conversion logic from read_raw and write_raw to
hmc425a_gain_dB_to_code() and hmc425a_code_to_gain_dB().

Signed-off-by: Dumitru Ceclan <[email protected]>
---
drivers/iio/amplifiers/hmc425a.c | 100 ++++++++++++++++++-------------
1 file changed, 57 insertions(+), 43 deletions(-)

diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c
index ed4d72922696..b5fd19403d15 100644
--- a/drivers/iio/amplifiers/hmc425a.c
+++ b/drivers/iio/amplifiers/hmc425a.c
@@ -56,35 +56,70 @@ static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
return 0;
}

+static int hmc425a_gain_dB_to_code(struct hmc425a_state *st, int val, int val2, int *code)
+{
+ struct hmc425a_chip_info *inf = st->chip_info;
+ int gain, temp;
+
+ if (val < 0)
+ gain = (val * 1000) - (val2 / 1000);
+ else
+ gain = (val * 1000) + (val2 / 1000);
+
+ if (gain > inf->gain_max || gain < inf->gain_min)
+ return -EINVAL;
+
+ switch (st->type) {
+ case ID_HMC425A:
+ *code = ~((abs(gain) / 500) & 0x3F);
+ break;
+ case ID_HMC540S:
+ *code = ~((abs(gain) / 1000) & 0xF);
+ break;
+ case ID_ADRF5740:
+ temp = (abs(gain) / 2000) & 0xF;
+ *code = temp & BIT(3) ? temp | BIT(2) : temp;
+ break;
+ }
+ return 0;
+}
+
+static int hmc425a_code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2)
+{
+ int code, gain;
+
+ code = st->gain;
+ switch (st->type) {
+ case ID_HMC425A:
+ gain = ~code * -500;
+ break;
+ case ID_HMC540S:
+ gain = ~code * -1000;
+ break;
+ case ID_ADRF5740:
+ code = code & BIT(3) ? code & ~BIT(2) : code;
+ gain = code * -2000;
+ break;
+ }
+
+ *val = gain / 1000;
+ *val2 = (gain % 1000) * 1000;
+ return 0;
+}
+
static int hmc425a_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long m)
{
struct hmc425a_state *st = iio_priv(indio_dev);
- int code, gain = 0;
int ret;

mutex_lock(&st->lock);
switch (m) {
case IIO_CHAN_INFO_HARDWAREGAIN:
- code = st->gain;
-
- switch (st->type) {
- case ID_HMC425A:
- gain = ~code * -500;
- break;
- case ID_HMC540S:
- gain = ~code * -1000;
- break;
- case ID_ADRF5740:
- code = code & BIT(3) ? code & ~BIT(2) : code;
- gain = code * -2000;
+ ret = hmc425a_code_to_gain_dB(st, val, val2);
+ if (ret)
break;
- }
-
- *val = gain / 1000;
- *val2 = (gain % 1000) * 1000;
-
ret = IIO_VAL_INT_PLUS_MICRO_DB;
break;
default:
@@ -100,36 +135,15 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct hmc425a_state *st = iio_priv(indio_dev);
- struct hmc425a_chip_info *inf = st->chip_info;
- int code = 0, gain;
- int ret;
-
- if (val < 0)
- gain = (val * 1000) - (val2 / 1000);
- else
- gain = (val * 1000) + (val2 / 1000);
-
- if (gain > inf->gain_max || gain < inf->gain_min)
- return -EINVAL;
-
- switch (st->type) {
- case ID_HMC425A:
- code = ~((abs(gain) / 500) & 0x3F);
- break;
- case ID_HMC540S:
- code = ~((abs(gain) / 1000) & 0xF);
- break;
- case ID_ADRF5740:
- code = (abs(gain) / 2000) & 0xF;
- code = code & BIT(3) ? code | BIT(2) : code;
- break;
- }
+ int code = 0, ret;

mutex_lock(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = hmc425a_gain_dB_to_code(st, val, val2, &code);
+ if (ret)
+ break;
st->gain = code;
-
ret = hmc425a_write(indio_dev, st->gain);
break;
default:
--
2.42.0


2023-12-21 11:39:57

by Ceclan, Dumitru

[permalink] [raw]
Subject: [PATCH 3/3] iio: amplifiers: hmc425a: add support for LTC6373 Instrumentation Amplifier

This adds support for LTC6373 36 V Fully-Differential Programmable-Gain
Instrumentation Amplifier with 25 pA Input Bias Current.
The user can program the gain to one of seven available settings through
a 3-bit parallel interface (A2 to A0).

Signed-off-by: Dumitru Ceclan <[email protected]>
---
drivers/iio/amplifiers/hmc425a.c | 78 +++++++++++++++++++++++++++++++-
1 file changed, 76 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c
index b5fd19403d15..27fdc32a0457 100644
--- a/drivers/iio/amplifiers/hmc425a.c
+++ b/drivers/iio/amplifiers/hmc425a.c
@@ -2,9 +2,10 @@
/*
* HMC425A and similar Gain Amplifiers
*
- * Copyright 2020 Analog Devices Inc.
+ * Copyright 2020, 2023 Analog Devices Inc.
*/

+#include <linux/bits.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -20,10 +21,23 @@
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>

+/*
+ * The LTC6373 amplifier supports configuring gain using GPIO's with the following
+ * values (OUTPUT_V / INPUT_V): 0(shutdown), 0.25, 0.5, 1, 2, 4, 8, 16
+ *
+ * Except for the shutdown value, all can be converted to dB using 20 * log10(x)
+ * From here, it is observed that all values are multiples of the '2' gain setting,
+ * with the correspondent of 6.020dB.
+ */
+#define LTC6373_CONVERSION_CONSTANT 6020
+#define LTC6373_CONVERSION_MASK GENMASK(2, 0)
+#define LTC6373_SHUTDOWN GENMASK(2, 0)
+
enum hmc425a_type {
ID_HMC425A,
ID_HMC540S,
- ID_ADRF5740
+ ID_ADRF5740,
+ ID_LTC6373,
};

struct hmc425a_chip_info {
@@ -42,6 +56,8 @@ struct hmc425a_state {
struct gpio_descs *gpios;
enum hmc425a_type type;
u32 gain;
+ bool enabled;
+
};

static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
@@ -80,6 +96,17 @@ static int hmc425a_gain_dB_to_code(struct hmc425a_state *st, int val, int val2,
temp = (abs(gain) / 2000) & 0xF;
*code = temp & BIT(3) ? temp | BIT(2) : temp;
break;
+ case ID_LTC6373:
+ if (!st->enabled)
+ return -EPERM;
+
+ /* add half of the value for rounding */
+ temp = LTC6373_CONVERSION_CONSTANT / 2;
+ if (val < 0)
+ temp *= -1;
+ *code = ~((gain + temp) / LTC6373_CONVERSION_CONSTANT + 3)
+ & LTC6373_CONVERSION_MASK;
+ break;
}
return 0;
}
@@ -100,6 +127,12 @@ static int hmc425a_code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2
code = code & BIT(3) ? code & ~BIT(2) : code;
gain = code * -2000;
break;
+ case ID_LTC6373:
+ if (!st->enabled)
+ return -EPERM;
+ gain = ((~code & LTC6373_CONVERSION_MASK) - 3) *
+ LTC6373_CONVERSION_CONSTANT;
+ break;
}

*val = gain / 1000;
@@ -122,6 +155,10 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev,
break;
ret = IIO_VAL_INT_PLUS_MICRO_DB;
break;
+ case IIO_CHAN_INFO_ENABLE:
+ *val = st->enabled;
+ ret = IIO_VAL_INT;
+ break;
default:
ret = -EINVAL;
}
@@ -146,6 +183,17 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev,
st->gain = code;
ret = hmc425a_write(indio_dev, st->gain);
break;
+ case IIO_CHAN_INFO_ENABLE:
+ switch (st->type) {
+ case ID_LTC6373:
+ code = (val) ? st->gain : LTC6373_SHUTDOWN;
+ st->enabled = val;
+ ret = hmc425a_write(indio_dev, code);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
default:
ret = -EINVAL;
}
@@ -161,6 +209,8 @@ static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
return IIO_VAL_INT_PLUS_MICRO_DB;
+ case IIO_CHAN_INFO_ENABLE:
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -181,15 +231,30 @@ static const struct iio_info hmc425a_info = {
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
}

+#define LTC6373_CHAN(_channel) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
+ BIT(IIO_CHAN_INFO_ENABLE), \
+}
+
static const struct iio_chan_spec hmc425a_channels[] = {
HMC425A_CHAN(0),
};

+static const struct iio_chan_spec ltc6373_channels[] = {
+ LTC6373_CHAN(0),
+};
+
/* Match table for of_platform binding */
static const struct of_device_id hmc425a_of_match[] = {
{ .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A },
{ .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S },
{ .compatible = "adi,adrf5740", .data = (void *)ID_ADRF5740 },
+ { .compatible = "adi,ltc6373", .data = (void *)ID_LTC6373 },
{},
};
MODULE_DEVICE_TABLE(of, hmc425a_of_match);
@@ -222,6 +287,15 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
.gain_max = 0,
.default_gain = 0xF, /* set default gain -22.0db*/
},
+ [ID_LTC6373] = {
+ .name = "ltc6373",
+ .channels = ltc6373_channels,
+ .num_channels = ARRAY_SIZE(ltc6373_channels),
+ .num_gpios = 3,
+ .gain_min = -12041, /* gain setting x0.25*/
+ .gain_max = 24082, /* gain setting x16 */
+ .default_gain = LTC6373_SHUTDOWN,
+ },
};

static int hmc425a_probe(struct platform_device *pdev)
--
2.42.0


2023-12-21 17:30:13

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 3/3] iio: amplifiers: hmc425a: add support for LTC6373 Instrumentation Amplifier

On Thu, 21 Dec 2023 13:38:40 +0200
Dumitru Ceclan <[email protected]> wrote:

> This adds support for LTC6373 36 V Fully-Differential Programmable-Gain
> Instrumentation Amplifier with 25 pA Input Bias Current.
> The user can program the gain to one of seven available settings through
> a 3-bit parallel interface (A2 to A0).
>
> Signed-off-by: Dumitru Ceclan <[email protected]>
One trivial comment inline.

The use of IIO_INFO_ENABLE is unusual. So far the only documented uses of
that are

What: /sys/.../iio:deviceX/in_energy_en
What: /sys/.../iio:deviceX/in_distance_en
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
What: /sys/.../iio:deviceX/in_steps_en
KernelVersion: 3.19
Contact: [email protected]
Description:
Activates a device feature that runs in firmware/hardware.
E.g. for steps: the pedometer saves power while not used;
when activated, it will count the steps taken by the user in
firmware and export them through in_steps_input.

And this doesn't match up with these sort of 'counting' or 'cumulative' measures.

I wonder treating it like a DAC output and instead using _powerdown
instead?

---
> drivers/iio/amplifiers/hmc425a.c | 78 +++++++++++++++++++++++++++++++-
> 1 file changed, 76 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c
> index b5fd19403d15..27fdc32a0457 100644
> --- a/drivers/iio/amplifiers/hmc425a.c
> +++ b/drivers/iio/amplifiers/hmc425a.c
> @@ -2,9 +2,10 @@
> /*
> * HMC425A and similar Gain Amplifiers
> *
> - * Copyright 2020 Analog Devices Inc.
> + * Copyright 2020, 2023 Analog Devices Inc.
> */
>
> +#include <linux/bits.h>
> #include <linux/bitops.h>
> #include <linux/device.h>
> #include <linux/err.h>
> @@ -20,10 +21,23 @@
> #include <linux/regulator/consumer.h>
> #include <linux/sysfs.h>
>
> +/*
> + * The LTC6373 amplifier supports configuring gain using GPIO's with the following
> + * values (OUTPUT_V / INPUT_V): 0(shutdown), 0.25, 0.5, 1, 2, 4, 8, 16
> + *
> + * Except for the shutdown value, all can be converted to dB using 20 * log10(x)
> + * From here, it is observed that all values are multiples of the '2' gain setting,
> + * with the correspondent of 6.020dB.
> + */
> +#define LTC6373_CONVERSION_CONSTANT 6020
> +#define LTC6373_CONVERSION_MASK GENMASK(2, 0)
> +#define LTC6373_SHUTDOWN GENMASK(2, 0)
> +
> enum hmc425a_type {
> ID_HMC425A,
> ID_HMC540S,
> - ID_ADRF5740
> + ID_ADRF5740,
> + ID_LTC6373,
> };
>
> struct hmc425a_chip_info {
> @@ -42,6 +56,8 @@ struct hmc425a_state {
> struct gpio_descs *gpios;
> enum hmc425a_type type;
> u32 gain;
> + bool enabled;
> +
> };
>
> static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
> @@ -80,6 +96,17 @@ static int hmc425a_gain_dB_to_code(struct hmc425a_state *st, int val, int val2,
> temp = (abs(gain) / 2000) & 0xF;
> *code = temp & BIT(3) ? temp | BIT(2) : temp;
> break;
> + case ID_LTC6373:
> + if (!st->enabled)
> + return -EPERM;
> +
> + /* add half of the value for rounding */
> + temp = LTC6373_CONVERSION_CONSTANT / 2;
> + if (val < 0)
> + temp *= -1;
> + *code = ~((gain + temp) / LTC6373_CONVERSION_CONSTANT + 3)
> + & LTC6373_CONVERSION_MASK;
> + break;
> }
> return 0;
> }
> @@ -100,6 +127,12 @@ static int hmc425a_code_to_gain_dB(struct hmc425a_state *st, int *val, int *val2
> code = code & BIT(3) ? code & ~BIT(2) : code;
> gain = code * -2000;
> break;
> + case ID_LTC6373:
> + if (!st->enabled)
> + return -EPERM;
> + gain = ((~code & LTC6373_CONVERSION_MASK) - 3) *
> + LTC6373_CONVERSION_CONSTANT;
> + break;
> }
>
> *val = gain / 1000;
> @@ -122,6 +155,10 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev,
> break;
> ret = IIO_VAL_INT_PLUS_MICRO_DB;
> break;
> + case IIO_CHAN_INFO_ENABLE:
> + *val = st->enabled;
> + ret = IIO_VAL_INT;
> + break;
> default:
> ret = -EINVAL;
> }
> @@ -146,6 +183,17 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev,
> st->gain = code;
> ret = hmc425a_write(indio_dev, st->gain);
> break;
> + case IIO_CHAN_INFO_ENABLE:
> + switch (st->type) {
> + case ID_LTC6373:
> + code = (val) ? st->gain : LTC6373_SHUTDOWN;
> + st->enabled = val;
> + ret = hmc425a_write(indio_dev, code);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> + break;
> default:
> ret = -EINVAL;
> }
> @@ -161,6 +209,8 @@ static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev,
> switch (mask) {
> case IIO_CHAN_INFO_HARDWAREGAIN:
> return IIO_VAL_INT_PLUS_MICRO_DB;
> + case IIO_CHAN_INFO_ENABLE:
> + return IIO_VAL_INT;
> default:
> return -EINVAL;
> }
> @@ -181,15 +231,30 @@ static const struct iio_info hmc425a_info = {
> .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
> }
>
> +#define LTC6373_CHAN(_channel) \
> +{ \
> + .type = IIO_VOLTAGE, \
> + .output = 1, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
> + BIT(IIO_CHAN_INFO_ENABLE), \
> +}
> +
> static const struct iio_chan_spec hmc425a_channels[] = {
> HMC425A_CHAN(0),
> };
>
> +static const struct iio_chan_spec ltc6373_channels[] = {
> + LTC6373_CHAN(0),
> +};
> +
> /* Match table for of_platform binding */
> static const struct of_device_id hmc425a_of_match[] = {
> { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A },
> { .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S },
> { .compatible = "adi,adrf5740", .data = (void *)ID_ADRF5740 },
> + { .compatible = "adi,ltc6373", .data = (void *)ID_LTC6373 },
> {},
> };
> MODULE_DEVICE_TABLE(of, hmc425a_of_match);
> @@ -222,6 +287,15 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
> .gain_max = 0,
> .default_gain = 0xF, /* set default gain -22.0db*/
> },
> + [ID_LTC6373] = {

Looks to be one tab too far indented.

> + .name = "ltc6373",
> + .channels = ltc6373_channels,
> + .num_channels = ARRAY_SIZE(ltc6373_channels),
> + .num_gpios = 3,
> + .gain_min = -12041, /* gain setting x0.25*/
> + .gain_max = 24082, /* gain setting x16 */
> + .default_gain = LTC6373_SHUTDOWN,
> + },
> };
>
> static int hmc425a_probe(struct platform_device *pdev)


2023-12-21 17:38:30

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 1/3] dt-bindings: iio: hmc425a: add entry for LTC6373

On 21/12/2023 12:38, Dumitru Ceclan wrote:
>
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: adi,ltc6373
> +
> + then:
> + properties:
> + ctrl-gpios:
> + minItems: 3
> + maxItems: 3

Test your DTS first. This leads to errors. :(
You miss minItems top-level and here else:.

Best regards,
Krzysztof