aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenny Manne <jenny.v.manne@gmail.com>2021-07-09 14:26:35 +0200
committerHans Verkuil <hverkuil@xs4all.nl>2021-09-09 10:41:01 +0200
commitc86687a52f18a32f89ea7dce41506a347b8a85fa (patch)
tree9eafbd7c3539830f30a4d73738588a8cd10fcbb3
parentf60e316d6f7bef8bdb5973130f9dc34c187b0013 (diff)
ti-ads7128: driver for the TI ADS7128 ADC
New driver for TI ADS7128 ADC. It reports the voltages of the 8 inputs. In addition it also reports the minimum and maximum voltages measured since the last time the minimum and maximum voltages were read. Signed-off-by: Jenny Manne <jenny.v.manne@gmail.com> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
-rw-r--r--drivers/iio/adc/Kconfig10
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/ti-ads7128.c307
3 files changed, 318 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index db0c8fb60515..f1fc722d3d6d 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1111,6 +1111,16 @@ config TI_ADS1015
This driver can also be built as a module. If so, the module will be
called ti-ads1015.
+config TI_ADS7128
+ tristate "Texas Instruments ADS7128 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Texas Instruments ADS7128
+ 8 Channel ADC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ti-ads7128.
+
config TI_ADS7950
tristate "Texas Instruments ADS7950 ADC driver"
depends on SPI && GPIOLIB
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index f70d877c555a..eeb032e34414 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
+obj-$(CONFIG_TI_ADS7128) += ti-ads7128.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
diff --git a/drivers/iio/adc/ti-ads7128.c b/drivers/iio/adc/ti-ads7128.c
new file mode 100644
index 000000000000..eab781361754
--- /dev/null
+++ b/drivers/iio/adc/ti-ads7128.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI_ADS7128 8-Channel, I2C, 12-Bit SAR ADC
+ *
+ * Copyright 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * TI_ADS7128 registers definition
+ */
+#define TI_ADS7128_GENERAL_CFG 0x01
+#define TI_ADS7128_OPMODE_CFG 0x04
+#define TI_ADS7128_SEQUENCE_CFG 0x10
+#define TI_ADS7128_AUTO_SEQ_CH_SEL 0x12
+
+#define TI_ADS7128_MAX_LSB(ch) (0x60 + 2 * (ch))
+#define TI_ADS7128_MAX_MSB(ch) (0x61 + 2 * (ch))
+#define TI_ADS7128_MIN_LSB(ch) (0x80 + 2 * (ch))
+#define TI_ADS7128_MIN_MSB(ch) (0x81 + 2 * (ch))
+#define TI_ADS7128_RECENT_LSB(ch) (0xa0 + 2 * (ch))
+#define TI_ADS7128_RECENT_MSB(ch) (0xa1 + 2 * (ch))
+
+/*
+ * TI_ADS7128 command
+ */
+#define TI_ADS7128_SINGLE_REG_RD_MASK (0x10 << 8)
+#define TI_ADS7128_SINGLE_REG_WR_MASK (0x08 << 8)
+#define TI_ADS7128_SET_BIT_MASK (0x18 << 8)
+#define TI_ADS7128_CLR_BIT_MASK (0x20 << 8)
+
+
+struct ti_ads7128_chip_info {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regulator *regulator;
+ struct mutex state_lock;
+};
+
+static const struct regmap_config ti_ads7128_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8
+};
+
+static int ti_ads7128_read_min_max(struct ti_ads7128_chip_info *chip,
+ unsigned int channel_val, int *min_val, int *max_val)
+{
+ unsigned int val_lsb;
+ unsigned int val_msb;
+ unsigned int regval;
+ u8 stats_en = BIT(5);
+ int ret;
+
+ /* disable recording statistics */
+ regval = TI_ADS7128_CLR_BIT_MASK | TI_ADS7128_GENERAL_CFG;
+ ret = regmap_write(chip->regmap, regval, stats_en);
+ if (ret < 0)
+ return ret;
+
+ /* read minimum voltage value */
+ regval = TI_ADS7128_SINGLE_REG_RD_MASK | TI_ADS7128_MIN_LSB(channel_val);
+ ret = regmap_read(chip->regmap, regval, &val_lsb);
+ if (ret < 0)
+ return ret;
+ regval = TI_ADS7128_SINGLE_REG_RD_MASK | TI_ADS7128_MIN_MSB(channel_val);
+ ret = regmap_read(chip->regmap, regval, &val_msb);
+ if (ret < 0)
+ return ret;
+ *min_val = (val_msb << 4) | (val_lsb >> 4);
+
+ /* read maximum voltage value */
+ regval = TI_ADS7128_SINGLE_REG_RD_MASK | TI_ADS7128_MAX_LSB(channel_val);
+ ret = regmap_read(chip->regmap, regval, &val_lsb);
+ if (ret < 0)
+ return ret;
+ regval = TI_ADS7128_SINGLE_REG_RD_MASK | TI_ADS7128_MAX_MSB(channel_val);
+ ret = regmap_read(chip->regmap, regval, &val_msb);
+ *max_val = (val_msb << 4) | (val_lsb >> 4);
+
+ /* reset and re-enable recording statistics */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_GENERAL_CFG;
+ return regmap_write(chip->regmap, regval, stats_en);
+}
+
+static int ti_ads7128_read_raw_multi(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int max_len, int *val, int *val_len, long mask)
+{
+ struct ti_ads7128_chip_info *chip = iio_priv(indio_dev);
+ unsigned int channel_val = chan->channel;
+ unsigned int regval;
+ int val_lsb;
+ int val_msb;
+ int ret;
+
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ mutex_lock(&chip->state_lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Read most recent voltage value */
+ regval = TI_ADS7128_SINGLE_REG_RD_MASK | TI_ADS7128_RECENT_LSB(channel_val);
+ ret = regmap_read(chip->regmap, regval, &val_lsb);
+ if (ret < 0)
+ goto unlock;
+ regval = TI_ADS7128_SINGLE_REG_RD_MASK | TI_ADS7128_RECENT_MSB(channel_val);
+ ret = regmap_read(chip->regmap, regval, &val_msb);
+ if (ret < 0)
+ goto unlock;
+ *val = (val_msb << 4) | (val_lsb >> 4);
+ *val_len = 1;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_MIN_MAX_RAW:
+ /* the documentation specifies min and max values to be reset when read */
+ ret = ti_ads7128_read_min_max(chip, channel_val, &val[0], &val[1]);
+ if (ret < 0)
+ goto unlock;
+ *val_len = 2;
+ ret = IIO_VAL_INT_MULTIPLE;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ val[0] = regulator_get_voltage(chip->regulator);
+ val[1] = 1000 * 0xfff;
+ *val_len = 2;
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+unlock:
+ mutex_unlock(&chip->state_lock);
+ return ret;
+}
+
+#define TI_ADS7128_VOLTAGE_CHAN(chan) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_MIN_MAX_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
+ .indexed = 1, \
+ .channel = chan, \
+}
+
+static const struct iio_chan_spec ti_ads7128_channels[] = {
+ TI_ADS7128_VOLTAGE_CHAN(0),
+ TI_ADS7128_VOLTAGE_CHAN(1),
+ TI_ADS7128_VOLTAGE_CHAN(2),
+ TI_ADS7128_VOLTAGE_CHAN(3),
+ TI_ADS7128_VOLTAGE_CHAN(4),
+ TI_ADS7128_VOLTAGE_CHAN(5),
+ TI_ADS7128_VOLTAGE_CHAN(6),
+ TI_ADS7128_VOLTAGE_CHAN(7),
+};
+
+static const struct iio_info ti_ads7128_info = {
+ .read_raw_multi = &ti_ads7128_read_raw_multi,
+};
+
+static int ti_ads7128_autonomous_mode_config(struct ti_ads7128_chip_info *chip)
+{
+ u8 conv_mode = BIT(5);
+ u8 seq_start = BIT(4);
+ u8 seq_mode = BIT(0);
+ u8 stats_en = BIT(5);
+ unsigned int regval;
+ int ret;
+
+ /* enable analog input for sequencing */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_AUTO_SEQ_CH_SEL;
+ ret = regmap_write(chip->regmap, regval, 0xff);
+ if (ret < 0)
+ return ret;
+
+ /* select auto-sequence mode */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_SEQUENCE_CFG;
+ ret = regmap_write(chip->regmap, regval, seq_mode);
+ if (ret < 0)
+ return ret;
+
+ /* set mode to autonomous monitoring */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_OPMODE_CFG;
+ ret = regmap_write(chip->regmap, regval, conv_mode);
+ if (ret < 0)
+ return ret;
+
+ /* enable recording statistics */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_GENERAL_CFG;
+ ret = regmap_write(chip->regmap, regval, stats_en);
+ if (ret < 0)
+ return ret;
+
+ /* enable autonomous monitoring */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_SEQUENCE_CFG;
+ return regmap_write(chip->regmap, regval, seq_start);
+}
+
+static int ti_ads7128_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ti_ads7128_chip_info *chip;
+ struct iio_dev *indio_dev;
+ unsigned int regval;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+
+ mutex_init(&chip->state_lock);
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ chip->client = client;
+
+ indio_dev->name = "ti-ads7128";
+ indio_dev->channels = ti_ads7128_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ti_ads7128_channels);
+ indio_dev->info = &ti_ads7128_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ chip->regmap = devm_regmap_init_i2c(client, &ti_ads7128_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ dev_err(&client->dev, "Failed to allocate register map\n");
+ return PTR_ERR(chip->regmap);
+ }
+
+ chip->regulator = devm_regulator_get(&client->dev, "vref");
+ if (IS_ERR(chip->regulator)) {
+ dev_err(&client->dev, "Failed to get regulator\n");
+ return PTR_ERR(chip->regulator);
+ }
+ ret = regulator_enable(chip->regulator);
+ if (ret < 0)
+ return ret;
+
+ /* software reset by setting the RST bit */
+ regval = TI_ADS7128_SET_BIT_MASK | TI_ADS7128_GENERAL_CFG;
+ ret = regmap_write(chip->regmap, regval, 0x1);
+ if (ret < 0)
+ goto disable_regulator;
+ /* wait for device reset to complete */
+ usleep_range(100, 200);
+
+ ret = ti_ads7128_autonomous_mode_config(chip);
+ if (ret < 0)
+ goto disable_regulator;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto disable_regulator;
+
+ return ret;
+
+disable_regulator:
+ regulator_disable(chip->regulator);
+ return ret;
+}
+
+static int ti_ads7128_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ti_ads7128_chip_info *chip = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ regulator_disable(chip->regulator);
+
+ return 0;
+}
+
+static const struct of_device_id ti_ads7128_of_match[] = {
+ { .compatible = "ti,ads7128" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ti_ads7128_of_match);
+
+static struct i2c_driver ti_ads7128_driver = {
+ .driver = {
+ .name = "ti-ads7128",
+ .of_match_table = ti_ads7128_of_match,
+ },
+ .probe = ti_ads7128_probe,
+ .remove = ti_ads7128_remove
+};
+module_i2c_driver(ti_ads7128_driver);
+
+MODULE_AUTHOR("Jenny Veronika Ip Manne <jenny.v.manne@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments ADS7128 ADC driver");
+MODULE_LICENSE("GPL v2");

Privacy Policy