iio: adc: Add support for AD7091R-8
authorMarcelo Schmitt <marcelo.schmitt@analog.com>
Tue, 19 Dec 2023 20:32:36 +0000 (17:32 -0300)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Tue, 26 Dec 2023 15:43:33 +0000 (15:43 +0000)
Add support for Analog Devices AD7091R-2, AD7091R-4, and AD7091R-8
low power 12-Bit SAR ADCs with SPI interface.
Extend ad7091r-base driver so it can be used by the AD7091R-8 driver.

Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
Link: https://lore.kernel.org/r/09d1d1c4b39cecc528488efac6094233715f5659.1703013352.git.marcelo.schmitt1@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/Kconfig
drivers/iio/adc/Makefile
drivers/iio/adc/ad7091r-base.c
drivers/iio/adc/ad7091r-base.h
drivers/iio/adc/ad7091r8.c [new file with mode: 0644]

index 9d2d32d0916689d4a9e9f056f392c5d164440250..3b73c509bd68ef55094e90284b14466b98e48c9e 100644 (file)
@@ -47,6 +47,18 @@ config AD7091R5
        help
          Say yes here to build support for Analog Devices AD7091R-5 ADC.
 
+config AD7091R8
+       tristate "Analog Devices AD7091R8 ADC Driver"
+       depends on SPI
+       select AD7091R
+       select REGMAP_SPI
+       help
+         Say yes here to build support for Analog Devices AD7091R-2, AD7091R-4,
+         and AD7091R-8 ADC.
+
+         To compile this driver as a module, choose M here: the module will be
+         called ad7091r8.
+
 config AD7124
        tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
        depends on SPI_MASTER
index 1e289d674d4d37517e2cb2b94250c3837e2e82e9..d2fda54a3259c9766766d4bd80f1500d140260c2 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
 obj-$(CONFIG_AD4130) += ad4130.o
 obj-$(CONFIG_AD7091R) += ad7091r-base.o
 obj-$(CONFIG_AD7091R5) += ad7091r5.o
+obj-$(CONFIG_AD7091R8) += ad7091r8.o
 obj-$(CONFIG_AD7124) += ad7124.o
 obj-$(CONFIG_AD7192) += ad7192.o
 obj-$(CONFIG_AD7266) += ad7266.o
index 665e930d67d0fa9da8e333b83c114cc83792660d..f4255b91acfc9849df2986f0c619d3f816f3b57c 100644 (file)
@@ -322,6 +322,12 @@ int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info,
        iio_dev->info = &ad7091r_info;
        iio_dev->modes = INDIO_DIRECT_MODE;
 
+       if (init_info->setup) {
+               ret = init_info->setup(st);
+               if (ret < 0)
+                       return ret;
+       }
+
        if (irq) {
                st->chip_info = init_info->info_irq;
                ret = regmap_update_bits(st->map, AD7091R_REG_CONF,
index b50024ca33e91aea8fe323ee76ed2c31230efa0c..696bf7a897bb51de16477b5b802919d1ae21124c 100644 (file)
@@ -49,6 +49,7 @@
 }
 
 struct device;
+struct gpio_desc;
 
 enum ad7091r_mode {
        AD7091R_MODE_SAMPLE,
@@ -59,10 +60,14 @@ enum ad7091r_mode {
 struct ad7091r_state {
        struct device *dev;
        struct regmap *map;
+       struct gpio_desc *convst_gpio;
+       struct gpio_desc *reset_gpio;
        struct regulator *vref;
        const struct ad7091r_chip_info *chip_info;
        enum ad7091r_mode mode;
        struct mutex lock; /*lock to prevent concurent reads */
+       __be16 tx_buf __aligned(IIO_DMA_MINALIGN);
+       __be16 rx_buf;
 };
 
 struct ad7091r_chip_info {
@@ -80,6 +85,7 @@ struct ad7091r_init_info {
        const struct regmap_config *regmap_config;
        void (*init_adc_regmap)(struct ad7091r_state *st,
                                const struct regmap_config *regmap_conf);
+       int (*setup)(struct ad7091r_state *st);
 };
 
 extern const struct iio_event_spec ad7091r_events[3];
diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c
new file mode 100644 (file)
index 0000000..57700f1
--- /dev/null
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7091R8 12-bit SAR ADC driver
+ *
+ * Copyright 2023 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+
+#include "ad7091r-base.h"
+
+#define AD7091R8_REG_ADDR_MSK                          GENMASK(15, 11)
+#define AD7091R8_RD_WR_FLAG_MSK                                BIT(10)
+#define AD7091R8_REG_DATA_MSK                          GENMASK(9, 0)
+
+#define AD7091R_SPI_REGMAP_CONFIG(n) {                                 \
+       .reg_bits = 8,                                                  \
+       .val_bits = 16,                                                 \
+       .volatile_reg = ad7091r_volatile_reg,                           \
+       .writeable_reg = ad7091r_writeable_reg,                         \
+       .max_register = AD7091R_REG_CH_HYSTERESIS(n),                   \
+}
+
+static int ad7091r8_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode)
+{
+       /* AD7091R-2/-4/-8 don't set sample/command/autocycle mode in conf reg */
+       st->mode = mode;
+       return 0;
+}
+
+static unsigned int ad7091r8_reg_result_chan_id(unsigned int val)
+{
+       return AD7091R8_REG_RESULT_CH_ID(val);
+}
+
+#define AD7091R_SPI_CHIP_INFO(_n, _name) {                             \
+       .name = _name,                                                  \
+       .channels = ad7091r##_n##_channels,                             \
+       .num_channels = ARRAY_SIZE(ad7091r##_n##_channels),             \
+       .vref_mV = 2500,                                                \
+       .reg_result_chan_id = &ad7091r8_reg_result_chan_id,     \
+       .set_mode = &ad7091r8_set_mode,                         \
+}
+
+#define AD7091R_SPI_CHIP_INFO_IRQ(_n, _name) {                         \
+       .name = _name,                                                  \
+       .channels = ad7091r##_n##_channels_irq,                         \
+       .num_channels = ARRAY_SIZE(ad7091r##_n##_channels_irq),         \
+       .vref_mV = 2500,                                                \
+       .reg_result_chan_id = &ad7091r8_reg_result_chan_id,     \
+       .set_mode = &ad7091r8_set_mode,                         \
+}
+
+enum ad7091r8_info_ids {
+       AD7091R2_INFO,
+       AD7091R4_INFO,
+       AD7091R4_INFO_IRQ,
+       AD7091R8_INFO,
+       AD7091R8_INFO_IRQ,
+};
+
+static const struct iio_chan_spec ad7091r2_channels[] = {
+       AD7091R_CHANNEL(0, 12, NULL, 0),
+       AD7091R_CHANNEL(1, 12, NULL, 0),
+};
+
+static const struct iio_chan_spec ad7091r4_channels[] = {
+       AD7091R_CHANNEL(0, 12, NULL, 0),
+       AD7091R_CHANNEL(1, 12, NULL, 0),
+       AD7091R_CHANNEL(2, 12, NULL, 0),
+       AD7091R_CHANNEL(3, 12, NULL, 0),
+};
+
+static const struct iio_chan_spec ad7091r4_channels_irq[] = {
+       AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+};
+
+static const struct iio_chan_spec ad7091r8_channels[] = {
+       AD7091R_CHANNEL(0, 12, NULL, 0),
+       AD7091R_CHANNEL(1, 12, NULL, 0),
+       AD7091R_CHANNEL(2, 12, NULL, 0),
+       AD7091R_CHANNEL(3, 12, NULL, 0),
+       AD7091R_CHANNEL(4, 12, NULL, 0),
+       AD7091R_CHANNEL(5, 12, NULL, 0),
+       AD7091R_CHANNEL(6, 12, NULL, 0),
+       AD7091R_CHANNEL(7, 12, NULL, 0),
+};
+
+static const struct iio_chan_spec ad7091r8_channels_irq[] = {
+       AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(4, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(5, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(6, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+       AD7091R_CHANNEL(7, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+};
+
+static void ad7091r_pulse_convst(struct ad7091r_state *st)
+{
+       gpiod_set_value_cansleep(st->convst_gpio, 1);
+       gpiod_set_value_cansleep(st->convst_gpio, 0);
+}
+
+static int ad7091r_regmap_bus_reg_read(void *context, unsigned int reg,
+                                      unsigned int *val)
+{
+       struct ad7091r_state *st = context;
+       struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+       int ret;
+
+       struct spi_transfer t[] = {
+               {
+                       .tx_buf = &st->tx_buf,
+                       .len = 2,
+                       .cs_change = 1,
+               }, {
+                       .rx_buf = &st->rx_buf,
+                       .len = 2,
+               }
+       };
+
+       if (reg == AD7091R_REG_RESULT)
+               ad7091r_pulse_convst(st);
+
+       st->tx_buf = cpu_to_be16(reg << 11);
+
+       ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+       if (ret < 0)
+               return ret;
+
+       *val = be16_to_cpu(st->rx_buf);
+       return 0;
+}
+
+static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg,
+                                       unsigned int val)
+{
+       struct ad7091r_state *st = context;
+       struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+
+       /*
+        * AD7091R-2/-4/-8 protocol (datasheet page 31) is to do a single SPI
+        * transfer with reg address set in bits B15:B11 and value set in B9:B0.
+        */
+       st->tx_buf = cpu_to_be16(FIELD_PREP(AD7091R8_REG_DATA_MSK, val) |
+                                FIELD_PREP(AD7091R8_RD_WR_FLAG_MSK, 1) |
+                                FIELD_PREP(AD7091R8_REG_ADDR_MSK, reg));
+
+       return spi_write(spi, &st->tx_buf, 2);
+}
+
+static struct regmap_bus ad7091r8_regmap_bus = {
+       .reg_read = ad7091r_regmap_bus_reg_read,
+       .reg_write = ad7091r_regmap_bus_reg_write,
+       .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+       .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct ad7091r_chip_info ad7091r8_infos[] = {
+       [AD7091R2_INFO] = AD7091R_SPI_CHIP_INFO(2, "ad7091r-2"),
+       [AD7091R4_INFO] = AD7091R_SPI_CHIP_INFO(4, "ad7091r-4"),
+       [AD7091R4_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(4, "ad7091r-4"),
+       [AD7091R8_INFO] = AD7091R_SPI_CHIP_INFO(8, "ad7091r-8"),
+       [AD7091R8_INFO_IRQ] = AD7091R_SPI_CHIP_INFO_IRQ(8, "ad7091r-8")
+};
+
+static const struct regmap_config ad7091r2_reg_conf = AD7091R_SPI_REGMAP_CONFIG(2);
+static const struct regmap_config ad7091r4_reg_conf = AD7091R_SPI_REGMAP_CONFIG(4);
+static const struct regmap_config ad7091r8_reg_conf = AD7091R_SPI_REGMAP_CONFIG(8);
+
+static void ad7091r8_regmap_init(struct ad7091r_state *st,
+                                const struct regmap_config *regmap_conf)
+{
+       st->map = devm_regmap_init(st->dev, &ad7091r8_regmap_bus, st,
+                                  regmap_conf);
+}
+
+static int ad7091r8_gpio_setup(struct ad7091r_state *st)
+{
+       st->convst_gpio = devm_gpiod_get(st->dev, "convst", GPIOD_OUT_LOW);
+       if (IS_ERR(st->convst_gpio))
+               return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio),
+                                    "Error getting convst GPIO\n");
+
+       st->reset_gpio = devm_gpiod_get_optional(st->dev, "reset",
+                                                GPIOD_OUT_HIGH);
+       if (IS_ERR(st->reset_gpio))
+               return dev_err_probe(st->dev, PTR_ERR(st->convst_gpio),
+                                    "Error on requesting reset GPIO\n");
+
+       if (st->reset_gpio) {
+               fsleep(20);
+               gpiod_set_value_cansleep(st->reset_gpio, 0);
+       }
+
+       return 0;
+}
+
+static struct ad7091r_init_info ad7091r2_init_info = {
+       .info_no_irq = &ad7091r8_infos[AD7091R2_INFO],
+       .regmap_config = &ad7091r2_reg_conf,
+       .init_adc_regmap = &ad7091r8_regmap_init,
+       .setup = &ad7091r8_gpio_setup
+};
+
+static struct ad7091r_init_info ad7091r4_init_info = {
+       .info_no_irq = &ad7091r8_infos[AD7091R4_INFO],
+       .info_irq = &ad7091r8_infos[AD7091R4_INFO_IRQ],
+       .regmap_config = &ad7091r4_reg_conf,
+       .init_adc_regmap = &ad7091r8_regmap_init,
+       .setup = &ad7091r8_gpio_setup
+};
+
+static struct ad7091r_init_info ad7091r8_init_info = {
+       .info_no_irq = &ad7091r8_infos[AD7091R8_INFO],
+       .info_irq = &ad7091r8_infos[AD7091R8_INFO_IRQ],
+       .regmap_config = &ad7091r8_reg_conf,
+       .init_adc_regmap = &ad7091r8_regmap_init,
+       .setup = &ad7091r8_gpio_setup
+};
+
+static int ad7091r8_spi_probe(struct spi_device *spi)
+{
+       const struct ad7091r_init_info *init_info;
+
+       init_info = spi_get_device_match_data(spi);
+       if (!init_info)
+               return -EINVAL;
+
+       return ad7091r_probe(&spi->dev, init_info, spi->irq);
+}
+
+static const struct of_device_id ad7091r8_of_match[] = {
+       { .compatible = "adi,ad7091r2", .data = &ad7091r2_init_info },
+       { .compatible = "adi,ad7091r4", .data = &ad7091r4_init_info },
+       { .compatible = "adi,ad7091r8", .data = &ad7091r8_init_info },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ad7091r8_of_match);
+
+static const struct spi_device_id ad7091r8_spi_id[] = {
+       { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info },
+       { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info },
+       { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id);
+
+static struct spi_driver ad7091r8_driver = {
+       .driver = {
+               .name = "ad7091r8",
+               .of_match_table = ad7091r8_of_match,
+       },
+       .probe = ad7091r8_spi_probe,
+       .id_table = ad7091r8_spi_id,
+};
+module_spi_driver(ad7091r8_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7091R8 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_AD7091R);