summaryrefslogtreecommitdiff
path: root/drivers/iio/adc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/Kconfig48
-rw-r--r--drivers/iio/adc/Makefile4
-rw-r--r--drivers/iio/adc/ad4000.c722
-rw-r--r--drivers/iio/adc/ad4695.c1185
-rw-r--r--drivers/iio/adc/ad7091r5.c6
-rw-r--r--drivers/iio/adc/ad7091r8.c2
-rw-r--r--drivers/iio/adc/ad7124.c38
-rw-r--r--drivers/iio/adc/ad7192.c189
-rw-r--r--drivers/iio/adc/ad7266.c7
-rw-r--r--drivers/iio/adc/ad7280a.c14
-rw-r--r--drivers/iio/adc/ad7291.c4
-rw-r--r--drivers/iio/adc/ad7292.c4
-rw-r--r--drivers/iio/adc/ad7298.c7
-rw-r--r--drivers/iio/adc/ad7380.c525
-rw-r--r--drivers/iio/adc/ad7476.c58
-rw-r--r--drivers/iio/adc/ad7606.c47
-rw-r--r--drivers/iio/adc/ad7606_par.c2
-rw-r--r--drivers/iio/adc/ad7606_spi.c9
-rw-r--r--drivers/iio/adc/ad7766.c14
-rw-r--r--drivers/iio/adc/ad7768-1.c7
-rw-r--r--drivers/iio/adc/ad7780.c10
-rw-r--r--drivers/iio/adc/ad7793.c20
-rw-r--r--drivers/iio/adc/ad7887.c4
-rw-r--r--drivers/iio/adc/ad7923.c18
-rw-r--r--drivers/iio/adc/ad799x.c3
-rw-r--r--drivers/iio/adc/ad9467.c491
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c6
-rw-r--r--drivers/iio/adc/adi-axi-adc.c71
-rw-r--r--drivers/iio/adc/aspeed_adc.c5
-rw-r--r--drivers/iio/adc/at91_adc.c19
-rw-r--r--drivers/iio/adc/axp20x_adc.c182
-rw-r--r--drivers/iio/adc/axp288_adc.c2
-rw-r--r--drivers/iio/adc/bcm_iproc_adc.c2
-rw-r--r--drivers/iio/adc/berlin2-adc.c2
-rw-r--r--drivers/iio/adc/cc10001_adc.c4
-rw-r--r--drivers/iio/adc/dln2-adc.c8
-rw-r--r--drivers/iio/adc/ep93xx_adc.c2
-rw-r--r--drivers/iio/adc/exynos_adc.c2
-rw-r--r--drivers/iio/adc/hi8435.c2
-rw-r--r--drivers/iio/adc/hx711.c7
-rw-r--r--drivers/iio/adc/ina2xx-adc.c17
-rw-r--r--drivers/iio/adc/ingenic-adc.c2
-rw-r--r--drivers/iio/adc/lpc32xx_adc.c2
-rw-r--r--drivers/iio/adc/ltc2496.c2
-rw-r--r--drivers/iio/adc/ltc2497.c2
-rw-r--r--drivers/iio/adc/max1027.c16
-rw-r--r--drivers/iio/adc/max11100.c4
-rw-r--r--drivers/iio/adc/max1118.c7
-rw-r--r--drivers/iio/adc/max1241.c4
-rw-r--r--drivers/iio/adc/max1363.c34
-rw-r--r--drivers/iio/adc/max34408.c4
-rw-r--r--drivers/iio/adc/max9611.c6
-rw-r--r--drivers/iio/adc/mcp320x.c10
-rw-r--r--drivers/iio/adc/mcp3564.c54
-rw-r--r--drivers/iio/adc/mcp3911.c61
-rw-r--r--drivers/iio/adc/mp2629_adc.c4
-rw-r--r--drivers/iio/adc/mt6360-adc.c4
-rw-r--r--drivers/iio/adc/nau7802.c2
-rw-r--r--drivers/iio/adc/pac1921.c1261
-rw-r--r--drivers/iio/adc/pac1934.c6
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c2
-rw-r--r--drivers/iio/adc/qcom-spmi-rradc.c2
-rw-r--r--drivers/iio/adc/rockchip_saradc.c4
-rw-r--r--drivers/iio/adc/rtq6056.c4
-rw-r--r--drivers/iio/adc/sd_adc_modulator.c97
-rw-r--r--drivers/iio/adc/sophgo-cv1800b-adc.c227
-rw-r--r--drivers/iio/adc/stm32-adc.c6
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c297
-rw-r--r--drivers/iio/adc/stm32-dfsdm-core.c2
-rw-r--r--drivers/iio/adc/stmpe-adc.c2
-rw-r--r--drivers/iio/adc/ti-adc0832.c7
-rw-r--r--drivers/iio/adc/ti-adc084s021.c7
-rw-r--r--drivers/iio/adc/ti-adc12138.c7
-rw-r--r--drivers/iio/adc/ti-adc161s626.c8
-rw-r--r--drivers/iio/adc/ti-ads1015.c6
-rw-r--r--drivers/iio/adc/ti-ads1119.c4
-rw-r--r--drivers/iio/adc/ti-ads124s08.c5
-rw-r--r--drivers/iio/adc/ti-ads1298.c3
-rw-r--r--drivers/iio/adc/ti-ads131e08.c6
-rw-r--r--drivers/iio/adc/ti-ads7924.c4
-rw-r--r--drivers/iio/adc/ti-ads7950.c2
-rw-r--r--drivers/iio/adc/ti-ads8344.c2
-rw-r--r--drivers/iio/adc/ti-ads8688.c10
-rw-r--r--drivers/iio/adc/ti-lmp92064.c2
-rw-r--r--drivers/iio/adc/ti-tlc4541.c8
-rw-r--r--drivers/iio/adc/ti-tsc2046.c83
-rw-r--r--drivers/iio/adc/vf610_adc.c2
-rw-r--r--drivers/iio/adc/xilinx-ams.c15
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c5
89 files changed, 5389 insertions, 694 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index f60fe85a30d5..97ece1a4b7e3 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -21,6 +21,18 @@ config AD_SIGMA_DELTA
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+config AD4000
+ tristate "Analog Devices AD4000 ADC Driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD4000 high speed
+ SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4000.
+
config AD4130
tristate "Analog Device AD4130 ADC Driver"
depends on SPI
@@ -36,6 +48,17 @@ config AD4130
To compile this driver as a module, choose M here: the module will be
called ad4130.
+config AD4695
+ tristate "Analog Device AD4695 ADC Driver"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Say yes here to build support for Analog Devices AD4695 and similar
+ analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4695.
+
config AD7091R
tristate
@@ -991,6 +1014,19 @@ config NPCM_ADC
This driver can also be built as a module. If so, the module
will be called npcm_adc.
+config PAC1921
+ tristate "Microchip Technology PAC1921 driver"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Microchip Technology's PAC1921
+ High-Side Power/Current Monitor with Analog Output.
+
+ This driver can also be built as a module. If so, the module
+ will be called pac1921.
+
config PAC1934
tristate "Microchip Technology PAC1934 driver"
depends on I2C
@@ -1156,6 +1192,16 @@ config SC27XX_ADC
This driver can also be built as a module. If so, the module
will be called sc27xx_adc.
+config SOPHGO_CV1800B_ADC
+ tristate "Sophgo CV1800B SARADC"
+ depends on ARCH_SOPHGO || COMPILE_TEST
+ help
+ Say yes here to build support for the SARADC integrated inside
+ the Sophgo CV1800B SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called sophgo_cv1800b_adc.
+
config SPEAR_ADC
tristate "ST SPEAr ADC"
depends on PLAT_SPEAR || COMPILE_TEST
@@ -1171,6 +1217,7 @@ config SD_ADC_MODULATOR
tristate "Generic sigma delta modulator"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select IIO_BACKEND
help
Select this option to enables sigma delta modulator. This driver can
support generic sigma delta modulators.
@@ -1225,6 +1272,7 @@ config STM32_DFSDM_ADC
select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER
select IIO_TRIGGERED_BUFFER
+ select IIO_BACKEND
help
Select this option to support ADCSigma delta modulator for
STMicroelectronics STM32 digital filter for sigma delta converter.
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index d370e066544e..7b91cd98c0e0 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -6,7 +6,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
+obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4130) += ad4130.o
+obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD7091R) += ad7091r-base.o
obj-$(CONFIG_AD7091R5) += ad7091r5.o
obj-$(CONFIG_AD7091R8) += ad7091r8.o
@@ -90,6 +92,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_PAC1921) += pac1921.o
obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
@@ -105,6 +108,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
+obj-$(CONFIG_SOPHGO_CV1800B_ADC) += sophgo-cv1800b-adc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c
new file mode 100644
index 000000000000..6ea491245084
--- /dev/null
+++ b/drivers/iio/adc/ad4000.c
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD4000 SPI ADC driver
+ *
+ * Copyright 2024 Analog Devices Inc.
+ */
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+#include <linux/util_macros.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define AD4000_READ_COMMAND 0x54
+#define AD4000_WRITE_COMMAND 0x14
+
+#define AD4000_CONFIG_REG_DEFAULT 0xE1
+
+/* AD4000 Configuration Register programmable bits */
+#define AD4000_CFG_SPAN_COMP BIT(3) /* Input span compression */
+#define AD4000_CFG_HIGHZ BIT(2) /* High impedance mode */
+
+#define AD4000_SCALE_OPTIONS 2
+
+#define AD4000_TQUIET1_NS 190
+#define AD4000_TQUIET2_NS 60
+#define AD4000_TCONV_NS 320
+
+#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = 1, \
+ .channel = 0, \
+ .channel2 = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
+ .scan_type = { \
+ .sign = _sign, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = _storage_bits - _real_bits, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \
+ __AD4000_DIFF_CHANNEL((_sign), (_real_bits), \
+ ((_real_bits) > 16 ? 32 : 16), (_reg_access))
+
+#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
+ .scan_type = { \
+ .sign = _sign, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = _storage_bits - _real_bits, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \
+ __AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \
+ ((_real_bits) > 16 ? 32 : 16), (_reg_access))
+
+static const char * const ad4000_power_supplies[] = {
+ "vdd", "vio"
+};
+
+enum ad4000_sdi {
+ AD4000_SDI_MOSI,
+ AD4000_SDI_VIO,
+ AD4000_SDI_CS,
+ AD4000_SDI_GND,
+};
+
+/* maps adi,sdi-pin property value to enum */
+static const char * const ad4000_sdi_pin[] = {
+ [AD4000_SDI_MOSI] = "sdi",
+ [AD4000_SDI_VIO] = "high",
+ [AD4000_SDI_CS] = "cs",
+ [AD4000_SDI_GND] = "low",
+};
+
+/* Gains stored as fractions of 1000 so they can be expressed by integers. */
+static const int ad4000_gains[] = {
+ 454, 909, 1000, 1900,
+};
+
+struct ad4000_chip_info {
+ const char *dev_name;
+ struct iio_chan_spec chan_spec;
+ struct iio_chan_spec reg_access_chan_spec;
+ bool has_hardware_gain;
+};
+
+static const struct ad4000_chip_info ad4000_chip_info = {
+ .dev_name = "ad4000",
+ .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
+ .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
+};
+
+static const struct ad4000_chip_info ad4001_chip_info = {
+ .dev_name = "ad4001",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
+};
+
+static const struct ad4000_chip_info ad4002_chip_info = {
+ .dev_name = "ad4002",
+ .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
+ .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
+};
+
+static const struct ad4000_chip_info ad4003_chip_info = {
+ .dev_name = "ad4003",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
+};
+
+static const struct ad4000_chip_info ad4004_chip_info = {
+ .dev_name = "ad4004",
+ .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
+ .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
+};
+
+static const struct ad4000_chip_info ad4005_chip_info = {
+ .dev_name = "ad4005",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
+};
+
+static const struct ad4000_chip_info ad4006_chip_info = {
+ .dev_name = "ad4006",
+ .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
+ .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
+};
+
+static const struct ad4000_chip_info ad4007_chip_info = {
+ .dev_name = "ad4007",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
+};
+
+static const struct ad4000_chip_info ad4008_chip_info = {
+ .dev_name = "ad4008",
+ .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
+ .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
+};
+
+static const struct ad4000_chip_info ad4010_chip_info = {
+ .dev_name = "ad4010",
+ .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
+ .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
+};
+
+static const struct ad4000_chip_info ad4011_chip_info = {
+ .dev_name = "ad4011",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
+};
+
+static const struct ad4000_chip_info ad4020_chip_info = {
+ .dev_name = "ad4020",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
+};
+
+static const struct ad4000_chip_info ad4021_chip_info = {
+ .dev_name = "ad4021",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
+};
+
+static const struct ad4000_chip_info ad4022_chip_info = {
+ .dev_name = "ad4022",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
+};
+
+static const struct ad4000_chip_info adaq4001_chip_info = {
+ .dev_name = "adaq4001",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
+ .has_hardware_gain = true,
+};
+
+static const struct ad4000_chip_info adaq4003_chip_info = {
+ .dev_name = "adaq4003",
+ .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
+ .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
+ .has_hardware_gain = true,
+};
+
+struct ad4000_state {
+ struct spi_device *spi;
+ struct gpio_desc *cnv_gpio;
+ struct spi_transfer xfers[2];
+ struct spi_message msg;
+ struct mutex lock; /* Protect read modify write cycle */
+ int vref_mv;
+ enum ad4000_sdi sdi_pin;
+ bool span_comp;
+ u16 gain_milli;
+ int scale_tbl[AD4000_SCALE_OPTIONS][2];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ struct {
+ union {
+ __be16 sample_buf16;
+ __be32 sample_buf32;
+ } data;
+ s64 timestamp __aligned(8);
+ } scan __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[2];
+ u8 rx_buf[2];
+};
+
+static void ad4000_fill_scale_tbl(struct ad4000_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int val, tmp0, tmp1;
+ int scale_bits;
+ u64 tmp2;
+
+ /*
+ * ADCs that output two's complement code have one less bit to express
+ * voltage magnitude.
+ */
+ if (chan->scan_type.sign == 's')
+ scale_bits = chan->scan_type.realbits - 1;
+ else
+ scale_bits = chan->scan_type.realbits;
+
+ /*
+ * The gain is stored as a fraction of 1000 and, as we need to
+ * divide vref_mv by the gain, we invert the gain/1000 fraction.
+ * Also multiply by an extra MILLI to preserve precision.
+ * Thus, we have MILLI * MILLI equals MICRO as fraction numerator.
+ */
+ val = mult_frac(st->vref_mv, MICRO, st->gain_milli);
+
+ /* Would multiply by NANO here but we multiplied by extra MILLI */
+ tmp2 = shift_right((u64)val * MICRO, scale_bits);
+ tmp0 = div_s64_rem(tmp2, NANO, &tmp1);
+
+ /* Store scale for when span compression is disabled */
+ st->scale_tbl[0][0] = tmp0; /* Integer part */
+ st->scale_tbl[0][1] = abs(tmp1); /* Fractional part */
+
+ /* Store scale for when span compression is enabled */
+ st->scale_tbl[1][0] = tmp0;
+
+ /* The integer part is always zero so don't bother to divide it. */
+ if (chan->differential)
+ st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 4, 5);
+ else
+ st->scale_tbl[1][1] = DIV_ROUND_CLOSEST(abs(tmp1) * 9, 10);
+}
+
+static int ad4000_write_reg(struct ad4000_state *st, uint8_t val)
+{
+ st->tx_buf[0] = AD4000_WRITE_COMMAND;
+ st->tx_buf[1] = val;
+ return spi_write(st->spi, st->tx_buf, ARRAY_SIZE(st->tx_buf));
+}
+
+static int ad4000_read_reg(struct ad4000_state *st, unsigned int *val)
+{
+ struct spi_transfer t = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ .len = 2,
+ };
+ int ret;
+
+ st->tx_buf[0] = AD4000_READ_COMMAND;
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret < 0)
+ return ret;
+
+ *val = st->rx_buf[1];
+ return ret;
+}
+
+static int ad4000_convert_and_acquire(struct ad4000_state *st)
+{
+ int ret;
+
+ /*
+ * In 4-wire mode, the CNV line is held high for the entire conversion
+ * and acquisition process. In other modes, the CNV GPIO is optional
+ * and, if provided, replaces controller CS. If CNV GPIO is not defined
+ * gpiod_set_value_cansleep() has no effect.
+ */
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ ret = spi_sync(st->spi, &st->msg);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+
+ return ret;
+}
+
+static int ad4000_single_conversion(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val)
+{
+ struct ad4000_state *st = iio_priv(indio_dev);
+ u32 sample;
+ int ret;
+
+ ret = ad4000_convert_and_acquire(st);
+ if (ret < 0)
+ return ret;
+
+ if (chan->scan_type.storagebits > 16)
+ sample = be32_to_cpu(st->scan.data.sample_buf32);
+ else
+ sample = be16_to_cpu(st->scan.data.sample_buf16);
+
+ sample >>= chan->scan_type.shift;
+
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(sample, chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+}
+
+static int ad4000_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long info)
+{
+ struct ad4000_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
+ return ad4000_single_conversion(indio_dev, chan, val);
+ unreachable();
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->scale_tbl[st->span_comp][0];
+ *val2 = st->scale_tbl[st->span_comp][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 0;
+ if (st->span_comp)
+ *val = mult_frac(st->vref_mv, 1, 10);
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4000_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ struct ad4000_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (int *)st->scale_tbl;
+ *length = AD4000_SCALE_OPTIONS * 2;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4000_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int ad4000_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct ad4000_state *st = iio_priv(indio_dev);
+ unsigned int reg_val;
+ bool span_comp_en;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ guard(mutex)(&st->lock);
+
+ ret = ad4000_read_reg(st, &reg_val);
+ if (ret < 0)
+ return ret;
+
+ span_comp_en = val2 == st->scale_tbl[1][1];
+ reg_val &= ~AD4000_CFG_SPAN_COMP;
+ reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en);
+
+ ret = ad4000_write_reg(st, reg_val);
+ if (ret < 0)
+ return ret;
+
+ st->span_comp = span_comp_en;
+ return 0;
+ }
+ unreachable();
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t ad4000_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad4000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad4000_convert_and_acquire(st);
+ if (ret < 0)
+ goto err_out;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp);
+
+err_out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info ad4000_reg_access_info = {
+ .read_raw = &ad4000_read_raw,
+ .read_avail = &ad4000_read_avail,
+ .write_raw = &ad4000_write_raw,
+ .write_raw_get_fmt = &ad4000_write_raw_get_fmt,
+};
+
+static const struct iio_info ad4000_info = {
+ .read_raw = &ad4000_read_raw,
+};
+
+/*
+ * This executes a data sample transfer for when the device connections are
+ * in "3-wire" mode, selected when the adi,sdi-pin device tree property is
+ * absent or set to "high". In this connection mode, the ADC SDI pin is
+ * connected to MOSI or to VIO and ADC CNV pin is connected either to a SPI
+ * controller CS or to a GPIO.
+ * AD4000 series of devices initiate conversions on the rising edge of CNV pin.
+ *
+ * If the CNV pin is connected to an SPI controller CS line (which is by default
+ * active low), the ADC readings would have a latency (delay) of one read.
+ * Moreover, since we also do ADC sampling for filling the buffer on triggered
+ * buffer mode, the timestamps of buffer readings would be disarranged.
+ * To prevent the read latency and reduce the time discrepancy between the
+ * sample read request and the time of actual sampling by the ADC, do a
+ * preparatory transfer to pulse the CS/CNV line.
+ */
+static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int cnv_pulse_time = AD4000_TCONV_NS;
+ struct spi_transfer *xfers = st->xfers;
+
+ xfers[0].cs_change = 1;
+ xfers[0].cs_change_delay.value = cnv_pulse_time;
+ xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].rx_buf = &st->scan.data;
+ xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[1].delay.value = AD4000_TQUIET2_NS;
+ xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ spi_message_init_with_transfers(&st->msg, st->xfers, 2);
+
+ return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg);
+}
+
+/*
+ * This executes a data sample transfer for when the device connections are
+ * in "4-wire" mode, selected when the adi,sdi-pin device tree property is
+ * set to "cs". In this connection mode, the controller CS pin is connected to
+ * ADC SDI pin and a GPIO is connected to ADC CNV pin.
+ * The GPIO connected to ADC CNV pin is set outside of the SPI transfer.
+ */
+static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int cnv_to_sdi_time = AD4000_TCONV_NS;
+ struct spi_transfer *xfers = st->xfers;
+
+ /*
+ * Dummy transfer to cause enough delay between CNV going high and SDI
+ * going low.
+ */
+ xfers[0].cs_off = 1;
+ xfers[0].delay.value = cnv_to_sdi_time;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].rx_buf = &st->scan.data;
+ xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+
+ spi_message_init_with_transfers(&st->msg, st->xfers, 2);
+
+ return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg);
+}
+
+static int ad4000_config(struct ad4000_state *st)
+{
+ unsigned int reg_val = AD4000_CONFIG_REG_DEFAULT;
+
+ if (device_property_present(&st->spi->dev, "adi,high-z-input"))
+ reg_val |= FIELD_PREP(AD4000_CFG_HIGHZ, 1);
+
+ return ad4000_write_reg(st, reg_val);
+}
+
+static int ad4000_probe(struct spi_device *spi)
+{
+ const struct ad4000_chip_info *chip;
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad4000_state *st;
+ int gain_idx, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = spi_get_device_match_data(spi);
+ if (!chip)
+ return -EINVAL;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies),
+ ad4000_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable power supplies\n");
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get ref regulator reference\n");
+ st->vref_mv = ret / 1000;
+
+ st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_HIGH);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "Failed to get CNV GPIO");
+
+ ret = device_property_match_property_string(dev, "adi,sdi-pin",
+ ad4000_sdi_pin,
+ ARRAY_SIZE(ad4000_sdi_pin));
+ if (ret < 0 && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "getting adi,sdi-pin property failed\n");
+
+ /* Default to usual SPI connections if pin properties are not present */
+ st->sdi_pin = ret == -EINVAL ? AD4000_SDI_MOSI : ret;
+ switch (st->sdi_pin) {
+ case AD4000_SDI_MOSI:
+ indio_dev->info = &ad4000_reg_access_info;
+ indio_dev->channels = &chip->reg_access_chan_spec;
+
+ /*
+ * In "3-wire mode", the ADC SDI line must be kept high when
+ * data is not being clocked out of the controller.
+ * Request the SPI controller to make MOSI idle high.
+ */
+ spi->mode |= SPI_MOSI_IDLE_HIGH;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels);
+ if (ret)
+ return ret;
+
+ ret = ad4000_config(st);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to config device\n");
+
+ break;
+ case AD4000_SDI_VIO:
+ indio_dev->info = &ad4000_info;
+ indio_dev->channels = &chip->chan_spec;
+ ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels);
+ if (ret)
+ return ret;
+
+ break;
+ case AD4000_SDI_CS:
+ indio_dev->info = &ad4000_info;
+ indio_dev->channels = &chip->chan_spec;
+ ret = ad4000_prepare_4wire_mode_message(st, indio_dev->channels);
+ if (ret)
+ return ret;
+
+ break;
+ case AD4000_SDI_GND:
+ return dev_err_probe(dev, -EPROTONOSUPPORT,
+ "Unsupported connection mode\n");
+
+ default:
+ return dev_err_probe(dev, -EINVAL, "Unrecognized connection mode\n");
+ }
+
+ indio_dev->name = chip->dev_name;
+ indio_dev->num_channels = 1;
+
+ devm_mutex_init(dev, &st->lock);
+
+ st->gain_milli = 1000;
+ if (chip->has_hardware_gain) {
+ ret = device_property_read_u16(dev, "adi,gain-milli",
+ &st->gain_milli);
+ if (!ret) {
+ /* Match gain value from dt to one of supported gains */
+ gain_idx = find_closest(st->gain_milli, ad4000_gains,
+ ARRAY_SIZE(ad4000_gains));
+ st->gain_milli = ad4000_gains[gain_idx];
+ } else {
+ return dev_err_probe(dev, ret,
+ "Failed to read gain property\n");
+ }
+ }
+
+ ad4000_fill_scale_tbl(st, indio_dev->channels);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad4000_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad4000_id[] = {
+ { "ad4000", (kernel_ulong_t)&ad4000_chip_info },
+ { "ad4001", (kernel_ulong_t)&ad4001_chip_info },
+ { "ad4002", (kernel_ulong_t)&ad4002_chip_info },
+ { "ad4003", (kernel_ulong_t)&ad4003_chip_info },
+ { "ad4004", (kernel_ulong_t)&ad4004_chip_info },
+ { "ad4005", (kernel_ulong_t)&ad4005_chip_info },
+ { "ad4006", (kernel_ulong_t)&ad4006_chip_info },
+ { "ad4007", (kernel_ulong_t)&ad4007_chip_info },
+ { "ad4008", (kernel_ulong_t)&ad4008_chip_info },
+ { "ad4010", (kernel_ulong_t)&ad4010_chip_info },
+ { "ad4011", (kernel_ulong_t)&ad4011_chip_info },
+ { "ad4020", (kernel_ulong_t)&ad4020_chip_info },
+ { "ad4021", (kernel_ulong_t)&ad4021_chip_info },
+ { "ad4022", (kernel_ulong_t)&ad4022_chip_info },
+ { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info },
+ { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad4000_id);
+
+static const struct of_device_id ad4000_of_match[] = {
+ { .compatible = "adi,ad4000", .data = &ad4000_chip_info },
+ { .compatible = "adi,ad4001", .data = &ad4001_chip_info },
+ { .compatible = "adi,ad4002", .data = &ad4002_chip_info },
+ { .compatible = "adi,ad4003", .data = &ad4003_chip_info },
+ { .compatible = "adi,ad4004", .data = &ad4004_chip_info },
+ { .compatible = "adi,ad4005", .data = &ad4005_chip_info },
+ { .compatible = "adi,ad4006", .data = &ad4006_chip_info },
+ { .compatible = "adi,ad4007", .data = &ad4007_chip_info },
+ { .compatible = "adi,ad4008", .data = &ad4008_chip_info },
+ { .compatible = "adi,ad4010", .data = &ad4010_chip_info },
+ { .compatible = "adi,ad4011", .data = &ad4011_chip_info },
+ { .compatible = "adi,ad4020", .data = &ad4020_chip_info },
+ { .compatible = "adi,ad4021", .data = &ad4021_chip_info },
+ { .compatible = "adi,ad4022", .data = &ad4022_chip_info },
+ { .compatible = "adi,adaq4001", .data = &adaq4001_chip_info },
+ { .compatible = "adi,adaq4003", .data = &adaq4003_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad4000_of_match);
+
+static struct spi_driver ad4000_driver = {
+ .driver = {
+ .name = "ad4000",
+ .of_match_table = ad4000_of_match,
+ },
+ .probe = ad4000_probe,
+ .id_table = ad4000_id,
+};
+module_spi_driver(ad4000_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4000 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c
new file mode 100644
index 000000000000..595ec4158e73
--- /dev/null
+++ b/drivers/iio/adc/ad4695.c
@@ -0,0 +1,1185 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SPI ADC driver for Analog Devices Inc. AD4695 and similar chips
+ *
+ * https://www.analog.com/en/products/ad4695.html
+ * https://www.analog.com/en/products/ad4696.html
+ * https://www.analog.com/en/products/ad4697.html
+ * https://www.analog.com/en/products/ad4698.html
+ *
+ * Copyright 2024 Analog Devices Inc.
+ * Copyright 2024 BayLibre, SAS
+ */
+
+#include <linux/align.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/minmax.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <dt-bindings/iio/adi,ad4695.h>
+
+/* AD4695 registers */
+#define AD4695_REG_SPI_CONFIG_A 0x0000
+#define AD4695_REG_SPI_CONFIG_A_SW_RST (BIT(7) | BIT(0))
+#define AD4695_REG_SPI_CONFIG_A_ADDR_DIR BIT(5)
+#define AD4695_REG_SPI_CONFIG_B 0x0001
+#define AD4695_REG_SPI_CONFIG_B_INST_MODE BIT(7)
+#define AD4695_REG_DEVICE_TYPE 0x0003
+#define AD4695_REG_SCRATCH_PAD 0x000A
+#define AD4695_REG_VENDOR_L 0x000C
+#define AD4695_REG_VENDOR_H 0x000D
+#define AD4695_REG_LOOP_MODE 0x000E
+#define AD4695_REG_SPI_CONFIG_C 0x0010
+#define AD4695_REG_SPI_CONFIG_C_MB_STRICT BIT(7)
+#define AD4695_REG_SPI_STATUS 0x0011
+#define AD4695_REG_STATUS 0x0014
+#define AD4695_REG_ALERT_STATUS1 0x0015
+#define AD4695_REG_ALERT_STATUS2 0x0016
+#define AD4695_REG_CLAMP_STATUS 0x001A
+#define AD4695_REG_SETUP 0x0020
+#define AD4695_REG_SETUP_LDO_EN BIT(4)
+#define AD4695_REG_SETUP_SPI_MODE BIT(2)
+#define AD4695_REG_SETUP_SPI_CYC_CTRL BIT(1)
+#define AD4695_REG_REF_CTRL 0x0021
+#define AD4695_REG_REF_CTRL_OV_MODE BIT(7)
+#define AD4695_REG_REF_CTRL_VREF_SET GENMASK(4, 2)
+#define AD4695_REG_REF_CTRL_REFHIZ_EN BIT(1)
+#define AD4695_REG_REF_CTRL_REFBUF_EN BIT(0)
+#define AD4695_REG_SEQ_CTRL 0x0022
+#define AD4695_REG_SEQ_CTRL_STD_SEQ_EN BIT(7)
+#define AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS GENMASK(6, 0)
+#define AD4695_REG_AC_CTRL 0x0023
+#define AD4695_REG_STD_SEQ_CONFIG 0x0024
+#define AD4695_REG_GPIO_CTRL 0x0026
+#define AD4695_REG_GP_MODE 0x0027
+#define AD4695_REG_TEMP_CTRL 0x0029
+#define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0)
+#define AD4695_REG_CONFIG_IN(n) (0x0030 | (n))
+#define AD4695_REG_CONFIG_IN_MODE BIT(6)
+#define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4)
+#define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3)
+#define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n)))
+#define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n)))
+#define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n)))
+#define AD4695_REG_OFFSET_IN(n) (0x00A0 | (2 * (n)))
+#define AD4695_REG_GAIN_IN(n) (0x00C0 | (2 * (n)))
+#define AD4695_REG_AS_SLOT(n) (0x0100 | (n))
+#define AD4695_REG_AS_SLOT_INX GENMASK(3, 0)
+
+/* Conversion mode commands */
+#define AD4695_CMD_EXIT_CNV_MODE 0x0A
+#define AD4695_CMD_TEMP_CHAN 0x0F
+#define AD4695_CMD_VOLTAGE_CHAN(n) (0x10 | (n))
+
+/* timing specs */
+#define AD4695_T_CONVERT_NS 415
+#define AD4695_T_WAKEUP_HW_MS 3
+#define AD4695_T_WAKEUP_SW_MS 3
+#define AD4695_T_REFBUF_MS 100
+#define AD4695_T_REGCONFIG_NS 20
+#define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA)
+
+/* Max number of voltage input channels. */
+#define AD4695_MAX_CHANNELS 16
+/* Max size of 1 raw sample in bytes. */
+#define AD4695_MAX_CHANNEL_SIZE 2
+
+enum ad4695_in_pair {
+ AD4695_IN_PAIR_REFGND,
+ AD4695_IN_PAIR_COM,
+ AD4695_IN_PAIR_EVEN_ODD,
+};
+
+struct ad4695_chip_info {
+ const char *name;
+ int max_sample_rate;
+ u32 t_acq_ns;
+ u8 num_voltage_inputs;
+};
+
+struct ad4695_channel_config {
+ unsigned int channel;
+ bool highz_en;
+ bool bipolar;
+ enum ad4695_in_pair pin_pairing;
+ unsigned int common_mode_mv;
+};
+
+struct ad4695_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regmap *regmap16;
+ struct gpio_desc *reset_gpio;
+ /* voltages channels plus temperature and timestamp */
+ struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2];
+ struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS];
+ const struct ad4695_chip_info *chip_info;
+ /* Reference voltage. */
+ unsigned int vref_mv;
+ /* Common mode input pin voltage. */
+ unsigned int com_mv;
+ /* 1 per voltage and temperature chan plus 1 xfer to trigger 1st CNV */
+ struct spi_transfer buf_read_xfer[AD4695_MAX_CHANNELS + 2];
+ struct spi_message buf_read_msg;
+ /* Raw conversion data received. */
+ u8 buf[ALIGN((AD4695_MAX_CHANNELS + 2) * AD4695_MAX_CHANNEL_SIZE,
+ sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
+ u16 raw_data;
+ /* Commands to send for single conversion. */
+ u16 cnv_cmd;
+ u8 cnv_cmd2;
+};
+
+static const struct regmap_range ad4695_regmap_rd_ranges[] = {
+ regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B),
+ regmap_reg_range(AD4695_REG_DEVICE_TYPE, AD4695_REG_DEVICE_TYPE),
+ regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD),
+ regmap_reg_range(AD4695_REG_VENDOR_L, AD4695_REG_LOOP_MODE),
+ regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS),
+ regmap_reg_range(AD4695_REG_STATUS, AD4695_REG_ALERT_STATUS2),
+ regmap_reg_range(AD4695_REG_CLAMP_STATUS, AD4695_REG_CLAMP_STATUS),
+ regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL),
+ regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL),
+ regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)),
+ regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)),
+};
+
+static const struct regmap_access_table ad4695_regmap_rd_table = {
+ .yes_ranges = ad4695_regmap_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4695_regmap_rd_ranges),
+};
+
+static const struct regmap_range ad4695_regmap_wr_ranges[] = {
+ regmap_reg_range(AD4695_REG_SPI_CONFIG_A, AD4695_REG_SPI_CONFIG_B),
+ regmap_reg_range(AD4695_REG_SCRATCH_PAD, AD4695_REG_SCRATCH_PAD),
+ regmap_reg_range(AD4695_REG_LOOP_MODE, AD4695_REG_LOOP_MODE),
+ regmap_reg_range(AD4695_REG_SPI_CONFIG_C, AD4695_REG_SPI_STATUS),
+ regmap_reg_range(AD4695_REG_SETUP, AD4695_REG_AC_CTRL),
+ regmap_reg_range(AD4695_REG_GPIO_CTRL, AD4695_REG_TEMP_CTRL),
+ regmap_reg_range(AD4695_REG_CONFIG_IN(0), AD4695_REG_CONFIG_IN(15)),
+ regmap_reg_range(AD4695_REG_AS_SLOT(0), AD4695_REG_AS_SLOT(127)),
+};
+
+static const struct regmap_access_table ad4695_regmap_wr_table = {
+ .yes_ranges = ad4695_regmap_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4695_regmap_wr_ranges),
+};
+
+static const struct regmap_config ad4695_regmap_config = {
+ .name = "ad4695-8",
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = AD4695_REG_AS_SLOT(127),
+ .rd_table = &ad4695_regmap_rd_table,
+ .wr_table = &ad4695_regmap_wr_table,
+ .can_multi_write = true,
+};
+
+static const struct regmap_range ad4695_regmap16_rd_ranges[] = {
+ regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG),
+ regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)),
+};
+
+static const struct regmap_access_table ad4695_regmap16_rd_table = {
+ .yes_ranges = ad4695_regmap16_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_rd_ranges),
+};
+
+static const struct regmap_range ad4695_regmap16_wr_ranges[] = {
+ regmap_reg_range(AD4695_REG_STD_SEQ_CONFIG, AD4695_REG_STD_SEQ_CONFIG),
+ regmap_reg_range(AD4695_REG_UPPER_IN(0), AD4695_REG_GAIN_IN(15)),
+};
+
+static const struct regmap_access_table ad4695_regmap16_wr_table = {
+ .yes_ranges = ad4695_regmap16_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4695_regmap16_wr_ranges),
+};
+
+static const struct regmap_config ad4695_regmap16_config = {
+ .name = "ad4695-16",
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .max_register = AD4695_REG_GAIN_IN(15),
+ .rd_table = &ad4695_regmap16_rd_table,
+ .wr_table = &ad4695_regmap16_wr_table,
+ .can_multi_write = true,
+};
+
+static const struct iio_chan_spec ad4695_channel_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+};
+
+static const struct iio_chan_spec ad4695_temp_channel_template = {
+ .address = AD4695_CMD_TEMP_CHAN,
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+};
+
+static const struct iio_chan_spec ad4695_soft_timestamp_channel_template =
+ IIO_CHAN_SOFT_TIMESTAMP(0);
+
+static const char * const ad4695_power_supplies[] = {
+ "avdd", "vio"
+};
+
+static const struct ad4695_chip_info ad4695_chip_info = {
+ .name = "ad4695",
+ .max_sample_rate = 500 * KILO,
+ .t_acq_ns = 1715,
+ .num_voltage_inputs = 16,
+};
+
+static const struct ad4695_chip_info ad4696_chip_info = {
+ .name = "ad4696",
+ .max_sample_rate = 1 * MEGA,
+ .t_acq_ns = 715,
+ .num_voltage_inputs = 16,
+};
+
+static const struct ad4695_chip_info ad4697_chip_info = {
+ .name = "ad4697",
+ .max_sample_rate = 500 * KILO,
+ .t_acq_ns = 1715,
+ .num_voltage_inputs = 8,
+};
+
+static const struct ad4695_chip_info ad4698_chip_info = {
+ .name = "ad4698",
+ .max_sample_rate = 1 * MEGA,
+ .t_acq_ns = 715,
+ .num_voltage_inputs = 8,
+};
+
+/**
+ * ad4695_set_single_cycle_mode - Set the device in single cycle mode
+ * @st: The AD4695 state
+ * @channel: The first channel to read
+ *
+ * As per the datasheet, to enable single cycle mode, we need to set
+ * STD_SEQ_EN=0, NUM_SLOTS_AS=0 and CYC_CTRL=1 (Table 15). Setting SPI_MODE=1
+ * triggers the first conversion using the channel in AS_SLOT0.
+ *
+ * Context: can sleep, must be called with iio_device_claim_direct held
+ * Return: 0 on success, a negative error code on failure
+ */
+static int ad4695_set_single_cycle_mode(struct ad4695_state *st,
+ unsigned int channel)
+{
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD4695_REG_SEQ_CTRL,
+ AD4695_REG_SEQ_CTRL_STD_SEQ_EN |
+ AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(0),
+ FIELD_PREP(AD4695_REG_AS_SLOT_INX, channel));
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AD4695_REG_SETUP,
+ AD4695_REG_SETUP_SPI_MODE |
+ AD4695_REG_SETUP_SPI_CYC_CTRL);
+}
+
+/**
+ * ad4695_enter_advanced_sequencer_mode - Put the ADC in advanced sequencer mode
+ * @st: The driver state
+ * @n: The number of slots to use - must be >= 2, <= 128
+ *
+ * As per the datasheet, to enable advanced sequencer, we need to set
+ * STD_SEQ_EN=0, NUM_SLOTS_AS=n-1 and CYC_CTRL=0 (Table 15). Setting SPI_MODE=1
+ * triggers the first conversion using the channel in AS_SLOT0.
+ *
+ * Return: 0 on success, a negative error code on failure
+ */
+static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n)
+{
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, AD4695_REG_SEQ_CTRL,
+ AD4695_REG_SEQ_CTRL_STD_SEQ_EN |
+ AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS,
+ FIELD_PREP(AD4695_REG_SEQ_CTRL_STD_SEQ_EN, 0) |
+ FIELD_PREP(AD4695_REG_SEQ_CTRL_NUM_SLOTS_AS, n - 1));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD4695_REG_SETUP,
+ AD4695_REG_SETUP_SPI_MODE | AD4695_REG_SETUP_SPI_CYC_CTRL,
+ FIELD_PREP(AD4695_REG_SETUP_SPI_MODE, 1) |
+ FIELD_PREP(AD4695_REG_SETUP_SPI_CYC_CTRL, 0));
+}
+
+/**
+ * ad4695_exit_conversion_mode - Exit conversion mode
+ * @st: The AD4695 state
+ *
+ * Sends SPI command to exit conversion mode.
+ *
+ * Return: 0 on success, a negative error code on failure
+ */
+static int ad4695_exit_conversion_mode(struct ad4695_state *st)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &st->cnv_cmd2,
+ .len = 1,
+ .delay.value = AD4695_T_REGCONFIG_NS,
+ .delay.unit = SPI_DELAY_UNIT_NSECS,
+ };
+
+ /*
+ * Technically, could do a 5-bit transfer, but shifting to start of
+ * 8 bits instead for better SPI controller support.
+ */
+ st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3;
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv)
+{
+ u8 val;
+
+ if (vref_mv >= 2400 && vref_mv <= 2750)
+ val = 0;
+ else if (vref_mv > 2750 && vref_mv <= 3250)
+ val = 1;
+ else if (vref_mv > 3250 && vref_mv <= 3750)
+ val = 2;
+ else if (vref_mv > 3750 && vref_mv <= 4500)
+ val = 3;
+ else if (vref_mv > 4500 && vref_mv <= 5100)
+ val = 4;
+ else
+ return -EINVAL;
+
+ return regmap_update_bits(st->regmap, AD4695_REG_REF_CTRL,
+ AD4695_REG_REF_CTRL_VREF_SET,
+ FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val));
+}
+
+static int ad4695_write_chn_cfg(struct ad4695_state *st,
+ struct ad4695_channel_config *cfg)
+{
+ u32 mask, val;
+
+ mask = AD4695_REG_CONFIG_IN_MODE;
+ val = FIELD_PREP(AD4695_REG_CONFIG_IN_MODE, cfg->bipolar ? 1 : 0);
+
+ mask |= AD4695_REG_CONFIG_IN_PAIR;
+ val |= FIELD_PREP(AD4695_REG_CONFIG_IN_PAIR, cfg->pin_pairing);
+
+ mask |= AD4695_REG_CONFIG_IN_AINHIGHZ_EN;
+ val |= FIELD_PREP(AD4695_REG_CONFIG_IN_AINHIGHZ_EN,
+ cfg->highz_en ? 1 : 0);
+
+ return regmap_update_bits(st->regmap,
+ AD4695_REG_CONFIG_IN(cfg->channel),
+ mask, val);
+}
+
+static int ad4695_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ struct spi_transfer *xfer;
+ u8 temp_chan_bit = st->chip_info->num_voltage_inputs;
+ u32 bit, num_xfer, num_slots;
+ u32 temp_en = 0;
+ int ret;
+
+ /*
+ * We are using the advanced sequencer since it is the only way to read
+ * multiple channels that allows individual configuration of each
+ * voltage input channel. Slot 0 in the advanced sequencer is used to
+ * account for the gap between trigger polls - we don't read data from
+ * this slot. Each enabled voltage channel is assigned a slot starting
+ * with slot 1.
+ */
+ num_slots = 1;
+
+ memset(st->buf_read_xfer, 0, sizeof(st->buf_read_xfer));
+
+ /* First xfer is only to trigger conversion of slot 1, so no rx. */
+ xfer = &st->buf_read_xfer[0];
+ xfer->cs_change = 1;
+ xfer->delay.value = st->chip_info->t_acq_ns;
+ xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
+ xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ num_xfer = 1;
+
+ iio_for_each_active_channel(indio_dev, bit) {
+ xfer = &st->buf_read_xfer[num_xfer];
+ xfer->bits_per_word = 16;
+ xfer->rx_buf = &st->buf[(num_xfer - 1) * 2];
+ xfer->len = 2;
+ xfer->cs_change = 1;
+ xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
+ xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ if (bit == temp_chan_bit) {
+ temp_en = 1;
+ } else {
+ ret = regmap_write(st->regmap,
+ AD4695_REG_AS_SLOT(num_slots),
+ FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit));
+ if (ret)
+ return ret;
+
+ num_slots++;
+ }
+
+ num_xfer++;
+ }
+
+ /*
+ * The advanced sequencer requires that at least 2 slots are enabled.
+ * Since slot 0 is always used for other purposes, we need only 1
+ * enabled voltage channel to meet this requirement. If the temperature
+ * channel is the only enabled channel, we need to add one more slot
+ * in the sequence but not read from it.
+ */
+ if (num_slots < 2) {
+ /* move last xfer so we can insert one more xfer before it */
+ st->buf_read_xfer[num_xfer] = *xfer;
+ num_xfer++;
+
+ /* modify 2nd to last xfer for extra slot */
+ memset(xfer, 0, sizeof(*xfer));
+ xfer->cs_change = 1;
+ xfer->delay.value = st->chip_info->t_acq_ns;
+ xfer->delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer->cs_change_delay.value = AD4695_T_CONVERT_NS;
+ xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer++;
+
+ /* and add the extra slot in the sequencer */
+ ret = regmap_write(st->regmap,
+ AD4695_REG_AS_SLOT(num_slots),
+ FIELD_PREP(AD4695_REG_AS_SLOT_INX, 0));
+ if (ret)
+ return ret;
+
+ num_slots++;
+ }
+
+ /*
+ * Don't keep CS asserted after last xfer. Also triggers conversion of
+ * slot 0.
+ */
+ xfer->cs_change = 0;
+
+ /*
+ * Temperature channel isn't included in the sequence, but rather
+ * controlled by setting a bit in the TEMP_CTRL register.
+ */
+
+ ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL,
+ AD4695_REG_TEMP_CTRL_TEMP_EN,
+ FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, temp_en));
+ if (ret)
+ return ret;
+
+ spi_message_init_with_transfers(&st->buf_read_msg, st->buf_read_xfer,
+ num_xfer);
+
+ ret = spi_optimize_message(st->spi, &st->buf_read_msg);
+ if (ret)
+ return ret;
+
+ /* This triggers conversion of slot 0. */
+ ret = ad4695_enter_advanced_sequencer_mode(st, num_slots);
+ if (ret)
+ spi_unoptimize_message(&st->buf_read_msg);
+
+ return ret;
+}
+
+static int ad4695_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad4695_exit_conversion_mode(st);
+ if (ret)
+ return ret;
+
+ spi_unoptimize_message(&st->buf_read_msg);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad4695_buffer_setup_ops = {
+ .preenable = ad4695_buffer_preenable,
+ .postdisable = ad4695_buffer_postdisable,
+};
+
+static irqreturn_t ad4695_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad4695_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_sync(st->spi, &st->buf_read_msg);
+ if (ret)
+ goto out;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->buf, pf->timestamp);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ad4695_read_one_sample - Read a single sample using single-cycle mode
+ * @st: The AD4695 state
+ * @address: The address of the channel to read
+ *
+ * Upon successful return, the sample will be stored in `st->raw_data`.
+ *
+ * Context: can sleep, must be called with iio_device_claim_direct held
+ * Return: 0 on success, a negative error code on failure
+ */
+static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address)
+{
+ struct spi_transfer xfer[2] = { };
+ int ret, i = 0;
+
+ ret = ad4695_set_single_cycle_mode(st, address);
+ if (ret)
+ return ret;
+
+ /*
+ * Setting the first channel to the temperature channel isn't supported
+ * in single-cycle mode, so we have to do an extra xfer to read the
+ * temperature.
+ */
+ if (address == AD4695_CMD_TEMP_CHAN) {
+ /* We aren't reading, so we can make this a short xfer. */
+ st->cnv_cmd2 = AD4695_CMD_TEMP_CHAN << 3;
+ xfer[0].tx_buf = &st->cnv_cmd2;
+ xfer[0].len = 1;
+ xfer[0].cs_change = 1;
+ xfer[0].cs_change_delay.value = AD4695_T_CONVERT_NS;
+ xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ i = 1;
+ }
+
+ /* Then read the result and exit conversion mode. */
+ st->cnv_cmd = AD4695_CMD_EXIT_CNV_MODE << 11;
+ xfer[i].bits_per_word = 16;
+ xfer[i].tx_buf = &st->cnv_cmd;
+ xfer[i].rx_buf = &st->raw_data;
+ xfer[i].len = 2;
+
+ return spi_sync_transfer(st->spi, xfer, i + 1);
+}
+
+static int ad4695_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index];
+ u8 realbits = chan->scan_type.realbits;
+ unsigned int reg_val;
+ int ret, tmp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ ret = ad4695_read_one_sample(st, chan->address);
+ if (ret)
+ return ret;
+
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(st->raw_data, realbits - 1);
+ else
+ *val = st->raw_data;
+
+ return IIO_VAL_INT;
+ }
+ unreachable();
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ /* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */
+ *val = st->vref_mv * -556;
+ *val2 = 16;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (cfg->pin_pairing == AD4695_IN_PAIR_COM)
+ *val = st->com_mv * (1 << realbits) / st->vref_mv;
+ else if (cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD)
+ *val = cfg->common_mode_mv * (1 << realbits) / st->vref_mv;
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ /* T_offset (°C) = -725 mV / (-1.8 mV/°C) */
+ /* T_offset (raw) = T_offset (°C) * (-1.8 mV/°C) * 2^16 / V_REF (mV) */
+ *val = -47513600;
+ *val2 = st->vref_mv;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBSCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ ret = regmap_read(st->regmap16,
+ AD4695_REG_GAIN_IN(chan->scan_index),
+ &reg_val);
+ if (ret)
+ return ret;
+
+ *val = reg_val;
+ *val2 = 15;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ unreachable();
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ ret = regmap_read(st->regmap16,
+ AD4695_REG_OFFSET_IN(chan->scan_index),
+ &reg_val);
+ if (ret)
+ return ret;
+
+ tmp = sign_extend32(reg_val, 15);
+
+ *val = tmp / 4;
+ *val2 = abs(tmp) % 4 * MICRO / 4;
+
+ if (tmp < 0 && *val2) {
+ *val *= -1;
+ *val2 *= -1;
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ unreachable();
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4695_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ unsigned int reg_val;
+
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (val < 0 || val2 < 0)
+ reg_val = 0;
+ else if (val > 1)
+ reg_val = U16_MAX;
+ else
+ reg_val = (val * (1 << 16) +
+ mul_u64_u32_div(val2, 1 << 16,
+ MICRO)) / 2;
+
+ return regmap_write(st->regmap16,
+ AD4695_REG_GAIN_IN(chan->scan_index),
+ reg_val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (val2 >= 0 && val > S16_MAX / 4)
+ reg_val = S16_MAX;
+ else if ((val2 < 0 ? -val : val) < S16_MIN / 4)
+ reg_val = S16_MIN;
+ else if (val2 < 0)
+ reg_val = clamp_t(int,
+ -(val * 4 + -val2 * 4 / MICRO),
+ S16_MIN, S16_MAX);
+ else if (val < 0)
+ reg_val = clamp_t(int,
+ val * 4 - val2 * 4 / MICRO,
+ S16_MIN, S16_MAX);
+ else
+ reg_val = clamp_t(int,
+ val * 4 + val2 * 4 / MICRO,
+ S16_MIN, S16_MAX);
+
+ return regmap_write(st->regmap16,
+ AD4695_REG_OFFSET_IN(chan->scan_index),
+ reg_val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ unreachable();
+}
+
+static int ad4695_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ static const int ad4695_calibscale_available[6] = {
+ /* Range of 0 (inclusive) to 2 (exclusive) */
+ 0, 15, 1, 15, U16_MAX, 15
+ };
+ static const int ad4695_calibbias_available[6] = {
+ /*
+ * Datasheet says FSR/8 which translates to signed/4. The step
+ * depends on oversampling ratio which is always 1 for now.
+ */
+ S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 * MICRO / 4
+ };
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *vals = ad4695_calibscale_available;
+ *type = IIO_VAL_FRACTIONAL_LOG2;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *vals = ad4695_calibbias_available;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+
+ iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
+ if (readval) {
+ if (regmap_check_range_table(st->regmap, reg,
+ &ad4695_regmap_rd_table))
+ return regmap_read(st->regmap, reg, readval);
+ if (regmap_check_range_table(st->regmap16, reg,
+ &ad4695_regmap16_rd_table))
+ return regmap_read(st->regmap16, reg, readval);
+ } else {
+ if (regmap_check_range_table(st->regmap, reg,
+ &ad4695_regmap_wr_table))
+ return regmap_write(st->regmap, reg, writeval);
+ if (regmap_check_range_table(st->regmap16, reg,
+ &ad4695_regmap16_wr_table))
+ return regmap_write(st->regmap16, reg, writeval);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad4695_info = {
+ .read_raw = &ad4695_read_raw,
+ .write_raw = &ad4695_write_raw,
+ .read_avail = &ad4695_read_avail,
+ .debugfs_reg_access = &ad4695_debugfs_reg_access,
+};
+
+static int ad4695_parse_channel_cfg(struct ad4695_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct ad4695_channel_config *chan_cfg;
+ struct iio_chan_spec *iio_chan;
+ int ret, i;
+
+ /* populate defaults */
+ for (i = 0; i < st->chip_info->num_voltage_inputs; i++) {
+ chan_cfg = &st->channels_cfg[i];
+ iio_chan = &st->iio_chan[i];
+
+ chan_cfg->highz_en = true;
+ chan_cfg->channel = i;
+
+ *iio_chan = ad4695_channel_template;
+ iio_chan->channel = i;
+ iio_chan->scan_index = i;
+ iio_chan->address = AD4695_CMD_VOLTAGE_CHAN(i);
+ }
+
+ /* modify based on firmware description */
+ device_for_each_child_node_scoped(dev, child) {
+ u32 reg, val;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read reg property (%s)\n",
+ fwnode_get_name(child));
+
+ if (reg >= st->chip_info->num_voltage_inputs)
+ return dev_err_probe(dev, -EINVAL,
+ "reg out of range (%s)\n",
+ fwnode_get_name(child));
+
+ iio_chan = &st->iio_chan[reg];
+ chan_cfg = &st->channels_cfg[reg];
+
+ chan_cfg->highz_en =
+ !fwnode_property_read_bool(child, "adi,no-high-z");
+ chan_cfg->bipolar = fwnode_property_read_bool(child, "bipolar");
+
+ ret = fwnode_property_read_u32(child, "common-mode-channel",
+ &val);
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "failed to read common-mode-channel (%s)\n",
+ fwnode_get_name(child));
+
+ if (ret == -EINVAL || val == AD4695_COMMON_MODE_REFGND)
+ chan_cfg->pin_pairing = AD4695_IN_PAIR_REFGND;
+ else if (val == AD4695_COMMON_MODE_COM)
+ chan_cfg->pin_pairing = AD4695_IN_PAIR_COM;
+ else
+ chan_cfg->pin_pairing = AD4695_IN_PAIR_EVEN_ODD;
+
+ if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD &&
+ val % 2 == 0)
+ return dev_err_probe(dev, -EINVAL,
+ "common-mode-channel must be odd number (%s)\n",
+ fwnode_get_name(child));
+
+ if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD &&
+ val != reg + 1)
+ return dev_err_probe(dev, -EINVAL,
+ "common-mode-channel must be next consecutive channel (%s)\n",
+ fwnode_get_name(child));
+
+ if (chan_cfg->pin_pairing == AD4695_IN_PAIR_EVEN_ODD) {
+ char name[5];
+
+ snprintf(name, sizeof(name), "in%d", reg + 1);
+
+ ret = devm_regulator_get_enable_read_voltage(dev, name);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get %s voltage (%s)\n",
+ name, fwnode_get_name(child));
+
+ chan_cfg->common_mode_mv = ret / 1000;
+ }
+
+ if (chan_cfg->bipolar &&
+ chan_cfg->pin_pairing == AD4695_IN_PAIR_REFGND)
+ return dev_err_probe(dev, -EINVAL,
+ "bipolar mode is not available for inputs paired with REFGND (%s).\n",
+ fwnode_get_name(child));
+
+ if (chan_cfg->bipolar)
+ iio_chan->scan_type.sign = 's';
+
+ ret = ad4695_write_chn_cfg(st, chan_cfg);
+ if (ret)
+ return ret;
+ }
+
+ /* Temperature channel must be next scan index after voltage channels. */
+ st->iio_chan[i] = ad4695_temp_channel_template;
+ st->iio_chan[i].scan_index = i;
+ i++;
+
+ st->iio_chan[i] = ad4695_soft_timestamp_channel_template;
+ st->iio_chan[i].scan_index = i;
+
+ return 0;
+}
+
+static int ad4695_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ad4695_state *st;
+ struct iio_dev *indio_dev;
+ struct gpio_desc *cnv_gpio;
+ bool use_internal_ldo_supply;
+ bool use_internal_ref_buffer;
+ int ret;
+
+ cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(cnv_gpio),
+ "Failed to get CNV GPIO\n");
+
+ /* Driver currently requires CNV pin to be connected to SPI CS */
+ if (cnv_gpio)
+ return dev_err_probe(dev, -ENODEV,
+ "CNV GPIO is not supported\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return -EINVAL;
+
+ /* Registers cannot be read at the max allowable speed */
+ spi->max_speed_hz = AD4695_REG_ACCESS_SCLK_HZ;
+
+ st->regmap = devm_regmap_init_spi(spi, &ad4695_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap\n");
+
+ st->regmap16 = devm_regmap_init_spi(spi, &ad4695_regmap16_config);
+ if (IS_ERR(st->regmap16))
+ return dev_err_probe(dev, PTR_ERR(st->regmap16),
+ "Failed to initialize regmap16\n");
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(ad4695_power_supplies),
+ ad4695_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable power supplies\n");
+
+ /* If LDO_IN supply is present, then we are using internal LDO. */
+ ret = devm_regulator_get_enable_optional(dev, "ldo-in");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "Failed to enable LDO_IN supply\n");
+
+ use_internal_ldo_supply = ret == 0;
+
+ if (!use_internal_ldo_supply) {
+ /* Otherwise we need an external VDD supply. */
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to enable VDD supply\n");
+ }
+
+ /* If REFIN supply is given, then we are using internal buffer */
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get REFIN voltage\n");
+
+ if (ret != -ENODEV) {
+ st->vref_mv = ret / 1000;
+ use_internal_ref_buffer = true;
+ } else {
+ /* Otherwise, we need an external reference. */
+ ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get REF voltage\n");
+
+ st->vref_mv = ret / 1000;
+ use_internal_ref_buffer = false;
+ }
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "com");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get COM voltage\n");
+
+ st->com_mv = ret == -ENODEV ? 0 : ret / 1000;
+
+ /*
+ * Reset the device using hardware reset if available or fall back to
+ * software reset.
+ */
+
+ st->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(st->reset_gpio))
+ return PTR_ERR(st->reset_gpio);
+
+ if (st->reset_gpio) {
+ gpiod_set_value(st->reset_gpio, 0);
+ msleep(AD4695_T_WAKEUP_HW_MS);
+ } else {
+ ret = regmap_write(st->regmap, AD4695_REG_SPI_CONFIG_A,
+ AD4695_REG_SPI_CONFIG_A_SW_RST);
+ if (ret)
+ return ret;
+
+ msleep(AD4695_T_WAKEUP_SW_MS);
+ }
+
+ /* Needed for regmap16 to be able to work correctly. */
+ ret = regmap_set_bits(st->regmap, AD4695_REG_SPI_CONFIG_A,
+ AD4695_REG_SPI_CONFIG_A_ADDR_DIR);
+ if (ret)
+ return ret;
+
+ /* Disable internal LDO if it isn't needed. */
+ ret = regmap_update_bits(st->regmap, AD4695_REG_SETUP,
+ AD4695_REG_SETUP_LDO_EN,
+ FIELD_PREP(AD4695_REG_SETUP_LDO_EN,
+ use_internal_ldo_supply ? 1 : 0));
+ if (ret)
+ return ret;
+
+ /* configure reference supply */
+
+ if (device_property_present(dev, "adi,no-ref-current-limit")) {
+ ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL,
+ AD4695_REG_REF_CTRL_OV_MODE);
+ if (ret)
+ return ret;
+ }
+
+ if (device_property_present(dev, "adi,no-ref-high-z")) {
+ if (use_internal_ref_buffer)
+ return dev_err_probe(dev, -EINVAL,
+ "Cannot disable high-Z mode for internal reference buffer\n");
+
+ ret = regmap_clear_bits(st->regmap, AD4695_REG_REF_CTRL,
+ AD4695_REG_REF_CTRL_REFHIZ_EN);
+ if (ret)
+ return ret;
+ }
+
+ ret = ad4695_set_ref_voltage(st, st->vref_mv);
+ if (ret)
+ return ret;
+
+ if (use_internal_ref_buffer) {
+ ret = regmap_set_bits(st->regmap, AD4695_REG_REF_CTRL,
+ AD4695_REG_REF_CTRL_REFBUF_EN);
+ if (ret)
+ return ret;
+
+ /* Give the capacitor some time to charge up. */
+ msleep(AD4695_T_REFBUF_MS);
+ }
+
+ ret = ad4695_parse_channel_cfg(st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->chip_info->name;
+ indio_dev->info = &ad4695_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->iio_chan;
+ indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad4695_trigger_handler,
+ &ad4695_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad4695_spi_id_table[] = {
+ { .name = "ad4695", .driver_data = (kernel_ulong_t)&ad4695_chip_info },
+ { .name = "ad4696", .driver_data = (kernel_ulong_t)&ad4696_chip_info },
+ { .name = "ad4697", .driver_data = (kernel_ulong_t)&ad4697_chip_info },
+ { .name = "ad4698", .driver_data = (kernel_ulong_t)&ad4698_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad4695_spi_id_table);
+
+static const struct of_device_id ad4695_of_match_table[] = {
+ { .compatible = "adi,ad4695", .data = &ad4695_chip_info, },
+ { .compatible = "adi,ad4696", .data = &ad4696_chip_info, },
+ { .compatible = "adi,ad4697", .data = &ad4697_chip_info, },
+ { .compatible = "adi,ad4698", .data = &ad4698_chip_info, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad4695_of_match_table);
+
+static struct spi_driver ad4695_driver = {
+ .driver = {
+ .name = "ad4695",
+ .of_match_table = ad4695_of_match_table,
+ },
+ .probe = ad4695_probe,
+ .id_table = ad4695_spi_id_table,
+};
+module_spi_driver(ad4695_driver);
+
+MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
+MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
+MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c
index a75837334157..1b59708abf30 100644
--- a/drivers/iio/adc/ad7091r5.c
+++ b/drivers/iio/adc/ad7091r5.c
@@ -112,13 +112,13 @@ static int ad7091r5_i2c_probe(struct i2c_client *i2c)
static const struct of_device_id ad7091r5_dt_ids[] = {
{ .compatible = "adi,ad7091r5", .data = &ad7091r5_init_info },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids);
static const struct i2c_device_id ad7091r5_i2c_ids[] = {
- {"ad7091r5", (kernel_ulong_t)&ad7091r5_init_info },
- {}
+ { "ad7091r5", (kernel_ulong_t)&ad7091r5_init_info },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids);
diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c
index 700564305057..c9e014d6a77c 100644
--- a/drivers/iio/adc/ad7091r8.c
+++ b/drivers/iio/adc/ad7091r8.c
@@ -159,7 +159,7 @@ static int ad7091r_regmap_bus_reg_write(void *context, unsigned int reg,
return spi_write(spi, &st->tx_buf, 2);
}
-static struct regmap_bus ad7091r8_regmap_bus = {
+static const 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,
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 108e9ccab1ef..a5d91933f505 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -382,8 +382,7 @@ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channe
cfg->vref_mv = 2500;
st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK;
st->adc_control |= AD7124_ADC_CTRL_REF_EN(1);
- return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL,
- 2, st->adc_control);
+ return 0;
default:
dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
return -EINVAL;
@@ -401,24 +400,17 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co
tmp = (cfg->buf_positive << 1) + cfg->buf_negative;
val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) |
- AD7124_CONFIG_IN_BUFF(tmp);
- ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val);
- if (ret < 0)
- return ret;
+ AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits);
- tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type);
- ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_TYPE_MSK,
- tmp, 3);
- if (ret < 0)
- return ret;
-
- ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_FS_MSK,
- AD7124_FILTER_FS(cfg->odr_sel_bits), 3);
+ ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val);
if (ret < 0)
return ret;
- return ad7124_spi_write_mask(st, AD7124_CONFIG(cfg->cfg_slot), AD7124_CONFIG_PGA_MSK,
- AD7124_CONFIG_PGA(cfg->pga_bits), 2);
+ tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type) |
+ AD7124_FILTER_FS(cfg->odr_sel_bits);
+ return ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot),
+ AD7124_FILTER_TYPE_MSK | AD7124_FILTER_FS_MSK,
+ tmp, 3);
}
static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st)
@@ -907,9 +899,9 @@ static int ad7124_setup(struct ad7124_state *st)
/* Set the power mode */
st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode);
- ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
- if (ret < 0)
- return ret;
+
+ st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_MODE(AD_SD_MODE_IDLE);
mutex_init(&st->cfgs_lock);
INIT_KFIFO(st->live_cfgs_fifo);
@@ -927,6 +919,10 @@ static int ad7124_setup(struct ad7124_state *st)
ad7124_set_channel_odr(st, i, 10);
}
+ ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+ if (ret < 0)
+ return ret;
+
return ret;
}
@@ -1016,14 +1012,14 @@ static const struct of_device_id ad7124_of_match[] = {
.data = &ad7124_chip_info_tbl[ID_AD7124_4], },
{ .compatible = "adi,ad7124-8",
.data = &ad7124_chip_info_tbl[ID_AD7124_8], },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ad7124_of_match);
static const struct spi_device_id ad71124_ids[] = {
{ "ad7124-4", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_4] },
{ "ad7124-8", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_8] },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, ad71124_ids);
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index 334ab90991d4..7042ddfdfc03 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -8,6 +8,7 @@
#include <linux/interrupt.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -201,6 +202,7 @@ struct ad7192_chip_info {
struct ad7192_state {
const struct ad7192_chip_info *chip_info;
struct clk *mclk;
+ struct clk_hw int_clk_hw;
u16 int_vref_mv;
u32 aincom_mv;
u32 fclk;
@@ -284,7 +286,7 @@ static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = {
&ad7192_syscalib_mode_enum),
IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
&ad7192_syscalib_mode_enum),
- {}
+ { }
};
static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd)
@@ -396,25 +398,162 @@ static inline bool ad7192_valid_external_frequency(u32 freq)
freq <= AD7192_EXT_FREQ_MHZ_MAX);
}
-static int ad7192_clock_select(struct ad7192_state *st)
+/*
+ * Position 0 of ad7192_clock_names, xtal, corresponds to clock source
+ * configuration AD7192_CLK_EXT_MCLK1_2 and position 1, mclk, corresponds to
+ * AD7192_CLK_EXT_MCLK2
+ */
+static const char *const ad7192_clock_names[] = {
+ "xtal",
+ "mclk"
+};
+
+static struct ad7192_state *clk_hw_to_ad7192(struct clk_hw *hw)
+{
+ return container_of(hw, struct ad7192_state, int_clk_hw);
+}
+
+static unsigned long ad7192_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return AD7192_INT_FREQ_MHZ;
+}
+
+static int ad7192_clk_output_is_enabled(struct clk_hw *hw)
+{
+ struct ad7192_state *st = clk_hw_to_ad7192(hw);
+
+ return st->clock_sel == AD7192_CLK_INT_CO;
+}
+
+static int ad7192_clk_prepare(struct clk_hw *hw)
+{
+ struct ad7192_state *st = clk_hw_to_ad7192(hw);
+ int ret;
+
+ st->mode &= ~AD7192_MODE_CLKSRC_MASK;
+ st->mode |= AD7192_CLK_INT_CO;
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ if (ret)
+ return ret;
+
+ st->clock_sel = AD7192_CLK_INT_CO;
+
+ return 0;
+}
+
+static void ad7192_clk_unprepare(struct clk_hw *hw)
+{
+ struct ad7192_state *st = clk_hw_to_ad7192(hw);
+ int ret;
+
+ st->mode &= ~AD7192_MODE_CLKSRC_MASK;
+ st->mode |= AD7192_CLK_INT;
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ if (ret)
+ return;
+
+ st->clock_sel = AD7192_CLK_INT;
+}
+
+static const struct clk_ops ad7192_int_clk_ops = {
+ .recalc_rate = ad7192_clk_recalc_rate,
+ .is_enabled = ad7192_clk_output_is_enabled,
+ .prepare = ad7192_clk_prepare,
+ .unprepare = ad7192_clk_unprepare,
+};
+
+static int ad7192_register_clk_provider(struct ad7192_state *st)
{
struct device *dev = &st->sd.spi->dev;
- unsigned int clock_sel;
+ struct clk_init_data init = {};
+ int ret;
- clock_sel = AD7192_CLK_INT;
+ if (!IS_ENABLED(CONFIG_COMMON_CLK))
+ return 0;
- /* use internal clock */
- if (!st->mclk) {
- if (device_property_read_bool(dev, "adi,int-clock-output-enable"))
- clock_sel = AD7192_CLK_INT_CO;
- } else {
- if (device_property_read_bool(dev, "adi,clock-xtal"))
- clock_sel = AD7192_CLK_EXT_MCLK1_2;
- else
- clock_sel = AD7192_CLK_EXT_MCLK2;
+ if (!device_property_present(dev, "#clock-cells"))
+ return 0;
+
+ init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-clk",
+ fwnode_get_name(dev_fwnode(dev)));
+ if (!init.name)
+ return -ENOMEM;
+
+ init.ops = &ad7192_int_clk_ops;
+
+ st->int_clk_hw.init = &init;
+ ret = devm_clk_hw_register(dev, &st->int_clk_hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &st->int_clk_hw);
+}
+
+static int ad7192_clock_setup(struct ad7192_state *st)
+{
+ struct device *dev = &st->sd.spi->dev;
+ int ret;
+
+ /*
+ * The following two if branches are kept for backward compatibility but
+ * the use of the two devicetree properties is highly discouraged. Clock
+ * configuration should be done according to the bindings.
+ */
+
+ if (device_property_read_bool(dev, "adi,int-clock-output-enable")) {
+ st->clock_sel = AD7192_CLK_INT_CO;
+ st->fclk = AD7192_INT_FREQ_MHZ;
+ dev_warn(dev, "Property adi,int-clock-output-enable is deprecated! Check bindings!\n");
+ return 0;
+ }
+
+ if (device_property_read_bool(dev, "adi,clock-xtal")) {
+ st->clock_sel = AD7192_CLK_EXT_MCLK1_2;
+ st->mclk = devm_clk_get_enabled(dev, "mclk");
+ if (IS_ERR(st->mclk))
+ return dev_err_probe(dev, PTR_ERR(st->mclk),
+ "Failed to get mclk\n");
+
+ st->fclk = clk_get_rate(st->mclk);
+ if (!ad7192_valid_external_frequency(st->fclk))
+ return dev_err_probe(dev, -EINVAL,
+ "External clock frequency out of bounds\n");
+
+ dev_warn(dev, "Property adi,clock-xtal is deprecated! Check bindings!\n");
+ return 0;
+ }
+
+ ret = device_property_match_property_string(dev, "clock-names",
+ ad7192_clock_names,
+ ARRAY_SIZE(ad7192_clock_names));
+ if (ret < 0) {
+ st->clock_sel = AD7192_CLK_INT;
+ st->fclk = AD7192_INT_FREQ_MHZ;
+
+ ret = ad7192_register_clk_provider(st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register clock provider\n");
+ return 0;
}
- return clock_sel;
+ st->clock_sel = AD7192_CLK_EXT_MCLK1_2 + ret;
+
+ st->mclk = devm_clk_get_enabled(dev, ad7192_clock_names[ret]);
+ if (IS_ERR(st->mclk))
+ return dev_err_probe(dev, PTR_ERR(st->mclk),
+ "Failed to get clock source\n");
+
+ st->fclk = clk_get_rate(st->mclk);
+ if (!ad7192_valid_external_frequency(st->fclk))
+ return dev_err_probe(dev, -EINVAL,
+ "External clock frequency out of bounds\n");
+
+ return 0;
}
static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
@@ -1275,21 +1414,9 @@ static int ad7192_probe(struct spi_device *spi)
if (ret)
return ret;
- st->fclk = AD7192_INT_FREQ_MHZ;
-
- st->mclk = devm_clk_get_optional_enabled(dev, "mclk");
- if (IS_ERR(st->mclk))
- return PTR_ERR(st->mclk);
-
- st->clock_sel = ad7192_clock_select(st);
-
- if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
- st->clock_sel == AD7192_CLK_EXT_MCLK2) {
- st->fclk = clk_get_rate(st->mclk);
- if (!ad7192_valid_external_frequency(st->fclk))
- return dev_err_probe(dev, -EINVAL,
- "External clock frequency out of bounds\n");
- }
+ ret = ad7192_clock_setup(st);
+ if (ret)
+ return ret;
ret = ad7192_setup(indio_dev, dev);
if (ret)
@@ -1304,7 +1431,7 @@ static const struct of_device_id ad7192_of_match[] = {
{ .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] },
{ .compatible = "adi,ad7194", .data = &ad7192_chip_info_tbl[ID_AD7194] },
{ .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ad7192_of_match);
@@ -1314,7 +1441,7 @@ static const struct spi_device_id ad7192_ids[] = {
{ "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] },
{ "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] },
{ "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7192_ids);
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index bdac020045b4..7949b076fb87 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -123,7 +123,8 @@ static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad7266_state *st = iio_priv(indio_dev);
- unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength);
+ unsigned int nr = find_first_bit(scan_mask,
+ iio_get_masklength(indio_dev));
ad7266_select_input(st, nr);
@@ -456,8 +457,8 @@ static int ad7266_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7266_id[] = {
- {"ad7265", 0},
- {"ad7266", 0},
+ { "ad7265", 0 },
+ { "ad7266", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7266_id);
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
index d4a4e15c8244..35aa39fe4bde 100644
--- a/drivers/iio/adc/ad7280a.c
+++ b/drivers/iio/adc/ad7280a.c
@@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -803,16 +804,16 @@ static irqreturn_t ad7280_event_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct ad7280_state *st = iio_priv(indio_dev);
- unsigned int *channels;
int i, ret;
- channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL);
+ unsigned int *channels __free(kfree) = kcalloc(st->scan_cnt, sizeof(*channels),
+ GFP_KERNEL);
if (!channels)
return IRQ_HANDLED;
ret = ad7280_read_all_channels(st, st->scan_cnt, channels);
if (ret < 0)
- goto out;
+ return IRQ_HANDLED;
for (i = 0; i < st->scan_cnt; i++) {
unsigned int val;
@@ -852,9 +853,6 @@ static irqreturn_t ad7280_event_handler(int irq, void *private)
}
}
-out:
- kfree(channels);
-
return IRQ_HANDLED;
}
@@ -1092,8 +1090,8 @@ static int ad7280_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7280_id[] = {
- {"ad7280a", 0},
- {}
+ { "ad7280a", 0 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7280_id);
diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c
index b59b2a51623c..4c7f887adbbf 100644
--- a/drivers/iio/adc/ad7291.c
+++ b/drivers/iio/adc/ad7291.c
@@ -537,14 +537,14 @@ static int ad7291_probe(struct i2c_client *client)
static const struct i2c_device_id ad7291_id[] = {
{ "ad7291" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, ad7291_id);
static const struct of_device_id ad7291_of_match[] = {
{ .compatible = "adi,ad7291" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ad7291_of_match);
diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c
index ede80f380911..a398973f313d 100644
--- a/drivers/iio/adc/ad7292.c
+++ b/drivers/iio/adc/ad7292.c
@@ -301,13 +301,13 @@ static int ad7292_probe(struct spi_device *spi)
static const struct spi_device_id ad7292_id_table[] = {
{ "ad7292", 0 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7292_id_table);
static const struct of_device_id ad7292_of_match[] = {
{ .compatible = "adi,ad7292" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ad7292_of_match);
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index c0430f71f592..b35bd4d9ef81 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -109,7 +109,8 @@ static int ad7298_update_scan_mode(struct iio_dev *indio_dev,
int scan_count;
/* Now compute overall size */
- scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
+ scan_count = bitmap_weight(active_scan_mask,
+ iio_get_masklength(indio_dev));
command = AD7298_WRITE | st->ext_ref;
@@ -354,8 +355,8 @@ static const struct acpi_device_id ad7298_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids);
static const struct spi_device_id ad7298_id[] = {
- {"ad7298", 0},
- {}
+ { "ad7298", 0 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7298_id);
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 7568cd0a2b32..e8bddfb0d07d 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -8,9 +8,11 @@
* Datasheets of supported parts:
* ad7380/1 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7380-7381.pdf
* ad7383/4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-7384.pdf
+ * ad7386/7/8 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7386-7387-7388.pdf
* ad7380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7380-4.pdf
* ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf
* ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf
+ * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf
*/
#include <linux/align.h>
@@ -31,7 +33,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
-#define MAX_NUM_CHANNELS 4
+#define MAX_NUM_CHANNELS 8
/* 2.5V internal reference voltage */
#define AD7380_INTERNAL_REF_MV 2500
@@ -49,6 +51,8 @@
#define AD7380_REG_ADDR_ALERT_LOW_TH 0x4
#define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5
+#define AD7380_CONFIG1_CH BIT(11)
+#define AD7380_CONFIG1_SEQ BIT(10)
#define AD7380_CONFIG1_OS_MODE BIT(9)
#define AD7380_CONFIG1_OSR GENMASK(8, 6)
#define AD7380_CONFIG1_CRC_W BIT(5)
@@ -80,6 +84,8 @@ struct ad7380_chip_info {
const char *name;
const struct iio_chan_spec *channels;
unsigned int num_channels;
+ unsigned int num_simult_channels;
+ bool has_mux;
const char * const *vcm_supplies;
unsigned int num_vcm_supplies;
const unsigned long *available_scan_masks;
@@ -91,82 +97,151 @@ enum {
AD7380_SCAN_TYPE_RESOLUTION_BOOST,
};
-/* Extended scan types for 14-bit chips. */
-static const struct iio_scan_type ad7380_scan_type_14[] = {
+/* Extended scan types for 12-bit unsigned chips. */
+static const struct iio_scan_type ad7380_scan_type_12_u[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 14-bit signed chips. */
+static const struct iio_scan_type ad7380_scan_type_14_s[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 's',
.realbits = 14,
.storagebits = 16,
- .endianness = IIO_CPU
+ .endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_CPU
+ .endianness = IIO_CPU,
},
};
-/* Extended scan types for 16-bit chips. */
-static const struct iio_scan_type ad7380_scan_type_16[] = {
+/* Extended scan types for 14-bit unsigned chips. */
+static const struct iio_scan_type ad7380_scan_type_14_u[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 16-bit signed_chips. */
+static const struct iio_scan_type ad7380_scan_type_16_s[] = {
[AD7380_SCAN_TYPE_NORMAL] = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_CPU
+ .endianness = IIO_CPU,
},
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
.sign = 's',
.realbits = 18,
.storagebits = 32,
- .endianness = IIO_CPU
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 16-bit unsigned chips. */
+static const struct iio_scan_type ad7380_scan_type_16_u[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 18,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
},
};
-#define AD7380_CHANNEL(index, bits, diff) { \
- .type = IIO_VOLTAGE, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
- .info_mask_shared_by_type_available = \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
- .indexed = 1, \
- .differential = (diff), \
- .channel = (diff) ? (2 * (index)) : (index), \
- .channel2 = (diff) ? (2 * (index) + 1) : 0, \
- .scan_index = (index), \
- .has_ext_scan_type = 1, \
- .ext_scan_type = ad7380_scan_type_##bits, \
- .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits),\
+#define AD7380_CHANNEL(index, bits, diff, sign) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .indexed = 1, \
+ .differential = (diff), \
+ .channel = (diff) ? (2 * (index)) : (index), \
+ .channel2 = (diff) ? (2 * (index) + 1) : 0, \
+ .scan_index = (index), \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad7380_scan_type_##bits##_##sign, \
+ .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \
}
-#define DEFINE_AD7380_2_CHANNEL(name, bits, diff) \
+#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff), \
- AD7380_CHANNEL(1, bits, diff), \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(2), \
}
-#define DEFINE_AD7380_4_CHANNEL(name, bits, diff) \
+#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff), \
- AD7380_CHANNEL(1, bits, diff), \
- AD7380_CHANNEL(2, bits, diff), \
- AD7380_CHANNEL(3, bits, diff), \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ AD7380_CHANNEL(2, bits, diff, sign), \
+ AD7380_CHANNEL(3, bits, diff, sign), \
IIO_CHAN_SOFT_TIMESTAMP(4), \
}
+#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ AD7380_CHANNEL(2, bits, diff, sign), \
+ AD7380_CHANNEL(3, bits, diff, sign), \
+ AD7380_CHANNEL(4, bits, diff, sign), \
+ AD7380_CHANNEL(5, bits, diff, sign), \
+ AD7380_CHANNEL(6, bits, diff, sign), \
+ AD7380_CHANNEL(7, bits, diff, sign), \
+ IIO_CHAN_SOFT_TIMESTAMP(8), \
+}
+
/* fully differential */
-DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1);
-DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1);
-DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1);
-DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1);
+DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s);
+DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s);
+DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s);
+DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s);
/* pseudo differential */
-DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0);
-DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0);
-DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0);
-DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0);
+DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s);
+DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s);
+DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0, s);
+DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0, s);
+
+/* Single ended */
+DEFINE_AD7380_4_CHANNEL(ad7386_channels, 16, 0, u);
+DEFINE_AD7380_4_CHANNEL(ad7387_channels, 14, 0, u);
+DEFINE_AD7380_4_CHANNEL(ad7388_channels, 12, 0, u);
+DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u);
+DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u);
+DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u);
static const char * const ad7380_2_channel_vcm_supplies[] = {
"aina", "ainb",
@@ -187,6 +262,60 @@ static const unsigned long ad7380_4_channel_scan_masks[] = {
0
};
+/*
+ * Single ended parts have a 2:1 multiplexer in front of each ADC.
+ *
+ * From an IIO point of view, all inputs are exported, i.e ad7386/7/8
+ * export 4 channels and ad7386-4/7-4/8-4 export 8 channels.
+ *
+ * Inputs AinX0 of multiplexers correspond to the first half of IIO channels
+ * (i.e 0-1 or 0-3) and inputs AinX1 correspond to second half (i.e 2-3 or
+ * 4-7). Example for AD7386/7/8 (2 channels parts):
+ *
+ * IIO | AD7386/7/8
+ * | +----------------------------
+ * | | _____ ______
+ * | | | | | |
+ * voltage0 | AinA0 --|--->| | | |
+ * | | | mux |----->| ADCA |---
+ * voltage2 | AinA1 --|--->| | | |
+ * | | |_____| |_____ |
+ * | | _____ ______
+ * | | | | | |
+ * voltage1 | AinB0 --|--->| | | |
+ * | | | mux |----->| ADCB |---
+ * voltage3 | AinB1 --|--->| | | |
+ * | | |_____| |______|
+ * | |
+ * | +----------------------------
+ *
+ * Since this is simultaneous sampling for AinX0 OR AinX1 we have two separate
+ * scan masks.
+ * When sequencer mode is enabled, chip automatically cycles through
+ * AinX0 and AinX1 channels. From an IIO point of view, we ca enable all
+ * channels, at the cost of an extra read, thus dividing the maximum rate by
+ * two.
+ */
+enum {
+ AD7380_SCAN_MASK_CH_0,
+ AD7380_SCAN_MASK_CH_1,
+ AD7380_SCAN_MASK_SEQ,
+};
+
+static const unsigned long ad7380_2x2_channel_scan_masks[] = {
+ [AD7380_SCAN_MASK_CH_0] = GENMASK(1, 0),
+ [AD7380_SCAN_MASK_CH_1] = GENMASK(3, 2),
+ [AD7380_SCAN_MASK_SEQ] = GENMASK(3, 0),
+ 0
+};
+
+static const unsigned long ad7380_2x4_channel_scan_masks[] = {
+ [AD7380_SCAN_MASK_CH_0] = GENMASK(3, 0),
+ [AD7380_SCAN_MASK_CH_1] = GENMASK(7, 4),
+ [AD7380_SCAN_MASK_SEQ] = GENMASK(7, 0),
+ 0
+};
+
static const struct ad7380_timing_specs ad7380_timing = {
.t_csh_ns = 10,
};
@@ -208,6 +337,7 @@ static const struct ad7380_chip_info ad7380_chip_info = {
.name = "ad7380",
.channels = ad7380_channels,
.num_channels = ARRAY_SIZE(ad7380_channels),
+ .num_simult_channels = 2,
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
@@ -216,6 +346,7 @@ static const struct ad7380_chip_info ad7381_chip_info = {
.name = "ad7381",
.channels = ad7381_channels,
.num_channels = ARRAY_SIZE(ad7381_channels),
+ .num_simult_channels = 2,
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
@@ -224,6 +355,7 @@ static const struct ad7380_chip_info ad7383_chip_info = {
.name = "ad7383",
.channels = ad7383_channels,
.num_channels = ARRAY_SIZE(ad7383_channels),
+ .num_simult_channels = 2,
.vcm_supplies = ad7380_2_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
@@ -234,16 +366,48 @@ static const struct ad7380_chip_info ad7384_chip_info = {
.name = "ad7384",
.channels = ad7384_channels,
.num_channels = ARRAY_SIZE(ad7384_channels),
+ .num_simult_channels = 2,
.vcm_supplies = ad7380_2_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
};
+static const struct ad7380_chip_info ad7386_chip_info = {
+ .name = "ad7386",
+ .channels = ad7386_channels,
+ .num_channels = ARRAY_SIZE(ad7386_channels),
+ .num_simult_channels = 2,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x2_channel_scan_masks,
+ .timing_specs = &ad7380_timing,
+};
+
+static const struct ad7380_chip_info ad7387_chip_info = {
+ .name = "ad7387",
+ .channels = ad7387_channels,
+ .num_channels = ARRAY_SIZE(ad7387_channels),
+ .num_simult_channels = 2,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x2_channel_scan_masks,
+ .timing_specs = &ad7380_timing,
+};
+
+static const struct ad7380_chip_info ad7388_chip_info = {
+ .name = "ad7388",
+ .channels = ad7388_channels,
+ .num_channels = ARRAY_SIZE(ad7388_channels),
+ .num_simult_channels = 2,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x2_channel_scan_masks,
+ .timing_specs = &ad7380_timing,
+};
+
static const struct ad7380_chip_info ad7380_4_chip_info = {
.name = "ad7380-4",
.channels = ad7380_4_channels,
.num_channels = ARRAY_SIZE(ad7380_4_channels),
+ .num_simult_channels = 4,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
@@ -252,6 +416,7 @@ static const struct ad7380_chip_info ad7381_4_chip_info = {
.name = "ad7381-4",
.channels = ad7381_4_channels,
.num_channels = ARRAY_SIZE(ad7381_4_channels),
+ .num_simult_channels = 4,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
@@ -260,6 +425,7 @@ static const struct ad7380_chip_info ad7383_4_chip_info = {
.name = "ad7383-4",
.channels = ad7383_4_channels,
.num_channels = ARRAY_SIZE(ad7383_4_channels),
+ .num_simult_channels = 4,
.vcm_supplies = ad7380_4_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
@@ -270,23 +436,58 @@ static const struct ad7380_chip_info ad7384_4_chip_info = {
.name = "ad7384-4",
.channels = ad7384_4_channels,
.num_channels = ARRAY_SIZE(ad7384_4_channels),
+ .num_simult_channels = 4,
.vcm_supplies = ad7380_4_channel_vcm_supplies,
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
};
+static const struct ad7380_chip_info ad7386_4_chip_info = {
+ .name = "ad7386-4",
+ .channels = ad7386_4_channels,
+ .num_channels = ARRAY_SIZE(ad7386_4_channels),
+ .num_simult_channels = 4,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
+static const struct ad7380_chip_info ad7387_4_chip_info = {
+ .name = "ad7387-4",
+ .channels = ad7387_4_channels,
+ .num_channels = ARRAY_SIZE(ad7387_4_channels),
+ .num_simult_channels = 4,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
+static const struct ad7380_chip_info ad7388_4_chip_info = {
+ .name = "ad7388-4",
+ .channels = ad7388_4_channels,
+ .num_channels = ARRAY_SIZE(ad7388_4_channels),
+ .num_simult_channels = 4,
+ .has_mux = true,
+ .available_scan_masks = ad7380_2x4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
struct ad7380_state {
const struct ad7380_chip_info *chip_info;
struct spi_device *spi;
struct regmap *regmap;
unsigned int oversampling_ratio;
bool resolution_boost_enabled;
+ unsigned int ch;
+ bool seq;
unsigned int vref_mv;
unsigned int vcm_mv[MAX_NUM_CHANNELS];
/* xfers, message an buffer for reading sample data */
- struct spi_transfer xfer[2];
- struct spi_message msg;
+ struct spi_transfer normal_xfer[2];
+ struct spi_message normal_msg;
+ struct spi_transfer seq_xfer[4];
+ struct spi_message seq_msg;
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
@@ -379,6 +580,43 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
unreachable();
}
+/*
+ * When switching channel, the ADC require an additional settling time.
+ * According to the datasheet, data is value on the third CS low. We already
+ * have an extra toggle before each read (either direct reads or buffered reads)
+ * to sample correct data, so we just add a single CS toggle at the end of the
+ * register write.
+ */
+static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
+{
+ struct spi_transfer xfer = {
+ .delay = {
+ .value = T_CONVERT_NS,
+ .unit = SPI_DELAY_UNIT_NSECS,
+ }
+ };
+ int ret;
+
+ if (st->ch == ch)
+ return 0;
+
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_CH,
+ FIELD_PREP(AD7380_CONFIG1_CH, ch));
+
+ if (ret)
+ return ret;
+
+ st->ch = ch;
+
+ if (st->oversampling_ratio > 1)
+ xfer.delay.value = T_CONVERT_0_NS +
+ T_CONVERT_X_NS * (st->oversampling_ratio - 1);
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
/**
* ad7380_update_xfers - update the SPI transfers base on the current scan type
* @st: device instance specific state
@@ -387,33 +625,47 @@ static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
static void ad7380_update_xfers(struct ad7380_state *st,
const struct iio_scan_type *scan_type)
{
- /*
- * First xfer only triggers conversion and has to be long enough for
- * all conversions to complete, which can be multiple conversion in the
- * case of oversampling. Technically T_CONVERT_X_NS is lower for some
- * chips, but we use the maximum value for simplicity for now.
- */
- if (st->oversampling_ratio > 1)
- st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS *
- (st->oversampling_ratio - 1);
- else
- st->xfer[0].delay.value = T_CONVERT_NS;
-
- st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+ struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer;
+ unsigned int t_convert = T_CONVERT_NS;
/*
- * Second xfer reads all channels. Data size depends on if resolution
- * boost is enabled or not.
+ * In the case of oversampling, conversion time is higher than in normal
+ * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use
+ * the maximum value for simplicity for now.
*/
- st->xfer[1].bits_per_word = scan_type->realbits;
- st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
- (st->chip_info->num_channels - 1);
+ if (st->oversampling_ratio > 1)
+ t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
+ (st->oversampling_ratio - 1);
+
+ if (st->seq) {
+ xfer[0].delay.value = xfer[1].delay.value = t_convert;
+ xfer[0].delay.unit = xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer[2].bits_per_word = xfer[3].bits_per_word =
+ scan_type->realbits;
+ xfer[2].len = xfer[3].len =
+ BITS_TO_BYTES(scan_type->storagebits) *
+ st->chip_info->num_simult_channels;
+ xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len;
+ /* Additional delay required here when oversampling is enabled */
+ if (st->oversampling_ratio > 1)
+ xfer[2].delay.value = t_convert;
+ else
+ xfer[2].delay.value = 0;
+ xfer[2].delay.unit = SPI_DELAY_UNIT_NSECS;
+ } else {
+ xfer[0].delay.value = t_convert;
+ xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfer[1].bits_per_word = scan_type->realbits;
+ xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
+ st->chip_info->num_simult_channels;
+ }
}
static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
+ struct spi_message *msg = &st->normal_msg;
/*
* Currently, we always read all channels at the same time. The scan_type
@@ -423,16 +675,63 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
if (IS_ERR(scan_type))
return PTR_ERR(scan_type);
+ if (st->chip_info->has_mux) {
+ unsigned int index;
+ int ret;
+
+ /*
+ * Depending on the requested scan_mask and current state,
+ * we need to either change CH bit, or enable sequencer mode
+ * to sample correct data.
+ * Sequencer mode is enabled if active mask corresponds to all
+ * IIO channels enabled. Otherwise, CH bit is set.
+ */
+ ret = iio_active_scan_mask_index(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ index = ret;
+ if (index == AD7380_SCAN_MASK_SEQ) {
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_SEQ,
+ FIELD_PREP(AD7380_CONFIG1_SEQ, 1));
+ if (ret)
+ return ret;
+ msg = &st->seq_msg;
+ st->seq = true;
+ } else {
+ ret = ad7380_set_ch(st, index);
+ if (ret)
+ return ret;
+ }
+
+ }
+
ad7380_update_xfers(st, scan_type);
- return spi_optimize_message(st->spi, &st->msg);
+ return spi_optimize_message(st->spi, msg);
}
static int ad7380_triggered_buffer_postdisable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
+ struct spi_message *msg = &st->normal_msg;
+ int ret;
+
+ if (st->seq) {
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_SEQ,
+ FIELD_PREP(AD7380_CONFIG1_SEQ, 0));
+ if (ret)
+ return ret;
+
+ msg = &st->seq_msg;
+ st->seq = false;
+ }
- spi_unoptimize_message(&st->msg);
+ spi_unoptimize_message(msg);
return 0;
}
@@ -447,9 +746,10 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7380_state *st = iio_priv(indio_dev);
+ struct spi_message *msg = st->seq ? &st->seq_msg : &st->normal_msg;
int ret;
- ret = spi_sync(st->spi, &st->msg);
+ ret = spi_sync(st->spi, msg);
if (ret)
goto out;
@@ -465,20 +765,43 @@ out:
static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index,
const struct iio_scan_type *scan_type, int *val)
{
+ unsigned int index = scan_index;
int ret;
+ if (st->chip_info->has_mux) {
+ unsigned int ch = 0;
+
+ if (index >= st->chip_info->num_simult_channels) {
+ index -= st->chip_info->num_simult_channels;
+ ch = 1;
+ }
+
+ ret = ad7380_set_ch(st, ch);
+ if (ret)
+ return ret;
+ }
+
ad7380_update_xfers(st, scan_type);
- ret = spi_sync(st->spi, &st->msg);
+ ret = spi_sync(st->spi, &st->normal_msg);
if (ret < 0)
return ret;
- if (scan_type->storagebits > 16)
- *val = sign_extend32(*(u32 *)(st->scan_data + 4 * scan_index),
- scan_type->realbits - 1);
- else
- *val = sign_extend32(*(u16 *)(st->scan_data + 2 * scan_index),
- scan_type->realbits - 1);
+ if (scan_type->storagebits > 16) {
+ if (scan_type->sign == 's')
+ *val = sign_extend32(*(u32 *)(st->scan_data + 4 * index),
+ scan_type->realbits - 1);
+ else
+ *val = *(u32 *)(st->scan_data + 4 * index) &
+ GENMASK(scan_type->realbits - 1, 0);
+ } else {
+ if (scan_type->sign == 's')
+ *val = sign_extend32(*(u16 *)(st->scan_data + 2 * index),
+ scan_type->realbits - 1);
+ else
+ *val = *(u16 *)(st->scan_data + 2 * index) &
+ GENMASK(scan_type->realbits - 1, 0);
+ }
return IIO_VAL_INT;
}
@@ -655,6 +978,8 @@ static int ad7380_init(struct ad7380_state *st, struct regulator *vref)
/* This is the default value after reset. */
st->oversampling_ratio = 1;
+ st->ch = 0;
+ st->seq = false;
/* SPI 1-wire mode */
return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
@@ -756,21 +1081,45 @@ static int ad7380_probe(struct spi_device *spi)
"failed to allocate register map\n");
/*
- * Setting up a low latency read for getting sample data. Used for both
- * direct read an triggered buffer. Additional fields will be set up in
- * ad7380_update_xfers() based on the current state of the driver at the
- * time of the read.
+ * Setting up xfer structures for both normal and sequence mode. These
+ * struct are used for both direct read and triggered buffer. Additional
+ * fields will be set up in ad7380_update_xfers() based on the current
+ * state of the driver at the time of the read.
+ */
+
+ /*
+ * In normal mode a read is composed of two steps:
+ * - first, toggle CS (no data xfer) to trigger a conversion
+ * - then, read data
*/
+ st->normal_xfer[0].cs_change = 1;
+ st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ st->normal_xfer[1].rx_buf = st->scan_data;
- /* toggle CS (no data xfer) to trigger a conversion */
- st->xfer[0].cs_change = 1;
- st->xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
- st->xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer,
+ ARRAY_SIZE(st->normal_xfer));
+ /*
+ * In sequencer mode a read is composed of four steps:
+ * - CS toggle (no data xfer) to get the right point in the sequence
+ * - CS toggle (no data xfer) to trigger a conversion of AinX0 and
+ * acquisition of AinX1
+ * - 2 data reads, to read AinX0 and AinX1
+ */
+ st->seq_xfer[0].cs_change = 1;
+ st->seq_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->seq_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ st->seq_xfer[1].cs_change = 1;
+ st->seq_xfer[1].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->seq_xfer[1].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
- /* then do a second xfer to read the data */
- st->xfer[1].rx_buf = st->scan_data;
+ st->seq_xfer[2].rx_buf = st->scan_data;
+ st->seq_xfer[2].cs_change = 1;
+ st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
- spi_message_init_with_transfers(&st->msg, st->xfer, ARRAY_SIZE(st->xfer));
+ spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer,
+ ARRAY_SIZE(st->seq_xfer));
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
@@ -798,10 +1147,16 @@ static const struct of_device_id ad7380_of_match_table[] = {
{ .compatible = "adi,ad7381", .data = &ad7381_chip_info },
{ .compatible = "adi,ad7383", .data = &ad7383_chip_info },
{ .compatible = "adi,ad7384", .data = &ad7384_chip_info },
+ { .compatible = "adi,ad7386", .data = &ad7386_chip_info },
+ { .compatible = "adi,ad7387", .data = &ad7387_chip_info },
+ { .compatible = "adi,ad7388", .data = &ad7388_chip_info },
{ .compatible = "adi,ad7380-4", .data = &ad7380_4_chip_info },
{ .compatible = "adi,ad7381-4", .data = &ad7381_4_chip_info },
{ .compatible = "adi,ad7383-4", .data = &ad7383_4_chip_info },
{ .compatible = "adi,ad7384-4", .data = &ad7384_4_chip_info },
+ { .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info },
+ { .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info },
+ { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info },
{ }
};
@@ -810,10 +1165,16 @@ static const struct spi_device_id ad7380_id_table[] = {
{ "ad7381", (kernel_ulong_t)&ad7381_chip_info },
{ "ad7383", (kernel_ulong_t)&ad7383_chip_info },
{ "ad7384", (kernel_ulong_t)&ad7384_chip_info },
+ { "ad7386", (kernel_ulong_t)&ad7386_chip_info },
+ { "ad7387", (kernel_ulong_t)&ad7387_chip_info },
+ { "ad7388", (kernel_ulong_t)&ad7388_chip_info },
{ "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info },
{ "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info },
{ "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info },
{ "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info },
+ { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info },
+ { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info },
+ { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7380_id_table);
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index 80aebed47d1f..aeb8e383fe71 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -409,35 +409,35 @@ static int ad7476_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7476_id[] = {
- {"ad7091", ID_AD7091},
- {"ad7091r", ID_AD7091R},
- {"ad7273", ID_AD7273},
- {"ad7274", ID_AD7274},
- {"ad7276", ID_AD7276},
- {"ad7277", ID_AD7277},
- {"ad7278", ID_AD7278},
- {"ad7466", ID_AD7466},
- {"ad7467", ID_AD7467},
- {"ad7468", ID_AD7468},
- {"ad7475", ID_AD7475},
- {"ad7476", ID_AD7466},
- {"ad7476a", ID_AD7466},
- {"ad7477", ID_AD7467},
- {"ad7477a", ID_AD7467},
- {"ad7478", ID_AD7468},
- {"ad7478a", ID_AD7468},
- {"ad7495", ID_AD7495},
- {"ad7910", ID_AD7467},
- {"ad7920", ID_AD7466},
- {"ad7940", ID_AD7940},
- {"adc081s", ID_ADC081S},
- {"adc101s", ID_ADC101S},
- {"adc121s", ID_ADC121S},
- {"ads7866", ID_ADS7866},
- {"ads7867", ID_ADS7867},
- {"ads7868", ID_ADS7868},
- {"ltc2314-14", ID_LTC2314_14},
- {}
+ { "ad7091", ID_AD7091 },
+ { "ad7091r", ID_AD7091R },
+ { "ad7273", ID_AD7273 },
+ { "ad7274", ID_AD7274 },
+ { "ad7276", ID_AD7276},
+ { "ad7277", ID_AD7277 },
+ { "ad7278", ID_AD7278 },
+ { "ad7466", ID_AD7466 },
+ { "ad7467", ID_AD7467 },
+ { "ad7468", ID_AD7468 },
+ { "ad7475", ID_AD7475 },
+ { "ad7476", ID_AD7466 },
+ { "ad7476a", ID_AD7466 },
+ { "ad7477", ID_AD7467 },
+ { "ad7477a", ID_AD7467 },
+ { "ad7478", ID_AD7468 },
+ { "ad7478a", ID_AD7468 },
+ { "ad7495", ID_AD7495 },
+ { "ad7910", ID_AD7467 },
+ { "ad7920", ID_AD7466 },
+ { "ad7940", ID_AD7940 },
+ { "adc081s", ID_ADC081S },
+ { "adc101s", ID_ADC101S },
+ { "adc121s", ID_ADC121S },
+ { "ads7866", ID_ADS7866 },
+ { "ads7867", ID_ADS7867 },
+ { "ads7868", ID_ADS7868 },
+ { "ltc2314-14", ID_LTC2314_14 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index c321c6ef48df..9b457472d49c 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -70,19 +70,17 @@ static int ad7606_reg_access(struct iio_dev *indio_dev,
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
+
if (readval) {
ret = st->bops->reg_read(st, reg);
if (ret < 0)
- goto err_unlock;
+ return ret;
*readval = ret;
- ret = 0;
+ return 0;
} else {
- ret = st->bops->reg_write(st, reg, writeval);
+ return st->bops->reg_write(st, reg, writeval);
}
-err_unlock:
- mutex_unlock(&st->lock);
- return ret;
}
static int ad7606_read_samples(struct ad7606_state *st)
@@ -100,19 +98,19 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p)
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
ret = ad7606_read_samples(st);
- if (ret == 0)
- iio_push_to_buffers_with_timestamp(indio_dev, st->data,
- iio_get_time_ns(indio_dev));
+ if (ret)
+ goto error_ret;
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
+error_ret:
iio_trigger_notify_done(indio_dev->trig);
/* The rising edge of the CONVST signal starts a new conversion. */
gpiod_set_value(st->gpio_convst, 1);
- mutex_unlock(&st->lock);
-
return IRQ_HANDLED;
}
@@ -212,9 +210,9 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
struct ad7606_state *st = iio_priv(indio_dev);
DECLARE_BITMAP(values, 3);
- values[0] = val;
+ values[0] = val & GENMASK(2, 0);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
+ gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc,
st->gpio_os->info, values);
/* AD7616 requires a reset to update value */
@@ -233,19 +231,17 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
struct ad7606_state *st = iio_priv(indio_dev);
int i, ret, ch = 0;
+ guard(mutex)(&st->lock);
+
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- mutex_lock(&st->lock);
i = find_closest(val2, st->scale_avail, st->num_scales);
if (st->sw_mode_en)
ch = chan->address;
ret = st->write_scale(indio_dev, ch, i);
- if (ret < 0) {
- mutex_unlock(&st->lock);
+ if (ret < 0)
return ret;
- }
st->range[ch] = i;
- mutex_unlock(&st->lock);
return 0;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@@ -253,14 +249,9 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
- mutex_lock(&st->lock);
ret = st->write_os(indio_dev, i);
- if (ret < 0) {
- mutex_unlock(&st->lock);
+ if (ret < 0)
return ret;
- }
- st->oversampling = st->oversampling_avail[i];
- mutex_unlock(&st->lock);
return 0;
default:
@@ -419,7 +410,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
return PTR_ERR(st->gpio_range);
st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
- GPIOD_OUT_HIGH);
+ GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_standby))
return PTR_ERR(st->gpio_standby);
@@ -662,7 +653,7 @@ static int ad7606_suspend(struct device *dev)
if (st->gpio_standby) {
gpiod_set_value(st->gpio_range, 1);
- gpiod_set_value(st->gpio_standby, 0);
+ gpiod_set_value(st->gpio_standby, 1);
}
return 0;
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
index 6bc587b20f05..02d8c309304e 100644
--- a/drivers/iio/adc/ad7606_par.c
+++ b/drivers/iio/adc/ad7606_par.c
@@ -125,7 +125,7 @@ static const struct of_device_id ad7606_of_match[] = {
{ .compatible = "adi,ad7606-4" },
{ .compatible = "adi,ad7606-6" },
{ .compatible = "adi,ad7606-8" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ad7606_of_match);
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index 263a778bcf25..62ec12195307 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -249,8 +249,9 @@ static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
- unsigned long os[3] = {1};
+ DECLARE_BITMAP(os, 3);
+ bitmap_fill(os, 3);
/*
* Software mode is enabled when all three oversampling
* pins are set to high. If oversampling gpios are defined
@@ -258,7 +259,7 @@ static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
* otherwise, they must be hardwired to VDD
*/
if (st->gpio_os) {
- gpiod_set_array_value(ARRAY_SIZE(os),
+ gpiod_set_array_value(st->gpio_os->ndescs,
st->gpio_os->desc, st->gpio_os->info, os);
}
/* OS of 128 and 256 are available only in software mode */
@@ -333,7 +334,7 @@ static const struct spi_device_id ad7606_id_table[] = {
{ "ad7606-8", ID_AD7606_8 },
{ "ad7606b", ID_AD7606B },
{ "ad7616", ID_AD7616 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
@@ -344,7 +345,7 @@ static const struct of_device_id ad7606_of_match[] = {
{ .compatible = "adi,ad7606-8" },
{ .compatible = "adi,ad7606b" },
{ .compatible = "adi,ad7616" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ad7606_of_match);
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 3079a0872947..4d570383ef02 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -291,13 +291,13 @@ static int ad7766_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7766_id[] = {
- {"ad7766", ID_AD7766},
- {"ad7766-1", ID_AD7766_1},
- {"ad7766-2", ID_AD7766_2},
- {"ad7767", ID_AD7766},
- {"ad7767-1", ID_AD7766_1},
- {"ad7767-2", ID_AD7766_2},
- {}
+ { "ad7766", ID_AD7766 },
+ { "ad7766-1", ID_AD7766_1 },
+ { "ad7766-2", ID_AD7766_2 },
+ { "ad7767", ID_AD7766 },
+ { "ad7767-1", ID_AD7766_1 },
+ { "ad7767-2", ID_AD7766_2 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7766_id);
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 70a25949142c..113703fb7245 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -544,13 +544,10 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev,
{
struct ad7768_state *st = iio_priv(indio_dev);
struct device *device = indio_dev->dev.parent;
- struct fwnode_handle *fwnode;
- struct fwnode_handle *child;
const char *label;
int crt_ch = 0;
- fwnode = dev_fwnode(device);
- fwnode_for_each_child_node(fwnode, child) {
+ device_for_each_child_node_scoped(device, child) {
if (fwnode_property_read_u32(child, "reg", &crt_ch))
continue;
@@ -658,7 +655,7 @@ MODULE_DEVICE_TABLE(spi, ad7768_id_table);
static const struct of_device_id ad7768_of_match[] = {
{ .compatible = "adi,ad7768-1" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ad7768_of_match);
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
index a813fe04787c..e9b0c577c9cc 100644
--- a/drivers/iio/adc/ad7780.c
+++ b/drivers/iio/adc/ad7780.c
@@ -355,11 +355,11 @@ static int ad7780_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7780_id[] = {
- {"ad7170", ID_AD7170},
- {"ad7171", ID_AD7171},
- {"ad7780", ID_AD7780},
- {"ad7781", ID_AD7781},
- {}
+ { "ad7170", ID_AD7170 },
+ { "ad7171", ID_AD7171 },
+ { "ad7780", ID_AD7780 },
+ { "ad7781", ID_AD7781 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7780_id);
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index d4ad7e0b515a..abebd519cafa 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -824,16 +824,16 @@ static int ad7793_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7793_id[] = {
- {"ad7785", ID_AD7785},
- {"ad7792", ID_AD7792},
- {"ad7793", ID_AD7793},
- {"ad7794", ID_AD7794},
- {"ad7795", ID_AD7795},
- {"ad7796", ID_AD7796},
- {"ad7797", ID_AD7797},
- {"ad7798", ID_AD7798},
- {"ad7799", ID_AD7799},
- {}
+ { "ad7785", ID_AD7785 },
+ { "ad7792", ID_AD7792 },
+ { "ad7793", ID_AD7793 },
+ { "ad7794", ID_AD7794 },
+ { "ad7795", ID_AD7795 },
+ { "ad7796", ID_AD7796 },
+ { "ad7797", ID_AD7797 },
+ { "ad7798", ID_AD7798 },
+ { "ad7799", ID_AD7799 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7793_id);
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 965bdc8aa696..6265ce7df703 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -329,8 +329,8 @@ static int ad7887_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7887_id[] = {
- {"ad7887", ID_AD7887},
- {}
+ { "ad7887", ID_AD7887 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7887_id);
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index 9d6bf6d0927a..09680015a7ab 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -361,14 +361,14 @@ static int ad7923_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7923_id[] = {
- {"ad7904", AD7904},
- {"ad7914", AD7914},
- {"ad7923", AD7924},
- {"ad7924", AD7924},
- {"ad7908", AD7908},
- {"ad7918", AD7918},
- {"ad7928", AD7928},
- {}
+ { "ad7904", AD7904 },
+ { "ad7914", AD7914 },
+ { "ad7923", AD7924 },
+ { "ad7924", AD7924 },
+ { "ad7908", AD7908 },
+ { "ad7918", AD7918 },
+ { "ad7928", AD7928 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad7923_id);
@@ -380,7 +380,7 @@ static const struct of_device_id ad7923_of_match[] = {
{ .compatible = "adi,ad7908", },
{ .compatible = "adi,ad7918", },
{ .compatible = "adi,ad7928", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ad7923_of_match);
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index 0f0dcd9ca6b6..0f107e3fc2c8 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -237,7 +237,8 @@ static int ad799x_update_scan_mode(struct iio_dev *indio_dev,
if (!st->rx_buf)
return -ENOMEM;
- st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2;
+ st->transfer_size = bitmap_weight(scan_mask,
+ iio_get_masklength(indio_dev)) * 2;
switch (st->id) {
case ad7992:
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 41c1b519c573..05fb7a75531f 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
+#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
@@ -104,7 +105,30 @@
#define AD9467_DEF_OUTPUT_MODE 0x08
#define AD9467_REG_VREF_MASK 0x0F
+/*
+ * Analog Devices AD9643 14-Bit, 170/210/250 MSPS ADC
+ */
+
+#define CHIPID_AD9643 0x82
+#define AD9643_REG_VREF_MASK 0x1F
+
+/*
+ * Analog Devices AD9652 16-bit 310 MSPS ADC
+ */
+
+#define CHIPID_AD9652 0xC1
+#define AD9652_REG_VREF_MASK 0xC0
+
+/*
+ * Analog Devices AD9649 14-bit 20/40/65/80 MSPS ADC
+ */
+
+#define CHIPID_AD9649 0x6F
+#define AD9649_TEST_POINTS 8
+
#define AD9647_MAX_TEST_POINTS 32
+#define AD9467_CAN_INVERT(st) \
+ (!(st)->info->has_dco || (st)->info->has_dco_invert)
struct ad9467_chip_info {
const char *name;
@@ -113,12 +137,23 @@ struct ad9467_chip_info {
unsigned int num_channels;
const unsigned int (*scale_table)[2];
int num_scales;
+ unsigned long test_mask;
+ unsigned int test_mask_len;
unsigned long max_rate;
unsigned int default_output_mode;
unsigned int vref_mask;
unsigned int num_lanes;
+ unsigned int dco_en;
+ unsigned int test_points;
/* data clock output */
bool has_dco;
+ bool has_dco_invert;
+};
+
+struct ad9467_chan_test_mode {
+ struct ad9467_state *st;
+ unsigned int idx;
+ u8 mode;
};
struct ad9467_state {
@@ -126,6 +161,8 @@ struct ad9467_state {
struct iio_backend *back;
struct spi_device *spi;
struct clk *clk;
+ /* used for debugfs */
+ struct ad9467_chan_test_mode *chan_test;
unsigned int output_mode;
unsigned int (*scales)[2];
/*
@@ -138,6 +175,8 @@ struct ad9467_state {
* at the io delay control section.
*/
DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2);
+ /* number of bits of the map */
+ unsigned int calib_map_size;
struct gpio_desc *pwrdown_gpio;
/* ensure consistent state obtained on multiple related accesses */
struct mutex lock;
@@ -211,6 +250,24 @@ static const unsigned int ad9467_scale_table[][2] = {
{2300, 8}, {2400, 9}, {2500, 10},
};
+static const unsigned int ad9643_scale_table[][2] = {
+ {2087, 0x0F}, {2065, 0x0E}, {2042, 0x0D}, {2020, 0x0C}, {1997, 0x0B},
+ {1975, 0x0A}, {1952, 0x09}, {1930, 0x08}, {1907, 0x07}, {1885, 0x06},
+ {1862, 0x05}, {1840, 0x04}, {1817, 0x03}, {1795, 0x02}, {1772, 0x01},
+ {1750, 0x00}, {1727, 0x1F}, {1704, 0x1E}, {1681, 0x1D}, {1658, 0x1C},
+ {1635, 0x1B}, {1612, 0x1A}, {1589, 0x19}, {1567, 0x18}, {1544, 0x17},
+ {1521, 0x16}, {1498, 0x15}, {1475, 0x14}, {1452, 0x13}, {1429, 0x12},
+ {1406, 0x11}, {1383, 0x10},
+};
+
+static const unsigned int ad9649_scale_table[][2] = {
+ {2000, 0},
+};
+
+static const unsigned int ad9652_scale_table[][2] = {
+ {1250, 0}, {1125, 1}, {1200, 2}, {1250, 3}, {1000, 5},
+};
+
static void __ad9467_get_scale(struct ad9467_state *st, int index,
unsigned int *val, unsigned int *val2)
{
@@ -224,14 +281,14 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
*val2 = tmp % 1000000;
}
-#define AD9467_CHAN(_chan, _si, _bits, _sign) \
+#define AD9467_CHAN(_chan, avai_mask, _si, _bits, _sign) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
- .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = avai_mask, \
.scan_index = _si, \
.scan_type = { \
.sign = _sign, \
@@ -241,11 +298,42 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
}
static const struct iio_chan_spec ad9434_channels[] = {
- AD9467_CHAN(0, 0, 12, 's'),
+ AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 12, 's'),
};
static const struct iio_chan_spec ad9467_channels[] = {
- AD9467_CHAN(0, 0, 16, 's'),
+ AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'),
+};
+
+static const struct iio_chan_spec ad9643_channels[] = {
+ AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 14, 's'),
+ AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 14, 's'),
+};
+
+static const struct iio_chan_spec ad9649_channels[] = {
+ AD9467_CHAN(0, 0, 0, 14, 's'),
+};
+
+static const struct iio_chan_spec ad9652_channels[] = {
+ AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 16, 's'),
+ AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 16, 's'),
+};
+
+static const char * const ad9467_test_modes[] = {
+ [AN877_ADC_TESTMODE_OFF] = "off",
+ [AN877_ADC_TESTMODE_MIDSCALE_SHORT] = "midscale_short",
+ [AN877_ADC_TESTMODE_POS_FULLSCALE] = "pos_fullscale",
+ [AN877_ADC_TESTMODE_NEG_FULLSCALE] = "neg_fullscale",
+ [AN877_ADC_TESTMODE_ALT_CHECKERBOARD] = "checkerboard",
+ [AN877_ADC_TESTMODE_PN23_SEQ] = "prbs23",
+ [AN877_ADC_TESTMODE_PN9_SEQ] = "prbs9",
+ [AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE] = "one_zero_toggle",
+ [AN877_ADC_TESTMODE_USER] = "user",
+ [AN877_ADC_TESTMODE_BIT_TOGGLE] = "bit_toggle",
+ [AN877_ADC_TESTMODE_SYNC] = "sync",
+ [AN877_ADC_TESTMODE_ONE_BIT_HIGH] = "one_bit_high",
+ [AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY] = "mixed_bit_frequency",
+ [AN877_ADC_TESTMODE_RAMP] = "ramp",
};
static const struct ad9467_chip_info ad9467_chip_tbl = {
@@ -256,6 +344,10 @@ static const struct ad9467_chip_info ad9467_chip_tbl = {
.num_scales = ARRAY_SIZE(ad9467_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
+ AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.default_output_mode = AD9467_DEF_OUTPUT_MODE,
.vref_mask = AD9467_REG_VREF_MASK,
.num_lanes = 8,
@@ -269,6 +361,9 @@ static const struct ad9467_chip_info ad9434_chip_tbl = {
.num_scales = ARRAY_SIZE(ad9434_scale_table),
.channels = ad9434_channels,
.num_channels = ARRAY_SIZE(ad9434_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_USER, AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_USER + 1,
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
.num_lanes = 6,
@@ -282,17 +377,78 @@ static const struct ad9467_chip_info ad9265_chip_tbl = {
.num_scales = ARRAY_SIZE(ad9265_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
+ AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.default_output_mode = AD9265_DEF_OUTPUT_MODE,
.vref_mask = AD9265_REG_VREF_MASK,
.has_dco = true,
+ .has_dco_invert = true,
+};
+
+static const struct ad9467_chip_info ad9643_chip_tbl = {
+ .name = "ad9643",
+ .id = CHIPID_AD9643,
+ .max_rate = 250000000UL,
+ .scale_table = ad9643_scale_table,
+ .num_scales = ARRAY_SIZE(ad9643_scale_table),
+ .channels = ad9643_channels,
+ .num_channels = ARRAY_SIZE(ad9643_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = BIT(AN877_ADC_TESTMODE_RAMP) |
+ GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_RAMP + 1,
+ .vref_mask = AD9643_REG_VREF_MASK,
+ .has_dco = true,
+ .has_dco_invert = true,
+ .dco_en = AN877_ADC_DCO_DELAY_ENABLE,
+};
+
+static const struct ad9467_chip_info ad9649_chip_tbl = {
+ .name = "ad9649",
+ .id = CHIPID_AD9649,
+ .max_rate = 80000000UL,
+ .scale_table = ad9649_scale_table,
+ .num_scales = ARRAY_SIZE(ad9649_scale_table),
+ .channels = ad9649_channels,
+ .num_channels = ARRAY_SIZE(ad9649_channels),
+ .test_points = AD9649_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY,
+ AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1,
+ .has_dco = true,
+ .has_dco_invert = true,
+ .dco_en = AN877_ADC_DCO_DELAY_ENABLE,
+};
+
+static const struct ad9467_chip_info ad9652_chip_tbl = {
+ .name = "ad9652",
+ .id = CHIPID_AD9652,
+ .max_rate = 310000000UL,
+ .scale_table = ad9652_scale_table,
+ .num_scales = ARRAY_SIZE(ad9652_scale_table),
+ .channels = ad9652_channels,
+ .num_channels = ARRAY_SIZE(ad9652_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
+ AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
+ .vref_mask = AD9652_REG_VREF_MASK,
+ .has_dco = true,
};
static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
{
const struct ad9467_chip_info *info = st->info;
- unsigned int i, vref_val;
+ unsigned int vref_val;
+ unsigned int i = 0;
int ret;
+ /* nothing to read if we only have one possible scale */
+ if (info->num_scales == 1)
+ goto out_get_scale;
+
ret = ad9467_spi_read(st, AN877_ADC_REG_VREF);
if (ret < 0)
return ret;
@@ -307,6 +463,7 @@ static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
if (i == info->num_scales)
return -ERANGE;
+out_get_scale:
__ad9467_get_scale(st, i, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
@@ -321,6 +478,8 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
if (val != 0)
return -EINVAL;
+ if (info->num_scales == 1)
+ return -EOPNOTSUPP;
for (i = 0; i < info->num_scales; i++) {
__ad9467_get_scale(st, i, &scale_val[0], &scale_val[1]);
@@ -352,40 +511,96 @@ static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode)
AN877_ADC_TRANSFER_SYNC);
}
-static int ad9647_calibrate_prepare(struct ad9467_state *st)
+static int ad9467_testmode_set(struct ad9467_state *st, unsigned int chan,
+ unsigned int test_mode)
+{
+ int ret;
+
+ if (st->info->num_channels > 1) {
+ /* so that the test mode is only applied to one channel */
+ ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX, BIT(chan));
+ if (ret)
+ return ret;
+ }
+
+ ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, test_mode);
+ if (ret)
+ return ret;
+
+ if (st->info->num_channels > 1) {
+ /* go to default state where all channels get write commands */
+ ret = ad9467_spi_write(st, AN877_ADC_REG_CHAN_INDEX,
+ GENMASK(st->info->num_channels - 1, 0));
+ if (ret)
+ return ret;
+ }
+
+ return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+}
+
+static int ad9467_backend_testmode_on(struct ad9467_state *st,
+ unsigned int chan,
+ enum iio_backend_test_pattern pattern)
{
struct iio_backend_data_fmt data = {
.enable = false,
};
- unsigned int c;
int ret;
- ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO,
- AN877_ADC_TESTMODE_PN9_SEQ);
+ ret = iio_backend_data_format_set(st->back, chan, &data);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_test_pattern_set(st->back, chan, pattern);
+ if (ret)
+ return ret;
+
+ return iio_backend_chan_enable(st->back, chan);
+}
+
+static int ad9467_backend_testmode_off(struct ad9467_state *st,
+ unsigned int chan)
+{
+ struct iio_backend_data_fmt data = {
+ .enable = true,
+ .sign_extend = true,
+ };
+ int ret;
+
+ ret = iio_backend_chan_disable(st->back, chan);
if (ret)
return ret;
- ret = ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
- AN877_ADC_TRANSFER_SYNC);
+ ret = iio_backend_test_pattern_set(st->back, chan,
+ IIO_BACKEND_NO_TEST_PATTERN);
if (ret)
return ret;
+ return iio_backend_data_format_set(st->back, chan, &data);
+}
+
+static int ad9647_calibrate_prepare(struct ad9467_state *st)
+{
+ unsigned int c;
+ int ret;
+
ret = ad9467_outputmode_set(st, st->info->default_output_mode);
if (ret)
return ret;
for (c = 0; c < st->info->num_channels; c++) {
- ret = iio_backend_data_format_set(st->back, c, &data);
+ ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_PN9_SEQ);
if (ret)
return ret;
- }
- ret = iio_backend_test_pattern_set(st->back, 0,
- IIO_BACKEND_ADI_PRBS_9A);
- if (ret)
- return ret;
+ ret = ad9467_backend_testmode_on(st, c,
+ IIO_BACKEND_ADI_PRBS_9A);
+ if (ret)
+ return ret;
+ }
- return iio_backend_chan_enable(st->back, 0);
+ return 0;
}
static int ad9647_calibrate_polarity_set(struct ad9467_state *st,
@@ -442,7 +657,7 @@ static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val)
if (st->info->has_dco) {
ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_DELAY,
- val);
+ val | st->info->dco_en);
if (ret)
return ret;
@@ -461,57 +676,38 @@ static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val)
static int ad9647_calibrate_stop(struct ad9467_state *st)
{
- struct iio_backend_data_fmt data = {
- .sign_extend = true,
- .enable = true,
- };
unsigned int c, mode;
int ret;
- ret = iio_backend_chan_disable(st->back, 0);
- if (ret)
- return ret;
-
- ret = iio_backend_test_pattern_set(st->back, 0,
- IIO_BACKEND_NO_TEST_PATTERN);
- if (ret)
- return ret;
-
for (c = 0; c < st->info->num_channels; c++) {
- ret = iio_backend_data_format_set(st->back, c, &data);
+ ret = ad9467_backend_testmode_off(st, c);
+ if (ret)
+ return ret;
+
+ ret = ad9467_testmode_set(st, c, AN877_ADC_TESTMODE_OFF);
if (ret)
return ret;
}
mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
- ret = ad9467_outputmode_set(st, mode);
- if (ret)
- return ret;
-
- ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO,
- AN877_ADC_TESTMODE_OFF);
- if (ret)
- return ret;
-
- return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
- AN877_ADC_TRANSFER_SYNC);
+ return ad9467_outputmode_set(st, mode);
}
static int ad9467_calibrate(struct ad9467_state *st)
{
- unsigned int point, val, inv_val, cnt, inv_cnt = 0;
+ unsigned int point, val, inv_val, cnt, inv_cnt = 0, c;
/*
* Half of the bitmap is for the inverted signal. The number of test
* points is the same though...
*/
- unsigned int test_points = AD9647_MAX_TEST_POINTS;
+ unsigned int test_points = st->info->test_points;
unsigned long sample_rate = clk_get_rate(st->clk);
struct device *dev = &st->spi->dev;
bool invert = false, stat;
int ret;
/* all points invalid */
- bitmap_fill(st->calib_map, BITS_PER_TYPE(st->calib_map));
+ bitmap_fill(st->calib_map, st->calib_map_size);
ret = ad9647_calibrate_prepare(st);
if (ret)
@@ -521,16 +717,31 @@ retune:
if (ret)
return ret;
- for (point = 0; point < test_points; point++) {
+ for (point = 0; point < st->info->test_points; point++) {
ret = ad9467_calibrate_apply(st, point);
if (ret)
return ret;
- ret = iio_backend_chan_status(st->back, 0, &stat);
- if (ret)
- return ret;
+ for (c = 0; c < st->info->num_channels; c++) {
+ ret = iio_backend_chan_status(st->back, c, &stat);
+ if (ret)
+ return ret;
- __assign_bit(point + invert * test_points, st->calib_map, stat);
+ /*
+ * A point is considered valid if all channels report no
+ * error. If one reports an error, then we consider the
+ * point as invalid and we can break the loop right away.
+ */
+ if (stat) {
+ dev_dbg(dev, "Invalid point(%u, inv:%u) for CH:%u\n",
+ point, invert, c);
+ break;
+ }
+
+ if (c == st->info->num_channels - 1)
+ __clear_bit(point + invert * test_points,
+ st->calib_map);
+ }
}
if (!invert) {
@@ -541,8 +752,13 @@ retune:
* a row.
*/
if (cnt < 3) {
- invert = true;
- goto retune;
+ if (AD9467_CAN_INVERT(st)) {
+ invert = true;
+ goto retune;
+ }
+
+ if (!cnt)
+ return -EIO;
}
} else {
inv_cnt = ad9467_find_optimal_point(st->calib_map, test_points,
@@ -679,7 +895,7 @@ static int ad9467_update_scan_mode(struct iio_dev *indio_dev,
return 0;
}
-static const struct iio_info ad9467_info = {
+static struct iio_info ad9467_info = {
.read_raw = ad9467_read_raw,
.write_raw = ad9467_write_raw,
.update_scan_mode = ad9467_update_scan_mode,
@@ -762,12 +978,134 @@ static int ad9467_iio_backend_get(struct ad9467_state *st)
return -ENODEV;
}
+static int ad9467_test_mode_available_show(struct seq_file *s, void *ignored)
+{
+ struct ad9467_state *st = s->private;
+ unsigned int bit;
+
+ for_each_set_bit(bit, &st->info->test_mask, st->info->test_mask_len)
+ seq_printf(s, "%s\n", ad9467_test_modes[bit]);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ad9467_test_mode_available);
+
+static ssize_t ad9467_chan_test_mode_read(struct file *file,
+ char __user *userbuf, size_t count,
+ loff_t *ppos)
+{
+ struct ad9467_chan_test_mode *chan = file->private_data;
+ struct ad9467_state *st = chan->st;
+ char buf[128] = {0};
+ size_t len;
+ int ret;
+
+ if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ ||
+ chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) {
+ len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t",
+ ad9467_test_modes[chan->mode]);
+
+ ret = iio_backend_debugfs_print_chan_status(st->back, chan->idx,
+ buf + len,
+ sizeof(buf) - len);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ } else if (chan->mode == AN877_ADC_TESTMODE_OFF) {
+ len = scnprintf(buf, sizeof(buf), "No test Running...\n");
+ } else {
+ len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test on CH:%u\n",
+ ad9467_test_modes[chan->mode], chan->idx);
+ }
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static ssize_t ad9467_chan_test_mode_write(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ad9467_chan_test_mode *chan = file->private_data;
+ struct ad9467_state *st = chan->st;
+ char test_mode[32] = {0};
+ unsigned int mode;
+ int ret;
+
+ ret = simple_write_to_buffer(test_mode, sizeof(test_mode) - 1, ppos,
+ userbuf, count);
+ if (ret < 0)
+ return ret;
+
+ for_each_set_bit(mode, &st->info->test_mask, st->info->test_mask_len) {
+ if (sysfs_streq(test_mode, ad9467_test_modes[mode]))
+ break;
+ }
+
+ if (mode == st->info->test_mask_len)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ if (mode == AN877_ADC_TESTMODE_OFF) {
+ unsigned int out_mode;
+
+ if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ ||
+ chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) {
+ ret = ad9467_backend_testmode_off(st, chan->idx);
+ if (ret)
+ return ret;
+ }
+
+ ret = ad9467_testmode_set(st, chan->idx, mode);
+ if (ret)
+ return ret;
+
+ out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ ret = ad9467_outputmode_set(st, out_mode);
+ if (ret)
+ return ret;
+ } else {
+ ret = ad9467_outputmode_set(st, st->info->default_output_mode);
+ if (ret)
+ return ret;
+
+ ret = ad9467_testmode_set(st, chan->idx, mode);
+ if (ret)
+ return ret;
+
+ /* some patterns have a backend matching monitoring block */
+ if (mode == AN877_ADC_TESTMODE_PN9_SEQ) {
+ ret = ad9467_backend_testmode_on(st, chan->idx,
+ IIO_BACKEND_ADI_PRBS_9A);
+ if (ret)
+ return ret;
+ } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) {
+ ret = ad9467_backend_testmode_on(st, chan->idx,
+ IIO_BACKEND_ADI_PRBS_23A);
+ if (ret)
+ return ret;
+ }
+ }
+
+ chan->mode = mode;
+
+ return count;
+}
+
+static const struct file_operations ad9467_chan_test_mode_fops = {
+ .open = simple_open,
+ .read = ad9467_chan_test_mode_read,
+ .write = ad9467_chan_test_mode_write,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
static ssize_t ad9467_dump_calib_table(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ad9467_state *st = file->private_data;
- unsigned int bit, size = BITS_PER_TYPE(st->calib_map);
+ unsigned int bit;
/* +2 for the newline and +1 for the string termination */
unsigned char map[AD9647_MAX_TEST_POINTS * 2 + 3];
ssize_t len = 0;
@@ -776,8 +1114,8 @@ static ssize_t ad9467_dump_calib_table(struct file *file,
if (*ppos)
goto out_read;
- for (bit = 0; bit < size; bit++) {
- if (bit == size / 2)
+ for (bit = 0; bit < st->calib_map_size; bit++) {
+ if (AD9467_CAN_INVERT(st) && bit == st->calib_map_size / 2)
len += scnprintf(map + len, sizeof(map) - len, "\n");
len += scnprintf(map + len, sizeof(map) - len, "%c",
@@ -800,12 +1138,33 @@ static void ad9467_debugfs_init(struct iio_dev *indio_dev)
{
struct dentry *d = iio_get_debugfs_dentry(indio_dev);
struct ad9467_state *st = iio_priv(indio_dev);
+ char attr_name[32];
+ unsigned int chan;
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return;
+ st->chan_test = devm_kcalloc(&st->spi->dev, st->info->num_channels,
+ sizeof(*st->chan_test), GFP_KERNEL);
+ if (!st->chan_test)
+ return;
+
debugfs_create_file("calibration_table_dump", 0400, d, st,
&ad9467_calib_table_fops);
+
+ for (chan = 0; chan < st->info->num_channels; chan++) {
+ snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode",
+ chan);
+ st->chan_test[chan].idx = chan;
+ st->chan_test[chan].st = st;
+ debugfs_create_file(attr_name, 0600, d, &st->chan_test[chan],
+ &ad9467_chan_test_mode_fops);
+ }
+
+ debugfs_create_file("in_voltage_test_mode_available", 0400, d, st,
+ &ad9467_test_mode_available_fops);
+
+ iio_backend_debugfs_add(st->back, indio_dev);
}
static int ad9467_probe(struct spi_device *spi)
@@ -826,6 +1185,10 @@ static int ad9467_probe(struct spi_device *spi)
if (!st->info)
return -ENODEV;
+ st->calib_map_size = st->info->test_points;
+ if (AD9467_CAN_INVERT(st))
+ st->calib_map_size *= 2;
+
st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk");
if (IS_ERR(st->clk))
return PTR_ERR(st->clk);
@@ -850,6 +1213,8 @@ static int ad9467_probe(struct spi_device *spi)
return -ENODEV;
}
+ if (st->info->num_scales > 1)
+ ad9467_info.read_avail = ad9467_read_avail;
indio_dev->name = st->info->name;
indio_dev->channels = st->info->channels;
indio_dev->num_channels = st->info->num_channels;
@@ -884,7 +1249,10 @@ static const struct of_device_id ad9467_of_match[] = {
{ .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, },
{ .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, },
- {}
+ { .compatible = "adi,ad9643", .data = &ad9643_chip_tbl, },
+ { .compatible = "adi,ad9649", .data = &ad9649_chip_tbl, },
+ { .compatible = "adi,ad9652", .data = &ad9652_chip_tbl, },
+ { }
};
MODULE_DEVICE_TABLE(of, ad9467_of_match);
@@ -892,7 +1260,10 @@ static const struct spi_device_id ad9467_ids[] = {
{ "ad9265", (kernel_ulong_t)&ad9265_chip_tbl },
{ "ad9434", (kernel_ulong_t)&ad9434_chip_tbl },
{ "ad9467", (kernel_ulong_t)&ad9467_chip_tbl },
- {}
+ { "ad9643", (kernel_ulong_t)&ad9643_chip_tbl },
+ { "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, },
+ { "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, },
+ { }
};
MODULE_DEVICE_TABLE(spi, ad9467_ids);
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index dcd557e93586..e2bed2d648f2 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -351,7 +351,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
if (sigma_delta->num_slots == 1) {
channel = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
+ iio_get_masklength(indio_dev));
ret = ad_sigma_delta_set_channel(sigma_delta,
indio_dev->channels[channel].address);
if (ret)
@@ -364,7 +364,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
* implementation is mandatory.
*/
slot = 0;
- for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
sigma_delta->slots[slot] = indio_dev->channels[i].address;
slot++;
}
@@ -526,7 +526,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
- return bitmap_weight(mask, indio_dev->masklength) <= sigma_delta->num_slots;
+ return bitmap_weight(mask, iio_get_masklength(indio_dev)) <= sigma_delta->num_slots;
}
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 21ce7564e83d..5c8c87eb36d1 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -61,6 +61,10 @@
#define ADI_AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40)
#define ADI_AXI_ADC_CHAN_STAT_PN_MASK GENMASK(2, 1)
+/* out of sync */
+#define ADI_AXI_ADC_CHAN_STAT_PN_OOS BIT(1)
+/* spurious out of sync */
+#define ADI_AXI_ADC_CHAN_STAT_PN_ERR BIT(2)
#define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40)
#define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16)
@@ -199,17 +203,19 @@ static int axi_adc_test_pattern_set(struct iio_backend *back,
return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan),
ADI_AXI_ADC_CHAN_PN_SEL_MASK,
FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 0));
+ case IIO_BACKEND_ADI_PRBS_23A:
+ return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan),
+ ADI_AXI_ADC_CHAN_PN_SEL_MASK,
+ FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 1));
default:
return -EINVAL;
}
}
-static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
- bool *error)
+static int axi_adc_read_chan_status(struct adi_axi_adc_state *st, unsigned int chan,
+ unsigned int *status)
{
- struct adi_axi_adc_state *st = iio_backend_get_priv(back);
int ret;
- u32 val;
guard(mutex)(&st->lock);
/* reset test bits by setting them */
@@ -221,7 +227,18 @@ static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
/* let's give enough time to validate or erroring the incoming pattern */
fsleep(1000);
- ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), &val);
+ return regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan),
+ status);
+}
+
+static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
+ bool *error)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ u32 val;
+ int ret;
+
+ ret = axi_adc_read_chan_status(st, chan, &val);
if (ret)
return ret;
@@ -233,6 +250,30 @@ static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan,
return 0;
}
+static int axi_adc_debugfs_print_chan_status(struct iio_backend *back,
+ unsigned int chan, char *buf,
+ size_t len)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ u32 val;
+ int ret;
+
+ ret = axi_adc_read_chan_status(st, chan, &val);
+ if (ret)
+ return ret;
+
+ /*
+ * PN_ERR is cleared in case out of sync is set. Hence, no point in
+ * checking both bits.
+ */
+ if (val & ADI_AXI_ADC_CHAN_STAT_PN_OOS)
+ return scnprintf(buf, len, "CH%u: Out of Sync.\n", chan);
+ if (val & ADI_AXI_ADC_CHAN_STAT_PN_ERR)
+ return scnprintf(buf, len, "CH%u: Spurious Out of Sync.\n", chan);
+
+ return scnprintf(buf, len, "CH%u: OK.\n", chan);
+}
+
static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
@@ -267,13 +308,24 @@ static void axi_adc_free_buffer(struct iio_backend *back,
iio_dmaengine_buffer_free(buffer);
}
+static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
static const struct regmap_config axi_adc_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
.reg_stride = 4,
};
-static const struct iio_backend_ops adi_axi_adc_generic = {
+static const struct iio_backend_ops adi_axi_adc_ops = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
.data_format_set = axi_adc_data_format_set,
@@ -285,6 +337,13 @@ static const struct iio_backend_ops adi_axi_adc_generic = {
.iodelay_set = axi_adc_iodelays_set,
.test_pattern_set = axi_adc_test_pattern_set,
.chan_status = axi_adc_chan_status,
+ .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
+ .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
+};
+
+static const struct iio_backend_info adi_axi_adc_generic = {
+ .name = "axi-adc",
+ .ops = &adi_axi_adc_ops,
};
static int adi_axi_adc_probe(struct platform_device *pdev)
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 090416c0d622..1d5fd5f534b8 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -555,8 +555,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
- NULL)) {
+ if (of_property_present(data->dev->of_node, "aspeed,battery-sensing")) {
if (data->model_data->bat_sense_sup) {
data->battery_sensing = 1;
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
@@ -695,7 +694,7 @@ static const struct of_device_id aspeed_adc_matches[] = {
{ .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
{ .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data },
{ .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index eb501e3c86a5..9c39acff17e6 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -7,6 +7,7 @@
#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -268,9 +269,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
struct iio_chan_spec const *chan;
int i, j = 0;
- for (i = 0; i < idev->masklength; i++) {
- if (!test_bit(i, idev->active_scan_mask))
- continue;
+ iio_for_each_active_channel(idev, i) {
chan = idev->channels + i;
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
j++;
@@ -543,22 +542,18 @@ static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
int i;
for (i = 0; i < st->caps->trigger_number; i++) {
- char *name = kasprintf(GFP_KERNEL,
- "%s-dev%d-%s",
- idev->name,
- iio_device_id(idev),
- triggers[i].name);
+ char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s-dev%d-%s",
+ idev->name,
+ iio_device_id(idev),
+ triggers[i].name);
if (!name)
return -ENOMEM;
if (strcmp(trigger_name, name) == 0) {
- kfree(name);
if (triggers[i].value == 0)
return -EINVAL;
return triggers[i].value;
}
-
- kfree(name);
}
return -EINVAL;
@@ -1340,7 +1335,7 @@ static const struct of_device_id at91_adc_dt_ids[] = {
{ .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps },
{ .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps },
{ .compatible = "atmel,sama5d3-adc", .data = &sama5d3_caps },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index b487e577befb..d43c8d124a0c 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -5,6 +5,7 @@
* Quentin Schulz <quentin.schulz@free-electrons.com>
*/
+#include <asm/unaligned.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
@@ -30,6 +31,8 @@
#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
+#define AXP717_ADC_EN1_MASK GENMASK(7, 0)
+
#define AXP192_GPIO30_IN_RANGE_GPIO0 BIT(0)
#define AXP192_GPIO30_IN_RANGE_GPIO1 BIT(1)
#define AXP192_GPIO30_IN_RANGE_GPIO2 BIT(2)
@@ -43,6 +46,13 @@
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
+#define AXP717_ADC_DATA_TS 0x00
+#define AXP717_ADC_DATA_TEMP 0x01
+#define AXP717_ADC_DATA_VMID 0x02
+#define AXP717_ADC_DATA_BKUP_BATT 0x03
+
+#define AXP717_ADC_DATA_MASK GENMASK(13, 0)
+
#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
@@ -125,6 +135,20 @@ enum axp22x_adc_channel_i {
AXP22X_BATT_DISCHRG_I,
};
+enum axp717_adc_channel_v {
+ AXP717_BATT_V = 0,
+ AXP717_TS_IN,
+ AXP717_VBUS_V,
+ AXP717_VSYS_V,
+ AXP717_DIE_TEMP_V,
+ AXP717_VMID_V = 6,
+ AXP717_BKUP_BATT_V,
+};
+
+enum axp717_adc_channel_i {
+ AXP717_BATT_CHRG_I = 5,
+};
+
enum axp813_adc_channel_v {
AXP813_TS_IN = 0,
AXP813_GPIO0_V,
@@ -179,6 +203,22 @@ static struct iio_map axp22x_maps[] = {
}, { /* sentinel */ }
};
+static struct iio_map axp717_maps[] = {
+ {
+ .consumer_dev_name = "axp20x-usb-power-supply",
+ .consumer_channel = "vbus_v",
+ .adc_channel_label = "vbus_v",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_v",
+ .adc_channel_label = "batt_v",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_chrg_i",
+ .adc_channel_label = "batt_chrg_i",
+ },
+};
+
/*
* Channels are mapped by physical system. Their channels share the same index.
* i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
@@ -274,6 +314,29 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
AXP22X_TS_ADC_H),
};
+/*
+ * Scale and offset is unknown for temp, ts, batt_chrg_i, vmid_v, and
+ * bkup_batt_v channels. Leaving scale and offset undefined for now.
+ */
+static const struct iio_chan_spec axp717_adc_channels[] = {
+ AXP20X_ADC_CHANNEL(AXP717_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP717_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP717_TS_IN, "ts_v", IIO_VOLTAGE,
+ AXP717_ADC_DATA_H),
+ AXP20X_ADC_CHANNEL(AXP717_VBUS_V, "vbus_v", IIO_VOLTAGE,
+ AXP717_VBUS_V_H),
+ AXP20X_ADC_CHANNEL(AXP717_VSYS_V, "vsys_v", IIO_VOLTAGE,
+ AXP717_VSYS_V_H),
+ AXP20X_ADC_CHANNEL(AXP717_DIE_TEMP_V, "pmic_temp", IIO_TEMP,
+ AXP717_ADC_DATA_H),
+ AXP20X_ADC_CHANNEL(AXP717_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP717_BATT_CHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP717_VMID_V, "vmid_v", IIO_VOLTAGE,
+ AXP717_ADC_DATA_H),
+ AXP20X_ADC_CHANNEL(AXP717_BKUP_BATT_V, "bkup_batt_v", IIO_VOLTAGE,
+ AXP717_ADC_DATA_H),
+};
+
static const struct iio_chan_spec axp813_adc_channels[] = {
{
.type = IIO_TEMP,
@@ -354,6 +417,51 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
}
+static int axp717_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ u8 bulk_reg[2];
+ int ret;
+
+ /*
+ * A generic "ADC data" channel is used for TS, tdie, vmid,
+ * and vbackup. This channel must both first be enabled and
+ * also selected before it can be read.
+ */
+ switch (chan->channel) {
+ case AXP717_TS_IN:
+ regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
+ AXP717_ADC_DATA_TS);
+ break;
+ case AXP717_DIE_TEMP_V:
+ regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
+ AXP717_ADC_DATA_TEMP);
+ break;
+ case AXP717_VMID_V:
+ regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
+ AXP717_ADC_DATA_VMID);
+ break;
+ case AXP717_BKUP_BATT_V:
+ regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
+ AXP717_ADC_DATA_BKUP_BATT);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * All channels are 14 bits, with the first 2 bits on the high
+ * register reserved and the remaining bits as the ADC value.
+ */
+ ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2);
+ if (ret < 0)
+ return ret;
+
+ *val = FIELD_GET(AXP717_ADC_DATA_MASK, get_unaligned_be16(bulk_reg));
+ return IIO_VAL_INT;
+}
+
static int axp813_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -571,6 +679,27 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
}
}
+static int axp717_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_CURRENT:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
int *val2)
{
@@ -746,6 +875,22 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
}
}
+static int axp717_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return axp717_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp717_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp813_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -860,6 +1005,10 @@ static const struct iio_info axp22x_adc_iio_info = {
.read_raw = axp22x_read_raw,
};
+static const struct iio_info axp717_adc_iio_info = {
+ .read_raw = axp717_read_raw,
+};
+
static const struct iio_info axp813_adc_iio_info = {
.read_raw = axp813_read_raw,
};
@@ -889,7 +1038,9 @@ struct axp_data {
const struct iio_info *iio_info;
int num_channels;
struct iio_chan_spec const *channels;
+ unsigned long adc_en1;
unsigned long adc_en1_mask;
+ unsigned long adc_en2;
unsigned long adc_en2_mask;
int (*adc_rate)(struct axp20x_adc_iio *info,
int rate);
@@ -910,7 +1061,9 @@ static const struct axp_data axp20x_data = {
.iio_info = &axp20x_adc_iio_info,
.num_channels = ARRAY_SIZE(axp20x_adc_channels),
.channels = axp20x_adc_channels,
+ .adc_en1 = AXP20X_ADC_EN1,
.adc_en1_mask = AXP20X_ADC_EN1_MASK,
+ .adc_en2 = AXP20X_ADC_EN2,
.adc_en2_mask = AXP20X_ADC_EN2_MASK,
.adc_rate = axp20x_adc_rate,
.maps = axp20x_maps,
@@ -920,15 +1073,26 @@ static const struct axp_data axp22x_data = {
.iio_info = &axp22x_adc_iio_info,
.num_channels = ARRAY_SIZE(axp22x_adc_channels),
.channels = axp22x_adc_channels,
+ .adc_en1 = AXP20X_ADC_EN1,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp22x_adc_rate,
.maps = axp22x_maps,
};
+static const struct axp_data axp717_data = {
+ .iio_info = &axp717_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp717_adc_channels),
+ .channels = axp717_adc_channels,
+ .adc_en1 = AXP717_ADC_CH_EN_CONTROL,
+ .adc_en1_mask = AXP717_ADC_EN1_MASK,
+ .maps = axp717_maps,
+};
+
static const struct axp_data axp813_data = {
.iio_info = &axp813_adc_iio_info,
.num_channels = ARRAY_SIZE(axp813_adc_channels),
.channels = axp813_adc_channels,
+ .adc_en1 = AXP20X_ADC_EN1,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp813_adc_rate,
.maps = axp22x_maps,
@@ -938,6 +1102,7 @@ static const struct of_device_id axp20x_adc_of_match[] = {
{ .compatible = "x-powers,axp192-adc", .data = (void *)&axp192_data, },
{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
+ { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, },
{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
{ /* sentinel */ }
};
@@ -947,6 +1112,7 @@ static const struct platform_device_id axp20x_adc_id_match[] = {
{ .name = "axp192-adc", .driver_data = (kernel_ulong_t)&axp192_data, },
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
+ { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, },
{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
{ /* sentinel */ },
};
@@ -988,14 +1154,16 @@ static int axp20x_probe(struct platform_device *pdev)
indio_dev->channels = info->data->channels;
/* Enable the ADCs on IP */
- regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
+ regmap_write(info->regmap, info->data->adc_en1,
+ info->data->adc_en1_mask);
if (info->data->adc_en2_mask)
- regmap_set_bits(info->regmap, AXP20X_ADC_EN2,
+ regmap_set_bits(info->regmap, info->data->adc_en2,
info->data->adc_en2_mask);
/* Configure ADCs rate */
- info->data->adc_rate(info, 100);
+ if (info->data->adc_rate)
+ info->data->adc_rate(info, 100);
ret = iio_map_array_register(indio_dev, info->data->maps);
if (ret < 0) {
@@ -1015,10 +1183,10 @@ fail_register:
iio_map_array_unregister(indio_dev);
fail_map:
- regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
+ regmap_write(info->regmap, info->data->adc_en1, 0);
if (info->data->adc_en2_mask)
- regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
+ regmap_write(info->regmap, info->data->adc_en2, 0);
return ret;
}
@@ -1031,10 +1199,10 @@ static void axp20x_remove(struct platform_device *pdev)
iio_device_unregister(indio_dev);
iio_map_array_unregister(indio_dev);
- regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
+ regmap_write(info->regmap, info->data->adc_en1, 0);
if (info->data->adc_en2_mask)
- regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
+ regmap_write(info->regmap, info->data->adc_en2, 0);
}
static struct platform_driver axp20x_adc_driver = {
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index f135cf2362df..8c3acc0cd7e9 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -299,7 +299,7 @@ static int axp288_adc_probe(struct platform_device *pdev)
static const struct platform_device_id axp288_adc_id_table[] = {
{ .name = "axp288_adc" },
- {},
+ { }
};
static struct platform_driver axp288_adc_driver = {
diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c
index 6bc149c51414..cdfe304eaa20 100644
--- a/drivers/iio/adc/bcm_iproc_adc.c
+++ b/drivers/iio/adc/bcm_iproc_adc.c
@@ -606,7 +606,7 @@ static void iproc_adc_remove(struct platform_device *pdev)
static const struct of_device_id iproc_adc_of_match[] = {
{.compatible = "brcm,iproc-static-adc", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, iproc_adc_of_match);
diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c
index 4cdddc6e36e9..fa04e0a5f645 100644
--- a/drivers/iio/adc/berlin2-adc.c
+++ b/drivers/iio/adc/berlin2-adc.c
@@ -351,7 +351,7 @@ static int berlin2_adc_probe(struct platform_device *pdev)
static const struct of_device_id berlin2_adc_match[] = {
{ .compatible = "marvell,berlin2-adc", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, berlin2_adc_match);
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
index a432342348ab..2c51b90b7101 100644
--- a/drivers/iio/adc/cc10001_adc.c
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -157,9 +157,7 @@ static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
i = 0;
sample_invalid = false;
- for_each_set_bit(scan_idx, indio_dev->active_scan_mask,
- indio_dev->masklength) {
-
+ iio_for_each_active_channel(indio_dev, scan_idx) {
channel = indio_dev->channels[scan_idx].channel;
cc10001_adc_start(adc_dev, channel);
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
index 06cfbbabaf8d..de7252a10047 100644
--- a/drivers/iio/adc/dln2-adc.c
+++ b/drivers/iio/adc/dln2-adc.c
@@ -108,7 +108,7 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2)
dln2->demux_count = 0;
/* Optimize all 8-channels case */
- if (indio_dev->masklength &&
+ if (iio_get_masklength(indio_dev) &&
(*indio_dev->active_scan_mask & 0xff) == 0xff) {
dln2_adc_add_demux(dln2, 0, 0, 16);
dln2->ts_pad_offset = 0;
@@ -117,9 +117,7 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2)
}
/* Build demux table from fixed 8-channels to active_scan_mask */
- for_each_set_bit(out_ind,
- indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, out_ind) {
/* Handle timestamp separately */
if (out_ind == DLN2_ADC_MAX_CHANNELS)
break;
@@ -541,7 +539,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
/* Assign trigger channel based on first enabled channel */
trigger_chan = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
+ iio_get_masklength(indio_dev));
if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
dln2->trigger_chan = trigger_chan;
ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c
index 971942ce4c66..cc38d5e0608e 100644
--- a/drivers/iio/adc/ep93xx_adc.c
+++ b/drivers/iio/adc/ep93xx_adc.c
@@ -228,7 +228,7 @@ static void ep93xx_adc_remove(struct platform_device *pdev)
static const struct of_device_id ep93xx_adc_of_ids[] = {
{ .compatible = "cirrus,ep9301-adc" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ep93xx_adc_of_ids);
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 78fada4b7b1c..4d00ee8dd14d 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -519,7 +519,7 @@ static const struct of_device_id exynos_adc_match[] = {
.compatible = "samsung,exynos7-adc",
.data = &exynos7_adc_data,
},
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, exynos_adc_match);
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index 771fa12bdc02..fb635a756440 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -524,7 +524,7 @@ static int hi8435_probe(struct spi_device *spi)
static const struct of_device_id hi8435_dt_ids[] = {
{ .compatible = "holt,hi8435" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index b3372ccff7d5..8da0419ecfa3 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -363,10 +363,7 @@ static irqreturn_t hx711_trigger(int irq, void *p)
memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer));
- for (i = 0; i < indio_dev->masklength; i++) {
- if (!test_bit(i, indio_dev->active_scan_mask))
- continue;
-
+ iio_for_each_active_channel(indio_dev, i) {
hx711_data->buffer[j] = hx711_reset_read(hx711_data,
indio_dev->channels[i].channel);
j++;
@@ -555,7 +552,7 @@ static int hx711_probe(struct platform_device *pdev)
static const struct of_device_id of_hx711_match[] = {
{ .compatible = "avia,hx711", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, of_hx711_match);
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index 727e390bd979..48c95e12e791 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -755,8 +755,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
* Single register reads: bulk_read will not work with ina226/219
* as there is no auto-increment of the register pointer.
*/
- for_each_set_bit(bit, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, bit) {
unsigned int val;
ret = regmap_read(chip->regmap,
@@ -1053,12 +1052,12 @@ static void ina2xx_remove(struct i2c_client *client)
}
static const struct i2c_device_id ina2xx_id[] = {
- {"ina219", ina219},
- {"ina220", ina219},
- {"ina226", ina226},
- {"ina230", ina226},
- {"ina231", ina226},
- {}
+ { "ina219", ina219 },
+ { "ina220", ina219 },
+ { "ina226", ina226 },
+ { "ina230", ina226 },
+ { "ina231", ina226 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
@@ -1083,7 +1082,7 @@ static const struct of_device_id ina2xx_of_match[] = {
.compatible = "ti,ina231",
.data = (void *)ina226
},
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, ina2xx_of_match);
diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
index af70ca760797..1e802c8779a4 100644
--- a/drivers/iio/adc/ingenic-adc.c
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -908,7 +908,7 @@ static const struct of_device_id ingenic_adc_of_match[] = {
{ .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, },
{ .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, },
{ .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c
index e34ed7dacd89..43a7bc8158b5 100644
--- a/drivers/iio/adc/lpc32xx_adc.c
+++ b/drivers/iio/adc/lpc32xx_adc.c
@@ -217,7 +217,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
static const struct of_device_id lpc32xx_adc_match[] = {
{ .compatible = "nxp,lpc3220-adc" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, lpc32xx_adc_match);
diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c
index 2593fa4322eb..f06dd0b9a858 100644
--- a/drivers/iio/adc/ltc2496.c
+++ b/drivers/iio/adc/ltc2496.c
@@ -94,7 +94,7 @@ static const struct ltc2497_chip_info ltc2496_info = {
static const struct of_device_id ltc2496_of_match[] = {
{ .compatible = "lltc,ltc2496", .data = &ltc2496_info, },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, ltc2496_of_match);
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index 6401a7727c31..f010b2fd1202 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -151,7 +151,7 @@ MODULE_DEVICE_TABLE(i2c, ltc2497_id);
static const struct of_device_id ltc2497_of_match[] = {
{ .compatible = "lltc,ltc2497", .data = &ltc2497_info[TYPE_LTC2497] },
{ .compatible = "lltc,ltc2499", .data = &ltc2497_info[TYPE_LTC2499] },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, ltc2497_of_match);
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 136fcf753837..f5ba4a1b5a7d 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -73,13 +73,13 @@ enum max1027_id {
};
static const struct spi_device_id max1027_id[] = {
- {"max1027", max1027},
- {"max1029", max1029},
- {"max1031", max1031},
- {"max1227", max1227},
- {"max1229", max1229},
- {"max1231", max1231},
- {}
+ { "max1027", max1027 },
+ { "max1029", max1029 },
+ { "max1031", max1031 },
+ { "max1227", max1227 },
+ { "max1229", max1229 },
+ { "max1231", max1231 },
+ { }
};
MODULE_DEVICE_TABLE(spi, max1027_id);
@@ -90,7 +90,7 @@ static const struct of_device_id max1027_adc_dt_ids[] = {
{ .compatible = "maxim,max1227" },
{ .compatible = "maxim,max1229" },
{ .compatible = "maxim,max1231" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
index 49e38dca8fe2..2f07437caec3 100644
--- a/drivers/iio/adc/max11100.c
+++ b/drivers/iio/adc/max11100.c
@@ -143,8 +143,8 @@ static int max11100_probe(struct spi_device *spi)
}
static const struct of_device_id max11100_ids[] = {
- {.compatible = "maxim,max11100"},
- { },
+ { .compatible = "maxim,max11100" },
+ { }
};
MODULE_DEVICE_TABLE(of, max11100_ids);
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
index 75ab57d9aef7..3d0a7d0eb7ee 100644
--- a/drivers/iio/adc/max1118.c
+++ b/drivers/iio/adc/max1118.c
@@ -174,8 +174,7 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p)
mutex_lock(&adc->lock);
- for_each_set_bit(scan_index, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = max1118_read(indio_dev, scan_chan->channel);
@@ -261,7 +260,7 @@ static const struct spi_device_id max1118_id[] = {
{ "max1117", max1117 },
{ "max1118", max1118 },
{ "max1119", max1119 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, max1118_id);
@@ -269,7 +268,7 @@ static const struct of_device_id max1118_dt_ids[] = {
{ .compatible = "maxim,max1117" },
{ .compatible = "maxim,max1118" },
{ .compatible = "maxim,max1119" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, max1118_dt_ids);
diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c
index 500bb09ab19b..d62c1a011659 100644
--- a/drivers/iio/adc/max1241.c
+++ b/drivers/iio/adc/max1241.c
@@ -177,12 +177,12 @@ static int max1241_probe(struct spi_device *spi)
static const struct spi_device_id max1241_id[] = {
{ "max1241", max1241 },
- {}
+ { }
};
static const struct of_device_id max1241_dt_ids[] = {
{ .compatible = "maxim,max1241" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, max1241_dt_ids);
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index bf4b6dc53fd2..d0c6e94f7204 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -13,6 +13,7 @@
*/
#include <linux/interrupt.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
@@ -818,7 +819,6 @@ static int max1363_read_event_config(struct iio_dev *indio_dev,
static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
{
- u8 *tx_buf;
int ret, i = 3, j;
unsigned long numelements;
int len;
@@ -850,11 +850,10 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
}
numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS);
len = 3 * numelements + 3;
- tx_buf = kmalloc(len, GFP_KERNEL);
- if (!tx_buf) {
- ret = -ENOMEM;
- goto error_ret;
- }
+ u8 *tx_buf __free(kfree) = kmalloc(len, GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
+
tx_buf[0] = st->configbyte;
tx_buf[1] = st->setupbyte;
tx_buf[2] = (st->monitor_speed << 1);
@@ -893,11 +892,9 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
ret = st->send(st->client, tx_buf, len);
if (ret < 0)
- goto error_ret;
- if (ret != len) {
- ret = -EIO;
- goto error_ret;
- }
+ return ret;
+ if (ret != len)
+ return -EIO;
/*
* Now that we hopefully have sensible thresholds in place it is
@@ -910,18 +907,13 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0;
ret = st->send(st->client, tx_buf, 2);
if (ret < 0)
- goto error_ret;
- if (ret != 2) {
- ret = -EIO;
- goto error_ret;
- }
- ret = 0;
- st->monitor_on = true;
-error_ret:
+ return ret;
+ if (ret != 2)
+ return -EIO;
- kfree(tx_buf);
+ st->monitor_on = true;
- return ret;
+ return 0;
}
/*
diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c
index 6c2ea2bc52c6..ffec22be2d59 100644
--- a/drivers/iio/adc/max34408.c
+++ b/drivers/iio/adc/max34408.c
@@ -250,14 +250,14 @@ static const struct of_device_id max34408_of_match[] = {
.compatible = "maxim,max34409",
.data = &max34409_model_data,
},
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, max34408_of_match);
static const struct i2c_device_id max34408_id[] = {
{ "max34408", (kernel_ulong_t)&max34408_model_data },
{ "max34409", (kernel_ulong_t)&max34409_model_data },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, max34408_id);
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 76e517b7b1e4..14fe42fc4b7d 100644
--- a/drivers/iio/adc/max9611.c
+++ b/drivers/iio/adc/max9611.c
@@ -504,9 +504,9 @@ static int max9611_init(struct max9611_dev *max9611)
}
static const struct of_device_id max9611_of_table[] = {
- {.compatible = "maxim,max9611", .data = "max9611"},
- {.compatible = "maxim,max9612", .data = "max9612"},
- { },
+ { .compatible = "maxim,max9611", .data = "max9611" },
+ { .compatible = "maxim,max9612", .data = "max9612" },
+ { }
};
MODULE_DEVICE_TABLE(of, max9611_of_table);
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index da1421bd7b62..57cff3772ebe 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -459,16 +459,6 @@ static int mcp320x_probe(struct spi_device *spi)
}
static const struct of_device_id mcp320x_dt_ids[] = {
- /* NOTE: The use of compatibles with no vendor prefix is deprecated. */
- { .compatible = "mcp3001" },
- { .compatible = "mcp3002" },
- { .compatible = "mcp3004" },
- { .compatible = "mcp3008" },
- { .compatible = "mcp3201" },
- { .compatible = "mcp3202" },
- { .compatible = "mcp3204" },
- { .compatible = "mcp3208" },
- { .compatible = "mcp3301" },
{ .compatible = "microchip,mcp3001" },
{ .compatible = "microchip,mcp3002" },
{ .compatible = "microchip,mcp3004" },
diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c
index d83bed0e63d2..a68f1cd6883e 100644
--- a/drivers/iio/adc/mcp3564.c
+++ b/drivers/iio/adc/mcp3564.c
@@ -349,8 +349,6 @@ struct mcp3564_chip_info {
* struct mcp3564_state - working data for a ADC device
* @chip_info: chip specific data
* @spi: SPI device structure
- * @vref: the regulator device used as a voltage reference in case
- * external voltage reference is used
* @vref_mv: voltage reference value in miliVolts
* @lock: synchronize access to driver's state members
* @dev_addr: hardware device address
@@ -369,7 +367,6 @@ struct mcp3564_chip_info {
struct mcp3564_state {
const struct mcp3564_chip_info *chip_info;
struct spi_device *spi;
- struct regulator *vref;
unsigned short vref_mv;
struct mutex lock; /* Synchronize access to driver's state members */
u8 dev_addr;
@@ -1085,11 +1082,6 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
return 0;
}
-static void mcp3564_disable_reg(void *reg)
-{
- regulator_disable(reg);
-}
-
static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc)
{
unsigned int pow = adc->chip_info->resolution - 1;
@@ -1110,7 +1102,7 @@ static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc)
}
}
-static int mcp3564_config(struct iio_dev *indio_dev)
+static int mcp3564_config(struct iio_dev *indio_dev, bool *use_internal_vref_attr)
{
struct mcp3564_state *adc = iio_priv(indio_dev);
struct device *dev = &adc->spi->dev;
@@ -1119,6 +1111,7 @@ static int mcp3564_config(struct iio_dev *indio_dev)
enum mcp3564_ids ids;
int ret = 0;
unsigned int tmp = 0x01;
+ bool internal_vref;
bool err = false;
/*
@@ -1218,36 +1211,22 @@ static int mcp3564_config(struct iio_dev *indio_dev)
dev_dbg(dev, "Found %s chip\n", adc->chip_info->name);
- adc->vref = devm_regulator_get_optional(dev, "vref");
- if (IS_ERR(adc->vref)) {
- if (PTR_ERR(adc->vref) != -ENODEV)
- return dev_err_probe(dev, PTR_ERR(adc->vref),
- "failed to get regulator\n");
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get vref voltage\n");
+
+ internal_vref = ret == -ENODEV;
+ adc->vref_mv = internal_vref ? MCP3564R_INT_VREF_MV : ret / MILLI;
+ *use_internal_vref_attr = internal_vref;
+ if (internal_vref) {
/* Check if chip has internal vref */
if (!adc->have_vref)
- return dev_err_probe(dev, PTR_ERR(adc->vref),
- "Unknown Vref\n");
- adc->vref = NULL;
+ return dev_err_probe(dev, -ENODEV, "Unknown Vref\n");
+
dev_dbg(dev, "%s: Using internal Vref\n", __func__);
} else {
- ret = regulator_enable(adc->vref);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, mcp3564_disable_reg,
- adc->vref);
- if (ret)
- return ret;
-
dev_dbg(dev, "%s: Using External Vref\n", __func__);
-
- ret = regulator_get_voltage(adc->vref);
- if (ret < 0)
- return dev_err_probe(dev, ret,
- "Failed to read vref regulator\n");
-
- adc->vref_mv = ret / MILLI;
}
ret = mcp3564_parse_fw_children(indio_dev);
@@ -1350,10 +1329,8 @@ static int mcp3564_config(struct iio_dev *indio_dev)
tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK);
tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT;
- if (!adc->vref) {
+ if (internal_vref)
tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1);
- adc->vref_mv = MCP3564R_INT_VREF_MV;
- }
ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg);
@@ -1412,6 +1389,7 @@ static int mcp3564_probe(struct spi_device *spi)
int ret;
struct iio_dev *indio_dev;
struct mcp3564_state *adc;
+ bool use_internal_vref_attr;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
@@ -1428,7 +1406,7 @@ static int mcp3564_probe(struct spi_device *spi)
* enable/disable certain channels
* change the sampling rate to the requested value
*/
- ret = mcp3564_config(indio_dev);
+ ret = mcp3564_config(indio_dev, &use_internal_vref_attr);
if (ret)
return dev_err_probe(&spi->dev, ret,
"Can't configure MCP356X device\n");
@@ -1440,7 +1418,7 @@ static int mcp3564_probe(struct spi_device *spi)
indio_dev->name = adc->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
- if (!adc->vref)
+ if (use_internal_vref_attr)
indio_dev->info = &mcp3564r_info;
else
indio_dev->info = &mcp3564_info;
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index 7a32e7a1be9d..d0e77971c5d3 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -103,7 +103,7 @@ struct mcp3911_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
- int (*config)(struct mcp3911 *adc);
+ int (*config)(struct mcp3911 *adc, bool external_vref);
int (*get_osr)(struct mcp3911 *adc, u32 *val);
int (*set_osr)(struct mcp3911 *adc, u32 val);
int (*enable_offset)(struct mcp3911 *adc, bool enable);
@@ -115,7 +115,6 @@ struct mcp3911_chip_info {
struct mcp3911 {
struct spi_device *spi;
struct mutex lock;
- struct regulator *vref;
struct clk *clki;
u32 dev_addr;
struct iio_trigger *trig;
@@ -385,23 +384,11 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
}
}
-static int mcp3911_calc_scale_table(struct mcp3911 *adc)
+static int mcp3911_calc_scale_table(u32 vref_mv)
{
- struct device *dev = &adc->spi->dev;
- u32 ref = MCP3911_INT_VREF_MV;
u32 div;
- int ret;
u64 tmp;
- if (adc->vref) {
- ret = regulator_get_voltage(adc->vref);
- if (ret < 0) {
- return dev_err_probe(dev, ret, "failed to get vref voltage\n");
- }
-
- ref = ret / 1000;
- }
-
/*
* For 24-bit Conversion
* Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
@@ -412,7 +399,7 @@ static int mcp3911_calc_scale_table(struct mcp3911 *adc)
*/
for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
div = 12582912 * BIT(i);
- tmp = div_s64((s64)ref * 1000000000LL, div);
+ tmp = div_s64((s64)vref_mv * 1000000000LL, div);
mcp3911_scale_table[i][0] = 0;
mcp3911_scale_table[i][1] = tmp;
@@ -523,7 +510,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
goto out;
}
- for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index];
adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]);
@@ -544,7 +531,7 @@ static const struct iio_info mcp3911_info = {
.write_raw_get_fmt = mcp3911_write_raw_get_fmt,
};
-static int mcp3911_config(struct mcp3911 *adc)
+static int mcp3911_config(struct mcp3911 *adc, bool external_vref)
{
struct device *dev = &adc->spi->dev;
u32 regval;
@@ -555,7 +542,7 @@ static int mcp3911_config(struct mcp3911 *adc)
return ret;
regval &= ~MCP3911_CONFIG_VREFEXT;
- if (adc->vref) {
+ if (external_vref) {
dev_dbg(dev, "use external voltage reference\n");
regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1);
} else {
@@ -610,7 +597,7 @@ static int mcp3911_config(struct mcp3911 *adc)
return mcp3911_write(adc, MCP3911_REG_GAIN, regval, 1);
}
-static int mcp3910_config(struct mcp3911 *adc)
+static int mcp3910_config(struct mcp3911 *adc, bool external_vref)
{
struct device *dev = &adc->spi->dev;
u32 regval;
@@ -621,7 +608,7 @@ static int mcp3910_config(struct mcp3911 *adc)
return ret;
regval &= ~MCP3910_CONFIG1_VREFEXT;
- if (adc->vref) {
+ if (external_vref) {
dev_dbg(dev, "use external voltage reference\n");
regval |= FIELD_PREP(MCP3910_CONFIG1_VREFEXT, 1);
} else {
@@ -677,11 +664,6 @@ static int mcp3910_config(struct mcp3911 *adc)
return adc->chip->enable_offset(adc, 0);
}
-static void mcp3911_cleanup_regulator(void *vref)
-{
- regulator_disable(vref);
-}
-
static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct mcp3911 *adc = iio_trigger_get_drvdata(trig);
@@ -704,6 +686,8 @@ static int mcp3911_probe(struct spi_device *spi)
struct device *dev = &spi->dev;
struct iio_dev *indio_dev;
struct mcp3911 *adc;
+ bool external_vref;
+ u32 vref_mv;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
@@ -714,23 +698,12 @@ static int mcp3911_probe(struct spi_device *spi)
adc->spi = spi;
adc->chip = spi_get_device_match_data(spi);
- adc->vref = devm_regulator_get_optional(dev, "vref");
- if (IS_ERR(adc->vref)) {
- if (PTR_ERR(adc->vref) == -ENODEV) {
- adc->vref = NULL;
- } else {
- return dev_err_probe(dev, PTR_ERR(adc->vref), "failed to get regulator\n");
- }
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to get vref voltage\n");
- } else {
- ret = regulator_enable(adc->vref);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, mcp3911_cleanup_regulator, adc->vref);
- if (ret)
- return ret;
- }
+ external_vref = ret != -ENODEV;
+ vref_mv = external_vref ? ret / 1000 : MCP3911_INT_VREF_MV;
adc->clki = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(adc->clki)) {
@@ -755,11 +728,11 @@ static int mcp3911_probe(struct spi_device *spi)
}
dev_dbg(dev, "use device address %i\n", adc->dev_addr);
- ret = adc->chip->config(adc);
+ ret = adc->chip->config(adc, external_vref);
if (ret)
return ret;
- ret = mcp3911_calc_scale_table(adc);
+ ret = mcp3911_calc_scale_table(vref_mv);
if (ret)
return ret;
diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c
index 5f672765d4a2..5fbf9b6abd9c 100644
--- a/drivers/iio/adc/mp2629_adc.c
+++ b/drivers/iio/adc/mp2629_adc.c
@@ -184,8 +184,8 @@ static void mp2629_adc_remove(struct platform_device *pdev)
}
static const struct of_device_id mp2629_adc_of_match[] = {
- { .compatible = "mps,mp2629_adc"},
- {}
+ { .compatible = "mps,mp2629_adc" },
+ { }
};
MODULE_DEVICE_TABLE(of, mp2629_adc_of_match);
diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c
index 3710473e526f..e2ec805e834f 100644
--- a/drivers/iio/adc/mt6360-adc.c
+++ b/drivers/iio/adc/mt6360-adc.c
@@ -268,7 +268,7 @@ static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
int i = 0, bit, val, ret;
memset(&data, 0, sizeof(data));
- for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, bit) {
ret = mt6360_adc_read_channel(mad, bit, &val);
if (ret < 0) {
dev_warn(&indio_dev->dev, "Failed to get channel %d conversion val\n", bit);
@@ -355,7 +355,7 @@ static int mt6360_adc_probe(struct platform_device *pdev)
static const struct of_device_id mt6360_adc_of_id[] = {
{ .compatible = "mediatek,mt6360-adc", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, mt6360_adc_of_id);
diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c
index 600151a62f1f..458544cb8ee4 100644
--- a/drivers/iio/adc/nau7802.c
+++ b/drivers/iio/adc/nau7802.c
@@ -539,7 +539,7 @@ MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);
static const struct of_device_id nau7802_dt_ids[] = {
{ .compatible = "nuvoton,nau7802" },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, nau7802_dt_ids);
diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c
new file mode 100644
index 000000000000..4c2a1c07bc39
--- /dev/null
+++ b/drivers/iio/adc/pac1921.c
@@ -0,0 +1,1261 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IIO driver for PAC1921 High-Side Power/Current Monitor
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+/* pac1921 registers */
+#define PAC1921_REG_GAIN_CFG 0x00
+#define PAC1921_REG_INT_CFG 0x01
+#define PAC1921_REG_CONTROL 0x02
+#define PAC1921_REG_VBUS 0x10
+#define PAC1921_REG_VSENSE 0x12
+#define PAC1921_REG_OVERFLOW_STS 0x1C
+#define PAC1921_REG_VPOWER 0x1D
+
+/* pac1921 gain configuration bits */
+#define PAC1921_GAIN_DI_GAIN_MASK GENMASK(5, 3)
+#define PAC1921_GAIN_DV_GAIN_MASK GENMASK(2, 0)
+
+/* pac1921 integration configuration bits */
+#define PAC1921_INT_CFG_SMPL_MASK GENMASK(7, 4)
+#define PAC1921_INT_CFG_VSFEN BIT(3)
+#define PAC1921_INT_CFG_VBFEN BIT(2)
+#define PAC1921_INT_CFG_RIOV BIT(1)
+#define PAC1921_INT_CFG_INTEN BIT(0)
+
+/* pac1921 control bits */
+#define PAC1921_CONTROL_MXSL_MASK GENMASK(7, 6)
+enum pac1921_mxsl {
+ PAC1921_MXSL_VPOWER_PIN = 0,
+ PAC1921_MXSL_VSENSE_FREE_RUN = 1,
+ PAC1921_MXSL_VBUS_FREE_RUN = 2,
+ PAC1921_MXSL_VPOWER_FREE_RUN = 3,
+};
+#define PAC1921_CONTROL_SLEEP BIT(2)
+
+/* pac1921 result registers mask and resolution */
+#define PAC1921_RES_MASK GENMASK(15, 6)
+#define PAC1921_RES_RESOLUTION 1023
+
+/* pac1921 overflow status bits */
+#define PAC1921_OVERFLOW_VSOV BIT(2)
+#define PAC1921_OVERFLOW_VBOV BIT(1)
+#define PAC1921_OVERFLOW_VPOV BIT(0)
+
+/* pac1921 constants */
+#define PAC1921_MAX_VSENSE_MV 100
+#define PAC1921_MAX_VBUS_V 32
+/* Time to first communication after power up (tINT_T) */
+#define PAC1921_POWERUP_TIME_MS 20
+/* Time from Sleep State to Start of Integration Period (tSLEEP_TO_INT) */
+#define PAC1921_SLEEP_TO_INT_TIME_US 86
+
+/* pac1921 defaults */
+#define PAC1921_DEFAULT_DV_GAIN 0 /* 2^(value): 1x gain (HW default) */
+#define PAC1921_DEFAULT_DI_GAIN 0 /* 2^(value): 1x gain (HW default) */
+#define PAC1921_DEFAULT_NUM_SAMPLES 0 /* 2^(value): 1 sample (HW default) */
+
+/*
+ * Pre-computed scale factors for BUS voltage
+ * format: IIO_VAL_INT_PLUS_NANO
+ * unit: mV
+ *
+ * Vbus scale (mV) = max_vbus (mV) / dv_gain / resolution
+ */
+static const int pac1921_vbus_scales[][2] = {
+ { 31, 280547409 }, /* dv_gain x1 */
+ { 15, 640273704 }, /* dv_gain x2 */
+ { 7, 820136852 }, /* dv_gain x4 */
+ { 3, 910068426 }, /* dv_gain x8 */
+ { 1, 955034213 }, /* dv_gain x16 */
+ { 0, 977517106 }, /* dv_gain x32 */
+};
+
+/*
+ * Pre-computed scales for SENSE voltage
+ * format: IIO_VAL_INT_PLUS_NANO
+ * unit: mV
+ *
+ * Vsense scale (mV) = max_vsense (mV) / di_gain / resolution
+ */
+static const int pac1921_vsense_scales[][2] = {
+ { 0, 97751710 }, /* di_gain x1 */
+ { 0, 48875855 }, /* di_gain x2 */
+ { 0, 24437927 }, /* di_gain x4 */
+ { 0, 12218963 }, /* di_gain x8 */
+ { 0, 6109481 }, /* di_gain x16 */
+ { 0, 3054740 }, /* di_gain x32 */
+ { 0, 1527370 }, /* di_gain x64 */
+ { 0, 763685 }, /* di_gain x128 */
+};
+
+/*
+ * Numbers of samples used to integrate measurements at the end of an
+ * integration period.
+ *
+ * Changing the number of samples affects the integration period: higher the
+ * number of samples, longer the integration period.
+ *
+ * These correspond to the oversampling ratios available exposed to userspace.
+ */
+static const int pac1921_int_num_samples[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048
+};
+
+/*
+ * The integration period depends on the configuration of number of integration
+ * samples, measurement resolution and post filters. The following array
+ * contains integration periods, in microsecs unit, based on table 4-5 from
+ * datasheet considering power integration mode, 14-Bit resolution and post
+ * filters on. Each index corresponds to a specific number of samples from 1
+ * to 2048.
+ */
+static const unsigned int pac1921_int_periods_usecs[] = {
+ 2720, /* 1 sample */
+ 4050, /* 2 samples */
+ 6790, /* 4 samples */
+ 12200, /* 8 samples */
+ 23000, /* 16 samples */
+ 46000, /* 32 samples */
+ 92000, /* 64 samples */
+ 184000, /* 128 samples */
+ 368000, /* 256 samples */
+ 736000, /* 512 samples */
+ 1471000, /* 1024 samples */
+ 2941000 /* 2048 samples */
+};
+
+/* pac1921 regmap configuration */
+static const struct regmap_range pac1921_regmap_wr_ranges[] = {
+ regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL),
+};
+
+static const struct regmap_access_table pac1921_regmap_wr_table = {
+ .yes_ranges = pac1921_regmap_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(pac1921_regmap_wr_ranges),
+};
+
+static const struct regmap_range pac1921_regmap_rd_ranges[] = {
+ regmap_reg_range(PAC1921_REG_GAIN_CFG, PAC1921_REG_CONTROL),
+ regmap_reg_range(PAC1921_REG_VBUS, PAC1921_REG_VPOWER + 1),
+};
+
+static const struct regmap_access_table pac1921_regmap_rd_table = {
+ .yes_ranges = pac1921_regmap_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(pac1921_regmap_rd_ranges),
+};
+
+static const struct regmap_config pac1921_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &pac1921_regmap_rd_table,
+ .wr_table = &pac1921_regmap_wr_table,
+};
+
+enum pac1921_channels {
+ PAC1921_CHAN_VBUS = 0,
+ PAC1921_CHAN_VSENSE = 1,
+ PAC1921_CHAN_CURRENT = 2,
+ PAC1921_CHAN_POWER = 3,
+};
+#define PAC1921_NUM_MEAS_CHANS 4
+
+struct pac1921_priv {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regulator *vdd;
+ struct iio_info iio_info;
+
+ /*
+ * Synchronize access to private members, and ensure atomicity of
+ * consecutive regmap operations.
+ */
+ struct mutex lock;
+
+ u32 rshunt_uohm; /* uOhm */
+ u8 dv_gain;
+ u8 di_gain;
+ u8 n_samples;
+ u8 prev_ovf_flags;
+ u8 ovf_enabled_events;
+
+ bool first_integr_started;
+ bool first_integr_done;
+ unsigned long integr_started_time_jiffies;
+ unsigned int integr_period_usecs;
+
+ int current_scales[ARRAY_SIZE(pac1921_vsense_scales)][2];
+
+ struct {
+ u16 chan[PAC1921_NUM_MEAS_CHANS];
+ s64 timestamp __aligned(8);
+ } scan;
+};
+
+/*
+ * Check if first integration after configuration update has completed.
+ *
+ * Must be called with lock held.
+ */
+static bool pac1921_data_ready(struct pac1921_priv *priv)
+{
+ if (!priv->first_integr_started)
+ return false;
+
+ if (!priv->first_integr_done) {
+ unsigned long t_ready;
+
+ /*
+ * Data valid after the device entered into integration state,
+ * considering worst case where the device was in sleep state,
+ * and completed the first integration period.
+ */
+ t_ready = priv->integr_started_time_jiffies +
+ usecs_to_jiffies(PAC1921_SLEEP_TO_INT_TIME_US) +
+ usecs_to_jiffies(priv->integr_period_usecs);
+
+ if (time_before(jiffies, t_ready))
+ return false;
+
+ priv->first_integr_done = true;
+ }
+
+ return true;
+}
+
+static inline void pac1921_calc_scale(int dividend, int divisor, int *val,
+ int *val2)
+{
+ s64 tmp;
+
+ tmp = div_s64(dividend * (s64)NANO, divisor);
+ *val = (int)div_s64_rem(tmp, NANO, val2);
+}
+
+/*
+ * Fill the table of scale factors for current
+ * format: IIO_VAL_INT_PLUS_NANO
+ * unit: mA
+ *
+ * Vsense LSB (nV) = max_vsense (nV) * di_gain / resolution
+ * Current scale (mA) = Vsense LSB (nV) / shunt (uOhm)
+ *
+ * Must be called with held lock when updating after first initialization.
+ */
+static void pac1921_calc_current_scales(struct pac1921_priv *priv)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(priv->current_scales); i++) {
+ int max = (PAC1921_MAX_VSENSE_MV * MICRO) >> i;
+ int vsense_lsb = DIV_ROUND_CLOSEST(max, PAC1921_RES_RESOLUTION);
+
+ pac1921_calc_scale(vsense_lsb, (int)priv->rshunt_uohm,
+ &priv->current_scales[i][0],
+ &priv->current_scales[i][1]);
+ }
+}
+
+/*
+ * Check if overflow occurred and if so, push the corresponding events.
+ *
+ * Must be called with lock held.
+ */
+static int pac1921_check_push_overflow(struct iio_dev *indio_dev, s64 timestamp)
+{
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+ unsigned int flags;
+ int ret;
+
+ ret = regmap_read(priv->regmap, PAC1921_REG_OVERFLOW_STS, &flags);
+ if (ret)
+ return ret;
+
+ if (flags & PAC1921_OVERFLOW_VBOV &&
+ !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VBOV) &&
+ priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_VOLTAGE, PAC1921_CHAN_VBUS,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ timestamp);
+ }
+ if (flags & PAC1921_OVERFLOW_VSOV &&
+ !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VSOV) &&
+ priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_VOLTAGE, PAC1921_CHAN_VSENSE,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ timestamp);
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_CURRENT, PAC1921_CHAN_CURRENT,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ timestamp);
+ }
+ if (flags & PAC1921_OVERFLOW_VPOV &&
+ !(priv->prev_ovf_flags & PAC1921_OVERFLOW_VPOV) &&
+ priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_POWER, PAC1921_CHAN_POWER,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ timestamp);
+ }
+
+ priv->prev_ovf_flags = (u8)flags;
+
+ return 0;
+}
+
+/*
+ * Read the value from a result register
+ *
+ * Result registers contain the most recent averaged values of Vbus, Vsense and
+ * Vpower. Each value is 10 bits wide and spread across two consecutive 8 bit
+ * registers, with 6 bit LSB zero padding.
+ */
+static int pac1921_read_res(struct pac1921_priv *priv, unsigned long reg,
+ u16 *val)
+{
+ int ret = regmap_bulk_read(priv->regmap, (unsigned int)reg, val,
+ sizeof(*val));
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(PAC1921_RES_MASK, get_unaligned_be16(val));
+
+ return 0;
+}
+
+static int pac1921_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+
+ guard(mutex)(&priv->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ s64 ts;
+ u16 res_val;
+ int ret;
+
+ if (!pac1921_data_ready(priv))
+ return -EBUSY;
+
+ ts = iio_get_time_ns(indio_dev);
+
+ ret = pac1921_check_push_overflow(indio_dev, ts);
+ if (ret)
+ return ret;
+
+ ret = pac1921_read_res(priv, chan->address, &res_val);
+ if (ret)
+ return ret;
+
+ *val = (int)res_val;
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case PAC1921_CHAN_VBUS:
+ *val = pac1921_vbus_scales[priv->dv_gain][0];
+ *val2 = pac1921_vbus_scales[priv->dv_gain][1];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case PAC1921_CHAN_VSENSE:
+ *val = pac1921_vsense_scales[priv->di_gain][0];
+ *val2 = pac1921_vsense_scales[priv->di_gain][1];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case PAC1921_CHAN_CURRENT:
+ *val = priv->current_scales[priv->di_gain][0];
+ *val2 = priv->current_scales[priv->di_gain][1];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case PAC1921_CHAN_POWER: {
+ /*
+ * Power scale factor in mW:
+ * Current scale (mA) * max_vbus (V) / dv_gain
+ */
+
+ /* Get current scale based on di_gain */
+ int *curr_scale = priv->current_scales[priv->di_gain];
+
+ /* Convert current_scale from INT_PLUS_NANO to INT */
+ s64 tmp = curr_scale[0] * (s64)NANO + curr_scale[1];
+
+ /* Multiply by max_vbus (V) / dv_gain */
+ tmp *= PAC1921_MAX_VBUS_V >> (int)priv->dv_gain;
+
+ /* Convert back to INT_PLUS_NANO */
+ *val = (int)div_s64_rem(tmp, NANO, val2);
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = pac1921_int_num_samples[priv->n_samples];
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * The sampling frequency (Hz) is read-only and corresponds to
+ * how often the device provides integrated measurements into
+ * the result registers, thus it's 1/integration_period.
+ * The integration period depends on the number of integration
+ * samples, measurement resolution and post filters.
+ *
+ * 1/(integr_period_usecs/MICRO) = MICRO/integr_period_usecs
+ */
+ *val = MICRO;
+ *val2 = (int)priv->integr_period_usecs;
+ return IIO_VAL_FRACTIONAL;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pac1921_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *type = IIO_VAL_INT;
+ *vals = pac1921_int_num_samples;
+ *length = ARRAY_SIZE(pac1921_int_num_samples);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Perform configuration update sequence: set the device into read state, then
+ * write the config register and set the device back into integration state.
+ * Also reset integration start time and mark first integration to be yet
+ * completed.
+ *
+ * Must be called with lock held.
+ */
+static int pac1921_update_cfg_reg(struct pac1921_priv *priv, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ /* Enter READ state before configuration */
+ int ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
+ PAC1921_INT_CFG_INTEN, 0);
+ if (ret)
+ return ret;
+
+ /* Update configuration value */
+ ret = regmap_update_bits(priv->regmap, reg, mask, val);
+ if (ret)
+ return ret;
+
+ /* Re-enable integration */
+ ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
+ PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN);
+ if (ret)
+ return ret;
+
+ /*
+ * Reset integration started time and mark this integration period as
+ * the first one so that new measurements will be considered as valid
+ * only at the end of this integration period.
+ */
+ priv->integr_started_time_jiffies = jiffies;
+ priv->first_integr_done = false;
+
+ return 0;
+}
+
+/*
+ * Retrieve the index of the given scale (represented by scale_val and
+ * scale_val2) from scales_tbl. The returned index (if found) is the log2 of
+ * the gain corresponding to the given scale.
+ *
+ * Must be called with lock held if the scales_tbl can change runtime (e.g. for
+ * the current scales table)
+ */
+static int pac1921_lookup_scale(const int (*const scales_tbl)[2], size_t size,
+ int scale_val, int scale_val2)
+{
+ for (unsigned int i = 0; i < size; i++)
+ if (scales_tbl[i][0] == scale_val &&
+ scales_tbl[i][1] == scale_val2)
+ return (int)i;
+
+ return -EINVAL;
+}
+
+/*
+ * Configure device with the given gain (only if changed)
+ *
+ * Must be called with lock held.
+ */
+static int pac1921_update_gain(struct pac1921_priv *priv, u8 *priv_val, u8 gain,
+ unsigned int mask)
+{
+ unsigned int reg_val;
+ int ret;
+
+ if (*priv_val == gain)
+ return 0;
+
+ reg_val = (gain << __ffs(mask)) & mask;
+ ret = pac1921_update_cfg_reg(priv, PAC1921_REG_GAIN_CFG, mask, reg_val);
+ if (ret)
+ return ret;
+
+ *priv_val = gain;
+
+ return 0;
+}
+
+/*
+ * Given a scale factor represented by scale_val and scale_val2 with format
+ * IIO_VAL_INT_PLUS_NANO, find the corresponding gain value and write it to the
+ * device.
+ *
+ * Must be called with lock held.
+ */
+static int pac1921_update_gain_from_scale(struct pac1921_priv *priv,
+ struct iio_chan_spec const *chan,
+ int scale_val, int scale_val2)
+{
+ int ret;
+
+ switch (chan->channel) {
+ case PAC1921_CHAN_VBUS:
+ ret = pac1921_lookup_scale(pac1921_vbus_scales,
+ ARRAY_SIZE(pac1921_vbus_scales),
+ scale_val, scale_val2);
+ if (ret < 0)
+ return ret;
+
+ return pac1921_update_gain(priv, &priv->dv_gain, (u8)ret,
+ PAC1921_GAIN_DV_GAIN_MASK);
+ case PAC1921_CHAN_VSENSE:
+ ret = pac1921_lookup_scale(pac1921_vsense_scales,
+ ARRAY_SIZE(pac1921_vsense_scales),
+ scale_val, scale_val2);
+ if (ret < 0)
+ return ret;
+
+ return pac1921_update_gain(priv, &priv->di_gain, (u8)ret,
+ PAC1921_GAIN_DI_GAIN_MASK);
+ case PAC1921_CHAN_CURRENT:
+ ret = pac1921_lookup_scale(priv->current_scales,
+ ARRAY_SIZE(priv->current_scales),
+ scale_val, scale_val2);
+ if (ret < 0)
+ return ret;
+
+ return pac1921_update_gain(priv, &priv->di_gain, (u8)ret,
+ PAC1921_GAIN_DI_GAIN_MASK);
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Retrieve the index of the given number of samples from the constant table.
+ * The returned index (if found) is the log2 of the given num_samples.
+ */
+static int pac1921_lookup_int_num_samples(int num_samples)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(pac1921_int_num_samples); i++)
+ if (pac1921_int_num_samples[i] == num_samples)
+ return (int)i;
+
+ return -EINVAL;
+}
+
+/*
+ * Update the device with the given number of integration samples.
+ *
+ * Must be called with lock held.
+ */
+static int pac1921_update_int_num_samples(struct pac1921_priv *priv,
+ int num_samples)
+{
+ unsigned int reg_val;
+ u8 n_samples;
+ int ret;
+
+ ret = pac1921_lookup_int_num_samples(num_samples);
+ if (ret < 0)
+ return ret;
+
+ n_samples = (u8)ret;
+
+ if (priv->n_samples == n_samples)
+ return 0;
+
+ reg_val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, n_samples);
+
+ ret = pac1921_update_cfg_reg(priv, PAC1921_REG_INT_CFG,
+ PAC1921_INT_CFG_SMPL_MASK, reg_val);
+ if (ret)
+ return ret;
+
+ priv->n_samples = n_samples;
+
+ priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples];
+
+ return 0;
+}
+
+static int pac1921_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pac1921_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+
+ guard(mutex)(&priv->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return pac1921_update_gain_from_scale(priv, chan, val, val2);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return pac1921_update_int_num_samples(priv, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pac1921_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, char *label)
+{
+ switch (chan->channel) {
+ case PAC1921_CHAN_VBUS:
+ return sprintf(label, "vbus\n");
+ case PAC1921_CHAN_VSENSE:
+ return sprintf(label, "vsense\n");
+ case PAC1921_CHAN_CURRENT:
+ return sprintf(label, "current\n");
+ case PAC1921_CHAN_POWER:
+ return sprintf(label, "power\n");
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pac1921_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 pac1921_priv *priv = iio_priv(indio_dev);
+
+ guard(mutex)(&priv->lock);
+
+ switch (chan->channel) {
+ case PAC1921_CHAN_VBUS:
+ return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VBOV);
+ case PAC1921_CHAN_VSENSE:
+ case PAC1921_CHAN_CURRENT:
+ return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VSOV);
+ case PAC1921_CHAN_POWER:
+ return !!(priv->ovf_enabled_events & PAC1921_OVERFLOW_VPOV);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pac1921_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 pac1921_priv *priv = iio_priv(indio_dev);
+ u8 ovf_bit;
+
+ guard(mutex)(&priv->lock);
+
+ switch (chan->channel) {
+ case PAC1921_CHAN_VBUS:
+ ovf_bit = PAC1921_OVERFLOW_VBOV;
+ break;
+ case PAC1921_CHAN_VSENSE:
+ case PAC1921_CHAN_CURRENT:
+ ovf_bit = PAC1921_OVERFLOW_VSOV;
+ break;
+ case PAC1921_CHAN_POWER:
+ ovf_bit = PAC1921_OVERFLOW_VPOV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (state)
+ priv->ovf_enabled_events |= ovf_bit;
+ else
+ priv->ovf_enabled_events &= ~ovf_bit;
+
+ return 0;
+}
+
+static int pac1921_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)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ *val = PAC1921_RES_RESOLUTION;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info pac1921_iio = {
+ .read_raw = pac1921_read_raw,
+ .read_avail = pac1921_read_avail,
+ .write_raw = pac1921_write_raw,
+ .write_raw_get_fmt = pac1921_write_raw_get_fmt,
+ .read_label = pac1921_read_label,
+ .read_event_config = pac1921_read_event_config,
+ .write_event_config = pac1921_write_event_config,
+ .read_event_value = pac1921_read_event_value,
+};
+
+static ssize_t pac1921_read_shunt_resistor(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+ int vals[2];
+
+ if (chan->channel != PAC1921_CHAN_CURRENT)
+ return -EINVAL;
+
+ guard(mutex)(&priv->lock);
+
+ vals[0] = (int)priv->rshunt_uohm;
+ vals[1] = MICRO;
+
+ return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals);
+}
+
+static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+ u64 rshunt_uohm;
+ int val, val_fract;
+ int ret;
+
+ if (chan->channel != PAC1921_CHAN_CURRENT)
+ return -EINVAL;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
+ if (ret)
+ return ret;
+
+ rshunt_uohm = (u32)val * MICRO + (u32)val_fract;
+ if (rshunt_uohm == 0 || rshunt_uohm > INT_MAX)
+ return -EINVAL;
+
+ guard(mutex)(&priv->lock);
+
+ priv->rshunt_uohm = (u32)rshunt_uohm;
+
+ pac1921_calc_current_scales(priv);
+
+ return len;
+}
+
+/*
+ * Emit on sysfs the list of available scales contained in scales_tbl
+ *
+ * TODO:: this function can be replaced with iio_format_avail_list() if the
+ * latter will ever be exported.
+ *
+ * Must be called with lock held if the scales_tbl can change runtime (e.g. for
+ * the current scales table)
+ */
+static ssize_t pac1921_format_scale_avail(const int (*const scales_tbl)[2],
+ size_t size, char *buf)
+{
+ ssize_t len = 0;
+
+ for (unsigned int i = 0; i < size; i++) {
+ if (i != 0) {
+ len += sysfs_emit_at(buf, len, " ");
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ }
+ len += sysfs_emit_at(buf, len, "%d.%09d", scales_tbl[i][0],
+ scales_tbl[i][1]);
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ }
+
+ len += sysfs_emit_at(buf, len, "\n");
+ return len;
+}
+
+/*
+ * Read available scales for a specific channel
+ *
+ * NOTE: using extended info insted of iio.read_avail() because access to
+ * current scales must be locked as they depend on shunt resistor which may
+ * change runtime. Caller of iio.read_avail() would access the table unlocked
+ * instead.
+ */
+static ssize_t pac1921_read_scale_avail(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+ const int (*scales_tbl)[2];
+ size_t size;
+
+ switch (chan->channel) {
+ case PAC1921_CHAN_VBUS:
+ scales_tbl = pac1921_vbus_scales;
+ size = ARRAY_SIZE(pac1921_vbus_scales);
+ return pac1921_format_scale_avail(scales_tbl, size, buf);
+
+ case PAC1921_CHAN_VSENSE:
+ scales_tbl = pac1921_vsense_scales;
+ size = ARRAY_SIZE(pac1921_vsense_scales);
+ return pac1921_format_scale_avail(scales_tbl, size, buf);
+
+ case PAC1921_CHAN_CURRENT: {
+ guard(mutex)(&priv->lock);
+ scales_tbl = priv->current_scales;
+ size = ARRAY_SIZE(priv->current_scales);
+ return pac1921_format_scale_avail(scales_tbl, size, buf);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+#define PAC1921_EXT_INFO_SCALE_AVAIL { \
+ .name = "scale_available", \
+ .read = pac1921_read_scale_avail, \
+ .shared = IIO_SEPARATE, \
+}
+
+static const struct iio_chan_spec_ext_info pac1921_ext_info_voltage[] = {
+ PAC1921_EXT_INFO_SCALE_AVAIL,
+ {}
+};
+
+static const struct iio_chan_spec_ext_info pac1921_ext_info_current[] = {
+ PAC1921_EXT_INFO_SCALE_AVAIL,
+ {
+ .name = "shunt_resistor",
+ .read = pac1921_read_shunt_resistor,
+ .write = pac1921_write_shunt_resistor,
+ .shared = IIO_SEPARATE,
+ },
+ {}
+};
+
+static const struct iio_event_spec pac1921_overflow_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec pac1921_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .channel = PAC1921_CHAN_VBUS,
+ .address = PAC1921_REG_VBUS,
+ .scan_index = PAC1921_CHAN_VBUS,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 10,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ .indexed = 1,
+ .event_spec = pac1921_overflow_event,
+ .num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
+ .ext_info = pac1921_ext_info_voltage,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .channel = PAC1921_CHAN_VSENSE,
+ .address = PAC1921_REG_VSENSE,
+ .scan_index = PAC1921_CHAN_VSENSE,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 10,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ .indexed = 1,
+ .event_spec = pac1921_overflow_event,
+ .num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
+ .ext_info = pac1921_ext_info_voltage,
+ },
+ {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .channel = PAC1921_CHAN_CURRENT,
+ .address = PAC1921_REG_VSENSE,
+ .scan_index = PAC1921_CHAN_CURRENT,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 10,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ .event_spec = pac1921_overflow_event,
+ .num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
+ .ext_info = pac1921_ext_info_current,
+ },
+ {
+ .type = IIO_POWER,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .channel = PAC1921_CHAN_POWER,
+ .address = PAC1921_REG_VPOWER,
+ .scan_index = PAC1921_CHAN_POWER,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 10,
+ .storagebits = 16,
+ .endianness = IIO_CPU
+ },
+ .event_spec = pac1921_overflow_event,
+ .num_event_specs = ARRAY_SIZE(pac1921_overflow_event),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(PAC1921_NUM_MEAS_CHANS),
+};
+
+static irqreturn_t pac1921_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ struct pac1921_priv *priv = iio_priv(idev);
+ int ret;
+ int bit;
+ int ch = 0;
+
+ guard(mutex)(&priv->lock);
+
+ if (!pac1921_data_ready(priv))
+ goto done;
+
+ ret = pac1921_check_push_overflow(idev, pf->timestamp);
+ if (ret)
+ goto done;
+
+ iio_for_each_active_channel(idev, bit) {
+ u16 val;
+
+ ret = pac1921_read_res(priv, idev->channels[ch].address, &val);
+ if (ret)
+ goto done;
+
+ priv->scan.chan[ch++] = val;
+ }
+
+ iio_push_to_buffers_with_timestamp(idev, &priv->scan, pf->timestamp);
+
+done:
+ iio_trigger_notify_done(idev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Initialize device by writing initial configuration and putting it into
+ * integration state.
+ *
+ * Must be called with lock held when called after first initialization
+ * (e.g. from pm resume)
+ */
+static int pac1921_init(struct pac1921_priv *priv)
+{
+ unsigned int val;
+ int ret;
+
+ /* Enter READ state before configuration */
+ ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
+ PAC1921_INT_CFG_INTEN, 0);
+ if (ret)
+ return ret;
+
+ /* Configure gains, use 14-bits measurement resolution (HW default) */
+ val = FIELD_PREP(PAC1921_GAIN_DI_GAIN_MASK, priv->di_gain) |
+ FIELD_PREP(PAC1921_GAIN_DV_GAIN_MASK, priv->dv_gain);
+ ret = regmap_write(priv->regmap, PAC1921_REG_GAIN_CFG, val);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure integration:
+ * - num of integration samples
+ * - filters enabled (HW default)
+ * - set READ/INT pin override (RIOV) to control operation mode via
+ * register instead of pin
+ */
+ val = FIELD_PREP(PAC1921_INT_CFG_SMPL_MASK, priv->n_samples) |
+ PAC1921_INT_CFG_VSFEN | PAC1921_INT_CFG_VBFEN |
+ PAC1921_INT_CFG_RIOV;
+ ret = regmap_write(priv->regmap, PAC1921_REG_INT_CFG, val);
+ if (ret)
+ return ret;
+
+ /*
+ * Init control register:
+ * - VPower free run integration mode
+ * - OUT pin full scale range: 3V (HW detault)
+ * - no timeout, no sleep, no sleep override, no recalc (HW defaults)
+ */
+ val = FIELD_PREP(PAC1921_CONTROL_MXSL_MASK,
+ PAC1921_MXSL_VPOWER_FREE_RUN);
+ ret = regmap_write(priv->regmap, PAC1921_REG_CONTROL, val);
+ if (ret)
+ return ret;
+
+ /* Enable integration */
+ ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
+ PAC1921_INT_CFG_INTEN, PAC1921_INT_CFG_INTEN);
+ if (ret)
+ return ret;
+
+ priv->first_integr_started = true;
+ priv->integr_started_time_jiffies = jiffies;
+ priv->integr_period_usecs = pac1921_int_periods_usecs[priv->n_samples];
+
+ return 0;
+}
+
+static int pac1921_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ priv->first_integr_started = false;
+ priv->first_integr_done = false;
+
+ ret = regmap_update_bits(priv->regmap, PAC1921_REG_INT_CFG,
+ PAC1921_INT_CFG_INTEN, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, PAC1921_REG_CONTROL,
+ PAC1921_CONTROL_SLEEP, PAC1921_CONTROL_SLEEP);
+ if (ret)
+ return ret;
+
+ return regulator_disable(priv->vdd);
+
+}
+
+static int pac1921_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct pac1921_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ ret = regulator_enable(priv->vdd);
+ if (ret)
+ return ret;
+
+ msleep(PAC1921_POWERUP_TIME_MS);
+
+ return pac1921_init(priv);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pac1921_pm_ops, pac1921_suspend,
+ pac1921_resume);
+
+static void pac1921_regulator_disable(void *data)
+{
+ struct regulator *regulator = data;
+
+ regulator_disable(regulator);
+}
+
+static int pac1921_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct pac1921_priv *priv;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->client = client;
+ i2c_set_clientdata(client, indio_dev);
+
+ priv->regmap = devm_regmap_init_i2c(client, &pac1921_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, (int)PTR_ERR(priv->regmap),
+ "Cannot initialize register map\n");
+
+ devm_mutex_init(dev, &priv->lock);
+
+ priv->dv_gain = PAC1921_DEFAULT_DV_GAIN;
+ priv->di_gain = PAC1921_DEFAULT_DI_GAIN;
+ priv->n_samples = PAC1921_DEFAULT_NUM_SAMPLES;
+
+ ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms",
+ &priv->rshunt_uohm);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Cannot read shunt resistor property\n");
+ if (priv->rshunt_uohm == 0 || priv->rshunt_uohm > INT_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid shunt resistor: %u\n",
+ priv->rshunt_uohm);
+
+ pac1921_calc_current_scales(priv);
+
+ priv->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(priv->vdd))
+ return dev_err_probe(dev, (int)PTR_ERR(priv->vdd),
+ "Cannot get vdd regulator\n");
+
+ ret = regulator_enable(priv->vdd);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot enable vdd regulator\n");
+
+ ret = devm_add_action_or_reset(dev, pac1921_regulator_disable,
+ priv->vdd);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Cannot add action for vdd regulator disposal\n");
+
+ msleep(PAC1921_POWERUP_TIME_MS);
+
+ ret = pac1921_init(priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot initialize device\n");
+
+ priv->iio_info = pac1921_iio;
+
+ indio_dev->name = "pac1921";
+ indio_dev->info = &priv->iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = pac1921_channels;
+ indio_dev->num_channels = ARRAY_SIZE(pac1921_channels);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &pac1921_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Cannot setup IIO triggered buffer\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register IIO device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id pac1921_id[] = {
+ { .name = "pac1921", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pac1921_id);
+
+static const struct of_device_id pac1921_of_match[] = {
+ { .compatible = "microchip,pac1921" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pac1921_of_match);
+
+static struct i2c_driver pac1921_driver = {
+ .driver = {
+ .name = "pac1921",
+ .pm = pm_sleep_ptr(&pac1921_pm_ops),
+ .of_match_table = pac1921_of_match,
+ },
+ .probe = pac1921_probe,
+ .id_table = pac1921_id,
+};
+
+module_i2c_driver(pac1921_driver);
+
+MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>");
+MODULE_DESCRIPTION("IIO driver for PAC1921 High-Side Power/Current Monitor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index ae24a27805ab..8210728034d0 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -1571,7 +1571,7 @@ static const struct i2c_device_id pac1934_id[] = {
{ .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] },
{ .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] },
{ .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, pac1934_id);
@@ -1592,7 +1592,7 @@ static const struct of_device_id pac1934_of_match[] = {
.compatible = "microchip,pac1934",
.data = &pac1934_chip_config[PAC1934]
},
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, pac1934_of_match);
@@ -1602,7 +1602,7 @@ MODULE_DEVICE_TABLE(of, pac1934_of_match);
*/
static const struct acpi_device_id pac1934_acpi_match[] = {
{ "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] },
- {}
+ { }
};
MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match);
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
index c9d2c66434e4..9e1112f5acc6 100644
--- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -1006,7 +1006,7 @@ static const struct of_device_id pm8xxx_xoadc_id_table[] = {
.compatible = "qcom,pm8921-adc",
.data = &pm8921_variant,
},
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, pm8xxx_xoadc_id_table);
diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c
index 1402df68dd52..6aa70b4629a7 100644
--- a/drivers/iio/adc/qcom-spmi-rradc.c
+++ b/drivers/iio/adc/qcom-spmi-rradc.c
@@ -1002,7 +1002,7 @@ static int rradc_probe(struct platform_device *pdev)
static const struct of_device_id rradc_match_table[] = {
{ .compatible = "qcom,pm660-rradc" },
{ .compatible = "qcom,pmi8998-rradc" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, rradc_match_table);
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index bbe954a738c7..240cfa391674 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -331,7 +331,7 @@ static const struct of_device_id rockchip_saradc_match[] = {
.compatible = "rockchip,rk3588-saradc",
.data = &rk3588_saradc_data,
},
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
@@ -370,7 +370,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p)
mutex_lock(&info->lock);
- for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) {
+ iio_for_each_active_channel(i_dev, i) {
const struct iio_chan_spec *chan = &i_dev->channels[i];
ret = rockchip_saradc_conversion(info, chan);
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
index bcb129840908..56ed948a8ae1 100644
--- a/drivers/iio/adc/rtq6056.c
+++ b/drivers/iio/adc/rtq6056.c
@@ -643,7 +643,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
pm_runtime_get_sync(dev);
- for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, bit) {
unsigned int addr = rtq6056_channels[bit].address;
ret = regmap_read(priv->regmap, addr, &raw);
@@ -865,7 +865,7 @@ static const struct richtek_dev_data rtq6059_devdata = {
static const struct of_device_id rtq6056_device_match[] = {
{ .compatible = "richtek,rtq6056", .data = &rtq6056_devdata },
{ .compatible = "richtek,rtq6059", .data = &rtq6059_devdata },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, rtq6056_device_match);
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
index 327cc2097f6c..654b6a38b650 100644
--- a/drivers/iio/adc/sd_adc_modulator.c
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -6,11 +6,14 @@
* Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
*/
+#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
static const struct iio_info iio_sd_mod_iio_info;
@@ -24,7 +27,59 @@ static const struct iio_chan_spec iio_sd_mod_ch = {
},
};
-static int iio_sd_mod_probe(struct platform_device *pdev)
+struct iio_sd_backend_priv {
+ struct regulator *vref;
+ int vref_mv;
+};
+
+static int iio_sd_mod_enable(struct iio_backend *backend)
+{
+ struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend);
+
+ if (priv->vref)
+ return regulator_enable(priv->vref);
+
+ return 0;
+};
+
+static void iio_sd_mod_disable(struct iio_backend *backend)
+{
+ struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend);
+
+ if (priv->vref)
+ regulator_disable(priv->vref);
+};
+
+static int iio_sd_mod_read(struct iio_backend *backend, struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct iio_sd_backend_priv *priv = iio_backend_get_priv(backend);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = priv->vref_mv;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 0;
+ return IIO_VAL_INT;
+ }
+
+ return -EOPNOTSUPP;
+};
+
+static const struct iio_backend_ops sd_backend_ops = {
+ .enable = iio_sd_mod_enable,
+ .disable = iio_sd_mod_disable,
+ .read_raw = iio_sd_mod_read,
+};
+
+static const struct iio_backend_info sd_backend_info = {
+ .name = "sd-modulator",
+ .ops = &sd_backend_ops,
+};
+
+static int iio_sd_mod_register(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *iio;
@@ -45,6 +100,45 @@ static int iio_sd_mod_probe(struct platform_device *pdev)
return devm_iio_device_register(&pdev->dev, iio);
}
+static int iio_sd_mod_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regulator *vref;
+ struct iio_sd_backend_priv *priv;
+ int ret;
+
+ /* If sd modulator is not defined as an IIO backend device, fallback to legacy */
+ if (!device_property_present(dev, "#io-backend-cells"))
+ return iio_sd_mod_register(pdev);
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /*
+ * Get regulator reference if any, but don't enable regulator right now.
+ * Rely on enable and disable callbacks to manage regulator power.
+ */
+ vref = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(vref)) {
+ if (PTR_ERR(vref) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(vref), "Failed to get vref\n");
+ } else {
+ /*
+ * Retrieve voltage right now, as regulator_get_voltage() provides it whatever
+ * the state of the regulator.
+ */
+ ret = regulator_get_voltage(vref);
+ if (ret < 0)
+ return ret;
+
+ priv->vref = vref;
+ priv->vref_mv = ret / 1000;
+ }
+
+ return devm_iio_backend_register(&pdev->dev, &sd_backend_info, priv);
+};
+
static const struct of_device_id sd_adc_of_match[] = {
{ .compatible = "sd-modulator" },
{ .compatible = "ads1201" },
@@ -65,3 +159,4 @@ module_platform_driver(iio_sd_mod_adc);
MODULE_DESCRIPTION("Basic sigma delta modulator");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_BACKEND);
diff --git a/drivers/iio/adc/sophgo-cv1800b-adc.c b/drivers/iio/adc/sophgo-cv1800b-adc.c
new file mode 100644
index 000000000000..0951deb7b111
--- /dev/null
+++ b/drivers/iio/adc/sophgo-cv1800b-adc.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sophgo CV1800B SARADC Driver
+ *
+ * Copyright (C) Bootlin 2024
+ * Author: Thomas Bonnefille <thomas.bonnefille@bootlin.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define CV1800B_ADC_CTRL_REG 0x04
+#define CV1800B_ADC_EN BIT(0)
+#define CV1800B_ADC_SEL(x) BIT((x) + 5)
+#define CV1800B_ADC_STATUS_REG 0x08
+#define CV1800B_ADC_BUSY BIT(0)
+#define CV1800B_ADC_CYC_SET_REG 0x0C
+#define CV1800B_MASK_STARTUP_CYCLE GENMASK(4, 0)
+#define CV1800B_MASK_SAMPLE_WINDOW GENMASK(11, 8)
+#define CV1800B_MASK_CLKDIV GENMASK(15, 12)
+#define CV1800B_MASK_COMPARE_CYCLE GENMASK(19, 16)
+#define CV1800B_ADC_CH_RESULT_REG(x) (0x14 + 4 * (x))
+#define CV1800B_ADC_CH_RESULT GENMASK(11, 0)
+#define CV1800B_ADC_CH_VALID BIT(15)
+#define CV1800B_ADC_INTR_EN_REG 0x20
+#define CV1800B_ADC_INTR_CLR_REG 0x24
+#define CV1800B_ADC_INTR_CLR_BIT BIT(0)
+#define CV1800B_ADC_INTR_STA_REG 0x28
+#define CV1800B_ADC_INTR_STA_BIT BIT(0)
+#define CV1800B_READ_TIMEOUT_MS 1000
+#define CV1800B_READ_TIMEOUT_US (CV1800B_READ_TIMEOUT_MS * 1000)
+
+#define CV1800B_ADC_CHANNEL(index) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+ .scan_index = index, \
+ }
+
+struct cv1800b_adc {
+ struct completion completion;
+ void __iomem *regs;
+ struct mutex lock; /* ADC Control and Result register */
+ struct clk *clk;
+ int irq;
+};
+
+static const struct iio_chan_spec sophgo_channels[] = {
+ CV1800B_ADC_CHANNEL(0),
+ CV1800B_ADC_CHANNEL(1),
+ CV1800B_ADC_CHANNEL(2),
+};
+
+static void cv1800b_adc_start_measurement(struct cv1800b_adc *saradc,
+ int channel)
+{
+ writel(0, saradc->regs + CV1800B_ADC_CTRL_REG);
+ writel(CV1800B_ADC_SEL(channel) | CV1800B_ADC_EN,
+ saradc->regs + CV1800B_ADC_CTRL_REG);
+}
+
+static int cv1800b_adc_wait(struct cv1800b_adc *saradc)
+{
+ if (saradc->irq < 0) {
+ u32 reg;
+
+ return readl_poll_timeout(saradc->regs + CV1800B_ADC_STATUS_REG,
+ reg, !(reg & CV1800B_ADC_BUSY),
+ 500, CV1800B_READ_TIMEOUT_US);
+ }
+
+ return wait_for_completion_timeout(&saradc->completion,
+ msecs_to_jiffies(CV1800B_READ_TIMEOUT_MS)) > 0 ?
+ 0 : -ETIMEDOUT;
+}
+
+static int cv1800b_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cv1800b_adc *saradc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ u32 sample;
+
+ scoped_guard(mutex, &saradc->lock) {
+ int ret;
+
+ cv1800b_adc_start_measurement(saradc, chan->scan_index);
+ ret = cv1800b_adc_wait(saradc);
+ if (ret < 0)
+ return ret;
+
+ sample = readl(saradc->regs + CV1800B_ADC_CH_RESULT_REG(chan->scan_index));
+ }
+ if (!(sample & CV1800B_ADC_CH_VALID))
+ return -ENODATA;
+
+ *val = sample & CV1800B_ADC_CH_RESULT;
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ *val = 3300;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ u32 status_reg = readl(saradc->regs + CV1800B_ADC_CYC_SET_REG);
+ unsigned int clk_div = (1 + FIELD_GET(CV1800B_MASK_CLKDIV, status_reg));
+ unsigned int freq = clk_get_rate(saradc->clk) / clk_div;
+ unsigned int nb_startup_cycle = 1 + FIELD_GET(CV1800B_MASK_STARTUP_CYCLE, status_reg);
+ unsigned int nb_sample_cycle = 1 + FIELD_GET(CV1800B_MASK_SAMPLE_WINDOW, status_reg);
+ unsigned int nb_compare_cycle = 1 + FIELD_GET(CV1800B_MASK_COMPARE_CYCLE, status_reg);
+
+ *val = freq / (nb_startup_cycle + nb_sample_cycle + nb_compare_cycle);
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t cv1800b_adc_interrupt_handler(int irq, void *private)
+{
+ struct cv1800b_adc *saradc = private;
+ u32 reg = readl(saradc->regs + CV1800B_ADC_INTR_STA_REG);
+
+ if (!(FIELD_GET(CV1800B_ADC_INTR_STA_BIT, reg)))
+ return IRQ_NONE;
+
+ writel(CV1800B_ADC_INTR_CLR_BIT, saradc->regs + CV1800B_ADC_INTR_CLR_REG);
+ complete(&saradc->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info cv1800b_adc_info = {
+ .read_raw = &cv1800b_adc_read_raw,
+};
+
+static int cv1800b_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cv1800b_adc *saradc;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*saradc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ saradc = iio_priv(indio_dev);
+ indio_dev->name = "sophgo-cv1800b-adc";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &cv1800b_adc_info;
+ indio_dev->num_channels = ARRAY_SIZE(sophgo_channels);
+ indio_dev->channels = sophgo_channels;
+
+ saradc->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(saradc->clk))
+ return PTR_ERR(saradc->clk);
+
+ saradc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(saradc->regs))
+ return PTR_ERR(saradc->regs);
+
+ saradc->irq = platform_get_irq_optional(pdev, 0);
+ if (saradc->irq > 0) {
+ init_completion(&saradc->completion);
+ ret = devm_request_irq(dev, saradc->irq,
+ cv1800b_adc_interrupt_handler, 0,
+ dev_name(dev), saradc);
+ if (ret)
+ return ret;
+
+ writel(1, saradc->regs + CV1800B_ADC_INTR_EN_REG);
+ }
+
+ ret = devm_mutex_init(dev, &saradc->lock);
+ if (ret)
+ return ret;
+
+ writel(FIELD_PREP(CV1800B_MASK_STARTUP_CYCLE, 15) |
+ FIELD_PREP(CV1800B_MASK_SAMPLE_WINDOW, 15) |
+ FIELD_PREP(CV1800B_MASK_CLKDIV, 1) |
+ FIELD_PREP(CV1800B_MASK_COMPARE_CYCLE, 15),
+ saradc->regs + CV1800B_ADC_CYC_SET_REG);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id cv1800b_adc_match[] = {
+ { .compatible = "sophgo,cv1800b-saradc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cv1800b_adc_match);
+
+static struct platform_driver cv1800b_adc_driver = {
+ .driver = {
+ .name = "sophgo-cv1800b-saradc",
+ .of_match_table = cv1800b_adc_match,
+ },
+ .probe = cv1800b_adc_probe,
+};
+module_platform_driver(cv1800b_adc_driver);
+
+MODULE_AUTHOR("Thomas Bonnefille <thomas.bonnefille@bootlin.com>");
+MODULE_DESCRIPTION("Sophgo CV1800B SARADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 375aa7720f80..32ca26ed59f7 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1261,7 +1261,7 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]);
stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]);
- for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ for_each_set_bit(bit, scan_mask, iio_get_masklength(indio_dev)) {
chan = indio_dev->channels + bit;
/*
* Assign one channel per SQ entry in regular
@@ -1619,7 +1619,7 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
+ adc->num_conv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev));
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
pm_runtime_mark_last_busy(dev);
@@ -2638,7 +2638,7 @@ static const struct of_device_id stm32_adc_of_match[] = {
{ .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
{ .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg },
{ .compatible = "st,stm32mp13-adc", .data = (void *)&stm32mp13_adc_cfg },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index fabd654245f5..2037f73426d4 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -9,6 +9,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
+#include <linux/iio/backend.h>
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
@@ -78,6 +79,7 @@ struct stm32_dfsdm_adc {
/* ADC specific */
unsigned int oversamp;
struct iio_hw_consumer *hwc;
+ struct iio_backend **backend;
struct completion completion;
u32 *buffer;
@@ -666,6 +668,74 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
return 0;
}
+static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm,
+ struct iio_dev *indio_dev,
+ struct iio_chan_spec *ch,
+ struct fwnode_handle *node)
+{
+ struct stm32_dfsdm_channel *df_ch;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct iio_backend *backend;
+ const char *of_str;
+ int ret, val;
+
+ ret = fwnode_property_read_u32(node, "reg", &ch->channel);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
+ return ret;
+ }
+
+ if (ch->channel >= dfsdm->num_chs) {
+ dev_err(&indio_dev->dev, " Error bad channel number %d (max = %d)\n",
+ ch->channel, dfsdm->num_chs);
+ return -EINVAL;
+ }
+
+ ret = fwnode_property_read_string(node, "label", &ch->datasheet_name);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ " Error parsing 'label' for idx %d\n", ch->channel);
+ return ret;
+ }
+
+ df_ch = &dfsdm->ch_list[ch->channel];
+ df_ch->id = ch->channel;
+
+ ret = fwnode_property_read_string(node, "st,adc-channel-type", &of_str);
+ if (!ret) {
+ val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
+ if (val < 0)
+ return val;
+ } else {
+ val = 0;
+ }
+ df_ch->type = val;
+
+ ret = fwnode_property_read_string(node, "st,adc-channel-clk-src", &of_str);
+ if (!ret) {
+ val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
+ if (val < 0)
+ return val;
+ } else {
+ val = 0;
+ }
+ df_ch->src = val;
+
+ ret = fwnode_property_read_u32(node, "st,adc-alt-channel", &df_ch->alt_si);
+ if (ret != -EINVAL)
+ df_ch->alt_si = 0;
+
+ if (adc->dev_data->type == DFSDM_IIO) {
+ backend = devm_iio_backend_fwnode_get(&indio_dev->dev, NULL, node);
+ if (IS_ERR(backend))
+ return dev_err_probe(&indio_dev->dev, PTR_ERR(backend),
+ "Failed to get backend\n");
+ adc->backend[ch->scan_index] = backend;
+ }
+
+ return 0;
+}
+
static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
@@ -987,7 +1057,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
+ adc->nconv = bitmap_weight(scan_mask, iio_get_masklength(indio_dev));
adc->smask = *scan_mask;
dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
@@ -998,6 +1068,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int i = 0;
int ret;
/* Reset adc buffer index */
@@ -1009,6 +1080,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
return ret;
}
+ if (adc->backend) {
+ while (adc->backend[i]) {
+ ret = iio_backend_enable(adc->backend[i]);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+ }
+
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
goto err_stop_hwc;
@@ -1041,6 +1121,7 @@ err_stop_hwc:
static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int i = 0;
stm32_dfsdm_stop_conv(indio_dev);
@@ -1048,6 +1129,13 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+ if (adc->backend) {
+ while (adc->backend[i]) {
+ iio_backend_disable(adc->backend[i]);
+ i++;
+ }
+ }
+
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);
@@ -1220,14 +1308,25 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ u32 max = flo->max << (flo->lshift - chan->scan_type.shift);
+ int idx = chan->scan_index;
int ret;
+ if (flo->lshift < chan->scan_type.shift)
+ max = flo->max >> (chan->scan_type.shift - flo->lshift);
+
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
- ret = iio_hw_consumer_enable(adc->hwc);
+ if (adc->hwc)
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (adc->backend)
+ ret = iio_backend_enable(adc->backend[idx]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
@@ -1236,7 +1335,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
- iio_hw_consumer_disable(adc->hwc);
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+ if (adc->backend)
+ iio_backend_disable(adc->backend[idx]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
@@ -1256,6 +1358,50 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
*val = adc->sample_freq;
return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * Scale is expressed in mV.
+ * When fast mode is disabled, actual resolution may be lower
+ * than 2^n, where n = realbits - 1.
+ * This leads to underestimating the input voltage.
+ * To compensate this deviation, the voltage reference can be
+ * corrected with a factor = realbits resolution / actual max
+ */
+ if (adc->backend) {
+ ret = iio_backend_read_scale(adc->backend[idx], chan, val, NULL);
+ if (ret < 0)
+ return ret;
+
+ *val = div_u64((u64)*val * (u64)BIT(DFSDM_DATA_RES - 1), max);
+ *val2 = chan->scan_type.realbits;
+ if (chan->differential)
+ *val *= 2;
+ }
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * DFSDM output data are in the range [-2^n, 2^n],
+ * with n = realbits - 1.
+ * - Differential modulator:
+ * Offset correspond to SD modulator offset.
+ * - Single ended modulator:
+ * Input is in [0V, Vref] range,
+ * where 0V corresponds to -2^n, and Vref to 2^n.
+ * Add 2^n to offset. (i.e. middle of input range)
+ * offset = offset(sd) * vref / res(sd) * max / vref.
+ */
+ if (adc->backend) {
+ ret = iio_backend_read_offset(adc->backend[idx], chan, val, NULL);
+ if (ret < 0)
+ return ret;
+
+ *val = div_u64((u64)max * *val, BIT(*val2 - 1));
+ if (!chan->differential)
+ *val += max;
+ }
+ return IIO_VAL_INT;
}
return -EINVAL;
@@ -1320,7 +1466,7 @@ static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = {
.read = dfsdm_adc_audio_get_spiclk,
.write = dfsdm_adc_audio_set_spiclk,
},
- {},
+ { }
};
static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
@@ -1362,15 +1508,18 @@ static int stm32_dfsdm_dma_request(struct device *dev,
return 0;
}
-static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
- struct iio_chan_spec *ch)
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_chan_spec *ch,
+ struct fwnode_handle *child)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int ret;
- ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
+ if (child)
+ ret = stm32_dfsdm_generic_channel_parse_of(adc->dfsdm, indio_dev, ch, child);
+ else /* Legacy binding */
+ ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
if (ret < 0)
- return ret;
+ return dev_err_probe(&indio_dev->dev, ret, "Failed to parse channel\n");
ch->type = IIO_VOLTAGE;
ch->indexed = 1;
@@ -1379,12 +1528,21 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
* IIO_CHAN_INFO_RAW: used to compute regular conversion
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/
- ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ if (child) {
+ ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET);
+ } else {
+ /* Legacy. Scaling not supported */
+ ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ }
+
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ);
if (adc->dev_data->type == DFSDM_AUDIO) {
ch->ext_info = dfsdm_adc_audio_ext_info;
+ ch->scan_index = 0;
} else {
ch->scan_type.shift = 8;
}
@@ -1396,20 +1554,67 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
&adc->dfsdm->ch_list[ch->channel]);
}
+static int stm32_dfsdm_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels)
+{
+ int num_ch = indio_dev->num_channels;
+ int chan_idx = 0;
+ int ret;
+
+ for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
+ channels[chan_idx].scan_index = chan_idx;
+ ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], NULL);
+ if (ret < 0)
+ return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n");
+ }
+
+ return 0;
+}
+
+static int stm32_dfsdm_generic_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels)
+{
+ int chan_idx = 0, ret;
+
+ device_for_each_child_node_scoped(&indio_dev->dev, child) {
+ /* Skip DAI node in DFSDM audio nodes */
+ if (fwnode_property_present(child, "compatible"))
+ continue;
+
+ channels[chan_idx].scan_index = chan_idx;
+ ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &channels[chan_idx], child);
+ if (ret < 0)
+ return dev_err_probe(&indio_dev->dev, ret, "Channels init failed\n");
+
+ chan_idx++;
+ }
+
+ return chan_idx;
+}
+
static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
{
struct iio_chan_spec *ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct stm32_dfsdm_channel *d_ch;
- int ret;
+ bool legacy = false;
+ int num_ch, ret;
+
+ /* If st,adc-channels is defined legacy binding is used. Else assume generic binding. */
+ num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels");
+ if (num_ch == 1)
+ legacy = true;
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;
- ch->scan_index = 0;
+ indio_dev->num_channels = 1;
+ indio_dev->channels = ch;
+
+ if (legacy)
+ ret = stm32_dfsdm_chan_init(indio_dev, ch);
+ else
+ ret = stm32_dfsdm_generic_chan_init(indio_dev, ch);
- ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
if (ret < 0) {
dev_err(&indio_dev->dev, "Channels init failed\n");
return ret;
@@ -1420,9 +1625,6 @@ static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
adc->spi_freq = adc->dfsdm->spi_master_freq;
- indio_dev->num_channels = 1;
- indio_dev->channels = ch;
-
return stm32_dfsdm_dma_request(dev, indio_dev);
}
@@ -1430,43 +1632,61 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
{
struct iio_chan_spec *ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- int num_ch;
- int ret, chan_idx;
+ int num_ch, ret;
+ bool legacy = false;
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
if (ret < 0)
return ret;
- num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
- "st,adc-channels");
- if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) {
- dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
- return num_ch < 0 ? num_ch : -EINVAL;
+ num_ch = device_get_child_node_count(&indio_dev->dev);
+ if (!num_ch) {
+ /* No channels nodes found. Assume legacy binding */
+ num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, "st,adc-channels");
+ if (num_ch < 0) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
+ return num_ch;
+ }
+
+ legacy = true;
}
- /* Bind to SD modulator IIO device */
- adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
- if (IS_ERR(adc->hwc))
- return -EPROBE_DEFER;
+ if (num_ch > adc->dfsdm->num_chs) {
+ dev_err(&indio_dev->dev, "Number of channel [%d] exceeds [%d]\n",
+ num_ch, adc->dfsdm->num_chs);
+ return -EINVAL;
+ }
+ indio_dev->num_channels = num_ch;
- ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
- GFP_KERNEL);
- if (!ch)
- return -ENOMEM;
+ if (legacy) {
+ /* Bind to SD modulator IIO device. */
+ adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
+ if (IS_ERR(adc->hwc))
+ return dev_err_probe(&indio_dev->dev, -EPROBE_DEFER,
+ "waiting for SD modulator\n");
+ } else {
+ /* Generic binding. SD modulator IIO device not used. Use SD modulator backend. */
+ adc->hwc = NULL;
- for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
- ch[chan_idx].scan_index = chan_idx;
- ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]);
- if (ret < 0) {
- dev_err(&indio_dev->dev, "Channels init failed\n");
- return ret;
- }
+ adc->backend = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*adc->backend),
+ GFP_KERNEL);
+ if (!adc->backend)
+ return -ENOMEM;
}
- indio_dev->num_channels = num_ch;
+ ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL);
+ if (!ch)
+ return -ENOMEM;
indio_dev->channels = ch;
+ if (legacy)
+ ret = stm32_dfsdm_chan_init(indio_dev, ch);
+ else
+ ret = stm32_dfsdm_generic_chan_init(indio_dev, ch);
+ if (ret < 0)
+ return ret;
+
init_completion(&adc->completion);
/* Optionally request DMA */
@@ -1677,3 +1897,4 @@ module_platform_driver(stm32_dfsdm_adc_driver);
MODULE_DESCRIPTION("STM32 sigma delta ADC");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_BACKEND);
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index a05d978b8cb8..bef59fcc0d80 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -299,7 +299,7 @@ static const struct of_device_id stm32_dfsdm_of_match[] = {
.compatible = "st,stm32mp1-dfsdm",
.data = &stm32mp1_dfsdm_data,
},
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c
index 8e56def1c9e5..b0add5a2eab5 100644
--- a/drivers/iio/adc/stmpe-adc.c
+++ b/drivers/iio/adc/stmpe-adc.c
@@ -347,7 +347,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume);
static const struct of_device_id stmpe_adc_ids[] = {
{ .compatible = "st,stmpe-adc", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, stmpe_adc_ids);
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
index b11ce555ba3b..e2dbd070c7c4 100644
--- a/drivers/iio/adc/ti-adc0832.c
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -211,8 +211,7 @@ static irqreturn_t adc0832_trigger_handler(int irq, void *p)
mutex_lock(&adc->lock);
- for_each_set_bit(scan_index, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = adc0832_adc_conversion(adc, scan_chan->channel,
@@ -310,7 +309,7 @@ static const struct of_device_id adc0832_dt_ids[] = {
{ .compatible = "ti,adc0832", },
{ .compatible = "ti,adc0834", },
{ .compatible = "ti,adc0838", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, adc0832_dt_ids);
@@ -319,7 +318,7 @@ static const struct spi_device_id adc0832_id[] = {
{ "adc0832", adc0832 },
{ "adc0834", adc0834 },
{ "adc0838", adc0838 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, adc0832_id);
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index 1f6e53832e06..bf98f9bf942a 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -166,8 +166,7 @@ static int adc084s021_buffer_preenable(struct iio_dev *indio_dev)
int scan_index;
int i = 0;
- for_each_set_bit(scan_index, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *channel =
&indio_dev->channels[scan_index];
adc->tx_buf[i++] = channel->channel << 3;
@@ -243,13 +242,13 @@ static int adc084s021_probe(struct spi_device *spi)
static const struct of_device_id adc084s021_of_match[] = {
{ .compatible = "ti,adc084s021", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, adc084s021_of_match);
static const struct spi_device_id adc084s021_id[] = {
{ ADC084S021_DRIVER_NAME, 0 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, adc084s021_id);
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
index c0a72d72f3a9..7f065f457b36 100644
--- a/drivers/iio/adc/ti-adc12138.c
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -344,8 +344,7 @@ static irqreturn_t adc12138_trigger_handler(int irq, void *p)
mutex_lock(&adc->lock);
- for_each_set_bit(scan_index, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
@@ -520,7 +519,7 @@ static const struct of_device_id adc12138_dt_ids[] = {
{ .compatible = "ti,adc12130", },
{ .compatible = "ti,adc12132", },
{ .compatible = "ti,adc12138", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, adc12138_dt_ids);
@@ -528,7 +527,7 @@ static const struct spi_device_id adc12138_id[] = {
{ "adc12130", adc12130 },
{ "adc12132", adc12132 },
{ "adc12138", adc12138 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, adc12138_id);
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index f7c78d0dd449..474e733fb8e0 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -226,14 +226,14 @@ static int ti_adc_probe(struct spi_device *spi)
static const struct of_device_id ti_adc_dt_ids[] = {
{ .compatible = "ti,adc141s626", },
{ .compatible = "ti,adc161s626", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
static const struct spi_device_id ti_adc_id[] = {
- {"adc141s626", TI_ADC141S626},
- {"adc161s626", TI_ADC161S626},
- {},
+ { "adc141s626", TI_ADC141S626 },
+ { "adc161s626", TI_ADC161S626 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ti_adc_id);
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index d3363d02f292..6d1bc9659946 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -456,7 +456,7 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p)
mutex_lock(&data->lock);
chan = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
+ iio_get_masklength(indio_dev));
ret = ads1015_get_adc_result(data, chan, &res);
if (ret < 0) {
mutex_unlock(&data->lock);
@@ -1173,7 +1173,7 @@ static const struct i2c_device_id ads1015_id[] = {
{ "ads1015", (kernel_ulong_t)&ads1015_data },
{ "ads1115", (kernel_ulong_t)&ads1115_data },
{ "tla2024", (kernel_ulong_t)&tla2024_data },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, ads1015_id);
@@ -1181,7 +1181,7 @@ static const struct of_device_id ads1015_of_match[] = {
{ .compatible = "ti,ads1015", .data = &ads1015_data },
{ .compatible = "ti,ads1115", .data = &ads1115_data },
{ .compatible = "ti,tla2024", .data = &tla2024_data },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ads1015_of_match);
diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c
index d649980479e4..1c7606375149 100644
--- a/drivers/iio/adc/ti-ads1119.c
+++ b/drivers/iio/adc/ti-ads1119.c
@@ -435,7 +435,7 @@ static int ads1119_triggered_buffer_preenable(struct iio_dev *indio_dev)
int ret;
index = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
+ iio_get_masklength(indio_dev));
ret = ads1119_set_conv_mode(st, true);
if (ret)
@@ -508,7 +508,7 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private)
if (!iio_trigger_using_own(indio_dev)) {
index = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
+ iio_get_masklength(indio_dev));
ret = ads1119_poll_data_ready(st, &indio_dev->channels[index]);
if (ret) {
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
index 4ca62121f0d1..14941f384dad 100644
--- a/drivers/iio/adc/ti-ads124s08.c
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -279,8 +279,7 @@ static irqreturn_t ads124s_trigger_handler(int irq, void *p)
int scan_index, j = 0;
int ret;
- for_each_set_bit(scan_index, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
scan_index);
if (ret)
@@ -358,7 +357,7 @@ MODULE_DEVICE_TABLE(spi, ads124s_id);
static const struct of_device_id ads124s_of_table[] = {
{ .compatible = "ti,ads124s06" },
{ .compatible = "ti,ads124s08" },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ads124s_of_table);
diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c
index 1d1eaba3d6d1..13cb32125eef 100644
--- a/drivers/iio/adc/ti-ads1298.c
+++ b/drivers/iio/adc/ti-ads1298.c
@@ -502,8 +502,7 @@ static void ads1298_rdata_complete(void *context)
}
/* Demux the channel data into our bounce buffer */
- for_each_set_bit(scan_index, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, scan_index) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
const u8 *data = priv->rx_buffer + scan_chan->address;
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index 96dd9366f8ff..91a427eb0882 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -637,7 +637,7 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
if (ret)
goto out;
- for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, chn) {
src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes;
dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
@@ -918,7 +918,7 @@ static const struct of_device_id ads131e08_of_match[] = {
.data = &ads131e08_info_tbl[ads131e06], },
{ .compatible = "ti,ads131e08",
.data = &ads131e08_info_tbl[ads131e08], },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ads131e08_of_match);
@@ -926,7 +926,7 @@ static const struct spi_device_id ads131e08_ids[] = {
{ "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
{ "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
{ "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, ads131e08_ids);
diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c
index 4da78302359b..66b54c0d75aa 100644
--- a/drivers/iio/adc/ti-ads7924.c
+++ b/drivers/iio/adc/ti-ads7924.c
@@ -448,13 +448,13 @@ static int ads7924_probe(struct i2c_client *client)
static const struct i2c_device_id ads7924_id[] = {
{ "ads7924" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, ads7924_id);
static const struct of_device_id ads7924_of_match[] = {
{ .compatible = "ti,ads7924", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ads7924_of_match);
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index 263fc3a1b87e..af28672aa803 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -705,7 +705,7 @@ static const struct of_device_id ads7950_of_table[] = {
{ .compatible = "ti,ads7959", .data = &ti_ads7950_chip_info[TI_ADS7959] },
{ .compatible = "ti,ads7960", .data = &ti_ads7950_chip_info[TI_ADS7960] },
{ .compatible = "ti,ads7961", .data = &ti_ads7950_chip_info[TI_ADS7961] },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, ads7950_of_table);
diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c
index bbd85cb47f81..3bec8a2e61ab 100644
--- a/drivers/iio/adc/ti-ads8344.c
+++ b/drivers/iio/adc/ti-ads8344.c
@@ -175,7 +175,7 @@ static int ads8344_probe(struct spi_device *spi)
static const struct of_device_id ads8344_of_match[] = {
{ .compatible = "ti,ads8344", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ads8344_of_match);
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
index 7a79f0cebfbf..9b1814f1965a 100644
--- a/drivers/iio/adc/ti-ads8688.c
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -384,9 +384,7 @@ static irqreturn_t ads8688_trigger_handler(int irq, void *p)
u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)] __aligned(8);
int i, j = 0;
- for (i = 0; i < indio_dev->masklength; i++) {
- if (!test_bit(i, indio_dev->active_scan_mask))
- continue;
+ iio_for_each_active_channel(indio_dev, i) {
buffer[j] = ads8688_read(indio_dev, i);
j++;
}
@@ -454,9 +452,9 @@ static int ads8688_probe(struct spi_device *spi)
}
static const struct spi_device_id ads8688_id[] = {
- {"ads8684", ID_ADS8684},
- {"ads8688", ID_ADS8688},
- {}
+ { "ads8684", ID_ADS8684 },
+ { "ads8688", ID_ADS8688 },
+ { }
};
MODULE_DEVICE_TABLE(spi, ads8688_id);
diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
index 84ba5c4a0eea..169e3591320b 100644
--- a/drivers/iio/adc/ti-lmp92064.c
+++ b/drivers/iio/adc/ti-lmp92064.c
@@ -360,7 +360,7 @@ static int lmp92064_adc_probe(struct spi_device *spi)
static const struct spi_device_id lmp92064_id_table[] = {
{ "lmp92064" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(spi, lmp92064_id_table);
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
index 30f629a553a1..08de997584fd 100644
--- a/drivers/iio/adc/ti-tlc4541.c
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -237,14 +237,14 @@ static void tlc4541_remove(struct spi_device *spi)
static const struct of_device_id tlc4541_dt_ids[] = {
{ .compatible = "ti,tlc3541", },
{ .compatible = "ti,tlc4541", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
static const struct spi_device_id tlc4541_id[] = {
- {"tlc3541", TLC3541},
- {"tlc4541", TLC4541},
- {}
+ { "tlc3541", TLC3541 },
+ { "tlc4541", TLC4541 },
+ { }
};
MODULE_DEVICE_TABLE(spi, tlc4541_id);
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
index edcef8f11522..311d97001249 100644
--- a/drivers/iio/adc/ti-tsc2046.c
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -6,6 +6,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
@@ -141,7 +142,7 @@ enum tsc2046_state {
struct tsc2046_adc_priv {
struct spi_device *spi;
const struct tsc2046_adc_dcfg *dcfg;
- struct regulator *vref_reg;
+ bool internal_vref;
struct iio_trigger *trig;
struct hrtimer trig_timer;
@@ -257,7 +258,7 @@ static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx,
case TI_TSC2046_ADDR_VBAT:
case TI_TSC2046_ADDR_TEMP0:
pd |= TI_TSC2046_SER;
- if (!priv->vref_reg)
+ if (priv->internal_vref)
pd |= TI_TSC2046_PD1_VREF_ON;
}
@@ -273,7 +274,6 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
u32 *effective_speed_hz)
{
struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx];
- struct tsc2046_adc_atom *rx_buf, *tx_buf;
unsigned int val, val_normalized = 0;
int ret, i, count_skip = 0, max_count;
struct spi_transfer xfer;
@@ -287,18 +287,20 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
max_count = 1;
}
- if (sizeof(*tx_buf) * max_count > PAGE_SIZE)
+ if (sizeof(struct tsc2046_adc_atom) * max_count > PAGE_SIZE)
return -ENOSPC;
- tx_buf = kcalloc(max_count, sizeof(*tx_buf), GFP_KERNEL);
+ struct tsc2046_adc_atom *tx_buf __free(kfree) = kcalloc(max_count,
+ sizeof(*tx_buf),
+ GFP_KERNEL);
if (!tx_buf)
return -ENOMEM;
- rx_buf = kcalloc(max_count, sizeof(*rx_buf), GFP_KERNEL);
- if (!rx_buf) {
- ret = -ENOMEM;
- goto free_tx;
- }
+ struct tsc2046_adc_atom *rx_buf __free(kfree) = kcalloc(max_count,
+ sizeof(*rx_buf),
+ GFP_KERNEL);
+ if (!rx_buf)
+ return -ENOMEM;
/*
* Do not enable automatic power down on working samples. Otherwise the
@@ -326,7 +328,7 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
if (ret) {
dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n",
ERR_PTR(ret));
- goto free_bufs;
+ return ret;
}
if (effective_speed_hz)
@@ -337,14 +339,7 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
val_normalized += val;
}
- ret = DIV_ROUND_UP(val_normalized, max_count - count_skip);
-
-free_bufs:
- kfree(rx_buf);
-free_tx:
- kfree(tx_buf);
-
- return ret;
+ return DIV_ROUND_UP(val_normalized, max_count - count_skip);
}
static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv,
@@ -746,49 +741,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv)
}
}
-static void tsc2046_adc_regulator_disable(void *data)
-{
- struct tsc2046_adc_priv *priv = data;
-
- regulator_disable(priv->vref_reg);
-}
-
-static int tsc2046_adc_configure_regulator(struct tsc2046_adc_priv *priv)
-{
- struct device *dev = &priv->spi->dev;
- int ret;
-
- priv->vref_reg = devm_regulator_get_optional(dev, "vref");
- if (IS_ERR(priv->vref_reg)) {
- /* If regulator exists but can't be get, return an error */
- if (PTR_ERR(priv->vref_reg) != -ENODEV)
- return PTR_ERR(priv->vref_reg);
- priv->vref_reg = NULL;
- }
- if (!priv->vref_reg) {
- /* Use internal reference */
- priv->vref_mv = TI_TSC2046_INT_VREF;
- return 0;
- }
-
- ret = regulator_enable(priv->vref_reg);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, tsc2046_adc_regulator_disable,
- priv);
- if (ret)
- return ret;
-
- ret = regulator_get_voltage(priv->vref_reg);
- if (ret < 0)
- return ret;
-
- priv->vref_mv = ret / MILLI;
-
- return 0;
-}
-
static int tsc2046_adc_probe(struct spi_device *spi)
{
const struct tsc2046_adc_dcfg *dcfg;
@@ -830,10 +782,13 @@ static int tsc2046_adc_probe(struct spi_device *spi)
indio_dev->num_channels = dcfg->num_channels;
indio_dev->info = &tsc2046_adc_info;
- ret = tsc2046_adc_configure_regulator(priv);
- if (ret)
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0 && ret != -ENODEV)
return ret;
+ priv->internal_vref = ret == -ENODEV;
+ priv->vref_mv = priv->internal_vref ? TI_TSC2046_INT_VREF : ret / MILLI;
+
tsc2046_adc_parse_fwnode(priv);
ret = tsc2046_adc_setup_spi_msg(priv);
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index e4548df3f8fb..5afd2feb8c3d 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -752,7 +752,7 @@ static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev)
writel(val, info->regs + VF610_REG_ADC_GC);
channel = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
+ iio_get_masklength(indio_dev));
val = VF610_ADC_ADCHC(channel);
val |= VF610_ADC_AIEN;
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
index f051358d6b50..ebc583b07e0c 100644
--- a/drivers/iio/adc/xilinx-ams.c
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -1275,7 +1275,6 @@ static int ams_parse_firmware(struct iio_dev *indio_dev)
struct ams *ams = iio_priv(indio_dev);
struct iio_chan_spec *ams_channels, *dev_channels;
struct device *dev = indio_dev->dev.parent;
- struct fwnode_handle *child = NULL;
struct fwnode_handle *fwnode = dev_fwnode(dev);
size_t ams_size;
int ret, ch_cnt = 0, i, rising_off, falling_off;
@@ -1297,16 +1296,12 @@ static int ams_parse_firmware(struct iio_dev *indio_dev)
num_channels += ret;
}
- fwnode_for_each_child_node(fwnode, child) {
- if (fwnode_device_is_available(child)) {
- ret = ams_init_module(indio_dev, child, ams_channels + num_channels);
- if (ret < 0) {
- fwnode_handle_put(child);
- return ret;
- }
+ device_for_each_child_node_scoped(dev, child) {
+ ret = ams_init_module(indio_dev, child, ams_channels + num_channels);
+ if (ret < 0)
+ return ret;
- num_channels += ret;
- }
+ num_channels += ret;
}
for (i = 0; i < num_channels; i++) {
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 564c0cad0fc7..cfbfcaefec0f 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -628,7 +628,7 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev,
size_t n;
void *data;
- n = bitmap_weight(mask, indio_dev->masklength);
+ n = bitmap_weight(mask, iio_get_masklength(indio_dev));
data = devm_krealloc_array(indio_dev->dev.parent, xadc->data,
n, sizeof(*xadc->data), GFP_KERNEL);
@@ -681,8 +681,7 @@ static irqreturn_t xadc_trigger_handler(int irq, void *p)
goto out;
j = 0;
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
chan = xadc_scan_index_to_channel(i);
xadc_read_adc_reg(xadc, chan, &xadc->data[j]);
j++;