summaryrefslogtreecommitdiff
path: root/drivers/iio/dac
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-03-28 12:27:35 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-03-28 12:27:35 -0700
commit02e2af20f4f9f2aa0c84e9a30a35c02f0fbb7daa (patch)
treed126449a7d2ea2270627183f7cebd726fbe56a9d /drivers/iio/dac
parentff61bc81b3feebcef4d0431a92e2e40e8d4fe8b3 (diff)
parent37fd83916da2e4cae03d350015c82a67b1b334c4 (diff)
Merge tag 'char-misc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc and other driver updates from Greg KH: "Here is the big set of char/misc and other small driver subsystem updates for 5.18-rc1. Included in here are merges from driver subsystems which contain: - iio driver updates and new drivers - fsi driver updates - fpga driver updates - habanalabs driver updates and support for new hardware - soundwire driver updates and new drivers - phy driver updates and new drivers - coresight driver updates - icc driver updates Individual changes include: - mei driver updates - interconnect driver updates - new PECI driver subsystem added - vmci driver updates - lots of tiny misc/char driver updates All of these have been in linux-next for a while with no reported problems" * tag 'char-misc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (556 commits) firmware: google: Properly state IOMEM dependency kgdbts: fix return value of __setup handler firmware: sysfb: fix platform-device leak in error path firmware: stratix10-svc: add missing callback parameter on RSU arm64: dts: qcom: add non-secure domain property to fastrpc nodes misc: fastrpc: Add dma handle implementation misc: fastrpc: Add fdlist implementation misc: fastrpc: Add helper function to get list and page misc: fastrpc: Add support to secure memory map dt-bindings: misc: add fastrpc domain vmid property misc: fastrpc: check before loading process to the DSP misc: fastrpc: add secure domain support dt-bindings: misc: add property to support non-secure DSP misc: fastrpc: Add support to get DSP capabilities misc: fastrpc: add support for FASTRPC_IOCTL_MEM_MAP/UNMAP misc: fastrpc: separate fastrpc device from channel context dt-bindings: nvmem: brcm,nvram: add basic NVMEM cells dt-bindings: nvmem: make "reg" property optional nvmem: brcm_nvram: parse NVRAM content into NVMEM cells nvmem: dt-bindings: Fix the error of dt-bindings check ...
Diffstat (limited to 'drivers/iio/dac')
-rw-r--r--drivers/iio/dac/Kconfig11
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad5592r-base.c5
-rw-r--r--drivers/iio/dac/ad5592r.c1
-rw-r--r--drivers/iio/dac/ad5593r.c1
-rw-r--r--drivers/iio/dac/ad5686-spi.c1
-rw-r--r--drivers/iio/dac/ad5686.c4
-rw-r--r--drivers/iio/dac/ad5696-i2c.c1
-rw-r--r--drivers/iio/dac/ltc2688.c1071
-rw-r--r--drivers/iio/dac/m62332.c11
-rw-r--r--drivers/iio/dac/stm32-dac-core.c16
-rw-r--r--drivers/iio/dac/stm32-dac.c9
-rw-r--r--drivers/iio/dac/vf610_dac.c7
13 files changed, 1108 insertions, 31 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index bfcf7568de32..c0bf0d84197f 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,17 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config LTC2688
+ tristate "Analog Devices LTC2688 DAC spi driver"
+ depends on SPI
+ select REGMAP
+ help
+ Say yes here to build support for Analog Devices
+ LTC2688 converters (DAC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ltc2688.
+
config AD5686
tristate
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 01a50131572f..ec3e42713f00 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_DS4424) += ds4424.o
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
obj-$(CONFIG_LTC1660) += ltc1660.o
obj-$(CONFIG_LTC2632) += ltc2632.o
+obj-$(CONFIG_LTC2688) += ltc2688.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c
index 2fcc59728fd6..a424b7220b61 100644
--- a/drivers/iio/dac/ad5592r-base.c
+++ b/drivers/iio/dac/ad5592r-base.c
@@ -11,7 +11,6 @@
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -661,7 +660,7 @@ error_disable_reg:
return ret;
}
-EXPORT_SYMBOL_GPL(ad5592r_probe);
+EXPORT_SYMBOL_NS_GPL(ad5592r_probe, IIO_AD5592R);
void ad5592r_remove(struct device *dev)
{
@@ -675,7 +674,7 @@ void ad5592r_remove(struct device *dev)
if (st->reg)
regulator_disable(st->reg);
}
-EXPORT_SYMBOL_GPL(ad5592r_remove);
+EXPORT_SYMBOL_NS_GPL(ad5592r_remove, IIO_AD5592R);
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c
index 0f7abfa75bec..32d950bbb1ca 100644
--- a/drivers/iio/dac/ad5592r.c
+++ b/drivers/iio/dac/ad5592r.c
@@ -168,3 +168,4 @@ module_spi_driver(ad5592r_spi_driver);
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5592R);
diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c
index 64dd7a0bddf7..34e1319a9712 100644
--- a/drivers/iio/dac/ad5593r.c
+++ b/drivers/iio/dac/ad5593r.c
@@ -137,3 +137,4 @@ module_i2c_driver(ad5593r_driver);
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5593R multi-channel converters");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5592R);
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index d26fb29b6b04..8ba2ea70451a 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -135,3 +135,4 @@ module_spi_driver(ad5686_spi_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5686);
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index e592a995f404..f78dd3f33199 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -536,7 +536,7 @@ error_disable_reg:
regulator_disable(st->reg);
return ret;
}
-EXPORT_SYMBOL_GPL(ad5686_probe);
+EXPORT_SYMBOL_NS_GPL(ad5686_probe, IIO_AD5686);
void ad5686_remove(struct device *dev)
{
@@ -547,7 +547,7 @@ void ad5686_remove(struct device *dev)
if (!IS_ERR(st->reg))
regulator_disable(st->reg);
}
-EXPORT_SYMBOL_GPL(ad5686_remove);
+EXPORT_SYMBOL_NS_GPL(ad5686_remove, IIO_AD5686);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index 93f0e0e66c22..762503c1901b 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -125,3 +125,4 @@ module_i2c_driver(ad5686_i2c_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD5686);
diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c
new file mode 100644
index 000000000000..e41861d29767
--- /dev/null
+++ b/drivers/iio/dac/ltc2688.c
@@ -0,0 +1,1071 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LTC2688 16 channel, 16 bit Voltage Output SoftSpan DAC driver
+ *
+ * Copyright 2022 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/limits.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define LTC2688_DAC_CHANNELS 16
+
+#define LTC2688_CMD_CH_CODE(x) (0x00 + (x))
+#define LTC2688_CMD_CH_SETTING(x) (0x10 + (x))
+#define LTC2688_CMD_CH_OFFSET(x) (0X20 + (x))
+#define LTC2688_CMD_CH_GAIN(x) (0x30 + (x))
+#define LTC2688_CMD_CH_CODE_UPDATE(x) (0x40 + (x))
+
+#define LTC2688_CMD_CONFIG 0x70
+#define LTC2688_CMD_POWERDOWN 0x71
+#define LTC2688_CMD_A_B_SELECT 0x72
+#define LTC2688_CMD_SW_TOGGLE 0x73
+#define LTC2688_CMD_TOGGLE_DITHER_EN 0x74
+#define LTC2688_CMD_THERMAL_STAT 0x77
+#define LTC2688_CMD_UPDATE_ALL 0x7C
+#define LTC2688_CMD_NOOP 0xFF
+
+#define LTC2688_READ_OPERATION 0x80
+
+/* Channel Settings */
+#define LTC2688_CH_SPAN_MSK GENMASK(2, 0)
+#define LTC2688_CH_OVERRANGE_MSK BIT(3)
+#define LTC2688_CH_TD_SEL_MSK GENMASK(5, 4)
+#define LTC2688_CH_TGP_MAX 3
+#define LTC2688_CH_DIT_PER_MSK GENMASK(8, 6)
+#define LTC2688_CH_DIT_PH_MSK GENMASK(10, 9)
+#define LTC2688_CH_MODE_MSK BIT(11)
+
+#define LTC2688_DITHER_RAW_MASK GENMASK(15, 2)
+#define LTC2688_CH_CALIBBIAS_MASK GENMASK(15, 2)
+#define LTC2688_DITHER_RAW_MAX_VAL (BIT(14) - 1)
+#define LTC2688_CH_CALIBBIAS_MAX_VAL (BIT(14) - 1)
+
+/* Configuration register */
+#define LTC2688_CONFIG_RST BIT(15)
+#define LTC2688_CONFIG_EXT_REF BIT(1)
+
+#define LTC2688_DITHER_FREQ_AVAIL_N 5
+
+enum {
+ LTC2688_SPAN_RANGE_0V_5V,
+ LTC2688_SPAN_RANGE_0V_10V,
+ LTC2688_SPAN_RANGE_M5V_5V,
+ LTC2688_SPAN_RANGE_M10V_10V,
+ LTC2688_SPAN_RANGE_M15V_15V,
+ LTC2688_SPAN_RANGE_MAX
+};
+
+enum {
+ LTC2688_MODE_DEFAULT,
+ LTC2688_MODE_DITHER_TOGGLE,
+};
+
+struct ltc2688_chan {
+ long dither_frequency[LTC2688_DITHER_FREQ_AVAIL_N];
+ bool overrange;
+ bool toggle_chan;
+ u8 mode;
+};
+
+struct ltc2688_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator_bulk_data regulators[2];
+ struct ltc2688_chan channels[LTC2688_DAC_CHANNELS];
+ struct iio_chan_spec *iio_chan;
+ /* lock to protect against multiple access to the device and shared data */
+ struct mutex lock;
+ int vref;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 tx_data[6] ____cacheline_aligned;
+ u8 rx_data[3];
+};
+
+static int ltc2688_spi_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct ltc2688_state *st = context;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx_data,
+ .bits_per_word = 8,
+ .len = reg_size + val_size,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx_data + 3,
+ .rx_buf = st->rx_data,
+ .bits_per_word = 8,
+ .len = reg_size + val_size,
+ },
+ };
+ int ret;
+
+ memcpy(st->tx_data, reg, reg_size);
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret)
+ return ret;
+
+ memcpy(val, &st->rx_data[1], val_size);
+
+ return 0;
+}
+
+static int ltc2688_spi_write(void *context, const void *data, size_t count)
+{
+ struct ltc2688_state *st = context;
+
+ return spi_write(st->spi, data, count);
+}
+
+static int ltc2688_span_get(const struct ltc2688_state *st, int c)
+{
+ int ret, reg, span;
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(c), &reg);
+ if (ret)
+ return ret;
+
+ span = FIELD_GET(LTC2688_CH_SPAN_MSK, reg);
+ /* sanity check to make sure we don't get any weird value from the HW */
+ if (span >= LTC2688_SPAN_RANGE_MAX)
+ return -EIO;
+
+ return span;
+}
+
+static const int ltc2688_span_helper[LTC2688_SPAN_RANGE_MAX][2] = {
+ {0, 5000}, {0, 10000}, {-5000, 5000}, {-10000, 10000}, {-15000, 15000},
+};
+
+static int ltc2688_scale_get(const struct ltc2688_state *st, int c, int *val)
+{
+ const struct ltc2688_chan *chan = &st->channels[c];
+ int span, fs;
+
+ span = ltc2688_span_get(st, c);
+ if (span < 0)
+ return span;
+
+ fs = ltc2688_span_helper[span][1] - ltc2688_span_helper[span][0];
+ if (chan->overrange)
+ fs = mult_frac(fs, 105, 100);
+
+ *val = DIV_ROUND_CLOSEST(fs * st->vref, 4096);
+
+ return 0;
+}
+
+static int ltc2688_offset_get(const struct ltc2688_state *st, int c, int *val)
+{
+ int span;
+
+ span = ltc2688_span_get(st, c);
+ if (span < 0)
+ return span;
+
+ if (ltc2688_span_helper[span][0] < 0)
+ *val = -32768;
+ else
+ *val = 0;
+
+ return 0;
+}
+
+enum {
+ LTC2688_INPUT_A,
+ LTC2688_INPUT_B,
+ LTC2688_INPUT_B_AVAIL,
+ LTC2688_DITHER_OFF,
+ LTC2688_DITHER_FREQ_AVAIL,
+};
+
+static int ltc2688_dac_code_write(struct ltc2688_state *st, u32 chan, u32 input,
+ u16 code)
+{
+ struct ltc2688_chan *c = &st->channels[chan];
+ int ret, reg;
+
+ /* 2 LSBs set to 0 if writing dither amplitude */
+ if (!c->toggle_chan && input == LTC2688_INPUT_B) {
+ if (code > LTC2688_DITHER_RAW_MAX_VAL)
+ return -EINVAL;
+
+ code = FIELD_PREP(LTC2688_DITHER_RAW_MASK, code);
+ }
+
+ mutex_lock(&st->lock);
+ /* select the correct input register to read from */
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan),
+ input << chan);
+ if (ret)
+ goto out_unlock;
+
+ /*
+ * If in dither/toggle mode the dac should be updated by an
+ * external signal (or sw toggle) and not here.
+ */
+ if (c->mode == LTC2688_MODE_DEFAULT)
+ reg = LTC2688_CMD_CH_CODE_UPDATE(chan);
+ else
+ reg = LTC2688_CMD_CH_CODE(chan);
+
+ ret = regmap_write(st->regmap, reg, code);
+out_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ltc2688_dac_code_read(struct ltc2688_state *st, u32 chan, u32 input,
+ u32 *code)
+{
+ struct ltc2688_chan *c = &st->channels[chan];
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan),
+ input << chan);
+ if (ret)
+ goto out_unlock;
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_CODE(chan), code);
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ if (!c->toggle_chan && input == LTC2688_INPUT_B)
+ *code = FIELD_GET(LTC2688_DITHER_RAW_MASK, *code);
+
+ return ret;
+}
+
+static const int ltc2688_raw_range[] = {0, 1, U16_MAX};
+
+static int ltc2688_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ *vals = ltc2688_raw_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2688_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long info)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ltc2688_dac_code_read(st, chan->channel, LTC2688_INPUT_A,
+ val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ ret = ltc2688_offset_get(st, chan->channel, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = ltc2688_scale_get(st, chan->channel, val);
+ if (ret)
+ return ret;
+
+ *val = 16;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(st->regmap,
+ LTC2688_CMD_CH_OFFSET(chan->channel), val);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(LTC2688_CH_CALIBBIAS_MASK, *val);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = regmap_read(st->regmap,
+ LTC2688_CMD_CH_GAIN(chan->channel), val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2688_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long info)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (val > U16_MAX || val < 0)
+ return -EINVAL;
+
+ return ltc2688_dac_code_write(st, chan->channel,
+ LTC2688_INPUT_A, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val > LTC2688_CH_CALIBBIAS_MAX_VAL)
+ return -EINVAL;
+
+ return regmap_write(st->regmap,
+ LTC2688_CMD_CH_OFFSET(chan->channel),
+ FIELD_PREP(LTC2688_CH_CALIBBIAS_MASK, val));
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return regmap_write(st->regmap,
+ LTC2688_CMD_CH_GAIN(chan->channel), val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ltc2688_dither_toggle_set(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ struct ltc2688_chan *c = &st->channels[chan->channel];
+ int ret;
+ bool en;
+
+ ret = kstrtobool(buf, &en);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_TOGGLE_DITHER_EN,
+ BIT(chan->channel), en << chan->channel);
+ if (ret)
+ goto out_unlock;
+
+ c->mode = en ? LTC2688_MODE_DITHER_TOGGLE : LTC2688_MODE_DEFAULT;
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret ?: len;
+}
+
+static ssize_t ltc2688_reg_bool_get(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(st->regmap, private, &val);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", !!(val & BIT(chan->channel)));
+}
+
+static ssize_t ltc2688_reg_bool_set(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ bool en;
+
+ ret = kstrtobool(buf, &en);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, private, BIT(chan->channel),
+ en << chan->channel);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t ltc2688_dither_freq_avail(const struct ltc2688_state *st,
+ const struct ltc2688_chan *chan,
+ char *buf)
+{
+ int sz = 0;
+ u32 f;
+
+ for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++)
+ sz += sysfs_emit_at(buf, sz, "%ld ", chan->dither_frequency[f]);
+
+ buf[sz - 1] = '\n';
+
+ return sz;
+}
+
+static ssize_t ltc2688_dither_freq_get(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ const struct ltc2688_chan *c = &st->channels[chan->channel];
+ u32 reg, freq;
+ int ret;
+
+ if (private == LTC2688_DITHER_FREQ_AVAIL)
+ return ltc2688_dither_freq_avail(st, c, buf);
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel),
+ &reg);
+ if (ret)
+ return ret;
+
+ freq = FIELD_GET(LTC2688_CH_DIT_PER_MSK, reg);
+ if (freq >= ARRAY_SIZE(c->dither_frequency))
+ return -EIO;
+
+ return sysfs_emit(buf, "%ld\n", c->dither_frequency[freq]);
+}
+
+static ssize_t ltc2688_dither_freq_set(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ const struct ltc2688_state *st = iio_priv(indio_dev);
+ const struct ltc2688_chan *c = &st->channels[chan->channel];
+ long val;
+ u32 freq;
+ int ret;
+
+ if (private == LTC2688_DITHER_FREQ_AVAIL)
+ return -EINVAL;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ for (freq = 0; freq < ARRAY_SIZE(c->dither_frequency); freq++) {
+ if (val == c->dither_frequency[freq])
+ break;
+ }
+
+ if (freq == ARRAY_SIZE(c->dither_frequency))
+ return -EINVAL;
+
+ ret = regmap_update_bits(st->regmap,
+ LTC2688_CMD_CH_SETTING(chan->channel),
+ LTC2688_CH_DIT_PER_MSK,
+ FIELD_PREP(LTC2688_CH_DIT_PER_MSK, freq));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t ltc2688_dac_input_read(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ if (private == LTC2688_INPUT_B_AVAIL)
+ return sysfs_emit(buf, "[%u %u %u]\n", ltc2688_raw_range[0],
+ ltc2688_raw_range[1],
+ ltc2688_raw_range[2] / 4);
+
+ if (private == LTC2688_DITHER_OFF)
+ return sysfs_emit(buf, "0\n");
+
+ ret = ltc2688_dac_code_read(st, chan->channel, private, &val);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t ltc2688_dac_input_write(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+ int ret;
+ u16 val;
+
+ if (private == LTC2688_INPUT_B_AVAIL || private == LTC2688_DITHER_OFF)
+ return -EINVAL;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = ltc2688_dac_code_write(st, chan->channel, private, val);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int ltc2688_get_dither_phase(struct iio_dev *dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ltc2688_state *st = iio_priv(dev);
+ int ret, regval;
+
+ ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel),
+ &regval);
+ if (ret)
+ return ret;
+
+ return FIELD_GET(LTC2688_CH_DIT_PH_MSK, regval);
+}
+
+static int ltc2688_set_dither_phase(struct iio_dev *dev,
+ const struct iio_chan_spec *chan,
+ unsigned int phase)
+{
+ struct ltc2688_state *st = iio_priv(dev);
+
+ return regmap_update_bits(st->regmap,
+ LTC2688_CMD_CH_SETTING(chan->channel),
+ LTC2688_CH_DIT_PH_MSK,
+ FIELD_PREP(LTC2688_CH_DIT_PH_MSK, phase));
+}
+
+static int ltc2688_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ltc2688_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static const char * const ltc2688_dither_phase[] = {
+ "0", "1.5708", "3.14159", "4.71239",
+};
+
+static const struct iio_enum ltc2688_dither_phase_enum = {
+ .items = ltc2688_dither_phase,
+ .num_items = ARRAY_SIZE(ltc2688_dither_phase),
+ .set = ltc2688_set_dither_phase,
+ .get = ltc2688_get_dither_phase,
+};
+
+#define LTC2688_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { \
+ .name = _name, \
+ .read = (_read), \
+ .write = (_write), \
+ .private = (_what), \
+ .shared = (_shared), \
+}
+
+/*
+ * For toggle mode we only expose the symbol attr (sw_toggle) in case a TGPx is
+ * not provided in dts.
+ */
+static const struct iio_chan_spec_ext_info ltc2688_toggle_sym_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN,
+ IIO_SEPARATE, ltc2688_reg_bool_get,
+ ltc2688_dither_toggle_set),
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ LTC2688_CHAN_EXT_INFO("symbol", LTC2688_CMD_SW_TOGGLE, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN,
+ IIO_SEPARATE, ltc2688_reg_bool_get,
+ ltc2688_dither_toggle_set),
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("dither_raw", LTC2688_INPUT_B, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("dither_raw_available", LTC2688_INPUT_B_AVAIL,
+ IIO_SEPARATE, ltc2688_dac_input_read,
+ ltc2688_dac_input_write),
+ LTC2688_CHAN_EXT_INFO("dither_offset", LTC2688_DITHER_OFF, IIO_SEPARATE,
+ ltc2688_dac_input_read, ltc2688_dac_input_write),
+ /*
+ * Not IIO_ENUM because the available freq needs to be computed at
+ * probe. We could still use it, but it didn't felt much right.
+ */
+ LTC2688_CHAN_EXT_INFO("dither_frequency", 0, IIO_SEPARATE,
+ ltc2688_dither_freq_get, ltc2688_dither_freq_set),
+ LTC2688_CHAN_EXT_INFO("dither_frequency_available",
+ LTC2688_DITHER_FREQ_AVAIL, IIO_SEPARATE,
+ ltc2688_dither_freq_get, ltc2688_dither_freq_set),
+ IIO_ENUM("dither_phase", IIO_SEPARATE, &ltc2688_dither_phase_enum),
+ IIO_ENUM_AVAILABLE("dither_phase", IIO_SEPARATE,
+ &ltc2688_dither_phase_enum),
+ LTC2688_CHAN_EXT_INFO("dither_en", LTC2688_CMD_TOGGLE_DITHER_EN,
+ IIO_SEPARATE, ltc2688_reg_bool_get,
+ ltc2688_dither_toggle_set),
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+static const struct iio_chan_spec_ext_info ltc2688_ext_info[] = {
+ LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
+ ltc2688_reg_bool_get, ltc2688_reg_bool_set),
+ {}
+};
+
+#define LTC2688_CHANNEL(_chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
+ .ext_info = ltc2688_ext_info, \
+}
+
+static const struct iio_chan_spec ltc2688_channels[] = {
+ LTC2688_CHANNEL(0),
+ LTC2688_CHANNEL(1),
+ LTC2688_CHANNEL(2),
+ LTC2688_CHANNEL(3),
+ LTC2688_CHANNEL(4),
+ LTC2688_CHANNEL(5),
+ LTC2688_CHANNEL(6),
+ LTC2688_CHANNEL(7),
+ LTC2688_CHANNEL(8),
+ LTC2688_CHANNEL(9),
+ LTC2688_CHANNEL(10),
+ LTC2688_CHANNEL(11),
+ LTC2688_CHANNEL(12),
+ LTC2688_CHANNEL(13),
+ LTC2688_CHANNEL(14),
+ LTC2688_CHANNEL(15),
+};
+
+static void ltc2688_clk_disable(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static const int ltc2688_period[LTC2688_DITHER_FREQ_AVAIL_N] = {
+ 4, 8, 16, 32, 64,
+};
+
+static int ltc2688_tgp_clk_setup(struct ltc2688_state *st,
+ struct ltc2688_chan *chan,
+ struct fwnode_handle *node, int tgp)
+{
+ unsigned long rate;
+ struct clk *clk;
+ int ret, f;
+
+ clk = devm_get_clk_from_child(&st->spi->dev, to_of_node(node), NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&st->spi->dev, PTR_ERR(clk),
+ "failed to get tgp clk.\n");
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return dev_err_probe(&st->spi->dev, ret,
+ "failed to enable tgp clk.\n");
+
+ ret = devm_add_action_or_reset(&st->spi->dev, ltc2688_clk_disable, clk);
+ if (ret)
+ return ret;
+
+ if (chan->toggle_chan)
+ return 0;
+
+ /* calculate available dither frequencies */
+ rate = clk_get_rate(clk);
+ for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++)
+ chan->dither_frequency[f] = DIV_ROUND_CLOSEST(rate, ltc2688_period[f]);
+
+ return 0;
+}
+
+static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max)
+{
+ u32 span;
+
+ for (span = 0; span < ARRAY_SIZE(ltc2688_span_helper); span++) {
+ if (min == ltc2688_span_helper[span][0] &&
+ max == ltc2688_span_helper[span][1])
+ return span;
+ }
+
+ return -EINVAL;
+}
+
+static int ltc2688_channel_config(struct ltc2688_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct fwnode_handle *child;
+ u32 reg, clk_input, val, tmp[2];
+ int ret, span;
+
+ device_for_each_child_node(dev, child) {
+ struct ltc2688_chan *chan;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, ret,
+ "Failed to get reg property\n");
+ }
+
+ if (reg >= LTC2688_DAC_CHANNELS) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "reg bigger than: %d\n",
+ LTC2688_DAC_CHANNELS);
+ }
+
+ val = 0;
+ chan = &st->channels[reg];
+ if (fwnode_property_read_bool(child, "adi,toggle-mode")) {
+ chan->toggle_chan = true;
+ /* assume sw toggle ABI */
+ st->iio_chan[reg].ext_info = ltc2688_toggle_sym_ext_info;
+ /*
+ * Clear IIO_CHAN_INFO_RAW bit as toggle channels expose
+ * out_voltage_raw{0|1} files.
+ */
+ __clear_bit(IIO_CHAN_INFO_RAW,
+ &st->iio_chan[reg].info_mask_separate);
+ }
+
+ ret = fwnode_property_read_u32_array(child, "adi,output-range-microvolt",
+ tmp, ARRAY_SIZE(tmp));
+ if (!ret) {
+ span = ltc2688_span_lookup(st, (int)tmp[0] / 1000,
+ tmp[1] / 1000);
+ if (span < 0) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "output range not valid:[%d %d]\n",
+ tmp[0], tmp[1]);
+ }
+
+ val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span);
+ }
+
+ ret = fwnode_property_read_u32(child, "adi,toggle-dither-input",
+ &clk_input);
+ if (!ret) {
+ if (clk_input >= LTC2688_CH_TGP_MAX) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "toggle-dither-input inv value(%d)\n",
+ clk_input);
+ }
+
+ ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+
+ /*
+ * 0 means software toggle which is the default mode.
+ * Hence the +1.
+ */
+ val |= FIELD_PREP(LTC2688_CH_TD_SEL_MSK, clk_input + 1);
+
+ /*
+ * If a TGPx is given, we automatically assume a dither
+ * capable channel (unless toggle is already enabled).
+ * On top of this we just set here the dither bit in the
+ * channel settings. It won't have any effect until the
+ * global toggle/dither bit is enabled.
+ */
+ if (!chan->toggle_chan) {
+ val |= FIELD_PREP(LTC2688_CH_MODE_MSK, 1);
+ st->iio_chan[reg].ext_info = ltc2688_dither_ext_info;
+ } else {
+ /* wait, no sw toggle after all */
+ st->iio_chan[reg].ext_info = ltc2688_toggle_ext_info;
+ }
+ }
+
+ if (fwnode_property_read_bool(child, "adi,overrange")) {
+ chan->overrange = true;
+ val |= LTC2688_CH_OVERRANGE_MSK;
+ }
+
+ if (!val)
+ continue;
+
+ ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg),
+ val);
+ if (ret) {
+ fwnode_handle_put(child);
+ return dev_err_probe(dev, -EINVAL,
+ "failed to set chan settings\n");
+ }
+ }
+
+ return 0;
+}
+
+static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ /*
+ * If we have a reset pin, use that to reset the board, If not, use
+ * the reset bit.
+ */
+ gpio = devm_gpiod_get_optional(&st->spi->dev, "clr", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return dev_err_probe(&st->spi->dev, PTR_ERR(gpio),
+ "Failed to get reset gpio");
+ if (gpio) {
+ usleep_range(1000, 1200);
+ /* bring device out of reset */
+ gpiod_set_value_cansleep(gpio, 0);
+ } else {
+ ret = regmap_update_bits(st->regmap, LTC2688_CMD_CONFIG,
+ LTC2688_CONFIG_RST,
+ LTC2688_CONFIG_RST);
+ if (ret)
+ return ret;
+ }
+
+ usleep_range(10000, 12000);
+
+ /*
+ * Duplicate the default channel configuration as it can change during
+ * @ltc2688_channel_config()
+ */
+ st->iio_chan = devm_kmemdup(&st->spi->dev, ltc2688_channels,
+ sizeof(ltc2688_channels), GFP_KERNEL);
+ if (!st->iio_chan)
+ return -ENOMEM;
+
+ ret = ltc2688_channel_config(st);
+ if (ret)
+ return ret;
+
+ if (!vref)
+ return 0;
+
+ return regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG,
+ LTC2688_CONFIG_EXT_REF);
+}
+
+static void ltc2688_disable_regulators(void *data)
+{
+ struct ltc2688_state *st = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
+}
+
+static void ltc2688_disable_regulator(void *regulator)
+{
+ regulator_disable(regulator);
+}
+
+static bool ltc2688_reg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC2688_CMD_CH_CODE(0) ... LTC2688_CMD_CH_GAIN(15):
+ return true;
+ case LTC2688_CMD_CONFIG ... LTC2688_CMD_THERMAL_STAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ltc2688_reg_writable(struct device *dev, unsigned int reg)
+{
+ /*
+ * There's a jump from 0x76 to 0x78 in the write codes and the thermal
+ * status code is 0x77 (which is read only) so that we need to check
+ * that special condition.
+ */
+ if (reg <= LTC2688_CMD_UPDATE_ALL && reg != LTC2688_CMD_THERMAL_STAT)
+ return true;
+
+ return false;
+}
+
+static struct regmap_bus ltc2688_regmap_bus = {
+ .read = ltc2688_spi_read,
+ .write = ltc2688_spi_write,
+ .read_flag_mask = LTC2688_READ_OPERATION,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config ltc2688_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .readable_reg = ltc2688_reg_readable,
+ .writeable_reg = ltc2688_reg_writable,
+ /* ignoring the no op command */
+ .max_register = LTC2688_CMD_UPDATE_ALL,
+};
+
+static const struct iio_info ltc2688_info = {
+ .write_raw = ltc2688_write_raw,
+ .read_raw = ltc2688_read_raw,
+ .read_avail = ltc2688_read_avail,
+ .debugfs_reg_access = ltc2688_reg_access,
+};
+
+static int ltc2688_probe(struct spi_device *spi)
+{
+ struct ltc2688_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *vref_reg;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ /* Just write this once. No need to do it in every regmap read. */
+ st->tx_data[3] = LTC2688_CMD_NOOP;
+ mutex_init(&st->lock);
+
+ st->regmap = devm_regmap_init(dev, &ltc2688_regmap_bus, st,
+ &ltc2688_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to init regmap");
+
+ st->regulators[0].supply = "vcc";
+ st->regulators[1].supply = "iovcc";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators),
+ st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(dev, ltc2688_disable_regulators, st);
+ if (ret)
+ return ret;
+
+ vref_reg = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(vref_reg)) {
+ if (PTR_ERR(vref_reg) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(vref_reg),
+ "Failed to get vref regulator");
+
+ vref_reg = NULL;
+ /* internal reference */
+ st->vref = 4096;
+ } else {
+ ret = regulator_enable(vref_reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable vref regulators\n");
+
+ ret = devm_add_action_or_reset(dev, ltc2688_disable_regulator,
+ vref_reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(vref_reg);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get vref\n");
+
+ st->vref = ret / 1000;
+ }
+
+ ret = ltc2688_setup(st, vref_reg);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ltc2688";
+ indio_dev->info = &ltc2688_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->iio_chan;
+ indio_dev->num_channels = ARRAY_SIZE(ltc2688_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ltc2688_of_id[] = {
+ { .compatible = "adi,ltc2688" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ltc2688_of_id);
+
+static const struct spi_device_id ltc2688_id[] = {
+ { "ltc2688" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ltc2688_id);
+
+static struct spi_driver ltc2688_driver = {
+ .driver = {
+ .name = "ltc2688",
+ .of_match_table = ltc2688_of_id,
+ },
+ .probe = ltc2688_probe,
+ .id_table = ltc2688_id,
+};
+module_spi_driver(ltc2688_driver);
+
+MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2688 DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
index 225b1a374dc1..22b02f50fe41 100644
--- a/drivers/iio/dac/m62332.c
+++ b/drivers/iio/dac/m62332.c
@@ -25,9 +25,7 @@ struct m62332_data {
struct regulator *vcc;
struct mutex mutex;
u8 raw[M62332_CHANNELS];
-#ifdef CONFIG_PM_SLEEP
u8 save[M62332_CHANNELS];
-#endif
};
static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel)
@@ -124,7 +122,6 @@ static int m62332_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-#ifdef CONFIG_PM_SLEEP
static int m62332_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -156,11 +153,7 @@ static int m62332_resume(struct device *dev)
return m62332_set_value(indio_dev, data->save[1], 1);
}
-static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
-#define M62332_PM_OPS (&m62332_pm_ops)
-#else
-#define M62332_PM_OPS NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
static const struct iio_info m62332_info = {
.read_raw = m62332_read_raw,
@@ -246,7 +239,7 @@ MODULE_DEVICE_TABLE(i2c, m62332_id);
static struct i2c_driver m62332_driver = {
.driver = {
.name = "m62332",
- .pm = M62332_PM_OPS,
+ .pm = pm_sleep_ptr(&m62332_pm_ops),
},
.probe = m62332_probe,
.remove = m62332_remove,
diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c
index bd7a3b20e645..83bf184e3adc 100644
--- a/drivers/iio/dac/stm32-dac-core.c
+++ b/drivers/iio/dac/stm32-dac-core.c
@@ -195,7 +195,7 @@ static int stm32_dac_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_dac_core_resume(struct device *dev)
+static int stm32_dac_core_resume(struct device *dev)
{
struct stm32_dac_common *common = dev_get_drvdata(dev);
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
@@ -213,23 +213,23 @@ static int __maybe_unused stm32_dac_core_resume(struct device *dev)
return pm_runtime_force_resume(dev);
}
-static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev)
+static int stm32_dac_core_runtime_suspend(struct device *dev)
{
stm32_dac_core_hw_stop(dev);
return 0;
}
-static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev)
+static int stm32_dac_core_runtime_resume(struct device *dev)
{
return stm32_dac_core_hw_start(dev);
}
static const struct dev_pm_ops stm32_dac_core_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume)
- SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend,
- stm32_dac_core_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume)
+ RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend,
+ stm32_dac_core_runtime_resume,
+ NULL)
};
static const struct stm32_dac_cfg stm32h7_dac_cfg = {
@@ -253,7 +253,7 @@ static struct platform_driver stm32_dac_driver = {
.driver = {
.name = "stm32-dac-core",
.of_match_table = stm32_dac_of_match,
- .pm = &stm32_dac_core_pm_ops,
+ .pm = pm_ptr(&stm32_dac_core_pm_ops),
},
};
module_platform_driver(stm32_dac_driver);
diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
index cd71cc4553a7..b20192a071cb 100644
--- a/drivers/iio/dac/stm32-dac.c
+++ b/drivers/iio/dac/stm32-dac.c
@@ -372,7 +372,7 @@ static int stm32_dac_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused stm32_dac_suspend(struct device *dev)
+static int stm32_dac_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
int channel = indio_dev->channels[0].channel;
@@ -386,9 +386,8 @@ static int __maybe_unused stm32_dac_suspend(struct device *dev)
return pm_runtime_force_suspend(dev);
}
-static const struct dev_pm_ops stm32_dac_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dac_pm_ops, stm32_dac_suspend,
+ pm_runtime_force_resume);
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32-dac", },
@@ -402,7 +401,7 @@ static struct platform_driver stm32_dac_driver = {
.driver = {
.name = "stm32-dac",
.of_match_table = stm32_dac_of_match,
- .pm = &stm32_dac_pm_ops,
+ .pm = pm_sleep_ptr(&stm32_dac_pm_ops),
},
};
module_platform_driver(stm32_dac_driver);
diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c
index 636b4009f763..92429c0d2685 100644
--- a/drivers/iio/dac/vf610_dac.c
+++ b/drivers/iio/dac/vf610_dac.c
@@ -242,7 +242,6 @@ static int vf610_dac_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int vf610_dac_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -268,9 +267,9 @@ static int vf610_dac_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend,
+ vf610_dac_resume);
static struct platform_driver vf610_dac_driver = {
.probe = vf610_dac_probe,
@@ -278,7 +277,7 @@ static struct platform_driver vf610_dac_driver = {
.driver = {
.name = "vf610-dac",
.of_match_table = vf610_dac_match,
- .pm = &vf610_dac_pm_ops,
+ .pm = pm_sleep_ptr(&vf610_dac_pm_ops),
},
};
module_platform_driver(vf610_dac_driver);