From 8a90c0c0921e33fab005a64ccc4922c69d57fda9 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 27 Jun 2024 16:50:25 +0530 Subject: [PATCH 01/21] iio: addac: ad7294: add basic DAC support --- drivers/iio/addac/Kconfig | 10 +++ drivers/iio/addac/Makefile | 1 + drivers/iio/addac/ad7294.c | 159 +++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/iio/addac/ad7294.c diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index c6c72ce42d36d4..c78969fdf2a76b 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -19,6 +19,16 @@ config AD74115 To compile this driver as a module, choose M here: the module will be called ad74115. +config AD7294 + tristate "Analog Devices AD7294/AD7294-2 driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for Analog Devices AD7294/AD7294-2. + + To compile this driver as a module, choose M here: the + module will be called ad7294. + config AD74413R tristate "Analog Devices AD74412R/AD74413R driver" depends on GPIOLIB && SPI diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile index fbc3b668029dba..065f2c40f9b354 100644 --- a/drivers/iio/addac/Makefile +++ b/drivers/iio/addac/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD7294) += ad7294.o obj-$(CONFIG_AD74115) += ad74115.o obj-$(CONFIG_AD74413R) += ad74413r.o obj-$(CONFIG_STX104) += stx104.o diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c new file mode 100644 index 00000000000000..6c20aba6254bf2 --- /dev/null +++ b/drivers/iio/addac/ad7294.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7294/AD7294-2 I2C driver + * + * Copyright (c) 2024 Analog Devices Inc. + * Author: Anshul Dalal + */ + +#include +#include +#include +#include +#include + +#define AD7294_REG_CMD 0x00 +#define AD7294_REG_DAC_A 0x01 + +#define AD7294_DAC_CHAN(chan_id) \ + { \ + .type = IIO_VOLTAGE, .channel = chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, .output = 1, \ + } + +bool ad7294_readable_reg(struct device *dev, unsigned int reg) +{ + return reg != AD7294_REG_CMD; +}; + +static const struct regmap_config ad7294_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x27, + .readable_reg = ad7294_readable_reg, +}; + +struct ad7294_data { + struct mutex lock; + struct regmap *regmap; + u16 dac_value[2]; +}; + +struct iio_chan_spec ad7294_chan_spec[] = { + AD7294_DAC_CHAN(0), + AD7294_DAC_CHAN(1), + AD7294_DAC_CHAN(2), + AD7294_DAC_CHAN(3), +}; + +static int ad7294_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ad7294_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + *val = data->dac_value[chan->channel]; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + return -EINVAL; +} + +static int ad7294_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + int ret; + struct ad7294_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->output) { + /* DAC has 12-bit channels */ + if (val < 0 || val > 0xFFF || val2) + return -EINVAL; + ret = regmap_write(data->regmap, + AD7294_REG_DAC_A + chan->channel, + val); + if (ret) + return ret; + data->dac_value[chan->channel] = val; + } + } + + return 0; +} + +static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, + unsigned writeval, unsigned *readval) +{ + struct ad7294_data *data = iio_priv(indio_dev); + if (readval) + return regmap_read(data->regmap, reg, readval); + return regmap_write(data->regmap, reg, writeval); +} + +struct iio_info ad7294_info = { + .read_raw = ad7294_read_raw, + .write_raw = ad7294_write_raw, + .debugfs_reg_access = ad7294_reg_access, +}; + +static int ad7294_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ad7294_data *data; + + dev_info(&client->dev, "Driver Probed\n"); + indio_dev = + devm_iio_device_alloc(&client->dev, sizeof(struct ad7294_data)); + if (!indio_dev) + return -1; + indio_dev->name = "ad7294"; + indio_dev->info = &ad7294_info; + indio_dev->channels = ad7294_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(ad7294_chan_spec); + + data = iio_priv(indio_dev); + + mutex_init(&data->lock); + + data->regmap = devm_regmap_init_i2c(client, &ad7294_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static void ad7294_remove(struct i2c_client *client) +{ + dev_info(&client->dev, "Driver Removed\n"); +} + +static struct of_device_id ad7294_of_table[] = { { .compatible = "adi,ad7294" }, + { .compatible = + "adi,ad7294-2" }, + { /* Sentinel */ } }; + +static struct i2c_driver ad7294_driver = { + .driver = { .name = "ad7294", .of_match_table = ad7294_of_table }, + .probe = ad7294_probe, + .remove = ad7294_remove, +}; + +module_i2c_driver(ad7294_driver); + +MODULE_AUTHOR("Anshul Dalal "); +MODULE_DESCRIPTION("Analog Devices AD7294/AD7294-2 ADDAC"); +MODULE_LICENSE("GPL"); From c6f1f255f1705ed7dadddaf5db7cd77eceebd974 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 27 Jun 2024 21:04:57 +0530 Subject: [PATCH 02/21] iio: addac: ad7294: add basic ADC support --- drivers/iio/addac/ad7294.c | 58 ++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 6c20aba6254bf2..037e8708bc4da3 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -13,16 +13,27 @@ #include #define AD7294_REG_CMD 0x00 -#define AD7294_REG_DAC_A 0x01 +#define AD7294_REG_RESULT 0x01 +#define AD7294_REG_DAC(x) ((x) + 0x01) -#define AD7294_DAC_CHAN(chan_id) \ +#define AD7294_VALUE_MASK GENMASK(11, 0) + +#define AD7294_DAC_CHAN(_chan_id) \ { \ - .type = IIO_VOLTAGE, .channel = chan_id, \ + .type = IIO_VOLTAGE, .channel = _chan_id, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW), \ .indexed = 1, .output = 1, \ } +#define AD7294_ADC_CHAN(_type, _chan_id) \ + { \ + .type = _type, .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, .output = 0, \ + } + bool ad7294_readable_reg(struct device *dev, unsigned int reg) { return reg != AD7294_REG_CMD; @@ -46,6 +57,12 @@ struct iio_chan_spec ad7294_chan_spec[] = { AD7294_DAC_CHAN(1), AD7294_DAC_CHAN(2), AD7294_DAC_CHAN(3), + AD7294_ADC_CHAN(IIO_VOLTAGE, 0), + AD7294_ADC_CHAN(IIO_VOLTAGE, 1), + AD7294_ADC_CHAN(IIO_VOLTAGE, 2), + AD7294_ADC_CHAN(IIO_VOLTAGE, 3), + AD7294_ADC_CHAN(IIO_CURRENT, 4), + AD7294_ADC_CHAN(IIO_CURRENT, 5), }; static int ad7294_read_raw(struct iio_dev *indio_dev, @@ -53,15 +70,34 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct ad7294_data *data = iio_priv(indio_dev); + int ret; + unsigned int regval, reg_channel_mask; + guard(mutex)(&data->lock); switch (mask) { case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_VOLTAGE: + if (chan->output) { + if (chan->type != IIO_VOLTAGE) + return -EINVAL; *val = data->dac_value[chan->channel]; return IIO_VAL_INT; - default: - return -EINVAL; + } else { + if (chan->type != IIO_VOLTAGE && + chan->type != IIO_CURRENT) + return -EINVAL; + /* TODO: Potential endianess issue here */ + reg_channel_mask = BIT(chan->channel) << 8; + ret = regmap_write(data->regmap, AD7294_REG_CMD, + reg_channel_mask); + if (ret) + return ret; + /* TODO: Potential endianess issue here */ + ret = regmap_read(data->regmap, AD7294_REG_RESULT, + ®val); + if (ret) + return ret; + *val = regval & AD7294_VALUE_MASK; + return IIO_VAL_INT; } } return -EINVAL; @@ -82,15 +118,15 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, if (val < 0 || val > 0xFFF || val2) return -EINVAL; ret = regmap_write(data->regmap, - AD7294_REG_DAC_A + chan->channel, - val); + AD7294_REG_DAC(chan->channel), val); if (ret) return ret; data->dac_value[chan->channel] = val; + return 0; } } - return 0; + return -EINVAL; } static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, @@ -118,7 +154,7 @@ static int ad7294_probe(struct i2c_client *client, indio_dev = devm_iio_device_alloc(&client->dev, sizeof(struct ad7294_data)); if (!indio_dev) - return -1; + return -ENOMEM; indio_dev->name = "ad7294"; indio_dev->info = &ad7294_info; indio_dev->channels = ad7294_chan_spec; From fb46b7fa6e58b8a446c3c394b8ee900a3b188ad3 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Sun, 30 Jun 2024 14:36:27 +0530 Subject: [PATCH 03/21] arch: dts: overlays: add ad7294 overlay --- arch/arm/boot/dts/overlays/Makefile | 1 + .../boot/dts/overlays/rpi-ad7294-overlay.dts | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile index 37832a7f68eddd..8a403977e2b707 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -225,6 +225,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ rpi-ad7173.dtbo \ rpi-ad7190.dtbo \ rpi-ad7293.dtbo \ + rpi-ad7294.dtbo \ rpi-ad738x.dtbo \ rpi-ad7746.dtbo \ rpi-ad7768-1.dtbo \ diff --git a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts new file mode 100644 index 00000000000000..c1e4bd0f008c80 --- /dev/null +++ b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts @@ -0,0 +1,68 @@ +/* + * DT Overlay for Analog Devices AD7294/AD7294-2 + */ + +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2835"; + + fragment@0 { + target-path = "/"; + __overlay__ { + avdd: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "avdd"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + }; + vdrive: fixedregulator@1 { + compatible = "regulator-fixed"; + regulator-name = "vdrive"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + adc_vref: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "adc-vref"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-boot-on; + }; + dac_vref: fixedregulator@3 { + compatible = "regulator-fixed"; + regulator-name = "dac-vref"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-boot-on; + }; + }; + }; + + fragment@1 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + ad7294: ad7294@62 { + compatible = "adi,ad7294"; + reg = <0x62>; + adc-vref-supply = <&adc_vref>; + dac-vref-supply = <&dac_vref>; + avdd-supply = <&avdd>; + vdrive-supply = <&vdrive>; + }; + }; + }; + + fragment@2 { + target = <&ad7294>; + __dormant__ { + compatible = "adi,ad7294-2"; + }; + }; +}; From acd6a57246734117b4268e1a2a9e1bc85ece8396 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Sun, 30 Jun 2024 14:36:44 +0530 Subject: [PATCH 04/21] iio: addac: ad7294: add regulator support --- drivers/iio/addac/ad7294.c | 249 +++++++++++++++++++++++++++++-------- 1 file changed, 197 insertions(+), 52 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 037e8708bc4da3..4be1db7d205bed 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -11,27 +11,36 @@ #include #include #include +#include -#define AD7294_REG_CMD 0x00 -#define AD7294_REG_RESULT 0x01 -#define AD7294_REG_DAC(x) ((x) + 0x01) +#define AD7294_REG_CMD 0x00 +#define AD7294_REG_RESULT 0x01 +#define AD7294_REG_DAC(x) ((x) + 0x01) +#define AD7294_REG_PWDN 0x0A -#define AD7294_VALUE_MASK GENMASK(11, 0) +#define AD7294_VALUE_MASK GENMASK(11, 0) +#define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) +#define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) -#define AD7294_DAC_CHAN(_chan_id) \ - { \ - .type = IIO_VOLTAGE, .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW), \ - .indexed = 1, .output = 1, \ +#define AD7294_ADC_INTERNAL_VREF_MV 2500 +#define AD7294_DAC_INTERNAL_VREF_MV 2500 +#define AD7294_RESOLUTION 12 +#define AD7294_UV_IN_MV 1000 + +#define AD7294_DAC_CHAN(_chan_id) \ + { \ + .type = IIO_VOLTAGE, .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, .output = 1, \ } -#define AD7294_ADC_CHAN(_type, _chan_id) \ - { \ - .type = _type, .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW), \ - .indexed = 1, .output = 0, \ +#define AD7294_ADC_CHAN(_type, _chan_id) \ + { \ + .type = _type, .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, .output = 0, \ } bool ad7294_readable_reg(struct device *dev, unsigned int reg) @@ -44,11 +53,17 @@ static const struct regmap_config ad7294_regmap_config = { .val_bits = 16, .max_register = 0x27, .readable_reg = ad7294_readable_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, }; -struct ad7294_data { +struct ad7294_state { struct mutex lock; struct regmap *regmap; + struct i2c_client *i2c; + struct regulator *avdd_reg; + struct regulator *vdrive_reg; + struct regulator *adc_vref_reg; + struct regulator *dac_vref_reg; u16 dac_value[2]; }; @@ -65,40 +80,88 @@ struct iio_chan_spec ad7294_chan_spec[] = { AD7294_ADC_CHAN(IIO_CURRENT, 5), }; +static int ad7294_read_u8(struct ad7294_state *st, u8 reg, u8 *val) +{ + int ret; + struct i2c_client *client = st->i2c; + struct i2c_msg message[2] = { { + .addr = client->addr, + .flags = client->flags, + .len = sizeof(reg), + .buf = ®, + }, + { + .addr = client->addr, + .flags = client->flags | I2C_M_RD, + .len = sizeof(*val), + .buf = val, + } }; + ret = i2c_transfer(client->adapter, message, ARRAY_SIZE(message)); + return (ret == ARRAY_SIZE(message)) ? 0 : ret; +}; + +static int ad7294_write_u8(struct ad7294_state *st, u8 reg, u8 val) +{ + int ret; + __be16 write_buffer = cpu_to_be16(reg << 8 | val); + ret = i2c_master_send(st->i2c, (char *)&write_buffer, + sizeof(write_buffer)); + return (ret == sizeof(write_buffer)) ? 0 : ret; +}; + static int ad7294_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct ad7294_data *data = iio_priv(indio_dev); + struct ad7294_state *st = iio_priv(indio_dev); int ret; - unsigned int regval, reg_channel_mask; + unsigned int regval; - guard(mutex)(&data->lock); + guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->output) { if (chan->type != IIO_VOLTAGE) return -EINVAL; - *val = data->dac_value[chan->channel]; + *val = st->dac_value[chan->channel]; return IIO_VAL_INT; } else { if (chan->type != IIO_VOLTAGE && chan->type != IIO_CURRENT) return -EINVAL; - /* TODO: Potential endianess issue here */ - reg_channel_mask = BIT(chan->channel) << 8; - ret = regmap_write(data->regmap, AD7294_REG_CMD, - reg_channel_mask); + ret = ad7294_write_u8(st, AD7294_REG_CMD, + BIT(chan->channel)); if (ret) return ret; - /* TODO: Potential endianess issue here */ - ret = regmap_read(data->regmap, AD7294_REG_RESULT, + ret = regmap_read(st->regmap, AD7294_REG_RESULT, ®val); if (ret) return ret; *val = regval & AD7294_VALUE_MASK; return IIO_VAL_INT; } + case IIO_CHAN_INFO_SCALE: + if (chan->output) { + if (st->dac_vref_reg) { + ret = regulator_get_voltage(st->dac_vref_reg); + if (ret < 0) + return ret; + *val = ret / AD7294_UV_IN_MV; + } else { + *val = AD7294_DAC_INTERNAL_VREF_MV; + } + } else { + if (st->adc_vref_reg) { + ret = regulator_get_voltage(st->adc_vref_reg); + if (ret < 0) + return ret; + *val = ret / AD7294_UV_IN_MV; + } else { + *val = AD7294_ADC_INTERNAL_VREF_MV; + } + } + *val2 = AD7294_RESOLUTION; + return IIO_VAL_FRACTIONAL_LOG2; } return -EINVAL; } @@ -108,20 +171,20 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, long mask) { int ret; - struct ad7294_data *data = iio_priv(indio_dev); + struct ad7294_state *st = iio_priv(indio_dev); - guard(mutex)(&data->lock); + guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->output) { /* DAC has 12-bit channels */ - if (val < 0 || val > 0xFFF || val2) + if (val < 0 || val >= BIT(AD7294_RESOLUTION) || val2) return -EINVAL; - ret = regmap_write(data->regmap, + ret = regmap_write(st->regmap, AD7294_REG_DAC(chan->channel), val); if (ret) return ret; - data->dac_value[chan->channel] = val; + st->dac_value[chan->channel] = val; return 0; } } @@ -132,10 +195,10 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval) { - struct ad7294_data *data = iio_priv(indio_dev); + struct ad7294_state *st = iio_priv(indio_dev); if (readval) - return regmap_read(data->regmap, reg, readval); - return regmap_write(data->regmap, reg, writeval); + return regmap_read(st->regmap, reg, readval); + return regmap_write(st->regmap, reg, writeval); } struct iio_info ad7294_info = { @@ -144,39 +207,122 @@ struct iio_info ad7294_info = { .debugfs_reg_access = ad7294_reg_access, }; +static void ad7294_reg_disable(void *data) +{ + regulator_disable(data); +} + +static int ad7294_init(struct ad7294_state *st) +{ + int ret; + u8 pwdn_config; + struct i2c_client *i2c = st->i2c; + + mutex_init(&st->lock); + + st->regmap = devm_regmap_init_i2c(i2c, &ad7294_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = ad7294_read_u8(st, AD7294_REG_PWDN, &pwdn_config); + if (ret) + return ret; + + st->vdrive_reg = devm_regulator_get(&i2c->dev, "vdrive"); + if (IS_ERR(st->vdrive_reg)) + return PTR_ERR(st->vdrive_reg); + ret = regulator_enable(st->vdrive_reg); + if (ret) + return ret; + ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, + st->vdrive_reg); + if (ret) + return ret; + + st->avdd_reg = devm_regulator_get(&i2c->dev, "avdd"); + if (IS_ERR(st->avdd_reg)) + return PTR_ERR(st->avdd_reg); + ret = regulator_enable(st->avdd_reg); + if (ret) + return ret; + ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, + st->avdd_reg); + if (ret) + return ret; + + st->adc_vref_reg = devm_regulator_get_optional(&i2c->dev, "adc-vref"); + if (IS_ERR(st->adc_vref_reg)) { + ret = PTR_ERR(st->adc_vref_reg); + if (ret != -ENODEV) + return ret; + dev_info(&i2c->dev, + "ADC Vref not found, using internal reference"); + pwdn_config &= ~AD7294_ADC_EXTERNAL_REF_MASK; + st->adc_vref_reg = NULL; + } else { + ret = regulator_enable(st->adc_vref_reg); + if (ret) + return ret; + ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, + st->adc_vref_reg); + if (ret) + return ret; + pwdn_config |= AD7294_ADC_EXTERNAL_REF_MASK; + } + + st->dac_vref_reg = devm_regulator_get_optional(&i2c->dev, "dac-vref"); + if (IS_ERR(st->dac_vref_reg)) { + ret = PTR_ERR(st->dac_vref_reg); + if (ret != -ENODEV) + return ret; + dev_info(&i2c->dev, + "DAC Vref not found, using internal reference"); + pwdn_config &= ~AD7294_DAC_EXTERNAL_REF_MASK; + st->dac_vref_reg = NULL; + } else { + ret = regulator_enable(st->dac_vref_reg); + if (ret) + return ret; + ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, + st->dac_vref_reg); + if (ret) + return ret; + pwdn_config |= AD7294_DAC_EXTERNAL_REF_MASK; + } + + ret = ad7294_write_u8(st, AD7294_REG_PWDN, pwdn_config); + if (ret) + return ret; + + return 0; +}; + static int ad7294_probe(struct i2c_client *client, const struct i2c_device_id *id) { + int ret; struct iio_dev *indio_dev; - struct ad7294_data *data; + struct ad7294_state *st; - dev_info(&client->dev, "Driver Probed\n"); - indio_dev = - devm_iio_device_alloc(&client->dev, sizeof(struct ad7294_data)); + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; + indio_dev->name = "ad7294"; indio_dev->info = &ad7294_info; indio_dev->channels = ad7294_chan_spec; indio_dev->num_channels = ARRAY_SIZE(ad7294_chan_spec); - data = iio_priv(indio_dev); + st = iio_priv(indio_dev); + st->i2c = client; - mutex_init(&data->lock); - - data->regmap = devm_regmap_init_i2c(client, &ad7294_regmap_config); - if (IS_ERR(data->regmap)) - return dev_err_probe(&client->dev, PTR_ERR(data->regmap), - "regmap initialization failed\n"); + ret = ad7294_init(st); + if (ret) + return ret; return devm_iio_device_register(&client->dev, indio_dev); } -static void ad7294_remove(struct i2c_client *client) -{ - dev_info(&client->dev, "Driver Removed\n"); -} - static struct of_device_id ad7294_of_table[] = { { .compatible = "adi,ad7294" }, { .compatible = "adi,ad7294-2" }, @@ -185,7 +331,6 @@ static struct of_device_id ad7294_of_table[] = { { .compatible = "adi,ad7294" }, static struct i2c_driver ad7294_driver = { .driver = { .name = "ad7294", .of_match_table = ad7294_of_table }, .probe = ad7294_probe, - .remove = ad7294_remove, }; module_i2c_driver(ad7294_driver); From 595d123ec8e0adb786da2d177ffa7a134d1238ff Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Sun, 30 Jun 2024 15:36:33 +0530 Subject: [PATCH 05/21] iio: addac: ad7294: code refactor --- drivers/iio/addac/ad7294.c | 92 +++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 4be1db7d205bed..475e7bc8efe7c6 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* * AD7294/AD7294-2 I2C driver + * Datasheet: + * https://www.analog.com/media/en/technical-documentation/data-sheets/AD7294.pdf * * Copyright (c) 2024 Analog Devices Inc. * Author: Anshul Dalal @@ -43,7 +45,7 @@ .indexed = 1, .output = 0, \ } -bool ad7294_readable_reg(struct device *dev, unsigned int reg) +static bool ad7294_readable_reg(struct device *dev, unsigned int reg) { return reg != AD7294_REG_CMD; }; @@ -67,7 +69,7 @@ struct ad7294_state { u16 dac_value[2]; }; -struct iio_chan_spec ad7294_chan_spec[] = { +static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_DAC_CHAN(0), AD7294_DAC_CHAN(1), AD7294_DAC_CHAN(2), @@ -120,15 +122,15 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: - if (chan->output) { - if (chan->type != IIO_VOLTAGE) - return -EINVAL; - *val = st->dac_value[chan->channel]; - return IIO_VAL_INT; - } else { - if (chan->type != IIO_VOLTAGE && - chan->type != IIO_CURRENT) - return -EINVAL; + switch (chan->type) { + case IIO_CURRENT: + goto adc_read; + case IIO_VOLTAGE: + if (chan->output) { + *val = st->dac_value[chan->channel]; + return IIO_VAL_INT; + } +adc_read: ret = ad7294_write_u8(st, AD7294_REG_CMD, BIT(chan->channel)); if (ret) @@ -139,29 +141,40 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, return ret; *val = regval & AD7294_VALUE_MASK; return IIO_VAL_INT; + default: + return -EINVAL; } case IIO_CHAN_INFO_SCALE: - if (chan->output) { - if (st->dac_vref_reg) { - ret = regulator_get_voltage(st->dac_vref_reg); - if (ret < 0) - return ret; - *val = ret / AD7294_UV_IN_MV; + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) { + if (st->dac_vref_reg) { + ret = regulator_get_voltage( + st->dac_vref_reg); + if (ret < 0) + return ret; + *val = ret / AD7294_UV_IN_MV; + } else { + *val = AD7294_DAC_INTERNAL_VREF_MV; + } } else { - *val = AD7294_DAC_INTERNAL_VREF_MV; - } - } else { - if (st->adc_vref_reg) { - ret = regulator_get_voltage(st->adc_vref_reg); - if (ret < 0) - return ret; - *val = ret / AD7294_UV_IN_MV; - } else { - *val = AD7294_ADC_INTERNAL_VREF_MV; + if (st->adc_vref_reg) { + ret = regulator_get_voltage( + st->adc_vref_reg); + if (ret < 0) + return ret; + *val = ret / AD7294_UV_IN_MV; + } else { + *val = AD7294_ADC_INTERNAL_VREF_MV; + } } + *val2 = AD7294_RESOLUTION; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CURRENT: + /* TODO */ + default: + return -EINVAL; } - *val2 = AD7294_RESOLUTION; - return IIO_VAL_FRACTIONAL_LOG2; } return -EINVAL; } @@ -176,17 +189,16 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: - if (chan->output) { - /* DAC has 12-bit channels */ - if (val < 0 || val >= BIT(AD7294_RESOLUTION) || val2) - return -EINVAL; - ret = regmap_write(st->regmap, - AD7294_REG_DAC(chan->channel), val); - if (ret) - return ret; - st->dac_value[chan->channel] = val; - return 0; - } + if (!chan->output) + return -EINVAL; + if (val < 0 || val >= BIT(AD7294_RESOLUTION) || val2) + return -EINVAL; + ret = regmap_write(st->regmap, AD7294_REG_DAC(chan->channel), + val); + if (ret) + return ret; + st->dac_value[chan->channel] = val; + return 0; } return -EINVAL; From 1ddcfa3d09ea6a101732a5f510593c8868349667 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Sun, 30 Jun 2024 17:06:18 +0530 Subject: [PATCH 06/21] iio: addac: ad7294: add temperature sensing --- drivers/iio/addac/ad7294.c | 45 +++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 475e7bc8efe7c6..5d2d4f301e9ffb 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -20,9 +20,10 @@ #define AD7294_REG_DAC(x) ((x) + 0x01) #define AD7294_REG_PWDN 0x0A -#define AD7294_VALUE_MASK GENMASK(11, 0) -#define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) +#define AD7294_TEMP_VALUE_MASK GENMASK(10, 0) +#define AD7294_ADC_VALUE_MASK GENMASK(11, 0) #define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) +#define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) #define AD7294_ADC_INTERNAL_VREF_MV 2500 #define AD7294_DAC_INTERNAL_VREF_MV 2500 @@ -45,6 +46,20 @@ .indexed = 1, .output = 0, \ } +#define AD7294_TEMP_CHAN(_chan_id) \ + { \ + .type = IIO_TEMP, .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, .output = 0, \ + } + +enum ad7294_temp_chan { + TSENSE_1 = 0x02, + TSENSE_2, + TSENSE_INTERNAL, +}; + static bool ad7294_readable_reg(struct device *dev, unsigned int reg) { return reg != AD7294_REG_CMD; @@ -80,6 +95,9 @@ static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_ADC_CHAN(IIO_VOLTAGE, 3), AD7294_ADC_CHAN(IIO_CURRENT, 4), AD7294_ADC_CHAN(IIO_CURRENT, 5), + AD7294_TEMP_CHAN(TSENSE_1), + AD7294_TEMP_CHAN(TSENSE_2), + AD7294_TEMP_CHAN(TSENSE_INTERNAL), }; static int ad7294_read_u8(struct ad7294_state *st, u8 reg, u8 *val) @@ -116,8 +134,8 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct ad7294_state *st = iio_priv(indio_dev); - int ret; - unsigned int regval; + int ret, temperature; + unsigned int i, regval; guard(mutex)(&st->lock); switch (mask) { @@ -139,7 +157,20 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, ®val); if (ret) return ret; - *val = regval & AD7294_VALUE_MASK; + *val = regval & AD7294_ADC_VALUE_MASK; + return IIO_VAL_INT; + case IIO_TEMP: + ret = regmap_read(st->regmap, chan->channel, ®val); + if (ret) + return ret; + regval &= AD7294_TEMP_VALUE_MASK; + /* Raw data is read in 11-bit Two's completement format + * Ref: Datasheet Page#29 + */ + temperature = regval & GENMASK(9, 0); + if (regval & BIT(9)) + temperature -= (1 << 10); + *val = temperature; return IIO_VAL_INT; default: return -EINVAL; @@ -170,6 +201,10 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, } *val2 = AD7294_RESOLUTION; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* Data resolution is 0.25 degree Celsius */ + *val = 250; + return IIO_VAL_INT; case IIO_CURRENT: /* TODO */ default: From 6757d475357d4e2241341698ec86bdf65f4dbe3d Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 4 Jul 2024 14:14:24 +0530 Subject: [PATCH 07/21] iio: addac: ad7294: code refactor --- drivers/iio/addac/ad7294.c | 103 ++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 54 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 5d2d4f301e9ffb..3b26efcc1132c5 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -14,6 +14,7 @@ #include #include #include +#include #define AD7294_REG_CMD 0x00 #define AD7294_REG_RESULT 0x01 @@ -28,31 +29,33 @@ #define AD7294_ADC_INTERNAL_VREF_MV 2500 #define AD7294_DAC_INTERNAL_VREF_MV 2500 #define AD7294_RESOLUTION 12 -#define AD7294_UV_IN_MV 1000 - -#define AD7294_DAC_CHAN(_chan_id) \ - { \ - .type = IIO_VOLTAGE, .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, .output = 1, \ - } -#define AD7294_ADC_CHAN(_type, _chan_id) \ - { \ - .type = _type, .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, .output = 0, \ - } +#define AD7294_DAC_CHAN(_chan_id) { \ + .type = IIO_VOLTAGE, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 1, \ +} -#define AD7294_TEMP_CHAN(_chan_id) \ - { \ - .type = IIO_TEMP, .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, .output = 0, \ - } +#define AD7294_ADC_CHAN(_type, _chan_id) { \ + .type = _type, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 0, \ +} + +#define AD7294_TEMP_CHAN(_chan_id) { \ + .type = IIO_TEMP, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 0, \ +} enum ad7294_temp_chan { TSENSE_1 = 0x02, @@ -60,6 +63,11 @@ enum ad7294_temp_chan { TSENSE_INTERNAL, }; +static const char *const ad7294_power_supplies[] = { + "vdrive", + "avdd", +}; + static bool ad7294_readable_reg(struct device *dev, unsigned int reg) { return reg != AD7294_REG_CMD; @@ -77,8 +85,6 @@ struct ad7294_state { struct mutex lock; struct regmap *regmap; struct i2c_client *i2c; - struct regulator *avdd_reg; - struct regulator *vdrive_reg; struct regulator *adc_vref_reg; struct regulator *dac_vref_reg; u16 dac_value[2]; @@ -135,7 +141,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, { struct ad7294_state *st = iio_priv(indio_dev); int ret, temperature; - unsigned int i, regval; + unsigned int regval; guard(mutex)(&st->lock); switch (mask) { @@ -165,7 +171,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, return ret; regval &= AD7294_TEMP_VALUE_MASK; /* Raw data is read in 11-bit Two's completement format - * Ref: Datasheet Page#29 + * Reference: Datasheet Page#29 */ temperature = regval & GENMASK(9, 0); if (regval & BIT(9)) @@ -184,7 +190,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, st->dac_vref_reg); if (ret < 0) return ret; - *val = ret / AD7294_UV_IN_MV; + *val = ret / MILLI; } else { *val = AD7294_DAC_INTERNAL_VREF_MV; } @@ -194,7 +200,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, st->adc_vref_reg); if (ret < 0) return ret; - *val = ret / AD7294_UV_IN_MV; + *val = ret / MILLI; } else { *val = AD7294_ADC_INTERNAL_VREF_MV; } @@ -275,27 +281,12 @@ static int ad7294_init(struct ad7294_state *st) if (ret) return ret; - st->vdrive_reg = devm_regulator_get(&i2c->dev, "vdrive"); - if (IS_ERR(st->vdrive_reg)) - return PTR_ERR(st->vdrive_reg); - ret = regulator_enable(st->vdrive_reg); + ret = devm_regulator_bulk_get_enable(&i2c->dev, + ARRAY_SIZE(ad7294_power_supplies), + ad7294_power_supplies); if (ret) - return ret; - ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, - st->vdrive_reg); - if (ret) - return ret; - - st->avdd_reg = devm_regulator_get(&i2c->dev, "avdd"); - if (IS_ERR(st->avdd_reg)) - return PTR_ERR(st->avdd_reg); - ret = regulator_enable(st->avdd_reg); - if (ret) - return ret; - ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, - st->avdd_reg); - if (ret) - return ret; + return dev_err_probe(&i2c->dev, ret, + "Failed to enable power supplies\n"); st->adc_vref_reg = devm_regulator_get_optional(&i2c->dev, "adc-vref"); if (IS_ERR(st->adc_vref_reg)) { @@ -370,13 +361,17 @@ static int ad7294_probe(struct i2c_client *client, return devm_iio_device_register(&client->dev, indio_dev); } -static struct of_device_id ad7294_of_table[] = { { .compatible = "adi,ad7294" }, - { .compatible = - "adi,ad7294-2" }, - { /* Sentinel */ } }; +static const struct of_device_id ad7294_of_table[] = { + { .compatible = "adi,ad7294" }, + { .compatible = "adi,ad7294-2" }, + { /* Sentinel */ }, +}; static struct i2c_driver ad7294_driver = { - .driver = { .name = "ad7294", .of_match_table = ad7294_of_table }, + .driver = { + .name = "ad7294", + .of_match_table = ad7294_of_table, + }, .probe = ad7294_probe, }; From e3ae35e44e2c9c71d3c48db51d407fdd7a2aabb0 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 4 Jul 2024 16:00:34 +0530 Subject: [PATCH 08/21] dt-bindings: iio: addac: add support for AD7294 --- .../bindings/iio/addac/adi,ad7294.yaml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml new file mode 100644 index 00000000000000..f6a04e917657f5 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/addac/adi,ad7294.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Device AD7294/AD7294-2 device + +maintainers: + - Anshul Dalal + +description: | + The AD7294/AD7294-2 is a monitor and control system with multichannel + ADC, DAC, temperature and current sensor. The device features a 12-bit ADC + and DAC with an i2c interface. + + Datasheet: + AD7294: https://www.analog.com/media/en/technical-documentation/data-sheets/AD7294.pdf + AD7294-2: https://www.analog.com/media/en/technical-documentation/data-sheets/AD7294-2.pdf + +properties: + compatible: + enum: + - adi,ad7294 + - adi,ad7294-2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: Alert interrupt + + adc-vref-supply: + description: The regulator supply for ADC reference voltage (0.1V to 4.1V). + + dac-vref-supply: + description: | + The regulator supply for DAC reference voltage (0V to AVDD - 2V). + + avdd-supply: + description: Fixed regulator supply for analog circuitry (4.5V to 5.5V). + + vdrive-supply: + description: Logic power supply (2.7V to 5.5V). + +required: + - compatible + - reg + - avdd-supply + - vdrive-supply + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ad7294: addac@7b { + compatible = "adi,ad7294"; + reg = <0x7b>; + interrupt-parent = <&gpio>; + interrupts = <26 IRQ_TYPE_LEVEL_LOW>; + adc-vref-supply = <&adc_vref>; + dac-vref-supply = <&dac_vref>; + avdd-supply = <&avdd>; + vdrive-supply = <&vdrive>; + }; + }; From a8bd7c32f99d86920f1a84d577ed96cf7afa9830 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 11 Jul 2024 16:51:44 +0530 Subject: [PATCH 09/21] iio: addac: ad7294: use regmap for 8-bit registers --- drivers/iio/addac/ad7294.c | 70 +++++++++++++++----------------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 3b26efcc1132c5..4120a681fac20f 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -73,7 +73,7 @@ static bool ad7294_readable_reg(struct device *dev, unsigned int reg) return reg != AD7294_REG_CMD; }; -static const struct regmap_config ad7294_regmap_config = { +static const struct regmap_config ad7294_regmap_config_u16 = { .reg_bits = 8, .val_bits = 16, .max_register = 0x27, @@ -81,9 +81,18 @@ static const struct regmap_config ad7294_regmap_config = { .val_format_endian = REGMAP_ENDIAN_BIG, }; +static const struct regmap_config ad7294_regmap_config_u8 = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x27, + .readable_reg = ad7294_readable_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + struct ad7294_state { struct mutex lock; - struct regmap *regmap; + struct regmap *regmap_u16; + struct regmap *regmap_u8; struct i2c_client *i2c; struct regulator *adc_vref_reg; struct regulator *dac_vref_reg; @@ -106,35 +115,6 @@ static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_TEMP_CHAN(TSENSE_INTERNAL), }; -static int ad7294_read_u8(struct ad7294_state *st, u8 reg, u8 *val) -{ - int ret; - struct i2c_client *client = st->i2c; - struct i2c_msg message[2] = { { - .addr = client->addr, - .flags = client->flags, - .len = sizeof(reg), - .buf = ®, - }, - { - .addr = client->addr, - .flags = client->flags | I2C_M_RD, - .len = sizeof(*val), - .buf = val, - } }; - ret = i2c_transfer(client->adapter, message, ARRAY_SIZE(message)); - return (ret == ARRAY_SIZE(message)) ? 0 : ret; -}; - -static int ad7294_write_u8(struct ad7294_state *st, u8 reg, u8 val) -{ - int ret; - __be16 write_buffer = cpu_to_be16(reg << 8 | val); - ret = i2c_master_send(st->i2c, (char *)&write_buffer, - sizeof(write_buffer)); - return (ret == sizeof(write_buffer)) ? 0 : ret; -}; - static int ad7294_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -155,18 +135,18 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } adc_read: - ret = ad7294_write_u8(st, AD7294_REG_CMD, + ret = regmap_write(st->regmap_u8, AD7294_REG_CMD, BIT(chan->channel)); if (ret) return ret; - ret = regmap_read(st->regmap, AD7294_REG_RESULT, + ret = regmap_read(st->regmap_u16, AD7294_REG_RESULT, ®val); if (ret) return ret; *val = regval & AD7294_ADC_VALUE_MASK; return IIO_VAL_INT; case IIO_TEMP: - ret = regmap_read(st->regmap, chan->channel, ®val); + ret = regmap_read(st->regmap_u16, chan->channel, ®val); if (ret) return ret; regval &= AD7294_TEMP_VALUE_MASK; @@ -234,7 +214,7 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, return -EINVAL; if (val < 0 || val >= BIT(AD7294_RESOLUTION) || val2) return -EINVAL; - ret = regmap_write(st->regmap, AD7294_REG_DAC(chan->channel), + ret = regmap_write(st->regmap_u16, AD7294_REG_DAC(chan->channel), val); if (ret) return ret; @@ -250,8 +230,8 @@ static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, { struct ad7294_state *st = iio_priv(indio_dev); if (readval) - return regmap_read(st->regmap, reg, readval); - return regmap_write(st->regmap, reg, writeval); + return regmap_read(st->regmap_u16, reg, readval); + return regmap_write(st->regmap_u16, reg, writeval); } struct iio_info ad7294_info = { @@ -268,16 +248,20 @@ static void ad7294_reg_disable(void *data) static int ad7294_init(struct ad7294_state *st) { int ret; - u8 pwdn_config; + int pwdn_config; struct i2c_client *i2c = st->i2c; mutex_init(&st->lock); - st->regmap = devm_regmap_init_i2c(i2c, &ad7294_regmap_config); - if (IS_ERR(st->regmap)) - return PTR_ERR(st->regmap); + st->regmap_u16 = devm_regmap_init_i2c(i2c, &ad7294_regmap_config_u16); + if (IS_ERR(st->regmap_u16)) + return PTR_ERR(st->regmap_u16); + + st->regmap_u8 = devm_regmap_init_i2c(i2c, &ad7294_regmap_config_u8); + if (IS_ERR(st->regmap_u8)) + return PTR_ERR(st->regmap_u8); - ret = ad7294_read_u8(st, AD7294_REG_PWDN, &pwdn_config); + ret = regmap_read(st->regmap_u8, AD7294_REG_PWDN, &pwdn_config); if (ret) return ret; @@ -328,7 +312,7 @@ static int ad7294_init(struct ad7294_state *st) pwdn_config |= AD7294_DAC_EXTERNAL_REF_MASK; } - ret = ad7294_write_u8(st, AD7294_REG_PWDN, pwdn_config); + ret = regmap_write(st->regmap_u8, AD7294_REG_PWDN, pwdn_config); if (ret) return ret; From d75120e61ce4af70417716d6f9b1821f062f4989 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 13:13:37 +0530 Subject: [PATCH 10/21] iio: addac: ad7294: use reg_[read/write] in regmap --- drivers/iio/addac/ad7294.c | 128 +++++++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 32 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 4120a681fac20f..6211d4e2ea4840 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -73,32 +73,90 @@ static bool ad7294_readable_reg(struct device *dev, unsigned int reg) return reg != AD7294_REG_CMD; }; -static const struct regmap_config ad7294_regmap_config_u16 = { - .reg_bits = 8, - .val_bits = 16, - .max_register = 0x27, - .readable_reg = ad7294_readable_reg, - .val_format_endian = REGMAP_ENDIAN_BIG, -}; - -static const struct regmap_config ad7294_regmap_config_u8 = { - .reg_bits = 8, - .val_bits = 8, - .max_register = 0x27, - .readable_reg = ad7294_readable_reg, - .val_format_endian = REGMAP_ENDIAN_BIG, +static int ad7294_reg_size(unsigned int reg) +{ + switch (reg) { + case AD7294_REG_CMD: + case AD7294_VOLTAGE_STATUS: + case AD7294_CURRENT_STATUS: + case AD7294_TEMP_STATUS: + case AD7294_REG_PWDN: + return 1; + default: + return 2; + } }; struct ad7294_state { struct mutex lock; - struct regmap *regmap_u16; - struct regmap *regmap_u8; + struct regmap *regmap; struct i2c_client *i2c; struct regulator *adc_vref_reg; struct regulator *dac_vref_reg; u16 dac_value[2]; }; +static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + int ret; + struct i2c_client *client = context; + unsigned char buffer[3] = { reg }; + + int reg_size = ad7294_reg_size(reg); + + ret = i2c_master_send(client, buffer, 1); + if (ret < 0) + return ret; + + ret = i2c_master_recv(client, buffer + 1, reg_size); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "Read [%x, %x] from reg:0x%x, size: %d", + buffer[1], buffer[2], reg, reg_size); + if (reg_size == 1) { + *val = buffer[1]; + } else { + *val = buffer[1] << 8 | buffer[2]; + } + + return 0; +}; + +static int ad7294_reg_write(void *context, unsigned int reg, unsigned int val) +{ + int ret; + struct i2c_client *client = context; + unsigned char buffer[3] = { reg }; + + int reg_size = ad7294_reg_size(reg); + dev_dbg(&client->dev, "Write [%x] to reg: %x, size: %d", val, reg, + reg_size); + + if (reg_size == 1) { + /* Only take LSB of the data when writing to 1 byte reg */ + buffer[1] = val & 0xff; + } else { + buffer[1] = val >> 8; + buffer[2] = val & 0xff; + } + + ret = i2c_master_send(client, buffer, reg_size + 1); + if (ret < 0) + return ret; + + return 0; +}; + +static const struct regmap_config ad7294_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x27, + .reg_read = ad7294_reg_read, + .reg_write = ad7294_reg_write, + .readable_reg = ad7294_readable_reg, +}; + static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_DAC_CHAN(0), AD7294_DAC_CHAN(1), @@ -135,18 +193,20 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } adc_read: - ret = regmap_write(st->regmap_u8, AD7294_REG_CMD, - BIT(chan->channel)); + ret = regmap_write(st->regmap, AD7294_REG_CMD, + BIT(chan->channel)); if (ret) return ret; - ret = regmap_read(st->regmap_u16, AD7294_REG_RESULT, + ret = regmap_read(st->regmap, AD7294_REG_RESULT, ®val); if (ret) return ret; *val = regval & AD7294_ADC_VALUE_MASK; return IIO_VAL_INT; case IIO_TEMP: - ret = regmap_read(st->regmap_u16, chan->channel, ®val); + ret = regmap_read(st->regmap, + chan->channel + AD7294_REG_TEMP_BASE, + ®val); if (ret) return ret; regval &= AD7294_TEMP_VALUE_MASK; @@ -214,7 +274,7 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, return -EINVAL; if (val < 0 || val >= BIT(AD7294_RESOLUTION) || val2) return -EINVAL; - ret = regmap_write(st->regmap_u16, AD7294_REG_DAC(chan->channel), + ret = regmap_write(st->regmap, AD7294_REG_DAC(chan->channel), val); if (ret) return ret; @@ -230,8 +290,8 @@ static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, { struct ad7294_state *st = iio_priv(indio_dev); if (readval) - return regmap_read(st->regmap_u16, reg, readval); - return regmap_write(st->regmap_u16, reg, writeval); + return regmap_read(st->regmap, reg, readval); + return regmap_write(st->regmap, reg, writeval); } struct iio_info ad7294_info = { @@ -248,20 +308,21 @@ static void ad7294_reg_disable(void *data) static int ad7294_init(struct ad7294_state *st) { int ret; - int pwdn_config; + int pwdn_config, config_reg; struct i2c_client *i2c = st->i2c; mutex_init(&st->lock); - st->regmap_u16 = devm_regmap_init_i2c(i2c, &ad7294_regmap_config_u16); - if (IS_ERR(st->regmap_u16)) - return PTR_ERR(st->regmap_u16); + st->regmap = + devm_regmap_init(&i2c->dev, NULL, i2c, &ad7294_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); - st->regmap_u8 = devm_regmap_init_i2c(i2c, &ad7294_regmap_config_u8); - if (IS_ERR(st->regmap_u8)) - return PTR_ERR(st->regmap_u8); + ret = regmap_read(st->regmap, AD7294_REG_PWDN, &pwdn_config); + if (ret) + return ret; - ret = regmap_read(st->regmap_u8, AD7294_REG_PWDN, &pwdn_config); + ret = regmap_read(st->regmap, AD7294_REG_CONFIG, &config_reg); if (ret) return ret; @@ -312,7 +373,10 @@ static int ad7294_init(struct ad7294_state *st) pwdn_config |= AD7294_DAC_EXTERNAL_REF_MASK; } - ret = regmap_write(st->regmap_u8, AD7294_REG_PWDN, pwdn_config); + ret = regmap_write(st->regmap, AD7294_REG_PWDN, pwdn_config); + if (ret) + return ret; + ret = regmap_write(st->regmap, AD7294_REG_CONFIG, config_reg); if (ret) return ret; From 72f8894cabaa1a167d8134e0ac21ef8dd1a473bf Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 13:16:02 +0530 Subject: [PATCH 11/21] iio: addac: ad7294: temperature sensing refactor --- drivers/iio/addac/ad7294.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 6211d4e2ea4840..1bdb780313ff60 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -18,6 +18,7 @@ #define AD7294_REG_CMD 0x00 #define AD7294_REG_RESULT 0x01 +#define AD7294_REG_TEMP_BASE 0x02 #define AD7294_REG_DAC(x) ((x) + 0x01) #define AD7294_REG_PWDN 0x0A @@ -58,7 +59,7 @@ } enum ad7294_temp_chan { - TSENSE_1 = 0x02, + TSENSE_1, TSENSE_2, TSENSE_INTERNAL, }; @@ -178,7 +179,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct ad7294_state *st = iio_priv(indio_dev); - int ret, temperature; + int ret; unsigned int regval; guard(mutex)(&st->lock); @@ -210,13 +211,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, if (ret) return ret; regval &= AD7294_TEMP_VALUE_MASK; - /* Raw data is read in 11-bit Two's completement format - * Reference: Datasheet Page#29 - */ - temperature = regval & GENMASK(9, 0); - if (regval & BIT(9)) - temperature -= (1 << 10); - *val = temperature; + *val = sign_extend32(regval, 11); return IIO_VAL_INT; default: return -EINVAL; From cd38a5b437f750445626b9cffac48be1836756dd Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 13:18:43 +0530 Subject: [PATCH 12/21] iio: addac: ad7294: add events for handling alerts --- drivers/iio/addac/ad7294.c | 196 ++++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 2 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 1bdb780313ff60..424a7e9e1ad483 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -20,16 +21,48 @@ #define AD7294_REG_RESULT 0x01 #define AD7294_REG_TEMP_BASE 0x02 #define AD7294_REG_DAC(x) ((x) + 0x01) +#define AD7294_VOLTAGE_STATUS 0x05 +#define AD7294_CURRENT_STATUS 0x06 +#define AD7294_TEMP_STATUS 0x07 +#define AD7294_REG_CONFIG 0x09 #define AD7294_REG_PWDN 0x0A +#define AD7294_REG_DATA_LOW(x) ((x) * 3 + 0x0B) +#define AD7294_REG_DATA_HIGH(x) ((x) * 3 + 0x0C) +#define AD7294_REG_HYSTERESIS(x) ((x) * 3 + 0x0D) +#define AD7294_TEMP_ALERT_MASK GENMASK(6, 0) +#define AD7294_CURRENT_ALERT_MASK GENMASK(4, 0) #define AD7294_TEMP_VALUE_MASK GENMASK(10, 0) #define AD7294_ADC_VALUE_MASK GENMASK(11, 0) #define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) #define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) +#define AD7294_ALERT_PIN BIT(2) #define AD7294_ADC_INTERNAL_VREF_MV 2500 #define AD7294_DAC_INTERNAL_VREF_MV 2500 #define AD7294_RESOLUTION 12 +#define AD7294_VOLTAGE_CHENNEL_COUNT 4 + +#define AD7294_ALERT_LOW(x) BIT((x) * 2) +#define AD7294_ALERT_HIGH(x) BIT((x) * 2 + 1) + +static const struct iio_event_spec ad7294_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; #define AD7294_DAC_CHAN(_chan_id) { \ .type = IIO_VOLTAGE, \ @@ -47,6 +80,8 @@ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .indexed = 1, \ .output = 0, \ + .event_spec = ad7294_events, \ + .num_event_specs = ARRAY_SIZE(ad7294_events), \ } #define AD7294_TEMP_CHAN(_chan_id) { \ @@ -56,6 +91,8 @@ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .indexed = 1, \ .output = 0, \ + .event_spec = ad7294_events, \ + .num_event_specs = ARRAY_SIZE(ad7294_events), \ } enum ad7294_temp_chan { @@ -174,6 +211,79 @@ static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_TEMP_CHAN(TSENSE_INTERNAL), }; +static irqreturn_t ad7294_event_handler(int irq, void *private) +{ + int i; + struct iio_dev *indio_dev = private; + dev_info(&indio_dev->dev, "IRQ requested\n"); + struct ad7294_state *st = iio_priv(indio_dev); + unsigned int voltage_status, temp_status, current_status; + s64 timestamp = iio_get_time_ns(indio_dev); + + if (regmap_read(st->regmap, AD7294_VOLTAGE_STATUS, &voltage_status)) + return IRQ_HANDLED; + + if (regmap_read(st->regmap, AD7294_CURRENT_STATUS, ¤t_status)) + return IRQ_HANDLED; + current_status &= AD7294_CURRENT_ALERT_MASK; + + if (regmap_read(st->regmap, AD7294_TEMP_STATUS, &temp_status)) + return IRQ_HANDLED; + temp_status &= AD7294_TEMP_ALERT_MASK; + + if (!(voltage_status || current_status || temp_status)) + return IRQ_HANDLED; + + dev_info(&indio_dev->dev, "Alert received: V: %x, C: %x, T: %x\n", + voltage_status, current_status, temp_status); + for (i = 0; i < AD7294_VOLTAGE_CHENNEL_COUNT; i++) { + if (voltage_status & AD7294_ALERT_LOW(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if (voltage_status & AD7294_ALERT_HIGH(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + } + + for_each_set_bit(i, (long *)&temp_status, AD7294_TEMP_ALERT_MASK) { + if (i & 1) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, i >> 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + else + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, i >> 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + } + + for_each_set_bit(i, (long *)¤t_status, AD7294_CURRENT_ALERT_MASK) { + if (i & 1) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i >> 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + else + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i >> 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + } + + return IRQ_HANDLED; +} + static int ad7294_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -289,10 +399,82 @@ static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, return regmap_write(st->regmap, reg, writeval); } +static unsigned int ad7294_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + unsigned int offset; + + switch (chan->type) { + case IIO_VOLTAGE: + offset = chan->channel; + break; + case IIO_CURRENT: + offset = chan->channel + 4; + break; + case IIO_TEMP: + offset = chan->channel + 6; + break; + default: + return 0; + } + + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7294_REG_DATA_LOW(offset); + else + return AD7294_REG_DATA_HIGH(offset); + case IIO_EV_INFO_HYSTERESIS: + return AD7294_REG_HYSTERESIS(offset); + default: + return 0; + } + return 0; +} + +static int ad7294_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2) +{ + int ret; + unsigned int readval; + struct ad7294_state *st = iio_priv(indio_dev); + + ret = regmap_read(st->regmap, ad7294_threshold_reg(chan, dir, info), + &readval); + if (ret) + return ret; + + if (chan->type == IIO_TEMP) + *val = sign_extend32(readval, 11); + else + *val = readval & AD7294_ADC_VALUE_MASK; + + return IIO_VAL_INT; +} + +static int ad7294_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ad7294_state *st = iio_priv(indio_dev); + + return regmap_write(st->regmap, ad7294_threshold_reg(chan, dir, info), + val); +} + struct iio_info ad7294_info = { .read_raw = ad7294_read_raw, .write_raw = ad7294_write_raw, .debugfs_reg_access = ad7294_reg_access, + .read_event_value = &ad7294_read_event_value, + .write_event_value = &ad7294_write_event_value, }; static void ad7294_reg_disable(void *data) @@ -300,7 +482,7 @@ static void ad7294_reg_disable(void *data) regulator_disable(data); } -static int ad7294_init(struct ad7294_state *st) +static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) { int ret; int pwdn_config, config_reg; @@ -368,6 +550,16 @@ static int ad7294_init(struct ad7294_state *st) pwdn_config |= AD7294_DAC_EXTERNAL_REF_MASK; } + if (i2c->irq > 0) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + ad7294_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ad7294", indio_dev); + if (ret) + return ret; + config_reg |= AD7294_ALERT_PIN; + } + ret = regmap_write(st->regmap, AD7294_REG_PWDN, pwdn_config); if (ret) return ret; @@ -397,7 +589,7 @@ static int ad7294_probe(struct i2c_client *client, st = iio_priv(indio_dev); st->i2c = client; - ret = ad7294_init(st); + ret = ad7294_init(indio_dev, st); if (ret) return ret; From a62a9d81aef9e6187855405b1631baded20b7155 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 13:20:13 +0530 Subject: [PATCH 13/21] arch: dts: overlays: add interrupts for ad7294 --- arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts index c1e4bd0f008c80..e7c76597bc55ed 100644 --- a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts +++ b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts @@ -5,6 +5,9 @@ /dts-v1/; /plugin/; +#include +#include + / { compatible = "brcm,bcm2835"; @@ -51,6 +54,8 @@ ad7294: ad7294@62 { compatible = "adi,ad7294"; reg = <0x62>; + interrupts = <4 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; adc-vref-supply = <&adc_vref>; dac-vref-supply = <&dac_vref>; avdd-supply = <&avdd>; From 69047260e7f7f0f56c3a2a8cec5ff78eceaa4494 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 13:57:32 +0530 Subject: [PATCH 14/21] iio: addac: ad7294: add current scale --- .../boot/dts/overlays/rpi-ad7294-overlay.dts | 1 + drivers/iio/addac/ad7294.c | 59 ++++++++++++++----- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts index e7c76597bc55ed..3f90c1d2f6ef1a 100644 --- a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts +++ b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts @@ -56,6 +56,7 @@ reg = <0x62>; interrupts = <4 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpio>; + shunt-resistor-ohms = <1000 1000>; adc-vref-supply = <&adc_vref>; dac-vref-supply = <&dac_vref>; avdd-supply = <&avdd>; diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 424a7e9e1ad483..b623ce8c95b748 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -20,6 +20,7 @@ #define AD7294_REG_CMD 0x00 #define AD7294_REG_RESULT 0x01 #define AD7294_REG_TEMP_BASE 0x02 +#define AD7294_REG_CURRENT_BASE 0x04 #define AD7294_REG_DAC(x) ((x) + 0x01) #define AD7294_VOLTAGE_STATUS 0x05 #define AD7294_CURRENT_STATUS 0x06 @@ -55,7 +56,8 @@ static const struct iio_event_spec ad7294_events[] = { { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), }, { .type = IIO_EV_TYPE_THRESH, @@ -73,7 +75,7 @@ static const struct iio_event_spec ad7294_events[] = { .output = 1, \ } -#define AD7294_ADC_CHAN(_type, _chan_id) { \ +#define AD7294_VOLTAGE_CHAN(_type, _chan_id) { \ .type = _type, \ .channel = _chan_id, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ @@ -84,6 +86,17 @@ static const struct iio_event_spec ad7294_events[] = { .num_event_specs = ARRAY_SIZE(ad7294_events), \ } +#define AD7294_CURRENT_CHAN(_type, _chan_id) { \ + .type = _type, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 0, \ + .event_spec = ad7294_events, \ + .num_event_specs = ARRAY_SIZE(ad7294_events), \ +} + #define AD7294_TEMP_CHAN(_chan_id) { \ .type = IIO_TEMP, \ .channel = _chan_id, \ @@ -131,6 +144,7 @@ struct ad7294_state { struct i2c_client *i2c; struct regulator *adc_vref_reg; struct regulator *dac_vref_reg; + u32 shunt_ohms[2]; u16 dac_value[2]; }; @@ -151,7 +165,7 @@ static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) return ret; dev_dbg(&client->dev, "Read [%x, %x] from reg:0x%x, size: %d", - buffer[1], buffer[2], reg, reg_size); + buffer[1], buffer[2], reg, reg_size); if (reg_size == 1) { *val = buffer[1]; } else { @@ -169,7 +183,7 @@ static int ad7294_reg_write(void *context, unsigned int reg, unsigned int val) int reg_size = ad7294_reg_size(reg); dev_dbg(&client->dev, "Write [%x] to reg: %x, size: %d", val, reg, - reg_size); + reg_size); if (reg_size == 1) { /* Only take LSB of the data when writing to 1 byte reg */ @@ -200,12 +214,12 @@ static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_DAC_CHAN(1), AD7294_DAC_CHAN(2), AD7294_DAC_CHAN(3), - AD7294_ADC_CHAN(IIO_VOLTAGE, 0), - AD7294_ADC_CHAN(IIO_VOLTAGE, 1), - AD7294_ADC_CHAN(IIO_VOLTAGE, 2), - AD7294_ADC_CHAN(IIO_VOLTAGE, 3), - AD7294_ADC_CHAN(IIO_CURRENT, 4), - AD7294_ADC_CHAN(IIO_CURRENT, 5), + AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 0), + AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 1), + AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 2), + AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 3), + AD7294_CURRENT_CHAN(IIO_CURRENT, 0), + AD7294_CURRENT_CHAN(IIO_CURRENT, 1), AD7294_TEMP_CHAN(TSENSE_1), AD7294_TEMP_CHAN(TSENSE_2), AD7294_TEMP_CHAN(TSENSE_INTERNAL), @@ -266,7 +280,8 @@ static irqreturn_t ad7294_event_handler(int irq, void *private) timestamp); } - for_each_set_bit(i, (long *)¤t_status, AD7294_CURRENT_ALERT_MASK) { + for_each_set_bit(i, (long *)¤t_status, + AD7294_CURRENT_ALERT_MASK) { if (i & 1) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i >> 1, @@ -289,7 +304,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct ad7294_state *st = iio_priv(indio_dev); - int ret; + int ret, base = 0; unsigned int regval; guard(mutex)(&st->lock); @@ -297,6 +312,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_CURRENT: + base = AD7294_VOLTAGE_CHENNEL_COUNT; goto adc_read; case IIO_VOLTAGE: if (chan->output) { @@ -305,7 +321,7 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, } adc_read: ret = regmap_write(st->regmap, AD7294_REG_CMD, - BIT(chan->channel)); + BIT(chan->channel + base)); if (ret) return ret; ret = regmap_read(st->regmap, AD7294_REG_RESULT, @@ -357,7 +373,13 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, *val = 250; return IIO_VAL_INT; case IIO_CURRENT: - /* TODO */ + /* + Current(in mA) = + ADC_READING * 100 / Shunt resistance (in ohm) + */ + *val = 100; + *val2 = st->shunt_ohms[chan->channel & 1]; + return IIO_VAL_FRACTIONAL; default: return -EINVAL; } @@ -550,6 +572,15 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) pwdn_config |= AD7294_DAC_EXTERNAL_REF_MASK; } + ret = device_property_read_u32_array(&i2c->dev, "shunt-resistor-ohms", + st->shunt_ohms, 2); + if (ret) { + dev_err(&i2c->dev, "Failed to read shunt resistor values"); + return ret; + } + dev_dbg(&i2c->dev, "Read shunt resistor values of %d and %d uohms", + st->shunt_ohms[0], st->shunt_ohms[1]); + if (i2c->irq > 0) { ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, ad7294_event_handler, From d7715743379bbdce6b051060151e6f34e196f663 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 14:02:56 +0530 Subject: [PATCH 15/21] dt-bindings: iio: addac: add `shunt-resistor-ohms` --- Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml index f6a04e917657f5..cf55f180cf69d0 100644 --- a/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad7294.yaml @@ -44,6 +44,11 @@ properties: vdrive-supply: description: Logic power supply (2.7V to 5.5V). + shunt-resistor-ohms: + $ref: /schemas/types.yaml#/definitions/int32-array + maxItems: 2 + description: Resistance for RS1 and RS2 in ohms + required: - compatible - reg @@ -69,5 +74,6 @@ examples: dac-vref-supply = <&dac_vref>; avdd-supply = <&avdd>; vdrive-supply = <&vdrive>; + shunt-resistor-ohms = <1000 1000>; }; }; From f23d91e6faa42b543e800d40d6097aa385caa636 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 8 Aug 2024 14:16:07 +0530 Subject: [PATCH 16/21] iio: addac: ad7294: code refactor --- .../boot/dts/overlays/rpi-ad7294-overlay.dts | 5 +- drivers/iio/addac/ad7294.c | 210 +++++++++--------- 2 files changed, 104 insertions(+), 111 deletions(-) diff --git a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts index 3f90c1d2f6ef1a..24d4c62ad1d8ff 100644 --- a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts +++ b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts @@ -1,7 +1,4 @@ -/* - * DT Overlay for Analog Devices AD7294/AD7294-2 - */ - +// SPDX-License-Identifier: GPL-2.0 /dts-v1/; /plugin/; diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index b623ce8c95b748..26d9924d66914f 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -20,7 +20,6 @@ #define AD7294_REG_CMD 0x00 #define AD7294_REG_RESULT 0x01 #define AD7294_REG_TEMP_BASE 0x02 -#define AD7294_REG_CURRENT_BASE 0x04 #define AD7294_REG_DAC(x) ((x) + 0x01) #define AD7294_VOLTAGE_STATUS 0x05 #define AD7294_CURRENT_STATUS 0x06 @@ -38,90 +37,22 @@ #define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) #define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) #define AD7294_ALERT_PIN BIT(2) +#define AD7294_ALERT_LOW(x) BIT((x) * 2) +#define AD7294_ALERT_HIGH(x) BIT((x) * 2 + 1) #define AD7294_ADC_INTERNAL_VREF_MV 2500 #define AD7294_DAC_INTERNAL_VREF_MV 2500 #define AD7294_RESOLUTION 12 #define AD7294_VOLTAGE_CHENNEL_COUNT 4 -#define AD7294_ALERT_LOW(x) BIT((x) * 2) -#define AD7294_ALERT_HIGH(x) BIT((x) * 2 + 1) - -static const struct iio_event_spec ad7294_events[] = { - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | - BIT(IIO_EV_INFO_ENABLE), - }, - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_EITHER, - .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), - }, -}; - -#define AD7294_DAC_CHAN(_chan_id) { \ - .type = IIO_VOLTAGE, \ - .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .output = 1, \ -} - -#define AD7294_VOLTAGE_CHAN(_type, _chan_id) { \ - .type = _type, \ - .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .output = 0, \ - .event_spec = ad7294_events, \ - .num_event_specs = ARRAY_SIZE(ad7294_events), \ -} - -#define AD7294_CURRENT_CHAN(_type, _chan_id) { \ - .type = _type, \ - .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ - | BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .output = 0, \ - .event_spec = ad7294_events, \ - .num_event_specs = ARRAY_SIZE(ad7294_events), \ -} - -#define AD7294_TEMP_CHAN(_chan_id) { \ - .type = IIO_TEMP, \ - .channel = _chan_id, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .output = 0, \ - .event_spec = ad7294_events, \ - .num_event_specs = ARRAY_SIZE(ad7294_events), \ -} - -enum ad7294_temp_chan { - TSENSE_1, - TSENSE_2, - TSENSE_INTERNAL, -}; - -static const char *const ad7294_power_supplies[] = { - "vdrive", - "avdd", -}; - -static bool ad7294_readable_reg(struct device *dev, unsigned int reg) -{ - return reg != AD7294_REG_CMD; +struct ad7294_state { + struct mutex lock; + struct regmap *regmap; + struct i2c_client *i2c; + struct regulator *adc_vref_reg; + struct regulator *dac_vref_reg; + u32 shunt_ohms[2]; + u16 dac_value[2]; }; static int ad7294_reg_size(unsigned int reg) @@ -138,22 +69,11 @@ static int ad7294_reg_size(unsigned int reg) } }; -struct ad7294_state { - struct mutex lock; - struct regmap *regmap; - struct i2c_client *i2c; - struct regulator *adc_vref_reg; - struct regulator *dac_vref_reg; - u32 shunt_ohms[2]; - u16 dac_value[2]; -}; - static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) { int ret; struct i2c_client *client = context; unsigned char buffer[3] = { reg }; - int reg_size = ad7294_reg_size(reg); ret = i2c_master_send(client, buffer, 1); @@ -166,12 +86,10 @@ static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) dev_dbg(&client->dev, "Read [%x, %x] from reg:0x%x, size: %d", buffer[1], buffer[2], reg, reg_size); - if (reg_size == 1) { + if (reg_size == 1) *val = buffer[1]; - } else { + else *val = buffer[1] << 8 | buffer[2]; - } - return 0; }; @@ -180,8 +98,8 @@ static int ad7294_reg_write(void *context, unsigned int reg, unsigned int val) int ret; struct i2c_client *client = context; unsigned char buffer[3] = { reg }; - int reg_size = ad7294_reg_size(reg); + dev_dbg(&client->dev, "Write [%x] to reg: %x, size: %d", val, reg, reg_size); @@ -192,7 +110,6 @@ static int ad7294_reg_write(void *context, unsigned int reg, unsigned int val) buffer[1] = val >> 8; buffer[2] = val & 0xff; } - ret = i2c_master_send(client, buffer, reg_size + 1); if (ret < 0) return ret; @@ -200,6 +117,11 @@ static int ad7294_reg_write(void *context, unsigned int reg, unsigned int val) return 0; }; +static bool ad7294_readable_reg(struct device *dev, unsigned int reg) +{ + return reg != AD7294_REG_CMD; +}; + static const struct regmap_config ad7294_regmap_config = { .reg_bits = 8, .val_bits = 16, @@ -209,6 +131,72 @@ static const struct regmap_config ad7294_regmap_config = { .readable_reg = ad7294_readable_reg, }; +static const struct iio_event_spec ad7294_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD7294_DAC_CHAN(_chan_id) { \ + .type = IIO_VOLTAGE, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 1, \ +} + +#define AD7294_VOLTAGE_CHAN(_type, _chan_id) { \ + .type = _type, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 0, \ + .event_spec = ad7294_events, \ + .num_event_specs = ARRAY_SIZE(ad7294_events), \ +} + +#define AD7294_CURRENT_CHAN(_type, _chan_id) { \ + .type = _type, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 0, \ + .event_spec = ad7294_events, \ + .num_event_specs = ARRAY_SIZE(ad7294_events), \ +} + +#define AD7294_TEMP_CHAN(_chan_id) { \ + .type = IIO_TEMP, \ + .channel = _chan_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .output = 0, \ + .event_spec = ad7294_events, \ + .num_event_specs = ARRAY_SIZE(ad7294_events), \ +} + +enum ad7294_temp_chan { + TSENSE_1, + TSENSE_2, + TSENSE_INTERNAL, +}; + static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_DAC_CHAN(0), AD7294_DAC_CHAN(1), @@ -225,11 +213,15 @@ static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_TEMP_CHAN(TSENSE_INTERNAL), }; +static const char *const ad7294_power_supplies[] = { + "vdrive", + "avdd", +}; + static irqreturn_t ad7294_event_handler(int irq, void *private) { int i; struct iio_dev *indio_dev = private; - dev_info(&indio_dev->dev, "IRQ requested\n"); struct ad7294_state *st = iio_priv(indio_dev); unsigned int voltage_status, temp_status, current_status; s64 timestamp = iio_get_time_ns(indio_dev); @@ -248,8 +240,6 @@ static irqreturn_t ad7294_event_handler(int irq, void *private) if (!(voltage_status || current_status || temp_status)) return IRQ_HANDLED; - dev_info(&indio_dev->dev, "Alert received: V: %x, C: %x, T: %x\n", - voltage_status, current_status, temp_status); for (i = 0; i < AD7294_VOLTAGE_CHENNEL_COUNT; i++) { if (voltage_status & AD7294_ALERT_LOW(i)) iio_push_event(indio_dev, @@ -373,10 +363,9 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, *val = 250; return IIO_VAL_INT; case IIO_CURRENT: - /* - Current(in mA) = - ADC_READING * 100 / Shunt resistance (in ohm) - */ + /* Current(in mA) = + * ADC_READING * 100 / Shunt resistance (in ohm) + */ *val = 100; *val2 = st->shunt_ohms[chan->channel & 1]; return IIO_VAL_FRACTIONAL; @@ -412,10 +401,11 @@ static int ad7294_write_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned reg, - unsigned writeval, unsigned *readval) +static int ad7294_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) { struct ad7294_state *st = iio_priv(indio_dev); + if (readval) return regmap_read(st->regmap, reg, readval); return regmap_write(st->regmap, reg, writeval); @@ -537,6 +527,7 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) ret = PTR_ERR(st->adc_vref_reg); if (ret != -ENODEV) return ret; + dev_info(&i2c->dev, "ADC Vref not found, using internal reference"); pwdn_config &= ~AD7294_ADC_EXTERNAL_REF_MASK; @@ -545,10 +536,12 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) ret = regulator_enable(st->adc_vref_reg); if (ret) return ret; + ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, st->adc_vref_reg); if (ret) return ret; + pwdn_config |= AD7294_ADC_EXTERNAL_REF_MASK; } @@ -557,6 +550,7 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) ret = PTR_ERR(st->dac_vref_reg); if (ret != -ENODEV) return ret; + dev_info(&i2c->dev, "DAC Vref not found, using internal reference"); pwdn_config &= ~AD7294_DAC_EXTERNAL_REF_MASK; @@ -565,10 +559,12 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) ret = regulator_enable(st->dac_vref_reg); if (ret) return ret; + ret = devm_add_action_or_reset(&i2c->dev, ad7294_reg_disable, st->dac_vref_reg); if (ret) return ret; + pwdn_config |= AD7294_DAC_EXTERNAL_REF_MASK; } @@ -578,8 +574,6 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) dev_err(&i2c->dev, "Failed to read shunt resistor values"); return ret; } - dev_dbg(&i2c->dev, "Read shunt resistor values of %d and %d uohms", - st->shunt_ohms[0], st->shunt_ohms[1]); if (i2c->irq > 0) { ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, @@ -588,12 +582,14 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) "ad7294", indio_dev); if (ret) return ret; + config_reg |= AD7294_ALERT_PIN; } ret = regmap_write(st->regmap, AD7294_REG_PWDN, pwdn_config); if (ret) return ret; + ret = regmap_write(st->regmap, AD7294_REG_CONFIG, config_reg); if (ret) return ret; From 1196a4620d1282856ac2a844c02c40f5d1ea1551 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Fri, 16 Aug 2024 17:02:30 +0530 Subject: [PATCH 17/21] iio: addac: ad7294: differential channel support --- drivers/iio/addac/ad7294.c | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 26d9924d66914f..219dc5fdf998fd 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -36,6 +36,8 @@ #define AD7294_ADC_VALUE_MASK GENMASK(11, 0) #define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) #define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) +#define AD7294_DIFF_V3_V2 BIT(1) +#define AD7294_DIFF_V1_V0 BIT(0) #define AD7294_ALERT_PIN BIT(2) #define AD7294_ALERT_LOW(x) BIT((x) * 2) #define AD7294_ALERT_HIGH(x) BIT((x) * 2 + 1) @@ -494,9 +496,25 @@ static void ad7294_reg_disable(void *data) regulator_disable(data); } +static const bool ad7294_diff_channels_valid(int chan1, int chan2) +{ + switch (chan1) { + case 0: + return chan2 == 1; + case 1: + return chan2 == 0; + case 2: + return chan2 == 3; + case 3: + return chan2 == 2; + default: + return false; + } +} + static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) { - int ret; + int ret, diff_channels[2]; int pwdn_config, config_reg; struct i2c_client *i2c = st->i2c; @@ -575,6 +593,28 @@ static int ad7294_init(struct iio_dev *indio_dev, struct ad7294_state *st) return ret; } + if (device_property_present(&i2c->dev, "diff-channels")) { + ret = device_property_read_u32_array(&i2c->dev, "diff-channels", + diff_channels, + ARRAY_SIZE(diff_channels)); + if (ret) { + dev_err(&i2c->dev, + "Failed to get differential channels"); + return ret; + } + if (!ad7294_diff_channels_valid(diff_channels[0], + diff_channels[1])) { + dev_err(&i2c->dev, "Invalid differential channels"); + return -EINVAL; + } + + /* TODO: Set differential to 1 for the corresponding channel */ + if (diff_channels[0] > 1) + config_reg |= AD7294_DIFF_V3_V2; + else + config_reg |= AD7294_DIFF_V1_V0; + } + if (i2c->irq > 0) { ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, ad7294_event_handler, @@ -610,6 +650,9 @@ static int ad7294_probe(struct i2c_client *client, indio_dev->name = "ad7294"; indio_dev->info = &ad7294_info; + /* TODO: + Memcpy ad7284_chan_spec to some writable space and store pointer in indio_dev->channels + */ indio_dev->channels = ad7294_chan_spec; indio_dev->num_channels = ARRAY_SIZE(ad7294_chan_spec); From 8905a7faf03661941f4ac2a09cc013523911fe5c Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 29 Aug 2024 16:52:23 +0530 Subject: [PATCH 18/21] iio: addac: ad7294: addressed reviewer comments --- drivers/iio/addac/ad7294.c | 282 +++++++++++++++++++++---------------- 1 file changed, 163 insertions(+), 119 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 219dc5fdf998fd..0769e2a1b28cd1 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -17,37 +17,47 @@ #include #include -#define AD7294_REG_CMD 0x00 -#define AD7294_REG_RESULT 0x01 -#define AD7294_REG_TEMP_BASE 0x02 -#define AD7294_REG_DAC(x) ((x) + 0x01) -#define AD7294_VOLTAGE_STATUS 0x05 -#define AD7294_CURRENT_STATUS 0x06 -#define AD7294_TEMP_STATUS 0x07 -#define AD7294_REG_CONFIG 0x09 -#define AD7294_REG_PWDN 0x0A -#define AD7294_REG_DATA_LOW(x) ((x) * 3 + 0x0B) -#define AD7294_REG_DATA_HIGH(x) ((x) * 3 + 0x0C) -#define AD7294_REG_HYSTERESIS(x) ((x) * 3 + 0x0D) - -#define AD7294_TEMP_ALERT_MASK GENMASK(6, 0) -#define AD7294_CURRENT_ALERT_MASK GENMASK(4, 0) -#define AD7294_TEMP_VALUE_MASK GENMASK(10, 0) -#define AD7294_ADC_VALUE_MASK GENMASK(11, 0) -#define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) -#define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) -#define AD7294_DIFF_V3_V2 BIT(1) -#define AD7294_DIFF_V1_V0 BIT(0) -#define AD7294_ALERT_PIN BIT(2) -#define AD7294_ALERT_LOW(x) BIT((x) * 2) -#define AD7294_ALERT_HIGH(x) BIT((x) * 2 + 1) - -#define AD7294_ADC_INTERNAL_VREF_MV 2500 -#define AD7294_DAC_INTERNAL_VREF_MV 2500 -#define AD7294_RESOLUTION 12 -#define AD7294_VOLTAGE_CHENNEL_COUNT 4 +#define AD7294_REG_CMD 0x00 +#define AD7294_REG_RESULT 0x01 +#define AD7294_REG_TEMP_BASE 0x02 +#define AD7294_REG_DAC(x) ((x) + 0x01) +#define AD7294_VOLTAGE_STATUS 0x05 +#define AD7294_CURRENT_STATUS 0x06 +#define AD7294_TEMP_STATUS 0x07 +#define AD7294_REG_CONFIG 0x09 +#define AD7294_REG_PWDN 0x0A + +#define AD7294_REG_VOLTAGE_DATA_LOW(x) ((x) * 3 + 0x0B) +#define AD7294_REG_VOLTAGE_DATA_HIGH(x) ((x) * 3 + 0x0C) +#define AD7294_REG_VOLTAGE_HYSTERESIS(x) ((x) * 3 + 0x0D) + +#define AD7294_REG_CURRENT_DATA_LOW(x) ((x) * 3 + 0x17) +#define AD7294_REG_CURRENT_DATA_HIGH(x) ((x) * 3 + 0x18) +#define AD7294_REG_CURRENT_HYSTERESIS(x) ((x) * 3 + 0x19) + +#define AD7294_REG_TEMP_DATA_LOW(x) ((x) * 3 + 0x1D) +#define AD7294_REG_TEMP_DATA_HIGH(x) ((x) * 3 + 0x1E) +#define AD7294_REG_TEMP_HYSTERESIS(x) ((x) * 3 + 0x1F) + +#define AD7294_TEMP_VALUE_MASK GENMASK(10, 0) +#define AD7294_ADC_VALUE_MASK GENMASK(11, 0) +#define AD7294_ADC_EXTERNAL_REF_MASK BIT(5) +#define AD7294_DAC_EXTERNAL_REF_MASK BIT(4) +#define AD7294_DIFF_V3_V2 BIT(1) +#define AD7294_DIFF_V1_V0 BIT(0) +#define AD7294_ALERT_PIN BIT(2) +#define AD7294_ALERT_LOW(x) BIT((x) * 2) +#define AD7294_ALERT_HIGH(x) BIT((x) * 2 + 1) + +#define AD7294_ADC_INTERNAL_VREF_MV 2500 +#define AD7294_DAC_INTERNAL_VREF_MV 2500 +#define AD7294_RESOLUTION 12 +#define AD7294_VOLTAGE_CHANNEL_COUNT 4 +#define AD7294_CURRENT_CHANNEL_COUNT 2 +#define AD7294_TEMP_CHANNEL_COUNT 3 struct ad7294_state { + /* Protects device raw read write operations */ struct mutex lock; struct regmap *regmap; struct i2c_client *i2c; @@ -73,10 +83,10 @@ static int ad7294_reg_size(unsigned int reg) static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) { - int ret; struct i2c_client *client = context; - unsigned char buffer[3] = { reg }; int reg_size = ad7294_reg_size(reg); + unsigned char buffer[3] = { reg }; + int ret; ret = i2c_master_send(client, buffer, 1); if (ret < 0) @@ -86,8 +96,6 @@ static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) if (ret < 0) return ret; - dev_dbg(&client->dev, "Read [%x, %x] from reg:0x%x, size: %d", - buffer[1], buffer[2], reg, reg_size); if (reg_size == 1) *val = buffer[1]; else @@ -97,13 +105,10 @@ static int ad7294_reg_read(void *context, unsigned int reg, unsigned int *val) static int ad7294_reg_write(void *context, unsigned int reg, unsigned int val) { - int ret; struct i2c_client *client = context; - unsigned char buffer[3] = { reg }; int reg_size = ad7294_reg_size(reg); - - dev_dbg(&client->dev, "Write [%x] to reg: %x, size: %d", val, reg, - reg_size); + unsigned char buffer[3] = { reg }; + int ret; if (reg_size == 1) { /* Only take LSB of the data when writing to 1 byte reg */ @@ -151,6 +156,7 @@ static const struct iio_event_spec ad7294_events[] = { }, }; +// clang-format off #define AD7294_DAC_CHAN(_chan_id) { \ .type = IIO_VOLTAGE, \ .channel = _chan_id, \ @@ -160,9 +166,8 @@ static const struct iio_event_spec ad7294_events[] = { .output = 1, \ } -#define AD7294_VOLTAGE_CHAN(_type, _chan_id) { \ +#define AD7294_VOLTAGE_CHAN(_type) { \ .type = _type, \ - .channel = _chan_id, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .indexed = 1, \ @@ -171,9 +176,8 @@ static const struct iio_event_spec ad7294_events[] = { .num_event_specs = ARRAY_SIZE(ad7294_events), \ } -#define AD7294_CURRENT_CHAN(_type, _chan_id) { \ +#define AD7294_CURRENT_CHAN(_type) { \ .type = _type, \ - .channel = _chan_id, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ | BIT(IIO_CHAN_INFO_SCALE), \ .indexed = 1, \ @@ -192,6 +196,7 @@ static const struct iio_event_spec ad7294_events[] = { .event_spec = ad7294_events, \ .num_event_specs = ARRAY_SIZE(ad7294_events), \ } +// clang-format on enum ad7294_temp_chan { TSENSE_1, @@ -204,12 +209,12 @@ static const struct iio_chan_spec ad7294_chan_spec[] = { AD7294_DAC_CHAN(1), AD7294_DAC_CHAN(2), AD7294_DAC_CHAN(3), - AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 0), - AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 1), - AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 2), - AD7294_VOLTAGE_CHAN(IIO_VOLTAGE, 3), - AD7294_CURRENT_CHAN(IIO_CURRENT, 0), - AD7294_CURRENT_CHAN(IIO_CURRENT, 1), + AD7294_VOLTAGE_CHAN(0), + AD7294_VOLTAGE_CHAN(1), + AD7294_VOLTAGE_CHAN(2), + AD7294_VOLTAGE_CHAN(3), + AD7294_CURRENT_CHAN(0), + AD7294_CURRENT_CHAN(1), AD7294_TEMP_CHAN(TSENSE_1), AD7294_TEMP_CHAN(TSENSE_2), AD7294_TEMP_CHAN(TSENSE_INTERNAL), @@ -222,27 +227,25 @@ static const char *const ad7294_power_supplies[] = { static irqreturn_t ad7294_event_handler(int irq, void *private) { - int i; struct iio_dev *indio_dev = private; + s64 timestamp = iio_get_time_ns(indio_dev); struct ad7294_state *st = iio_priv(indio_dev); unsigned int voltage_status, temp_status, current_status; - s64 timestamp = iio_get_time_ns(indio_dev); + int i; if (regmap_read(st->regmap, AD7294_VOLTAGE_STATUS, &voltage_status)) return IRQ_HANDLED; if (regmap_read(st->regmap, AD7294_CURRENT_STATUS, ¤t_status)) return IRQ_HANDLED; - current_status &= AD7294_CURRENT_ALERT_MASK; if (regmap_read(st->regmap, AD7294_TEMP_STATUS, &temp_status)) return IRQ_HANDLED; - temp_status &= AD7294_TEMP_ALERT_MASK; if (!(voltage_status || current_status || temp_status)) return IRQ_HANDLED; - for (i = 0; i < AD7294_VOLTAGE_CHENNEL_COUNT; i++) { + for (i = 0; i < AD7294_VOLTAGE_CHANNEL_COUNT; i++) { if (voltage_status & AD7294_ALERT_LOW(i)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, @@ -257,70 +260,109 @@ static irqreturn_t ad7294_event_handler(int irq, void *private) timestamp); } - for_each_set_bit(i, (long *)&temp_status, AD7294_TEMP_ALERT_MASK) { - if (i & 1) + for (i = 0; i < AD7294_CURRENT_CHANNEL_COUNT; i++) { + if (current_status & AD7294_ALERT_LOW(i)) iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, i >> 1, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i, IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), + IIO_EV_DIR_FALLING), timestamp); - else + if (current_status & AD7294_ALERT_HIGH(i)) iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, i >> 1, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i, IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), + IIO_EV_DIR_RISING), timestamp); } - for_each_set_bit(i, (long *)¤t_status, - AD7294_CURRENT_ALERT_MASK) { - if (i & 1) + for (i = 0; i < AD7294_TEMP_CHANNEL_COUNT; i++) { + if (temp_status & AD7294_ALERT_LOW(i)) iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i >> 1, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, i, IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), + IIO_EV_DIR_FALLING), timestamp); - else + if (temp_status & AD7294_ALERT_HIGH(i)) iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_CURRENT, i >> 1, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, i, IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), + IIO_EV_DIR_RISING), timestamp); } return IRQ_HANDLED; } +static int ad7294_adc_read(struct ad7294_state *st, int channel, int reg_base, + int *val) +{ + int ret; + + ret = regmap_write(st->regmap, AD7294_REG_CMD, BIT(channel + reg_base)); + if (ret) + return ret; + ret = regmap_read(st->regmap, AD7294_REG_RESULT, val); + if (ret) + return ret; + *val &= AD7294_ADC_VALUE_MASK; + + return 0; +} + +static int ad7294_voltage_scale(struct iio_chan_spec const *chan, + struct ad7294_state *st, int *val) +{ + int ret; + + if (chan->output) { + if (st->dac_vref_reg) { + ret = regulator_get_voltage(st->dac_vref_reg); + if (ret < 0) + return ret; + *val = ret / MILLI; + } else { + *val = AD7294_DAC_INTERNAL_VREF_MV; + } + } else { + if (st->adc_vref_reg) { + ret = regulator_get_voltage(st->adc_vref_reg); + if (ret < 0) + return ret; + *val = ret / MILLI; + } else { + *val = AD7294_ADC_INTERNAL_VREF_MV; + } + } + return 0; +} + static int ad7294_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct ad7294_state *st = iio_priv(indio_dev); - int ret, base = 0; unsigned int regval; + int ret; guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_CURRENT: - base = AD7294_VOLTAGE_CHENNEL_COUNT; - goto adc_read; + ret = ad7294_adc_read(st, chan->channel, + AD7294_VOLTAGE_CHANNEL_COUNT, + val); + if (ret) + return ret; + return IIO_VAL_INT; case IIO_VOLTAGE: if (chan->output) { *val = st->dac_value[chan->channel]; return IIO_VAL_INT; } -adc_read: - ret = regmap_write(st->regmap, AD7294_REG_CMD, - BIT(chan->channel + base)); + ret = ad7294_adc_read(st, chan->channel, 0, val); if (ret) return ret; - ret = regmap_read(st->regmap, AD7294_REG_RESULT, - ®val); - if (ret) - return ret; - *val = regval & AD7294_ADC_VALUE_MASK; return IIO_VAL_INT; case IIO_TEMP: ret = regmap_read(st->regmap, @@ -337,27 +379,9 @@ static int ad7294_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_VOLTAGE: - if (chan->output) { - if (st->dac_vref_reg) { - ret = regulator_get_voltage( - st->dac_vref_reg); - if (ret < 0) - return ret; - *val = ret / MILLI; - } else { - *val = AD7294_DAC_INTERNAL_VREF_MV; - } - } else { - if (st->adc_vref_reg) { - ret = regulator_get_voltage( - st->adc_vref_reg); - if (ret < 0) - return ret; - *val = ret / MILLI; - } else { - *val = AD7294_ADC_INTERNAL_VREF_MV; - } - } + ret = ad7294_voltage_scale(chan, st, val); + if (ret) + return ret; *val2 = AD7294_RESOLUTION; return IIO_VAL_FRACTIONAL_LOG2; case IIO_TEMP: @@ -417,33 +441,53 @@ static unsigned int ad7294_threshold_reg(const struct iio_chan_spec *chan, enum iio_event_direction dir, enum iio_event_info info) { - unsigned int offset; - switch (chan->type) { case IIO_VOLTAGE: - offset = chan->channel; - break; + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7294_REG_VOLTAGE_DATA_LOW( + chan->channel); + else + return AD7294_REG_VOLTAGE_DATA_HIGH( + chan->channel); + case IIO_EV_INFO_HYSTERESIS: + return AD7294_REG_VOLTAGE_HYSTERESIS(chan->channel); + default: + return 0; + } case IIO_CURRENT: - offset = chan->channel + 4; - break; + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7294_REG_CURRENT_DATA_LOW( + chan->channel); + else + return AD7294_REG_CURRENT_DATA_HIGH( + chan->channel); + case IIO_EV_INFO_HYSTERESIS: + return AD7294_REG_CURRENT_HYSTERESIS(chan->channel); + default: + return 0; + } case IIO_TEMP: - offset = chan->channel + 6; - break; + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7294_REG_CURRENT_DATA_LOW( + chan->channel); + else + return AD7294_REG_CURRENT_DATA_HIGH( + chan->channel); + case IIO_EV_INFO_HYSTERESIS: + return AD7294_REG_CURRENT_HYSTERESIS(chan->channel); + default: + return 0; + } default: return 0; } - switch (info) { - case IIO_EV_INFO_VALUE: - if (dir == IIO_EV_DIR_FALLING) - return AD7294_REG_DATA_LOW(offset); - else - return AD7294_REG_DATA_HIGH(offset); - case IIO_EV_INFO_HYSTERESIS: - return AD7294_REG_HYSTERESIS(offset); - default: - return 0; - } return 0; } From e1316fc85d1f2587901e1c030f3bd138f3220843 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 29 Aug 2024 16:53:00 +0530 Subject: [PATCH 19/21] iio: addac: Kconfig: remove mention of AD7294-2 --- drivers/iio/addac/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index c78969fdf2a76b..1653312e07b266 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -20,7 +20,7 @@ config AD74115 module will be called ad74115. config AD7294 - tristate "Analog Devices AD7294/AD7294-2 driver" + tristate "Analog Devices AD7294 driver" depends on I2C select REGMAP_I2C help From aa7ad795eeef6e4397fd867b2aa69cc970c0096d Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 29 Aug 2024 16:53:49 +0530 Subject: [PATCH 20/21] arch: dts: overlay: update overlay for AD7294-2 --- arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts index 24d4c62ad1d8ff..80ef7c5fe03074 100644 --- a/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts +++ b/arch/arm/boot/dts/overlays/rpi-ad7294-overlay.dts @@ -49,7 +49,7 @@ #size-cells = <0>; status = "okay"; ad7294: ad7294@62 { - compatible = "adi,ad7294"; + compatible = "adi,ad7294-2"; reg = <0x62>; interrupts = <4 IRQ_TYPE_EDGE_FALLING>; interrupt-parent = <&gpio>; @@ -61,11 +61,4 @@ }; }; }; - - fragment@2 { - target = <&ad7294>; - __dormant__ { - compatible = "adi,ad7294-2"; - }; - }; }; From 6d2cb941001330534f02e08fdbe7cb3c1572aa99 Mon Sep 17 00:00:00 2001 From: Anshul Dalal Date: Thu, 14 Nov 2024 14:15:32 +0530 Subject: [PATCH 21/21] iio: addac: ad7294: add events --- drivers/iio/addac/ad7294.c | 107 +++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/drivers/iio/addac/ad7294.c b/drivers/iio/addac/ad7294.c index 0769e2a1b28cd1..1691a1077e251e 100644 --- a/drivers/iio/addac/ad7294.c +++ b/drivers/iio/addac/ad7294.c @@ -63,8 +63,11 @@ struct ad7294_state { struct i2c_client *i2c; struct regulator *adc_vref_reg; struct regulator *dac_vref_reg; - u32 shunt_ohms[2]; - u16 dac_value[2]; + u32 shunt_ohms[AD7294_CURRENT_CHANNEL_COUNT]; + u16 dac_value[AD7294_VOLTAGE_CHANNEL_COUNT]; + bool voltage_alerts[AD7294_VOLTAGE_CHANNEL_COUNT][2]; + bool current_alerts[AD7294_CURRENT_CHANNEL_COUNT][2]; + bool temp_alerts[AD7294_CURRENT_CHANNEL_COUNT][2]; }; static int ad7294_reg_size(unsigned int reg) @@ -142,12 +145,14 @@ static const struct iio_event_spec ad7294_events[] = { { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), }, { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), }, { .type = IIO_EV_TYPE_THRESH, @@ -527,9 +532,103 @@ static int ad7294_write_event_value(struct iio_dev *indio_dev, val); } +static int ad7294_get_alert_status(struct ad7294_state *st, + const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + int offset; + + switch (dir) { + case IIO_EV_DIR_FALLING: + offset = 0; + break; + case IIO_EV_DIR_RISING: + offset = 1; + break; + default: + return -EINVAL; + } + + switch (chan->type) { + case IIO_VOLTAGE: + return st->voltage_alerts[chan->channel][offset]; + case IIO_CURRENT: + return st->current_alerts[chan->channel][offset]; + case IIO_TEMP: + return st->temp_alerts[chan->channel][offset]; + default: + return -EINVAL; + } +} + +static int ad7294_set_alert_status(struct ad7294_state *st, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, bool value) +{ + int offset; + + switch (dir) { + case IIO_EV_DIR_FALLING: + offset = 0; + break; + case IIO_EV_DIR_RISING: + offset = 1; + break; + default: + return -EINVAL; + } + + switch (chan->type) { + case IIO_VOLTAGE: + st->voltage_alerts[chan->channel][offset] = value; + break; + case IIO_CURRENT: + st->current_alerts[chan->channel][offset] = value; + break; + case IIO_TEMP: + st->temp_alerts[chan->channel][offset] = value; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ad7294_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7294_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + return ad7294_get_alert_status(st, chan, dir); +} + +static int ad7294_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct ad7294_state *st = iio_priv(indio_dev); + int ret = 0; + + guard(mutex)(&st->lock); + ret = ad7294_set_alert_status(st, chan, dir, dir > 0); + if (ret) + return ret; + + return regmap_write(st->regmap, + ad7294_threshold_reg(chan, dir, IIO_EV_INFO_VALUE), + state > 0 ? AD7294_ADC_VALUE_MASK : 0); +} + struct iio_info ad7294_info = { .read_raw = ad7294_read_raw, .write_raw = ad7294_write_raw, + .read_event_config = &ad7294_read_event_config, + .write_event_config = &ad7294_write_event_config, .debugfs_reg_access = ad7294_reg_access, .read_event_value = &ad7294_read_event_value, .write_event_value = &ad7294_write_event_value,