summaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc/ad7606.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc/ad7606.c')
-rw-r--r--drivers/iio/adc/ad7606.c174
1 files changed, 148 insertions, 26 deletions
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index d39354afd539..1a314fddd7eb 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -5,6 +5,7 @@
* Copyright 2011 Analog Devices Inc.
*/
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -85,6 +86,10 @@ static const unsigned int ad7606_oversampling_avail[7] = {
1, 2, 4, 8, 16, 32, 64,
};
+static const unsigned int ad7606b_oversampling_avail[9] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256,
+};
+
static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
@@ -187,6 +192,8 @@ static int ad7608_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
static int ad7609_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
+static int ad7616_sw_mode_setup(struct iio_dev *indio_dev);
+static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev);
const struct ad7606_chip_info ad7605_4_info = {
.channels = ad7605_channels,
@@ -239,6 +246,7 @@ const struct ad7606_chip_info ad7606b_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
@@ -250,6 +258,7 @@ const struct ad7606_chip_info ad7606c_16_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
@@ -294,6 +303,7 @@ const struct ad7606_chip_info ad7606c_18_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_18bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
@@ -307,6 +317,7 @@ const struct ad7606_chip_info ad7616_info = {
.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
.os_req_reset = true,
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7616_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606");
@@ -752,13 +763,13 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = ad7606_scan_direct(indio_dev, chan->address, val);
- if (ret < 0)
- return ret;
- return IIO_VAL_INT;
- }
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_scan_direct(indio_dev, chan->address, val);
+ iio_device_release_direct(indio_dev);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (st->sw_mode_en)
ch = chan->address;
@@ -818,8 +829,7 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
values[0] = val & GENMASK(2, 0);
- gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc,
- st->gpio_os->info, values);
+ gpiod_multi_set_value_cansleep(st->gpio_os, values);
/* AD7616 requires a reset to update value */
if (st->chip_info->os_req_reset)
@@ -852,7 +862,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
}
val = (val * MICRO) + val2;
i = find_closest(val, scale_avail_uv, cs->num_scales);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = st->write_scale(indio_dev, ch, i + cs->reg_offset);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
cs->range = i;
@@ -863,7 +877,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = st->write_os(indio_dev, i);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
st->oversampling = st->oversampling_avail[i];
@@ -1138,16 +1156,117 @@ static const struct iio_trigger_ops ad7606_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
-static int ad7606_sw_mode_setup(struct iio_dev *indio_dev)
+static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr,
+ unsigned long mask, unsigned int val)
+{
+ int readval;
+
+ readval = st->bops->reg_read(st, addr);
+ if (readval < 0)
+ return readval;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr, mode, ch_index;
- st->sw_mode_en = st->bops->sw_mode_config &&
- device_property_present(st->dev, "adi,sw-mode");
- if (!st->sw_mode_en)
- return 0;
+ /*
+ * Ad7616 has 16 channels divided in group A and group B.
+ * The range of channels from A are stored in registers with address 4
+ * while channels from B are stored in register with address 6.
+ * The last bit from channels determines if it is from group A or B
+ * because the order of channels in iio is 0A, 0B, 1A, 1B...
+ */
+ ch_index = ch >> 1;
+
+ ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+ if ((ch & 0x1) == 0) /* channel A */
+ ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+ else /* channel B */
+ ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
+
+ /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+ mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+
+ return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+ mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_OS_MASK, val << 2);
+}
+
+static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch),
+ AD7606_RANGE_CH_MSK(ch),
+ AD7606_RANGE_CH_MODE(ch, val));
+}
+
+static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return st->bops->reg_write(st, AD7606_OS_MODE, val);
+}
+
+static int ad7616_sw_mode_setup(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+
+ st->write_scale = ad7616_write_scale_sw;
+ st->write_os = &ad7616_write_os_sw;
+
+ ret = st->bops->sw_mode_config(indio_dev);
+ if (ret)
+ return ret;
- indio_dev->info = &ad7606_info_sw_mode;
+ /* Activate Burst mode and SEQEN MODE */
+ return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
+static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ 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
+ * in the device tree, then they need to be set to high,
+ * otherwise, they must be hardwired to VDD
+ */
+ if (st->gpio_os)
+ gpiod_multi_set_value_cansleep(st->gpio_os, os);
+
+ /* OS of 128 and 256 are available only in software mode */
+ st->oversampling_avail = ad7606b_oversampling_avail;
+ st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail);
+
+ st->write_scale = ad7606_write_scale_sw;
+ st->write_os = &ad7606_write_os_sw;
return st->bops->sw_mode_config(indio_dev);
}
@@ -1246,17 +1365,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return -ERESTARTSYS;
}
- st->write_scale = ad7606_write_scale_hw;
- st->write_os = ad7606_write_os_hw;
-
- ret = ad7606_sw_mode_setup(indio_dev);
- if (ret)
- return ret;
-
- ret = ad7606_chan_scales_setup(indio_dev);
- if (ret)
- return ret;
-
/* If convst pin is not defined, setup PWM. */
if (!st->gpio_convst) {
st->cnvst_pwm = devm_pwm_get(dev, NULL);
@@ -1334,6 +1442,20 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return ret;
}
+ st->write_scale = ad7606_write_scale_hw;
+ st->write_os = ad7606_write_os_hw;
+
+ st->sw_mode_en = st->chip_info->sw_setup_cb &&
+ device_property_present(st->dev, "adi,sw-mode");
+ if (st->sw_mode_en) {
+ indio_dev->info = &ad7606_info_sw_mode;
+ st->chip_info->sw_setup_cb(indio_dev);
+ }
+
+ ret = ad7606_chan_scales_setup(indio_dev);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606");