summaryrefslogtreecommitdiffstats
path: root/drivers/iio/dac
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
commit5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch)
treea94efe259b9009378be6d90eb30d2b019d95c194 /drivers/iio/dac
parentInitial commit. (diff)
downloadlinux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz
linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/iio/dac')
-rw-r--r--drivers/iio/dac/Kconfig410
-rw-r--r--drivers/iio/dac/Makefile46
-rw-r--r--drivers/iio/dac/ad5064.c1132
-rw-r--r--drivers/iio/dac/ad5360.c563
-rw-r--r--drivers/iio/dac/ad5380.c653
-rw-r--r--drivers/iio/dac/ad5421.c537
-rw-r--r--drivers/iio/dac/ad5446.c649
-rw-r--r--drivers/iio/dac/ad5449.c369
-rw-r--r--drivers/iio/dac/ad5504.c371
-rw-r--r--drivers/iio/dac/ad5592r-base.c684
-rw-r--r--drivers/iio/dac/ad5592r-base.h76
-rw-r--r--drivers/iio/dac/ad5592r.c170
-rw-r--r--drivers/iio/dac/ad5593r.c145
-rw-r--r--drivers/iio/dac/ad5624r.h78
-rw-r--r--drivers/iio/dac/ad5624r_spi.c331
-rw-r--r--drivers/iio/dac/ad5686-spi.c137
-rw-r--r--drivers/iio/dac/ad5686.c531
-rw-r--r--drivers/iio/dac/ad5686.h157
-rw-r--r--drivers/iio/dac/ad5696-i2c.c103
-rw-r--r--drivers/iio/dac/ad5755.c806
-rw-r--r--drivers/iio/dac/ad5758.c905
-rw-r--r--drivers/iio/dac/ad5761.c431
-rw-r--r--drivers/iio/dac/ad5764.c369
-rw-r--r--drivers/iio/dac/ad5770r.c700
-rw-r--r--drivers/iio/dac/ad5791.c468
-rw-r--r--drivers/iio/dac/ad7303.c304
-rw-r--r--drivers/iio/dac/ad8801.c229
-rw-r--r--drivers/iio/dac/cio-dac.c135
-rw-r--r--drivers/iio/dac/dpot-dac.c258
-rw-r--r--drivers/iio/dac/ds4424.c330
-rw-r--r--drivers/iio/dac/lpc18xx_dac.c203
-rw-r--r--drivers/iio/dac/ltc1660.c249
-rw-r--r--drivers/iio/dac/ltc2632.c484
-rw-r--r--drivers/iio/dac/m62332.c259
-rw-r--r--drivers/iio/dac/max517.c221
-rw-r--r--drivers/iio/dac/max5821.c396
-rw-r--r--drivers/iio/dac/mcp4725.c548
-rw-r--r--drivers/iio/dac/mcp4922.c208
-rw-r--r--drivers/iio/dac/stm32-dac-core.c272
-rw-r--r--drivers/iio/dac/stm32-dac-core.h38
-rw-r--r--drivers/iio/dac/stm32-dac.c414
-rw-r--r--drivers/iio/dac/ti-dac082s085.c363
-rw-r--r--drivers/iio/dac/ti-dac5571.c428
-rw-r--r--drivers/iio/dac/ti-dac7311.c336
-rw-r--r--drivers/iio/dac/ti-dac7612.c193
-rw-r--r--drivers/iio/dac/vf610_dac.c288
46 files changed, 16977 insertions, 0 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
new file mode 100644
index 000000000..dae8d27e7
--- /dev/null
+++ b/drivers/iio/dac/Kconfig
@@ -0,0 +1,410 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# DAC drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Digital to analog converters"
+
+config AD5064
+ tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
+ depends on (SPI_MASTER && I2C!=m) || I2C
+ help
+ Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
+ AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R, AD5627, AD5627R,
+ AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R, AD5666,
+ AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616,
+ LTC2617, LTC2619, LTC2626, LTC2627, LTC2629, LTC2631, LTC2633, LTC2635
+ Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5064.
+
+config AD5360
+ tristate "Analog Devices AD5360/61/62/63/70/71/73 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5360, AD5361,
+ AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel
+ Digital to Analog Converters (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad5360.
+
+config AD5380
+ tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver"
+ depends on (SPI_MASTER && I2C!=m) || I2C
+ select REGMAP_I2C if I2C
+ select REGMAP_SPI if SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5380, AD5381,
+ AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel
+ Digital to Analog Converters (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad5380.
+
+config AD5421
+ tristate "Analog Devices AD5421 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5421 loop-powered
+ digital-to-analog convertors (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad5421.
+
+config AD5446
+ tristate "Analog Devices AD5446 and similar single channel DACs driver"
+ depends on (SPI_MASTER && I2C!=m) || I2C
+ help
+ Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
+ AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
+ AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5600, AD5601, AD5602, AD5611,
+ AD5612, AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs
+ as well as Texas Instruments DAC081S101, DAC101S101, DAC121S101.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5446.
+
+config AD5449
+ tristate "Analog Devices AD5449 and similar DACs driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5415, AD5426, AD5429,
+ AD5432, AD5439, AD5443, AD5449 Digital to Analog Converters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5449.
+
+config AD5592R_BASE
+ tristate
+
+config AD5592R
+ tristate "Analog Devices AD5592R ADC/DAC driver"
+ depends on SPI_MASTER
+ select GPIOLIB
+ select AD5592R_BASE
+ help
+ Say yes here to build support for Analog Devices AD5592R
+ Digital to Analog / Analog to Digital Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5592r.
+
+config AD5593R
+ tristate "Analog Devices AD5593R ADC/DAC driver"
+ depends on I2C
+ select GPIOLIB
+ select AD5592R_BASE
+ help
+ Say yes here to build support for Analog Devices AD5593R
+ Digital to Analog / Analog to Digital Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5593r.
+
+config AD5504
+ tristate "Analog Devices AD5504/AD5501 DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5504, AD5501,
+ High Voltage Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5504.
+
+config AD5624R_SPI
+ tristate "Analog Devices AD5624/44/64R DAC spi driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5624R, AD5644R and
+ AD5664R converters (DAC). This driver uses the common SPI interface.
+
+config AD5686
+ tristate
+
+config AD5686_SPI
+ tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)"
+ depends on SPI
+ select AD5686
+ help
+ Say yes here to build support for Analog Devices AD5672R, AD5674R,
+ AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686,
+ AD5686R Voltage Output Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5686.
+
+config AD5696_I2C
+ tristate "Analog Devices AD5696 and similar multi-channel DACs (I2C)"
+ depends on I2C
+ select AD5686
+ help
+ Say yes here to build support for Analog Devices AD5671R, AD5675R,
+ AD5694, AD5694R, AD5695R, AD5696, AD5696R Voltage Output Digital to
+ Analog Converter.
+ To compile this driver as a module, choose M here: the module will be
+ called ad5696.
+
+config AD5755
+ tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5755, AD5755-1,
+ AD5757, AD5735, AD5737 quad channel Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5755.
+
+config AD5758
+ tristate "Analog Devices AD5758 DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5758 single channel
+ Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5758.
+
+config AD5761
+ tristate "Analog Devices AD5761/61R/21/21R DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5761, AD5761R, AD5721,
+ AD5721R Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5761.
+
+config AD5764
+ tristate "Analog Devices AD5764/64R/44/44R DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744,
+ AD5744R Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5764.
+
+config AD5770R
+ tristate "Analog Devices AD5770R IDAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5770R Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5770r.
+
+config AD5791
+ tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD5760, AD5780,
+ AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5791.
+
+config AD7303
+ tristate "Analog Devices AD7303 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD7303 Digital to Analog
+ Converters (DAC).
+
+ To compile this driver as module choose M here: the module will be called
+ ad7303.
+
+config AD8801
+ tristate "Analog Devices AD8801/AD8803 DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD8801, AD8803 Digital to
+ Analog Converters (DAC).
+
+ To compile this driver as a module choose M here: the module will be called
+ ad8801.
+
+config CIO_DAC
+ tristate "Measurement Computing CIO-DAC IIO driver"
+ depends on X86 && (ISA_BUS || PC104)
+ select ISA_BUS_API
+ help
+ Say yes here to build support for the Measurement Computing CIO-DAC
+ analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
+ base port addresses for the devices may be configured via the base
+ array module parameter.
+
+config DPOT_DAC
+ tristate "DAC emulation using a DPOT"
+ depends on OF
+ help
+ Say yes here to build support for DAC emulation using a digital
+ potentiometer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called dpot-dac.
+
+config DS4424
+ tristate "Maxim Integrated DS4422/DS4424 DAC driver"
+ depends on I2C
+ help
+ If you say yes here you get support for Maxim chips DS4422, DS4424.
+
+ This driver can also be built as a module. If so, the module
+ will be called ds4424.
+
+config LPC18XX_DAC
+ tristate "NXP LPC18xx DAC driver"
+ depends on ARCH_LPC18XX || COMPILE_TEST
+ depends on OF && HAS_IOMEM
+ help
+ Say yes here to build support for NXP LPC18XX DAC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called lpc18xx_dac.
+
+config LTC1660
+ tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Linear Technology
+ LTC1660 and LTC1665 Digital to Analog Converters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ltc1660.
+
+config LTC2632
+ tristate "Linear Technology LTC2632-12/10/8 and similar DAC spi driver"
+ depends on SPI
+ help
+ Say yes here to build support for Linear Technology
+ LTC2632, LTC2634 and LTC2636 DAC resolution 12/10/8 bit
+ low 0-2.5V and high 0-4.096V range converters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ltc2632.
+
+config M62332
+ tristate "Mitsubishi M62332 DAC driver"
+ depends on I2C
+ help
+ If you say yes here you get support for the Mitsubishi M62332
+ (I2C 8-Bit DACs with rail-to-rail outputs).
+
+ This driver can also be built as a module. If so, the module
+ will be called m62332.
+
+config MAX517
+ tristate "Maxim MAX517/518/519/520/521 DAC driver"
+ depends on I2C
+ help
+ If you say yes here you get support for the following Maxim chips
+ (I2C 8-Bit DACs with rail-to-rail outputs):
+ MAX517 - Single channel, single reference
+ MAX518 - Dual channel, ref=Vdd
+ MAX519 - Dual channel, dual reference
+ MAX520 - Quad channel, quad reference
+ MAX521 - Octal channel, independent ref for ch0-3, shared ref for ch4-7
+
+ This driver can also be built as a module. If so, the module
+ will be called max517.
+
+config MAX5821
+ tristate "Maxim MAX5821 DAC driver"
+ depends on I2C
+ depends on OF
+ help
+ Say yes here to build support for Maxim MAX5821
+ 10 bits DAC.
+
+config MCP4725
+ tristate "MCP4725/6 DAC driver"
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Microchip
+ MCP 4725/6 12-bit digital-to-analog converter (DAC) with I2C
+ interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp4725.
+
+config MCP4922
+ tristate "MCP4902, MCP4912, MCP4922 DAC driver"
+ depends on SPI
+ help
+ Say yes here to build the driver for the Microchip MCP4902
+ MCP4912, and MCP4922 DAC devices.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp4922.
+
+config STM32_DAC
+ tristate "STMicroelectronics STM32 DAC"
+ depends on (ARCH_STM32 && OF) || COMPILE_TEST
+ depends on REGULATOR
+ select STM32_DAC_CORE
+ help
+ Say yes here to build support for STMicroelectronics STM32 Digital
+ to Analog Converter (DAC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-dac.
+
+config STM32_DAC_CORE
+ tristate
+
+config TI_DAC082S085
+ tristate "Texas Instruments 8/10/12-bit 2/4-channel DAC driver"
+ depends on SPI_MASTER
+ help
+ Driver for the Texas Instruments (formerly National Semiconductor)
+ DAC082S085, DAC102S085, DAC122S085, DAC084S085, DAC104S085 and
+ DAC124S085.
+
+ If compiled as a module, it will be called ti-dac082s085.
+
+config TI_DAC5571
+ tristate "Texas Instruments 8/10/12/16-bit 1/2/4-channel DAC driver"
+ depends on I2C
+ help
+ Driver for the Texas Instruments
+ DAC5571, DAC6571, DAC7571, DAC5574, DAC6574, DAC7574, DAC5573,
+ DAC6573, DAC7573, DAC8571, DAC8574.
+
+ If compiled as a module, it will be called ti-dac5571.
+
+config TI_DAC7311
+ tristate "Texas Instruments 8/10/12-bit 1-channel DAC driver"
+ depends on SPI
+ help
+ Driver for the Texas Instruments
+ DAC7311, DAC6311, DAC5311.
+
+ If compiled as a module, it will be called ti-dac7311.
+
+config TI_DAC7612
+ tristate "Texas Instruments 12-bit 2-channel DAC driver"
+ depends on SPI_MASTER && GPIOLIB
+ help
+ Driver for the Texas Instruments DAC7612, DAC7612U, DAC7612UB
+ The driver hand drive the load pin automatically, otherwise
+ it needs to be toggled manually.
+
+ If compiled as a module, it will be called ti-dac7612.
+
+config VF610_DAC
+ tristate "Vybrid vf610 DAC driver"
+ depends on OF
+ depends on HAS_IOMEM
+ help
+ Say yes here to support Vybrid board digital-to-analog converter.
+
+ This driver can also be built as a module. If so, the module will
+ be called vf610_dac.
+
+endmenu
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
new file mode 100644
index 000000000..09506d248
--- /dev/null
+++ b/drivers/iio/dac/Makefile
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for industrial I/O DAC drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AD5360) += ad5360.o
+obj-$(CONFIG_AD5380) += ad5380.o
+obj-$(CONFIG_AD5421) += ad5421.o
+obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
+obj-$(CONFIG_AD5064) += ad5064.o
+obj-$(CONFIG_AD5504) += ad5504.o
+obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5449) += ad5449.o
+obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o
+obj-$(CONFIG_AD5592R) += ad5592r.o
+obj-$(CONFIG_AD5593R) += ad5593r.o
+obj-$(CONFIG_AD5755) += ad5755.o
+obj-$(CONFIG_AD5758) += ad5758.o
+obj-$(CONFIG_AD5761) += ad5761.o
+obj-$(CONFIG_AD5764) += ad5764.o
+obj-$(CONFIG_AD5770R) += ad5770r.o
+obj-$(CONFIG_AD5791) += ad5791.o
+obj-$(CONFIG_AD5686) += ad5686.o
+obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o
+obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
+obj-$(CONFIG_AD7303) += ad7303.o
+obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_CIO_DAC) += cio-dac.o
+obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
+obj-$(CONFIG_DS4424) += ds4424.o
+obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
+obj-$(CONFIG_LTC1660) += ltc1660.o
+obj-$(CONFIG_LTC2632) += ltc2632.o
+obj-$(CONFIG_M62332) += m62332.o
+obj-$(CONFIG_MAX517) += max517.o
+obj-$(CONFIG_MAX5821) += max5821.o
+obj-$(CONFIG_MCP4725) += mcp4725.o
+obj-$(CONFIG_MCP4922) += mcp4922.o
+obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
+obj-$(CONFIG_STM32_DAC) += stm32-dac.o
+obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
+obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
+obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
+obj-$(CONFIG_TI_DAC7612) += ti-dac7612.o
+obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c
new file mode 100644
index 000000000..82abd4d68
--- /dev/null
+++ b/drivers/iio/dac/ad5064.c
@@ -0,0 +1,1132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R,
+ * AD5627, AD5627R, AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R,
+ * AD5666, AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616,
+ * LTC2617, LTC2619, LTC2626, LTC2627, LTC2629, LTC2631, LTC2633, LTC2635
+ * Digital to analog converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5064_MAX_DAC_CHANNELS 8
+#define AD5064_MAX_VREFS 4
+
+#define AD5064_ADDR(x) ((x) << 20)
+#define AD5064_CMD(x) ((x) << 24)
+
+#define AD5064_ADDR_ALL_DAC 0xF
+
+#define AD5064_CMD_WRITE_INPUT_N 0x0
+#define AD5064_CMD_UPDATE_DAC_N 0x1
+#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
+#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define AD5064_CMD_POWERDOWN_DAC 0x4
+#define AD5064_CMD_CLEAR 0x5
+#define AD5064_CMD_LDAC_MASK 0x6
+#define AD5064_CMD_RESET 0x7
+#define AD5064_CMD_CONFIG 0x8
+
+#define AD5064_CMD_RESET_V2 0x5
+#define AD5064_CMD_CONFIG_V2 0x7
+
+#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
+#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
+
+#define AD5064_LDAC_PWRDN_NONE 0x0
+#define AD5064_LDAC_PWRDN_1K 0x1
+#define AD5064_LDAC_PWRDN_100K 0x2
+#define AD5064_LDAC_PWRDN_3STATE 0x3
+
+/**
+ * enum ad5064_regmap_type - Register layout variant
+ * @AD5064_REGMAP_ADI: Old Analog Devices register map layout
+ * @AD5064_REGMAP_ADI2: New Analog Devices register map layout
+ * @AD5064_REGMAP_LTC: LTC register map layout
+ */
+enum ad5064_regmap_type {
+ AD5064_REGMAP_ADI,
+ AD5064_REGMAP_ADI2,
+ AD5064_REGMAP_LTC,
+};
+
+/**
+ * struct ad5064_chip_info - chip specific information
+ * @shared_vref: whether the vref supply is shared between channels
+ * @internal_vref: internal reference voltage. 0 if the chip has no
+ * internal vref.
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @regmap_type: register map layout variant
+ */
+
+struct ad5064_chip_info {
+ bool shared_vref;
+ unsigned long internal_vref;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ enum ad5064_regmap_type regmap_type;
+};
+
+struct ad5064_state;
+
+typedef int (*ad5064_write_func)(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val);
+
+/**
+ * struct ad5064_state - driver instance specific data
+ * @dev: the device for this driver instance
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulators
+ * @pwr_down: whether channel is powered down
+ * @pwr_down_mode: channel's current power down mode
+ * @dac_cache: current DAC raw value (chip does not support readback)
+ * @use_internal_vref: set to true if the internal reference voltage should be
+ * used.
+ * @write: register write callback
+ * @lock: maintain consistency between cached and dev state
+ * @data: i2c/spi transfer buffers
+ */
+
+struct ad5064_state {
+ struct device *dev;
+ const struct ad5064_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS];
+ bool pwr_down[AD5064_MAX_DAC_CHANNELS];
+ u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS];
+ unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS];
+ bool use_internal_vref;
+
+ ad5064_write_func write;
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ u8 i2c[3];
+ __be32 spi;
+ } data ____cacheline_aligned;
+};
+
+enum ad5064_type {
+ ID_AD5024,
+ ID_AD5025,
+ ID_AD5044,
+ ID_AD5045,
+ ID_AD5064,
+ ID_AD5064_1,
+ ID_AD5065,
+ ID_AD5625,
+ ID_AD5625R_1V25,
+ ID_AD5625R_2V5,
+ ID_AD5627,
+ ID_AD5627R_1V25,
+ ID_AD5627R_2V5,
+ ID_AD5628_1,
+ ID_AD5628_2,
+ ID_AD5629_1,
+ ID_AD5629_2,
+ ID_AD5645R_1V25,
+ ID_AD5645R_2V5,
+ ID_AD5647R_1V25,
+ ID_AD5647R_2V5,
+ ID_AD5648_1,
+ ID_AD5648_2,
+ ID_AD5665,
+ ID_AD5665R_1V25,
+ ID_AD5665R_2V5,
+ ID_AD5666_1,
+ ID_AD5666_2,
+ ID_AD5667,
+ ID_AD5667R_1V25,
+ ID_AD5667R_2V5,
+ ID_AD5668_1,
+ ID_AD5668_2,
+ ID_AD5669_1,
+ ID_AD5669_2,
+ ID_LTC2606,
+ ID_LTC2607,
+ ID_LTC2609,
+ ID_LTC2616,
+ ID_LTC2617,
+ ID_LTC2619,
+ ID_LTC2626,
+ ID_LTC2627,
+ ID_LTC2629,
+ ID_LTC2631_L12,
+ ID_LTC2631_H12,
+ ID_LTC2631_L10,
+ ID_LTC2631_H10,
+ ID_LTC2631_L8,
+ ID_LTC2631_H8,
+ ID_LTC2633_L12,
+ ID_LTC2633_H12,
+ ID_LTC2633_L10,
+ ID_LTC2633_H10,
+ ID_LTC2633_L8,
+ ID_LTC2633_H8,
+ ID_LTC2635_L12,
+ ID_LTC2635_H12,
+ ID_LTC2635_L10,
+ ID_LTC2635_H10,
+ ID_LTC2635_L8,
+ ID_LTC2635_H8,
+};
+
+static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val, unsigned int shift)
+{
+ val <<= shift;
+
+ return st->write(st, cmd, addr, val);
+}
+
+static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int val, address;
+ unsigned int shift;
+ int ret;
+
+ if (st->chip_info->regmap_type == AD5064_REGMAP_LTC) {
+ val = 0;
+ address = chan->address;
+ } else {
+ if (st->chip_info->regmap_type == AD5064_REGMAP_ADI2)
+ shift = 4;
+ else
+ shift = 8;
+
+ val = (0x1 << chan->address);
+ address = 0;
+
+ if (st->pwr_down[chan->channel])
+ val |= st->pwr_down_mode[chan->channel] << shift;
+ }
+
+ ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, address, val, 0);
+
+ return ret;
+}
+
+static const char * const ad5064_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state",
+};
+
+static const char * const ltc2617_powerdown_modes[] = {
+ "90kohm_to_gnd",
+};
+
+static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode[chan->channel] - 1;
+}
+
+static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ st->pwr_down_mode[chan->channel] = mode + 1;
+
+ ret = ad5064_sync_powerdown_mode(st, chan);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static const struct iio_enum ad5064_powerdown_mode_enum = {
+ .items = ad5064_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5064_powerdown_modes),
+ .get = ad5064_get_powerdown_mode,
+ .set = ad5064_set_powerdown_mode,
+};
+
+static const struct iio_enum ltc2617_powerdown_mode_enum = {
+ .items = ltc2617_powerdown_modes,
+ .num_items = ARRAY_SIZE(ltc2617_powerdown_modes),
+ .get = ad5064_get_powerdown_mode,
+ .set = ad5064_set_powerdown_mode,
+};
+
+static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down[chan->channel]);
+}
+
+static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ st->pwr_down[chan->channel] = pwr_down;
+
+ ret = ad5064_sync_powerdown_mode(st, chan);
+ mutex_unlock(&st->lock);
+ return ret ? ret : len;
+}
+
+static int ad5064_get_vref(struct ad5064_state *st,
+ struct iio_chan_spec const *chan)
+{
+ unsigned int i;
+
+ if (st->use_internal_vref)
+ return st->chip_info->internal_vref;
+
+ i = st->chip_info->shared_vref ? 0 : chan->channel;
+ return regulator_get_voltage(st->vref_reg[i].consumer);
+}
+
+static int ad5064_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ int scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *val = st->dac_cache[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = ad5064_get_vref(st, chan);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ *val = scale_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ad5064_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad5064_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val, chan->scan_type.shift);
+ if (ret == 0)
+ st->dac_cache[chan->channel] = val;
+ mutex_unlock(&st->lock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5064_info = {
+ .read_raw = ad5064_read_raw,
+ .write_raw = ad5064_write_raw,
+};
+
+static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5064_read_dac_powerdown,
+ .write = ad5064_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum),
+ { },
+};
+
+static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5064_read_dac_powerdown,
+ .write = ad5064_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ltc2617_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ltc2617_powerdown_mode_enum),
+ { },
+};
+
+#define AD5064_CHANNEL(chan, addr, bits, _shift, _ext_info) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = addr, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = (_shift), \
+ }, \
+ .ext_info = (_ext_info), \
+}
+
+#define DECLARE_AD5064_CHANNELS(name, bits, shift, ext_info) \
+const struct iio_chan_spec name[] = { \
+ AD5064_CHANNEL(0, 0, bits, shift, ext_info), \
+ AD5064_CHANNEL(1, 1, bits, shift, ext_info), \
+ AD5064_CHANNEL(2, 2, bits, shift, ext_info), \
+ AD5064_CHANNEL(3, 3, bits, shift, ext_info), \
+ AD5064_CHANNEL(4, 4, bits, shift, ext_info), \
+ AD5064_CHANNEL(5, 5, bits, shift, ext_info), \
+ AD5064_CHANNEL(6, 6, bits, shift, ext_info), \
+ AD5064_CHANNEL(7, 7, bits, shift, ext_info), \
+}
+
+#define DECLARE_AD5065_CHANNELS(name, bits, shift, ext_info) \
+const struct iio_chan_spec name[] = { \
+ AD5064_CHANNEL(0, 0, bits, shift, ext_info), \
+ AD5064_CHANNEL(1, 3, bits, shift, ext_info), \
+}
+
+static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4, ad5064_ext_info);
+
+static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8, ad5064_ext_info);
+static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6, ad5064_ext_info);
+static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4, ad5064_ext_info);
+
+static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5645_channels, 14, 2, ad5064_ext_info);
+static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0, ad5064_ext_info);
+
+static DECLARE_AD5064_CHANNELS(ltc2607_channels, 16, 0, ltc2617_ext_info);
+static DECLARE_AD5064_CHANNELS(ltc2617_channels, 14, 2, ltc2617_ext_info);
+static DECLARE_AD5064_CHANNELS(ltc2627_channels, 12, 4, ltc2617_ext_info);
+#define ltc2631_12_channels ltc2627_channels
+static DECLARE_AD5064_CHANNELS(ltc2631_10_channels, 10, 6, ltc2617_ext_info);
+static DECLARE_AD5064_CHANNELS(ltc2631_8_channels, 8, 8, ltc2617_ext_info);
+
+#define LTC2631_INFO(vref, pchannels, nchannels) \
+ { \
+ .shared_vref = true, \
+ .internal_vref = vref, \
+ .channels = pchannels, \
+ .num_channels = nchannels, \
+ .regmap_type = AD5064_REGMAP_LTC, \
+ }
+
+
+static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
+ [ID_AD5024] = {
+ .shared_vref = false,
+ .channels = ad5024_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5025] = {
+ .shared_vref = false,
+ .channels = ad5025_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5044] = {
+ .shared_vref = false,
+ .channels = ad5044_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5045] = {
+ .shared_vref = false,
+ .channels = ad5045_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5064] = {
+ .shared_vref = false,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5064_1] = {
+ .shared_vref = true,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5065] = {
+ .shared_vref = false,
+ .channels = ad5065_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5625] = {
+ .shared_vref = true,
+ .channels = ad5629_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5625R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5629_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5625R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5629_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5627] = {
+ .shared_vref = true,
+ .channels = ad5629_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5627R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5629_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5627R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5629_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5628_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5024_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5628_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5024_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5629_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5629_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5629_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5629_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5645R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5645_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5645R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5645_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5647R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5645_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5647R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5645_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5648_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5044_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5648_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5044_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5665] = {
+ .shared_vref = true,
+ .channels = ad5669_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5665R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5669_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5665R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5669_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5666_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5666_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5064_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5667] = {
+ .shared_vref = true,
+ .channels = ad5669_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5667R_1V25] = {
+ .shared_vref = true,
+ .internal_vref = 1250000,
+ .channels = ad5669_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5667R_2V5] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5669_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_ADI2
+ },
+ [ID_AD5668_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5064_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5668_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5064_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5669_1] = {
+ .shared_vref = true,
+ .internal_vref = 2500000,
+ .channels = ad5669_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_AD5669_2] = {
+ .shared_vref = true,
+ .internal_vref = 5000000,
+ .channels = ad5669_channels,
+ .num_channels = 8,
+ .regmap_type = AD5064_REGMAP_ADI,
+ },
+ [ID_LTC2606] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2607_channels,
+ .num_channels = 1,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2607] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2607_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2609] = {
+ .shared_vref = false,
+ .internal_vref = 0,
+ .channels = ltc2607_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2616] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2617_channels,
+ .num_channels = 1,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2617] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2617_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2619] = {
+ .shared_vref = false,
+ .internal_vref = 0,
+ .channels = ltc2617_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2626] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2627_channels,
+ .num_channels = 1,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2627] = {
+ .shared_vref = true,
+ .internal_vref = 0,
+ .channels = ltc2627_channels,
+ .num_channels = 2,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2629] = {
+ .shared_vref = false,
+ .internal_vref = 0,
+ .channels = ltc2627_channels,
+ .num_channels = 4,
+ .regmap_type = AD5064_REGMAP_LTC,
+ },
+ [ID_LTC2631_L12] = LTC2631_INFO(2500000, ltc2631_12_channels, 1),
+ [ID_LTC2631_H12] = LTC2631_INFO(4096000, ltc2631_12_channels, 1),
+ [ID_LTC2631_L10] = LTC2631_INFO(2500000, ltc2631_10_channels, 1),
+ [ID_LTC2631_H10] = LTC2631_INFO(4096000, ltc2631_10_channels, 1),
+ [ID_LTC2631_L8] = LTC2631_INFO(2500000, ltc2631_8_channels, 1),
+ [ID_LTC2631_H8] = LTC2631_INFO(4096000, ltc2631_8_channels, 1),
+ [ID_LTC2633_L12] = LTC2631_INFO(2500000, ltc2631_12_channels, 2),
+ [ID_LTC2633_H12] = LTC2631_INFO(4096000, ltc2631_12_channels, 2),
+ [ID_LTC2633_L10] = LTC2631_INFO(2500000, ltc2631_10_channels, 2),
+ [ID_LTC2633_H10] = LTC2631_INFO(4096000, ltc2631_10_channels, 2),
+ [ID_LTC2633_L8] = LTC2631_INFO(2500000, ltc2631_8_channels, 2),
+ [ID_LTC2633_H8] = LTC2631_INFO(4096000, ltc2631_8_channels, 2),
+ [ID_LTC2635_L12] = LTC2631_INFO(2500000, ltc2631_12_channels, 4),
+ [ID_LTC2635_H12] = LTC2631_INFO(4096000, ltc2631_12_channels, 4),
+ [ID_LTC2635_L10] = LTC2631_INFO(2500000, ltc2631_10_channels, 4),
+ [ID_LTC2635_H10] = LTC2631_INFO(4096000, ltc2631_10_channels, 4),
+ [ID_LTC2635_L8] = LTC2631_INFO(2500000, ltc2631_8_channels, 4),
+ [ID_LTC2635_H8] = LTC2631_INFO(4096000, ltc2631_8_channels, 4),
+};
+
+static inline unsigned int ad5064_num_vref(struct ad5064_state *st)
+{
+ return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels;
+}
+
+static const char * const ad5064_vref_names[] = {
+ "vrefA",
+ "vrefB",
+ "vrefC",
+ "vrefD",
+};
+
+static const char *ad5064_vref_name(struct ad5064_state *st,
+ unsigned int vref)
+{
+ return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
+}
+
+static int ad5064_set_config(struct ad5064_state *st, unsigned int val)
+{
+ unsigned int cmd;
+
+ switch (st->chip_info->regmap_type) {
+ case AD5064_REGMAP_ADI2:
+ cmd = AD5064_CMD_CONFIG_V2;
+ break;
+ default:
+ cmd = AD5064_CMD_CONFIG;
+ break;
+ }
+
+ return ad5064_write(st, cmd, 0, val, 0);
+}
+
+static int ad5064_request_vref(struct ad5064_state *st, struct device *dev)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ad5064_num_vref(st); ++i)
+ st->vref_reg[i].supply = ad5064_vref_name(st, i);
+
+ if (!st->chip_info->internal_vref)
+ return devm_regulator_bulk_get(dev, ad5064_num_vref(st),
+ st->vref_reg);
+
+ /*
+ * This assumes that when the regulator has an internal VREF
+ * there is only one external VREF connection, which is
+ * currently the case for all supported devices.
+ */
+ st->vref_reg[0].consumer = devm_regulator_get_optional(dev, "vref");
+ if (!IS_ERR(st->vref_reg[0].consumer))
+ return 0;
+
+ ret = PTR_ERR(st->vref_reg[0].consumer);
+ if (ret != -ENODEV)
+ return ret;
+
+ /* If no external regulator was supplied use the internal VREF */
+ st->use_internal_vref = true;
+ ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE);
+ if (ret)
+ dev_err(dev, "Failed to enable internal vref: %d\n", ret);
+
+ return ret;
+}
+
+static int ad5064_probe(struct device *dev, enum ad5064_type type,
+ const char *name, ad5064_write_func write)
+{
+ struct iio_dev *indio_dev;
+ struct ad5064_state *st;
+ unsigned int midscale;
+ unsigned int i;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ mutex_init(&st->lock);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->chip_info = &ad5064_chip_info_tbl[type];
+ st->dev = dev;
+ st->write = write;
+
+ ret = ad5064_request_vref(st, dev);
+ if (ret)
+ return ret;
+
+ if (!st->use_internal_vref) {
+ ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev->name = name;
+ indio_dev->info = &ad5064_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ midscale = (1 << indio_dev->channels[0].scan_type.realbits) / 2;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i) {
+ st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K;
+ st->dac_cache[i] = midscale;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!st->use_internal_vref)
+ regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
+
+ return ret;
+}
+
+static int ad5064_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5064_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (!st->use_internal_vref)
+ regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+
+ st->data.spi = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);
+ return spi_write(spi, &st->data.spi, sizeof(st->data.spi));
+}
+
+static int ad5064_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return ad5064_probe(&spi->dev, id->driver_data, id->name,
+ ad5064_spi_write);
+}
+
+static int ad5064_spi_remove(struct spi_device *spi)
+{
+ return ad5064_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5064_spi_ids[] = {
+ {"ad5024", ID_AD5024},
+ {"ad5025", ID_AD5025},
+ {"ad5044", ID_AD5044},
+ {"ad5045", ID_AD5045},
+ {"ad5064", ID_AD5064},
+ {"ad5064-1", ID_AD5064_1},
+ {"ad5065", ID_AD5065},
+ {"ad5628-1", ID_AD5628_1},
+ {"ad5628-2", ID_AD5628_2},
+ {"ad5648-1", ID_AD5648_1},
+ {"ad5648-2", ID_AD5648_2},
+ {"ad5666-1", ID_AD5666_1},
+ {"ad5666-2", ID_AD5666_2},
+ {"ad5668-1", ID_AD5668_1},
+ {"ad5668-2", ID_AD5668_2},
+ {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);
+
+static struct spi_driver ad5064_spi_driver = {
+ .driver = {
+ .name = "ad5064",
+ },
+ .probe = ad5064_spi_probe,
+ .remove = ad5064_spi_remove,
+ .id_table = ad5064_spi_ids,
+};
+
+static int __init ad5064_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5064_spi_driver);
+}
+
+static void ad5064_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5064_spi_driver);
+}
+
+#else
+
+static inline int ad5064_spi_register_driver(void) { return 0; }
+static inline void ad5064_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd,
+ unsigned int addr, unsigned int val)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+ unsigned int cmd_shift;
+ int ret;
+
+ switch (st->chip_info->regmap_type) {
+ case AD5064_REGMAP_ADI2:
+ cmd_shift = 3;
+ break;
+ default:
+ cmd_shift = 4;
+ break;
+ }
+
+ st->data.i2c[0] = (cmd << cmd_shift) | addr;
+ put_unaligned_be16(val, &st->data.i2c[1]);
+
+ ret = i2c_master_send(i2c, st->data.i2c, 3);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ad5064_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5064_probe(&i2c->dev, id->driver_data, id->name,
+ ad5064_i2c_write);
+}
+
+static int ad5064_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5064_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5064_i2c_ids[] = {
+ {"ad5625", ID_AD5625 },
+ {"ad5625r-1v25", ID_AD5625R_1V25 },
+ {"ad5625r-2v5", ID_AD5625R_2V5 },
+ {"ad5627", ID_AD5627 },
+ {"ad5627r-1v25", ID_AD5627R_1V25 },
+ {"ad5627r-2v5", ID_AD5627R_2V5 },
+ {"ad5629-1", ID_AD5629_1},
+ {"ad5629-2", ID_AD5629_2},
+ {"ad5629-3", ID_AD5629_2}, /* similar enough to ad5629-2 */
+ {"ad5645r-1v25", ID_AD5645R_1V25 },
+ {"ad5645r-2v5", ID_AD5645R_2V5 },
+ {"ad5665", ID_AD5665 },
+ {"ad5665r-1v25", ID_AD5665R_1V25 },
+ {"ad5665r-2v5", ID_AD5665R_2V5 },
+ {"ad5667", ID_AD5667 },
+ {"ad5667r-1v25", ID_AD5667R_1V25 },
+ {"ad5667r-2v5", ID_AD5667R_2V5 },
+ {"ad5669-1", ID_AD5669_1},
+ {"ad5669-2", ID_AD5669_2},
+ {"ad5669-3", ID_AD5669_2}, /* similar enough to ad5669-2 */
+ {"ltc2606", ID_LTC2606},
+ {"ltc2607", ID_LTC2607},
+ {"ltc2609", ID_LTC2609},
+ {"ltc2616", ID_LTC2616},
+ {"ltc2617", ID_LTC2617},
+ {"ltc2619", ID_LTC2619},
+ {"ltc2626", ID_LTC2626},
+ {"ltc2627", ID_LTC2627},
+ {"ltc2629", ID_LTC2629},
+ {"ltc2631-l12", ID_LTC2631_L12},
+ {"ltc2631-h12", ID_LTC2631_H12},
+ {"ltc2631-l10", ID_LTC2631_L10},
+ {"ltc2631-h10", ID_LTC2631_H10},
+ {"ltc2631-l8", ID_LTC2631_L8},
+ {"ltc2631-h8", ID_LTC2631_H8},
+ {"ltc2633-l12", ID_LTC2633_L12},
+ {"ltc2633-h12", ID_LTC2633_H12},
+ {"ltc2633-l10", ID_LTC2633_L10},
+ {"ltc2633-h10", ID_LTC2633_H10},
+ {"ltc2633-l8", ID_LTC2633_L8},
+ {"ltc2633-h8", ID_LTC2633_H8},
+ {"ltc2635-l12", ID_LTC2635_L12},
+ {"ltc2635-h12", ID_LTC2635_H12},
+ {"ltc2635-l10", ID_LTC2635_L10},
+ {"ltc2635-h10", ID_LTC2635_H10},
+ {"ltc2635-l8", ID_LTC2635_L8},
+ {"ltc2635-h8", ID_LTC2635_H8},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);
+
+static struct i2c_driver ad5064_i2c_driver = {
+ .driver = {
+ .name = "ad5064",
+ },
+ .probe = ad5064_i2c_probe,
+ .remove = ad5064_i2c_remove,
+ .id_table = ad5064_i2c_ids,
+};
+
+static int __init ad5064_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5064_i2c_driver);
+}
+
+static void __exit ad5064_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5064_i2c_driver);
+}
+
+#else
+
+static inline int ad5064_i2c_register_driver(void) { return 0; }
+static inline void ad5064_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5064_init(void)
+{
+ int ret;
+
+ ret = ad5064_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5064_i2c_register_driver();
+ if (ret) {
+ ad5064_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5064_init);
+
+static void __exit ad5064_exit(void)
+{
+ ad5064_i2c_unregister_driver();
+ ad5064_spi_unregister_driver();
+}
+module_exit(ad5064_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5024 and similar multi-channel DACs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c
new file mode 100644
index 000000000..602dd2ba6
--- /dev/null
+++ b/drivers/iio/dac/ad5360.c
@@ -0,0 +1,563 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373
+ * multi-channel Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5360_CMD(x) ((x) << 22)
+#define AD5360_ADDR(x) ((x) << 16)
+
+#define AD5360_READBACK_TYPE(x) ((x) << 13)
+#define AD5360_READBACK_ADDR(x) ((x) << 7)
+
+#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8)
+
+#define AD5360_CMD_WRITE_DATA 0x3
+#define AD5360_CMD_WRITE_OFFSET 0x2
+#define AD5360_CMD_WRITE_GAIN 0x1
+#define AD5360_CMD_SPECIAL_FUNCTION 0x0
+
+/* Special function register addresses */
+#define AD5360_REG_SF_NOP 0x0
+#define AD5360_REG_SF_CTRL 0x1
+#define AD5360_REG_SF_OFS(x) (0x2 + (x))
+#define AD5360_REG_SF_READBACK 0x5
+
+#define AD5360_SF_CTRL_PWR_DOWN BIT(0)
+
+#define AD5360_READBACK_X1A 0x0
+#define AD5360_READBACK_X1B 0x1
+#define AD5360_READBACK_OFFSET 0x2
+#define AD5360_READBACK_GAIN 0x3
+#define AD5360_READBACK_SF 0x4
+
+
+/**
+ * struct ad5360_chip_info - chip specific information
+ * @channel_template: channel specification template
+ * @num_channels: number of channels
+ * @channels_per_group: number of channels per group
+ * @num_vrefs: number of vref supplies for the chip
+*/
+
+struct ad5360_chip_info {
+ struct iio_chan_spec channel_template;
+ unsigned int num_channels;
+ unsigned int channels_per_group;
+ unsigned int num_vrefs;
+};
+
+/**
+ * struct ad5360_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulators
+ * @ctrl: control register cache
+ * @lock: lock to protect the data buffer during SPI ops
+ * @data: spi transfer buffers
+ */
+
+struct ad5360_state {
+ struct spi_device *spi;
+ const struct ad5360_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[3];
+ unsigned int ctrl;
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5360_type {
+ ID_AD5360,
+ ID_AD5361,
+ ID_AD5362,
+ ID_AD5363,
+ ID_AD5370,
+ ID_AD5371,
+ ID_AD5372,
+ ID_AD5373,
+};
+
+#define AD5360_CHANNEL(bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 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), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 16 - (bits), \
+ }, \
+}
+
+static const struct ad5360_chip_info ad5360_chip_info_tbl[] = {
+ [ID_AD5360] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 16,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5361] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 16,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5362] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 8,
+ .channels_per_group = 4,
+ .num_vrefs = 2,
+ },
+ [ID_AD5363] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 8,
+ .channels_per_group = 4,
+ .num_vrefs = 2,
+ },
+ [ID_AD5370] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 40,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5371] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 40,
+ .channels_per_group = 8,
+ .num_vrefs = 3,
+ },
+ [ID_AD5372] = {
+ .channel_template = AD5360_CHANNEL(16),
+ .num_channels = 32,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+ [ID_AD5373] = {
+ .channel_template = AD5360_CHANNEL(14),
+ .num_channels = 32,
+ .channels_per_group = 8,
+ .num_vrefs = 2,
+ },
+};
+
+static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st,
+ unsigned int channel)
+{
+ unsigned int i;
+
+ /* The first groups have their own vref, while the remaining groups
+ * share the last vref */
+ i = channel / st->chip_info->channels_per_group;
+ if (i >= st->chip_info->num_vrefs)
+ i = st->chip_info->num_vrefs - 1;
+
+ return i;
+}
+
+static int ad5360_get_channel_vref(struct ad5360_state *st,
+ unsigned int channel)
+{
+ unsigned int i = ad5360_get_channel_vref_index(st, channel);
+
+ return regulator_get_voltage(st->vref_reg[i].consumer);
+}
+
+
+static int ad5360_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int cmd, unsigned int addr, unsigned int val,
+ unsigned int shift)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ val <<= shift;
+ val |= AD5360_CMD(cmd) | AD5360_ADDR(addr);
+ st->data[0].d32 = cpu_to_be32(val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd,
+ unsigned int addr, unsigned int val, unsigned int shift)
+{
+ int ret;
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
+ unsigned int addr)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&st->lock);
+
+ st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) |
+ AD5360_ADDR(AD5360_REG_SF_READBACK) |
+ AD5360_READBACK_TYPE(type) |
+ AD5360_READBACK_ADDR(addr));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static ssize_t ad5360_read_dac_powerdown(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN));
+}
+
+static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
+ unsigned int clr)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ unsigned int ret;
+
+ mutex_lock(&st->lock);
+
+ st->ctrl |= set;
+ st->ctrl &= ~clr;
+
+ ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
+ AD5360_REG_SF_CTRL, st->ctrl, 0);
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static ssize_t ad5360_write_dac_powerdown(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0);
+ else
+ ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(out_voltage_powerdown,
+ S_IRUGO | S_IWUSR,
+ ad5360_read_dac_powerdown,
+ ad5360_write_dac_powerdown, 0);
+
+static struct attribute *ad5360_attributes[] = {
+ &iio_dev_attr_out_voltage_powerdown.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad5360_attribute_group = {
+ .attrs = ad5360_attributes,
+};
+
+static int ad5360_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ int max_val = (1 << chan->scan_type.realbits);
+ unsigned int ofs_index;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA,
+ chan->address, val, chan->scan_type.shift);
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET,
+ chan->address, val, chan->scan_type.shift);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN,
+ chan->address, val, chan->scan_type.shift);
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (val <= -max_val || val > 0)
+ return -EINVAL;
+
+ val = -val;
+
+ /* offset is supposed to have the same scale as raw, but it
+ * is always 14bits wide, so on a chip where the raw value has
+ * more bits, we need to shift offset. */
+ val >>= (chan->scan_type.realbits - 14);
+
+ /* There is one DAC offset register per vref. Changing one
+ * channels offset will also change the offset for all other
+ * channels which share the same vref supply. */
+ ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
+ return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
+ AD5360_REG_SF_OFS(ofs_index), val, 0);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5360_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ unsigned int ofs_index;
+ int scale_uv;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5360_read(indio_dev, AD5360_READBACK_X1A,
+ chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret >> chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = ad5360_get_channel_vref(st, chan->channel);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ /* vout = 4 * vref * dac_code */
+ *val = scale_uv * 4 / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET,
+ chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN,
+ chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
+ ret = ad5360_read(indio_dev, AD5360_READBACK_SF,
+ AD5360_REG_SF_OFS(ofs_index));
+ if (ret < 0)
+ return ret;
+
+ ret <<= (chan->scan_type.realbits - 14);
+ *val = -ret;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5360_info = {
+ .read_raw = ad5360_read_raw,
+ .write_raw = ad5360_write_raw,
+ .attrs = &ad5360_attribute_group,
+};
+
+static const char * const ad5360_vref_name[] = {
+ "vref0", "vref1", "vref2"
+};
+
+static int ad5360_alloc_channels(struct iio_dev *indio_dev)
+{
+ struct ad5360_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels;
+ unsigned int i;
+
+ channels = kcalloc(st->chip_info->num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = AD5360_CHAN_ADDR(i);
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int ad5360_probe(struct spi_device *spi)
+{
+ enum ad5360_type type = spi_get_device_id(spi)->driver_data;
+ struct iio_dev *indio_dev;
+ struct ad5360_state *st;
+ unsigned int i;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5360_chip_info_tbl[type];
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5360_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ mutex_init(&st->lock);
+
+ ret = ad5360_alloc_channels(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < st->chip_info->num_vrefs; ++i)
+ st->vref_reg[i].supply = ad5360_vref_name[i];
+
+ ret = devm_regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs,
+ st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret);
+ goto error_free_channels;
+ }
+
+ ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret);
+ goto error_free_channels;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
+error_free_channels:
+ kfree(indio_dev->channels);
+
+ return ret;
+}
+
+static int ad5360_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5360_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ kfree(indio_dev->channels);
+
+ regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5360_ids[] = {
+ { "ad5360", ID_AD5360 },
+ { "ad5361", ID_AD5361 },
+ { "ad5362", ID_AD5362 },
+ { "ad5363", ID_AD5363 },
+ { "ad5370", ID_AD5370 },
+ { "ad5371", ID_AD5371 },
+ { "ad5372", ID_AD5372 },
+ { "ad5373", ID_AD5373 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5360_ids);
+
+static struct spi_driver ad5360_driver = {
+ .driver = {
+ .name = "ad5360",
+ },
+ .probe = ad5360_probe,
+ .remove = ad5360_remove,
+ .id_table = ad5360_ids,
+};
+module_spi_driver(ad5360_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c
new file mode 100644
index 000000000..37ef65356
--- /dev/null
+++ b/drivers/iio/dac/ad5380.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392
+ * multi-channel Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5380_REG_DATA(x) (((x) << 2) | 3)
+#define AD5380_REG_OFFSET(x) (((x) << 2) | 2)
+#define AD5380_REG_GAIN(x) (((x) << 2) | 1)
+#define AD5380_REG_SF_PWR_DOWN (8 << 2)
+#define AD5380_REG_SF_PWR_UP (9 << 2)
+#define AD5380_REG_SF_CTRL (12 << 2)
+
+#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13
+#define AD5380_CTRL_INT_VREF_2V5 BIT(12)
+#define AD5380_CTRL_INT_VREF_EN BIT(10)
+
+/**
+ * struct ad5380_chip_info - chip specific information
+ * @channel_template: channel specification template
+ * @num_channels: number of channels
+ * @int_vref: internal vref in uV
+*/
+
+struct ad5380_chip_info {
+ struct iio_chan_spec channel_template;
+ unsigned int num_channels;
+ unsigned int int_vref;
+};
+
+/**
+ * struct ad5380_state - driver instance specific data
+ * @regmap: regmap instance used by the device
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulator
+ * @vref: actual reference voltage used in uA
+ * @pwr_down: whether the chip is currently in power down mode
+ * @lock: lock to protect the data buffer during regmap ops
+ */
+
+struct ad5380_state {
+ struct regmap *regmap;
+ const struct ad5380_chip_info *chip_info;
+ struct regulator *vref_reg;
+ int vref;
+ bool pwr_down;
+ struct mutex lock;
+};
+
+enum ad5380_type {
+ ID_AD5380_3,
+ ID_AD5380_5,
+ ID_AD5381_3,
+ ID_AD5381_5,
+ ID_AD5382_3,
+ ID_AD5382_5,
+ ID_AD5383_3,
+ ID_AD5383_5,
+ ID_AD5390_3,
+ ID_AD5390_5,
+ ID_AD5391_3,
+ ID_AD5391_5,
+ ID_AD5392_3,
+ ID_AD5392_5,
+};
+
+static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+
+ if (pwr_down)
+ ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0);
+ else
+ ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0);
+
+ st->pwr_down = pwr_down;
+
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static const char * const ad5380_powerdown_modes[] = {
+ "100kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ unsigned int mode;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode);
+ if (ret)
+ return ret;
+
+ mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1;
+
+ return mode;
+}
+
+static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL,
+ 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET,
+ mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET);
+
+ return ret;
+}
+
+static const struct iio_enum ad5380_powerdown_mode_enum = {
+ .items = ad5380_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5380_powerdown_modes),
+ .get = ad5380_get_powerdown_mode,
+ .set = ad5380_set_powerdown_mode,
+};
+
+static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return AD5380_REG_DATA(chan->address);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return AD5380_REG_OFFSET(chan->address);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return AD5380_REG_GAIN(chan->address);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad5380_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ const unsigned int max_val = (1 << chan->scan_type.realbits);
+ struct ad5380_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return regmap_write(st->regmap,
+ ad5380_info_to_reg(chan, info),
+ val << chan->scan_type.shift);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val += (1 << chan->scan_type.realbits) / 2;
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return regmap_write(st->regmap,
+ AD5380_REG_OFFSET(chan->address),
+ val << chan->scan_type.shift);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ad5380_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info),
+ val);
+ if (ret)
+ return ret;
+ *val >>= chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address),
+ val);
+ if (ret)
+ return ret;
+ *val >>= chan->scan_type.shift;
+ *val -= (1 << chan->scan_type.realbits) / 2;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 2 * st->vref;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5380_info = {
+ .read_raw = ad5380_read_raw,
+ .write_raw = ad5380_write_raw,
+};
+
+static const struct iio_chan_spec_ext_info ad5380_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5380_read_dac_powerdown,
+ .write = ad5380_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
+ &ad5380_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum),
+ { },
+};
+
+#define AD5380_CHANNEL(_bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 14 - (_bits), \
+ }, \
+ .ext_info = ad5380_ext_info, \
+}
+
+static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {
+ [ID_AD5380_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 40,
+ .int_vref = 1250,
+ },
+ [ID_AD5380_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 40,
+ .int_vref = 2500,
+ },
+ [ID_AD5381_3] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 1250,
+ },
+ [ID_AD5381_5] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 2500,
+ },
+ [ID_AD5382_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 32,
+ .int_vref = 1250,
+ },
+ [ID_AD5382_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 32,
+ .int_vref = 2500,
+ },
+ [ID_AD5383_3] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 32,
+ .int_vref = 1250,
+ },
+ [ID_AD5383_5] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 32,
+ .int_vref = 2500,
+ },
+ [ID_AD5390_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 16,
+ .int_vref = 1250,
+ },
+ [ID_AD5390_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 16,
+ .int_vref = 2500,
+ },
+ [ID_AD5391_3] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 1250,
+ },
+ [ID_AD5391_5] = {
+ .channel_template = AD5380_CHANNEL(12),
+ .num_channels = 16,
+ .int_vref = 2500,
+ },
+ [ID_AD5392_3] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 8,
+ .int_vref = 1250,
+ },
+ [ID_AD5392_5] = {
+ .channel_template = AD5380_CHANNEL(14),
+ .num_channels = 8,
+ .int_vref = 2500,
+ },
+};
+
+static int ad5380_alloc_channels(struct iio_dev *indio_dev)
+{
+ struct ad5380_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels;
+ unsigned int i;
+
+ channels = kcalloc(st->chip_info->num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = i;
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int ad5380_probe(struct device *dev, struct regmap *regmap,
+ enum ad5380_type type, const char *name)
+{
+ struct iio_dev *indio_dev;
+ struct ad5380_state *st;
+ unsigned int ctrl = 0;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->chip_info = &ad5380_chip_info_tbl[type];
+ st->regmap = regmap;
+
+ indio_dev->name = name;
+ indio_dev->info = &ad5380_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ mutex_init(&st->lock);
+
+ ret = ad5380_alloc_channels(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to allocate channel spec: %d\n", ret);
+ return ret;
+ }
+
+ if (st->chip_info->int_vref == 2500)
+ ctrl |= AD5380_CTRL_INT_VREF_2V5;
+
+ st->vref_reg = devm_regulator_get(dev, "vref");
+ if (!IS_ERR(st->vref_reg)) {
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(dev, "Failed to enable vref regulators: %d\n",
+ ret);
+ goto error_free_reg;
+ }
+
+ ret = regulator_get_voltage(st->vref_reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ st->vref = ret / 1000;
+ } else {
+ st->vref = st->chip_info->int_vref;
+ ctrl |= AD5380_CTRL_INT_VREF_EN;
+ }
+
+ ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl);
+ if (ret) {
+ dev_err(dev, "Failed to write to device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register iio device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->vref_reg))
+ regulator_disable(st->vref_reg);
+error_free_reg:
+ kfree(indio_dev->channels);
+
+ return ret;
+}
+
+static int ad5380_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5380_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ kfree(indio_dev->channels);
+
+ if (!IS_ERR(st->vref_reg)) {
+ regulator_disable(st->vref_reg);
+ }
+
+ return 0;
+}
+
+static bool ad5380_reg_false(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct regmap_config ad5380_regmap_config = {
+ .reg_bits = 10,
+ .val_bits = 14,
+
+ .max_register = AD5380_REG_DATA(40),
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = ad5380_reg_false,
+ .readable_reg = ad5380_reg_false,
+};
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5380_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &ad5380_regmap_config);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name);
+}
+
+static int ad5380_spi_remove(struct spi_device *spi)
+{
+ return ad5380_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5380_spi_ids[] = {
+ { "ad5380-3", ID_AD5380_3 },
+ { "ad5380-5", ID_AD5380_5 },
+ { "ad5381-3", ID_AD5381_3 },
+ { "ad5381-5", ID_AD5381_5 },
+ { "ad5382-3", ID_AD5382_3 },
+ { "ad5382-5", ID_AD5382_5 },
+ { "ad5383-3", ID_AD5383_3 },
+ { "ad5383-5", ID_AD5383_5 },
+ { "ad5384-3", ID_AD5380_3 },
+ { "ad5384-5", ID_AD5380_5 },
+ { "ad5390-3", ID_AD5390_3 },
+ { "ad5390-5", ID_AD5390_5 },
+ { "ad5391-3", ID_AD5391_3 },
+ { "ad5391-5", ID_AD5391_5 },
+ { "ad5392-3", ID_AD5392_3 },
+ { "ad5392-5", ID_AD5392_5 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
+
+static struct spi_driver ad5380_spi_driver = {
+ .driver = {
+ .name = "ad5380",
+ },
+ .probe = ad5380_spi_probe,
+ .remove = ad5380_spi_remove,
+ .id_table = ad5380_spi_ids,
+};
+
+static inline int ad5380_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5380_spi_driver);
+}
+
+static inline void ad5380_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5380_spi_driver);
+}
+
+#else
+
+static inline int ad5380_spi_register_driver(void)
+{
+ return 0;
+}
+
+static inline void ad5380_spi_unregister_driver(void)
+{
+}
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5380_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &ad5380_regmap_config);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name);
+}
+
+static int ad5380_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5380_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5380_i2c_ids[] = {
+ { "ad5380-3", ID_AD5380_3 },
+ { "ad5380-5", ID_AD5380_5 },
+ { "ad5381-3", ID_AD5381_3 },
+ { "ad5381-5", ID_AD5381_5 },
+ { "ad5382-3", ID_AD5382_3 },
+ { "ad5382-5", ID_AD5382_5 },
+ { "ad5383-3", ID_AD5383_3 },
+ { "ad5383-5", ID_AD5383_5 },
+ { "ad5384-3", ID_AD5380_3 },
+ { "ad5384-5", ID_AD5380_5 },
+ { "ad5390-3", ID_AD5390_3 },
+ { "ad5390-5", ID_AD5390_5 },
+ { "ad5391-3", ID_AD5391_3 },
+ { "ad5391-5", ID_AD5391_5 },
+ { "ad5392-3", ID_AD5392_3 },
+ { "ad5392-5", ID_AD5392_5 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids);
+
+static struct i2c_driver ad5380_i2c_driver = {
+ .driver = {
+ .name = "ad5380",
+ },
+ .probe = ad5380_i2c_probe,
+ .remove = ad5380_i2c_remove,
+ .id_table = ad5380_i2c_ids,
+};
+
+static inline int ad5380_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5380_i2c_driver);
+}
+
+static inline void ad5380_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5380_i2c_driver);
+}
+
+#else
+
+static inline int ad5380_i2c_register_driver(void)
+{
+ return 0;
+}
+
+static inline void ad5380_i2c_unregister_driver(void)
+{
+}
+
+#endif
+
+static int __init ad5380_spi_init(void)
+{
+ int ret;
+
+ ret = ad5380_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5380_i2c_register_driver();
+ if (ret) {
+ ad5380_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5380_spi_init);
+
+static void __exit ad5380_spi_exit(void)
+{
+ ad5380_i2c_unregister_driver();
+ ad5380_spi_unregister_driver();
+
+}
+module_exit(ad5380_spi_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c
new file mode 100644
index 000000000..eedf661d3
--- /dev/null
+++ b/drivers/iio/dac/ad5421.c
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5421 Digital to analog converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/dac/ad5421.h>
+
+
+#define AD5421_REG_DAC_DATA 0x1
+#define AD5421_REG_CTRL 0x2
+#define AD5421_REG_OFFSET 0x3
+#define AD5421_REG_GAIN 0x4
+/* load dac and fault shared the same register number. Writing to it will cause
+ * a dac load command, reading from it will return the fault status register */
+#define AD5421_REG_LOAD_DAC 0x5
+#define AD5421_REG_FAULT 0x5
+#define AD5421_REG_FORCE_ALARM_CURRENT 0x6
+#define AD5421_REG_RESET 0x7
+#define AD5421_REG_START_CONVERSION 0x8
+#define AD5421_REG_NOOP 0x9
+
+#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12)
+#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11)
+#define AD5421_CTRL_MIN_CURRENT BIT(9)
+#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8)
+#define AD5421_CTRL_ADC_ENABLE BIT(7)
+#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6)
+
+#define AD5421_FAULT_SPI BIT(15)
+#define AD5421_FAULT_PEC BIT(14)
+#define AD5421_FAULT_OVER_CURRENT BIT(13)
+#define AD5421_FAULT_UNDER_CURRENT BIT(12)
+#define AD5421_FAULT_TEMP_OVER_140 BIT(11)
+#define AD5421_FAULT_TEMP_OVER_100 BIT(10)
+#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9)
+#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8)
+
+/* These bits will cause the fault pin to go high */
+#define AD5421_FAULT_TRIGGER_IRQ \
+ (AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \
+ AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140)
+
+/**
+ * struct ad5421_state - driver instance specific data
+ * @spi: spi_device
+ * @ctrl: control register cache
+ * @current_range: current range which the device is configured for
+ * @data: spi transfer buffers
+ * @fault_mask: software masking of events
+ * @lock: lock to protect the data buffer during SPI ops
+ */
+struct ad5421_state {
+ struct spi_device *spi;
+ unsigned int ctrl;
+ enum ad5421_current_range current_range;
+ unsigned int fault_mask;
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+static const struct iio_event_spec ad5421_current_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_event_spec ad5421_temp_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec ad5421_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ .event_spec = ad5421_current_event,
+ .num_event_specs = ARRAY_SIZE(ad5421_current_event),
+ },
+ {
+ .type = IIO_TEMP,
+ .channel = -1,
+ .event_spec = ad5421_temp_event,
+ .num_event_specs = ARRAY_SIZE(ad5421_temp_event),
+ },
+};
+
+static int ad5421_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int val)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad5421_write_unlocked(indio_dev, reg, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&st->lock);
+
+ st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
+ unsigned int clr)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int ret;
+
+ mutex_lock(&st->lock);
+
+ st->ctrl &= ~clr;
+ st->ctrl |= set;
+
+ ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl);
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static irqreturn_t ad5421_fault_handler(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int fault;
+ unsigned int old_fault = 0;
+ unsigned int events;
+
+ fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
+ if (!fault)
+ return IRQ_NONE;
+
+ /* If we had a fault, this might mean that the DAC has lost its state
+ * and has been reset. Make sure that the control register actually
+ * contains what we expect it to contain. Otherwise the watchdog might
+ * be enabled and we get watchdog timeout faults, which will render the
+ * DAC unusable. */
+ ad5421_update_ctrl(indio_dev, 0, 0);
+
+
+ /* The fault pin stays high as long as a fault condition is present and
+ * it is not possible to mask fault conditions. For certain fault
+ * conditions for example like over-temperature it takes some time
+ * until the fault condition disappears. If we would exit the interrupt
+ * handler immediately after handling the event it would be entered
+ * again instantly. Thus we fall back to polling in case we detect that
+ * a interrupt condition is still present.
+ */
+ do {
+ /* 0xffff is a invalid value for the register and will only be
+ * read if there has been a communication error */
+ if (fault == 0xffff)
+ fault = 0;
+
+ /* we are only interested in new events */
+ events = (old_fault ^ fault) & fault;
+ events &= st->fault_mask;
+
+ if (events & AD5421_FAULT_OVER_CURRENT) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (events & AD5421_FAULT_UNDER_CURRENT) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (events & AD5421_FAULT_TEMP_OVER_140) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ old_fault = fault;
+ fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
+
+ /* still active? go to sleep for some time */
+ if (fault & AD5421_FAULT_TRIGGER_IRQ)
+ msleep(1000);
+
+ } while (fault & AD5421_FAULT_TRIGGER_IRQ);
+
+
+ return IRQ_HANDLED;
+}
+
+static void ad5421_get_current_min_max(struct ad5421_state *st,
+ unsigned int *min, unsigned int *max)
+{
+ /* The current range is configured using external pins, which are
+ * usually hard-wired and not run-time switchable. */
+ switch (st->current_range) {
+ case AD5421_CURRENT_RANGE_4mA_20mA:
+ *min = 4000;
+ *max = 20000;
+ break;
+ case AD5421_CURRENT_RANGE_3mA8_21mA:
+ *min = 3800;
+ *max = 21000;
+ break;
+ case AD5421_CURRENT_RANGE_3mA2_24mA:
+ *min = 3200;
+ *max = 24000;
+ break;
+ default:
+ *min = 0;
+ *max = 1;
+ break;
+ }
+}
+
+static inline unsigned int ad5421_get_offset(struct ad5421_state *st)
+{
+ unsigned int min, max;
+
+ ad5421_get_current_min_max(st, &min, &max);
+ return (min * (1 << 16)) / (max - min);
+}
+
+static int ad5421_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long m)
+{
+ struct ad5421_state *st = iio_priv(indio_dev);
+ unsigned int min, max;
+ int ret;
+
+ if (chan->type != IIO_CURRENT)
+ return -EINVAL;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ad5421_get_current_min_max(st, &min, &max);
+ *val = max - min;
+ *val2 = (1 << 16) * 1000;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = ad5421_get_offset(st);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = ad5421_read(indio_dev, AD5421_REG_OFFSET);
+ if (ret < 0)
+ return ret;
+ *val = ret - 32768;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = ad5421_read(indio_dev, AD5421_REG_GAIN);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5421_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ const unsigned int max_val = 1 << 16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val += 32768;
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5421_write(indio_dev, AD5421_REG_OFFSET, val);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+
+ return ad5421_write(indio_dev, AD5421_REG_GAIN, val);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5421_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 ad5421_state *st = iio_priv(indio_dev);
+ unsigned int mask;
+
+ switch (chan->type) {
+ case IIO_CURRENT:
+ if (dir == IIO_EV_DIR_RISING)
+ mask = AD5421_FAULT_OVER_CURRENT;
+ else
+ mask = AD5421_FAULT_UNDER_CURRENT;
+ break;
+ case IIO_TEMP:
+ mask = AD5421_FAULT_TEMP_OVER_140;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&st->lock);
+ if (state)
+ st->fault_mask |= mask;
+ else
+ st->fault_mask &= ~mask;
+ mutex_unlock(&st->lock);
+
+ return 0;
+}
+
+static int ad5421_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 ad5421_state *st = iio_priv(indio_dev);
+ unsigned int mask;
+
+ switch (chan->type) {
+ case IIO_CURRENT:
+ if (dir == IIO_EV_DIR_RISING)
+ mask = AD5421_FAULT_OVER_CURRENT;
+ else
+ mask = AD5421_FAULT_UNDER_CURRENT;
+ break;
+ case IIO_TEMP:
+ mask = AD5421_FAULT_TEMP_OVER_140;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (bool)(st->fault_mask & mask);
+}
+
+static int ad5421_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)
+{
+ int ret;
+
+ switch (chan->type) {
+ case IIO_CURRENT:
+ ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ break;
+ case IIO_TEMP:
+ *val = 140000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info ad5421_info = {
+ .read_raw = ad5421_read_raw,
+ .write_raw = ad5421_write_raw,
+ .read_event_config = ad5421_read_event_config,
+ .write_event_config = ad5421_write_event_config,
+ .read_event_value = ad5421_read_event_value,
+};
+
+static int ad5421_probe(struct spi_device *spi)
+{
+ struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct iio_dev *indio_dev;
+ struct ad5421_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ indio_dev->name = "ad5421";
+ indio_dev->info = &ad5421_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad5421_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad5421_channels);
+
+ mutex_init(&st->lock);
+
+ st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE |
+ AD5421_CTRL_AUTO_FAULT_READBACK;
+
+ if (pdata) {
+ st->current_range = pdata->current_range;
+ if (pdata->external_vref)
+ st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF;
+ } else {
+ st->current_range = AD5421_CURRENT_RANGE_4mA_20mA;
+ }
+
+ /* write initial ctrl register value */
+ ad5421_update_ctrl(indio_dev, 0, 0);
+
+ if (spi->irq) {
+ ret = devm_request_threaded_irq(&spi->dev, spi->irq,
+ NULL,
+ ad5421_fault_handler,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ad5421 fault",
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static struct spi_driver ad5421_driver = {
+ .driver = {
+ .name = "ad5421",
+ },
+ .probe = ad5421_probe,
+};
+module_spi_driver(ad5421_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5421 DAC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad5421");
diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c
new file mode 100644
index 000000000..b168eb14c
--- /dev/null
+++ b/drivers/iio/dac/ad5446.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AD5446 SPI DAC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <asm/unaligned.h>
+
+#define MODE_PWRDWN_1k 0x1
+#define MODE_PWRDWN_100k 0x2
+#define MODE_PWRDWN_TRISTATE 0x3
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @dev: this device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @cached_val: store/retrieve values during power down
+ * @pwr_down_mode: power down mode (1k, 100k or tristate)
+ * @pwr_down: true if the device is in power down
+ * @lock: lock to protect the data buffer during write ops
+ */
+
+struct ad5446_state {
+ struct device *dev;
+ const struct ad5446_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned cached_val;
+ unsigned pwr_down_mode;
+ unsigned pwr_down;
+ struct mutex lock;
+};
+
+/**
+ * struct ad5446_chip_info - chip specific information
+ * @channel: channel spec for the DAC
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ * @write: chip specific helper function to write to the register
+ */
+
+struct ad5446_chip_info {
+ struct iio_chan_spec channel;
+ u16 int_vref_mv;
+ int (*write)(struct ad5446_state *st, unsigned val);
+};
+
+static const char * const ad5446_powerdown_modes[] = {
+ "1kohm_to_gnd", "100kohm_to_gnd", "three_state"
+};
+
+static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode + 1;
+
+ return 0;
+}
+
+static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode - 1;
+}
+
+static const struct iio_enum ad5446_powerdown_mode_enum = {
+ .items = ad5446_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5446_powerdown_modes),
+ .get = ad5446_get_powerdown_mode,
+ .set = ad5446_set_powerdown_mode,
+};
+
+static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+ unsigned int shift;
+ unsigned int val;
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ st->pwr_down = powerdown;
+
+ if (st->pwr_down) {
+ shift = chan->scan_type.realbits + chan->scan_type.shift;
+ val = st->pwr_down_mode << shift;
+ } else {
+ val = st->cached_val;
+ }
+
+ ret = st->chip_info->write(st, val);
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
+ {
+ .name = "powerdown",
+ .read = ad5446_read_dac_powerdown,
+ .write = ad5446_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum),
+ { },
+};
+
+#define _AD5446_CHANNEL(bits, storage, _shift, ext) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = (storage), \
+ .shift = (_shift), \
+ }, \
+ .ext_info = (ext), \
+}
+
+#define AD5446_CHANNEL(bits, storage, shift) \
+ _AD5446_CHANNEL(bits, storage, shift, NULL)
+
+#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
+ _AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown)
+
+static int ad5446_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *val = st->cached_val >> chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static int ad5446_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5446_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ val <<= chan->scan_type.shift;
+ mutex_lock(&st->lock);
+ st->cached_val = val;
+ if (!st->pwr_down)
+ ret = st->chip_info->write(st, val);
+ mutex_unlock(&st->lock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5446_info = {
+ .read_raw = ad5446_read_raw,
+ .write_raw = ad5446_write_raw,
+};
+
+static int ad5446_probe(struct device *dev, const char *name,
+ const struct ad5446_chip_info *chip_info)
+{
+ struct ad5446_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *reg;
+ int ret, voltage_uv = 0;
+
+ reg = devm_regulator_get(dev, "vcc");
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_disable_reg;
+ }
+ st = iio_priv(indio_dev);
+ st->chip_info = chip_info;
+
+ dev_set_drvdata(dev, indio_dev);
+ st->reg = reg;
+ st->dev = dev;
+
+ indio_dev->name = name;
+ indio_dev->info = &ad5446_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &st->chip_info->channel;
+ indio_dev->num_channels = 1;
+
+ mutex_init(&st->lock);
+
+ st->pwr_down_mode = MODE_PWRDWN_1k;
+
+ if (st->chip_info->int_vref_mv)
+ st->vref_mv = st->chip_info->int_vref_mv;
+ else if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ dev_warn(dev, "reference voltage unspecified\n");
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+ return ret;
+}
+
+static int ad5446_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5446_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5446_write(struct ad5446_state *st, unsigned val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ __be16 data = cpu_to_be16(val);
+
+ return spi_write(spi, &data, sizeof(data));
+}
+
+static int ad5660_write(struct ad5446_state *st, unsigned val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ uint8_t data[3];
+
+ put_unaligned_be24(val, &data[0]);
+
+ return spi_write(spi, data, sizeof(data));
+}
+
+/*
+ * ad5446_supported_spi_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_spi_device_ids {
+ ID_AD5300,
+ ID_AD5310,
+ ID_AD5320,
+ ID_AD5444,
+ ID_AD5446,
+ ID_AD5450,
+ ID_AD5451,
+ ID_AD5541A,
+ ID_AD5512A,
+ ID_AD5553,
+ ID_AD5600,
+ ID_AD5601,
+ ID_AD5611,
+ ID_AD5621,
+ ID_AD5641,
+ ID_AD5620_2500,
+ ID_AD5620_1250,
+ ID_AD5640_2500,
+ ID_AD5640_1250,
+ ID_AD5660_2500,
+ ID_AD5660_1250,
+ ID_AD5662,
+};
+
+static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
+ [ID_AD5300] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5310] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5320] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5444] = {
+ .channel = AD5446_CHANNEL(12, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5446] = {
+ .channel = AD5446_CHANNEL(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5450] = {
+ .channel = AD5446_CHANNEL(8, 16, 6),
+ .write = ad5446_write,
+ },
+ [ID_AD5451] = {
+ .channel = AD5446_CHANNEL(10, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5541A] = {
+ .channel = AD5446_CHANNEL(16, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5512A] = {
+ .channel = AD5446_CHANNEL(12, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5553] = {
+ .channel = AD5446_CHANNEL(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5600] = {
+ .channel = AD5446_CHANNEL(16, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5601] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
+ .write = ad5446_write,
+ },
+ [ID_AD5611] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5621] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5641] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5620_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .int_vref_mv = 2500,
+ .write = ad5446_write,
+ },
+ [ID_AD5620_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .int_vref_mv = 1250,
+ .write = ad5446_write,
+ },
+ [ID_AD5640_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .int_vref_mv = 2500,
+ .write = ad5446_write,
+ },
+ [ID_AD5640_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .int_vref_mv = 1250,
+ .write = ad5446_write,
+ },
+ [ID_AD5660_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .int_vref_mv = 2500,
+ .write = ad5660_write,
+ },
+ [ID_AD5660_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .int_vref_mv = 1250,
+ .write = ad5660_write,
+ },
+ [ID_AD5662] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .write = ad5660_write,
+ },
+};
+
+static const struct spi_device_id ad5446_spi_ids[] = {
+ {"ad5300", ID_AD5300},
+ {"ad5310", ID_AD5310},
+ {"ad5320", ID_AD5320},
+ {"ad5444", ID_AD5444},
+ {"ad5446", ID_AD5446},
+ {"ad5450", ID_AD5450},
+ {"ad5451", ID_AD5451},
+ {"ad5452", ID_AD5444}, /* ad5452 is compatible to the ad5444 */
+ {"ad5453", ID_AD5446}, /* ad5453 is compatible to the ad5446 */
+ {"ad5512a", ID_AD5512A},
+ {"ad5541a", ID_AD5541A},
+ {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */
+ {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */
+ {"ad5553", ID_AD5553},
+ {"ad5600", ID_AD5600},
+ {"ad5601", ID_AD5601},
+ {"ad5611", ID_AD5611},
+ {"ad5621", ID_AD5621},
+ {"ad5641", ID_AD5641},
+ {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */
+ {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */
+ {"ad5640-2500", ID_AD5640_2500},
+ {"ad5640-1250", ID_AD5640_1250},
+ {"ad5660-2500", ID_AD5660_2500},
+ {"ad5660-1250", ID_AD5660_1250},
+ {"ad5662", ID_AD5662},
+ {"dac081s101", ID_AD5300}, /* compatible Texas Instruments chips */
+ {"dac101s101", ID_AD5310},
+ {"dac121s101", ID_AD5320},
+ {"dac7512", ID_AD5320},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
+
+static const struct of_device_id ad5446_of_ids[] = {
+ { .compatible = "ti,dac7512" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad5446_of_ids);
+
+static int ad5446_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return ad5446_probe(&spi->dev, id->name,
+ &ad5446_spi_chip_info[id->driver_data]);
+}
+
+static int ad5446_spi_remove(struct spi_device *spi)
+{
+ return ad5446_remove(&spi->dev);
+}
+
+static struct spi_driver ad5446_spi_driver = {
+ .driver = {
+ .name = "ad5446",
+ .of_match_table = ad5446_of_ids,
+ },
+ .probe = ad5446_spi_probe,
+ .remove = ad5446_spi_remove,
+ .id_table = ad5446_spi_ids,
+};
+
+static int __init ad5446_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5446_spi_driver);
+}
+
+static void ad5446_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5446_spi_driver);
+}
+
+#else
+
+static inline int ad5446_spi_register_driver(void) { return 0; }
+static inline void ad5446_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5622_write(struct ad5446_state *st, unsigned val)
+{
+ struct i2c_client *client = to_i2c_client(st->dev);
+ __be16 data = cpu_to_be16(val);
+ int ret;
+
+ ret = i2c_master_send(client, (char *)&data, sizeof(data));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(data))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * ad5446_supported_i2c_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_i2c_device_ids {
+ ID_AD5602,
+ ID_AD5612,
+ ID_AD5622,
+};
+
+static const struct ad5446_chip_info ad5446_i2c_chip_info[] = {
+ [ID_AD5602] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+ .write = ad5622_write,
+ },
+ [ID_AD5612] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+ .write = ad5622_write,
+ },
+ [ID_AD5622] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+ .write = ad5622_write,
+ },
+};
+
+static int ad5446_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5446_probe(&i2c->dev, id->name,
+ &ad5446_i2c_chip_info[id->driver_data]);
+}
+
+static int ad5446_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5446_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5446_i2c_ids[] = {
+ {"ad5301", ID_AD5602},
+ {"ad5311", ID_AD5612},
+ {"ad5321", ID_AD5622},
+ {"ad5602", ID_AD5602},
+ {"ad5612", ID_AD5612},
+ {"ad5622", ID_AD5622},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
+
+static struct i2c_driver ad5446_i2c_driver = {
+ .driver = {
+ .name = "ad5446",
+ },
+ .probe = ad5446_i2c_probe,
+ .remove = ad5446_i2c_remove,
+ .id_table = ad5446_i2c_ids,
+};
+
+static int __init ad5446_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5446_i2c_driver);
+}
+
+static void __exit ad5446_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5446_i2c_driver);
+}
+
+#else
+
+static inline int ad5446_i2c_register_driver(void) { return 0; }
+static inline void ad5446_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5446_init(void)
+{
+ int ret;
+
+ ret = ad5446_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5446_i2c_register_driver();
+ if (ret) {
+ ad5446_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5446_init);
+
+static void __exit ad5446_exit(void)
+{
+ ad5446_i2c_unregister_driver();
+ ad5446_spi_unregister_driver();
+}
+module_exit(ad5446_exit);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c
new file mode 100644
index 000000000..f5e93c6ac
--- /dev/null
+++ b/drivers/iio/dac/ad5449.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog
+ * Converter driver.
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/platform_data/ad5449.h>
+
+#define AD5449_MAX_CHANNELS 2
+#define AD5449_MAX_VREFS 2
+
+#define AD5449_CMD_NOOP 0x0
+#define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3)
+#define AD5449_CMD_READ(x) (0x2 + (x) * 3)
+#define AD5449_CMD_LOAD(x) (0x3 + (x) * 3)
+#define AD5449_CMD_CTRL 13
+
+#define AD5449_CTRL_SDO_OFFSET 10
+#define AD5449_CTRL_DAISY_CHAIN BIT(9)
+#define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8)
+#define AD5449_CTRL_SAMPLE_RISING BIT(7)
+
+/**
+ * struct ad5449_chip_info - chip specific information
+ * @channels: Channel specification
+ * @num_channels: Number of channels
+ * @has_ctrl: Chip has a control register
+ */
+struct ad5449_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ bool has_ctrl;
+};
+
+/**
+ * struct ad5449 - driver instance specific data
+ * @spi: the SPI device for this driver instance
+ * @chip_info: chip model specific constants, available modes etc
+ * @vref_reg: vref supply regulators
+ * @has_sdo: whether the SDO line is connected
+ * @dac_cache: Cache for the DAC values
+ * @data: spi transfer buffers
+ * @lock: lock to protect the data buffer during SPI ops
+ */
+struct ad5449 {
+ struct spi_device *spi;
+ const struct ad5449_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS];
+ struct mutex lock;
+
+ bool has_sdo;
+ uint16_t dac_cache[AD5449_MAX_CHANNELS];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 data[2] ____cacheline_aligned;
+};
+
+enum ad5449_type {
+ ID_AD5426,
+ ID_AD5429,
+ ID_AD5432,
+ ID_AD5439,
+ ID_AD5443,
+ ID_AD5449,
+};
+
+static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr,
+ unsigned int val)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ st->data[0] = cpu_to_be16((addr << 12) | val);
+ ret = spi_write(st->spi, st->data, 2);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,
+ unsigned int *val)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0],
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1],
+ .rx_buf = &st->data[1],
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->lock);
+ st->data[0] = cpu_to_be16(addr << 12);
+ st->data[1] = cpu_to_be16(AD5449_CMD_NOOP);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ goto out_unlock;
+
+ *val = be16_to_cpu(st->data[1]);
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ad5449_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ struct regulator_bulk_data *reg;
+ int scale_uv;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (st->has_sdo) {
+ ret = ad5449_read(indio_dev,
+ AD5449_CMD_READ(chan->address), val);
+ if (ret)
+ return ret;
+ *val &= 0xfff;
+ } else {
+ *val = st->dac_cache[chan->address];
+ }
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ reg = &st->vref_reg[chan->channel];
+ scale_uv = regulator_get_voltage(reg->consumer);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ *val = scale_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5449_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ struct ad5449 *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val >= (1 << chan->scan_type.realbits))
+ return -EINVAL;
+
+ ret = ad5449_write(indio_dev,
+ AD5449_CMD_LOAD_AND_UPDATE(chan->address),
+ val << chan->scan_type.shift);
+ if (ret == 0)
+ st->dac_cache[chan->address] = val;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5449_info = {
+ .read_raw = ad5449_read_raw,
+ .write_raw = ad5449_write_raw,
+};
+
+#define AD5449_CHANNEL(chan, bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (chan), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 12 - (bits), \
+ }, \
+}
+
+#define DECLARE_AD5449_CHANNELS(name, bits) \
+const struct iio_chan_spec name[] = { \
+ AD5449_CHANNEL(0, bits), \
+ AD5449_CHANNEL(1, bits), \
+}
+
+static DECLARE_AD5449_CHANNELS(ad5429_channels, 8);
+static DECLARE_AD5449_CHANNELS(ad5439_channels, 10);
+static DECLARE_AD5449_CHANNELS(ad5449_channels, 12);
+
+static const struct ad5449_chip_info ad5449_chip_info[] = {
+ [ID_AD5426] = {
+ .channels = ad5429_channels,
+ .num_channels = 1,
+ .has_ctrl = false,
+ },
+ [ID_AD5429] = {
+ .channels = ad5429_channels,
+ .num_channels = 2,
+ .has_ctrl = true,
+ },
+ [ID_AD5432] = {
+ .channels = ad5439_channels,
+ .num_channels = 1,
+ .has_ctrl = false,
+ },
+ [ID_AD5439] = {
+ .channels = ad5439_channels,
+ .num_channels = 2,
+ .has_ctrl = true,
+ },
+ [ID_AD5443] = {
+ .channels = ad5449_channels,
+ .num_channels = 1,
+ .has_ctrl = false,
+ },
+ [ID_AD5449] = {
+ .channels = ad5449_channels,
+ .num_channels = 2,
+ .has_ctrl = true,
+ },
+};
+
+static const char *ad5449_vref_name(struct ad5449 *st, int n)
+{
+ if (st->chip_info->num_channels == 1)
+ return "VREF";
+
+ if (n == 0)
+ return "VREFA";
+ else
+ return "VREFB";
+}
+
+static int ad5449_spi_probe(struct spi_device *spi)
+{
+ struct ad5449_platform_data *pdata = spi->dev.platform_data;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct ad5449 *st;
+ unsigned int i;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5449_chip_info[id->driver_data];
+ st->spi = spi;
+
+ for (i = 0; i < st->chip_info->num_channels; ++i)
+ st->vref_reg[i].supply = ad5449_vref_name(st, i);
+
+ ret = devm_regulator_bulk_get(&spi->dev, st->chip_info->num_channels,
+ st->vref_reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg);
+ if (ret)
+ return ret;
+
+ indio_dev->name = id->name;
+ indio_dev->info = &ad5449_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ mutex_init(&st->lock);
+
+ if (st->chip_info->has_ctrl) {
+ unsigned int ctrl = 0x00;
+ if (pdata) {
+ if (pdata->hardware_clear_to_midscale)
+ ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE;
+ ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET;
+ st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED;
+ } else {
+ st->has_sdo = true;
+ }
+ ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl);
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
+
+ return ret;
+}
+
+static int ad5449_spi_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5449 *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5449_spi_ids[] = {
+ { "ad5415", ID_AD5449 },
+ { "ad5426", ID_AD5426 },
+ { "ad5429", ID_AD5429 },
+ { "ad5432", ID_AD5432 },
+ { "ad5439", ID_AD5439 },
+ { "ad5443", ID_AD5443 },
+ { "ad5449", ID_AD5449 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5449_spi_ids);
+
+static struct spi_driver ad5449_spi_driver = {
+ .driver = {
+ .name = "ad5449",
+ },
+ .probe = ad5449_spi_probe,
+ .remove = ad5449_spi_remove,
+ .id_table = ad5449_spi_ids,
+};
+module_spi_driver(ad5449_spi_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c
new file mode 100644
index 000000000..e9297c25d
--- /dev/null
+++ b/drivers/iio/dac/ad5504.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5504, AD5501 High Voltage Digital to Analog Converter
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/dac/ad5504.h>
+
+#define AD5504_RES_MASK GENMASK(11, 0)
+#define AD5504_CMD_READ BIT(15)
+#define AD5504_CMD_WRITE 0
+#define AD5504_ADDR(addr) ((addr) << 12)
+
+/* Registers */
+#define AD5504_ADDR_NOOP 0
+#define AD5504_ADDR_DAC(x) ((x) + 1)
+#define AD5504_ADDR_ALL_DAC 5
+#define AD5504_ADDR_CTRL 7
+
+/* Control Register */
+#define AD5504_DAC_PWR(ch) ((ch) << 2)
+#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6)
+#define AD5504_DAC_PWRDN_20K 0
+#define AD5504_DAC_PWRDN_3STATE 1
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @spi: spi_device
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @pwr_down_mask: power down mask
+ * @pwr_down_mode: current power down mode
+ * @data: transfer buffer
+ */
+struct ad5504_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned pwr_down_mask;
+ unsigned pwr_down_mode;
+
+ __be16 data[2] ____cacheline_aligned;
+};
+
+/*
+ * ad5504_supported_device_ids:
+ */
+enum ad5504_supported_device_ids {
+ ID_AD5504,
+ ID_AD5501,
+};
+
+static int ad5504_spi_write(struct ad5504_state *st, u8 addr, u16 val)
+{
+ st->data[0] = cpu_to_be16(AD5504_CMD_WRITE | AD5504_ADDR(addr) |
+ (val & AD5504_RES_MASK));
+
+ return spi_write(st->spi, &st->data[0], 2);
+}
+
+static int ad5504_spi_read(struct ad5504_state *st, u8 addr)
+{
+ int ret;
+ struct spi_transfer t = {
+ .tx_buf = &st->data[0],
+ .rx_buf = &st->data[1],
+ .len = 2,
+ };
+
+ st->data[0] = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr));
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->data[1]) & AD5504_RES_MASK;
+}
+
+static int ad5504_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5504_spi_read(st, chan->address);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static int ad5504_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ return ad5504_spi_write(st, chan->address, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const char * const ad5504_powerdown_modes[] = {
+ "20kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode;
+}
+
+static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode;
+
+ return 0;
+}
+
+static const struct iio_enum ad5504_powerdown_mode_enum = {
+ .items = ad5504_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5504_powerdown_modes),
+ .get = ad5504_get_powerdown_mode,
+ .set = ad5504_set_powerdown_mode,
+};
+
+static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ !(st->pwr_down_mask & (1 << chan->channel)));
+}
+
+static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ st->pwr_down_mask &= ~(1 << chan->channel);
+ else
+ st->pwr_down_mask |= (1 << chan->channel);
+
+ ret = ad5504_spi_write(st, AD5504_ADDR_CTRL,
+ AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) |
+ AD5504_DAC_PWR(st->pwr_down_mask));
+
+ /* writes to the CTRL register must be followed by a NOOP */
+ ad5504_spi_write(st, AD5504_ADDR_NOOP, 0);
+
+ return ret ? ret : len;
+}
+
+static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000");
+static IIO_CONST_ATTR(temp0_thresh_rising_en, "1");
+
+static struct attribute *ad5504_ev_attributes[] = {
+ &iio_const_attr_temp0_thresh_rising_value.dev_attr.attr,
+ &iio_const_attr_temp0_thresh_rising_en.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad5504_ev_attribute_group = {
+ .attrs = ad5504_ev_attributes,
+};
+
+static irqreturn_t ad5504_event_handler(int irq, void *private)
+{
+ iio_push_event(private,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(private));
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info ad5504_info = {
+ .write_raw = ad5504_write_raw,
+ .read_raw = ad5504_read_raw,
+ .event_attrs = &ad5504_ev_attribute_group,
+};
+
+static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5504_read_dac_powerdown,
+ .write = ad5504_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
+ &ad5504_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum),
+ { },
+};
+
+#define AD5504_CHANNEL(_chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = AD5504_ADDR_DAC(_chan), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .ext_info = ad5504_ext_info, \
+}
+
+static const struct iio_chan_spec ad5504_channels[] = {
+ AD5504_CHANNEL(0),
+ AD5504_CHANNEL(1),
+ AD5504_CHANNEL(2),
+ AD5504_CHANNEL(3),
+};
+
+static int ad5504_probe(struct spi_device *spi)
+{
+ struct ad5504_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad5504_state *st;
+ struct regulator *reg;
+ int ret, voltage_uv = 0;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ reg = devm_regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st = iio_priv(indio_dev);
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else if (pdata)
+ st->vref_mv = pdata->vref_mv;
+ else
+ dev_warn(&spi->dev, "reference voltage unspecified\n");
+
+ st->reg = reg;
+ st->spi = spi;
+ indio_dev->name = spi_get_device_id(st->spi)->name;
+ indio_dev->info = &ad5504_info;
+ if (spi_get_device_id(st->spi)->driver_data == ID_AD5501)
+ indio_dev->num_channels = 1;
+ else
+ indio_dev->num_channels = 4;
+ indio_dev->channels = ad5504_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (spi->irq) {
+ ret = devm_request_threaded_irq(&spi->dev, spi->irq,
+ NULL,
+ &ad5504_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ spi_get_device_id(st->spi)->name,
+ indio_dev);
+ if (ret)
+ goto error_disable_reg;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+
+ return ret;
+}
+
+static int ad5504_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5504_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5504_id[] = {
+ {"ad5504", ID_AD5504},
+ {"ad5501", ID_AD5501},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5504_id);
+
+static struct spi_driver ad5504_driver = {
+ .driver = {
+ .name = "ad5504",
+ },
+ .probe = ad5504_probe,
+ .remove = ad5504_remove,
+ .id_table = ad5504_id,
+};
+module_spi_driver(ad5504_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c
new file mode 100644
index 000000000..987264410
--- /dev/null
+++ b/drivers/iio/dac/ad5592r-base.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5592R Digital <-> Analog converters driver
+ *
+ * Copyright 2014-2016 Analog Devices Inc.
+ * Author: Paul Cercueil <paul.cercueil@analog.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#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>
+#include <linux/property.h>
+
+#include <dt-bindings/iio/adi,ad5592r.h>
+
+#include "ad5592r-base.h"
+
+static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct ad5592r_state *st = gpiochip_get_data(chip);
+ int ret = 0;
+ u8 val;
+
+ mutex_lock(&st->gpio_lock);
+
+ if (st->gpio_out & BIT(offset))
+ val = st->gpio_val;
+ else
+ ret = st->ops->gpio_read(st, &val);
+
+ mutex_unlock(&st->gpio_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return !!(val & BIT(offset));
+}
+
+static void ad5592r_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct ad5592r_state *st = gpiochip_get_data(chip);
+
+ mutex_lock(&st->gpio_lock);
+
+ if (value)
+ st->gpio_val |= BIT(offset);
+ else
+ st->gpio_val &= ~BIT(offset);
+
+ st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
+
+ mutex_unlock(&st->gpio_lock);
+}
+
+static int ad5592r_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct ad5592r_state *st = gpiochip_get_data(chip);
+ int ret;
+
+ mutex_lock(&st->gpio_lock);
+
+ st->gpio_out &= ~BIT(offset);
+ st->gpio_in |= BIT(offset);
+
+ ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
+
+err_unlock:
+ mutex_unlock(&st->gpio_lock);
+
+ return ret;
+}
+
+static int ad5592r_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct ad5592r_state *st = gpiochip_get_data(chip);
+ int ret;
+
+ mutex_lock(&st->gpio_lock);
+
+ if (value)
+ st->gpio_val |= BIT(offset);
+ else
+ st->gpio_val &= ~BIT(offset);
+
+ st->gpio_in &= ~BIT(offset);
+ st->gpio_out |= BIT(offset);
+
+ ret = st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
+
+err_unlock:
+ mutex_unlock(&st->gpio_lock);
+
+ return ret;
+}
+
+static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct ad5592r_state *st = gpiochip_get_data(chip);
+
+ if (!(st->gpio_map & BIT(offset))) {
+ dev_err(st->dev, "GPIO %d is reserved by alternate function\n",
+ offset);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ad5592r_gpio_init(struct ad5592r_state *st)
+{
+ if (!st->gpio_map)
+ return 0;
+
+ st->gpiochip.label = dev_name(st->dev);
+ st->gpiochip.base = -1;
+ st->gpiochip.ngpio = 8;
+ st->gpiochip.parent = st->dev;
+ st->gpiochip.can_sleep = true;
+ st->gpiochip.direction_input = ad5592r_gpio_direction_input;
+ st->gpiochip.direction_output = ad5592r_gpio_direction_output;
+ st->gpiochip.get = ad5592r_gpio_get;
+ st->gpiochip.set = ad5592r_gpio_set;
+ st->gpiochip.request = ad5592r_gpio_request;
+ st->gpiochip.owner = THIS_MODULE;
+
+ mutex_init(&st->gpio_lock);
+
+ return gpiochip_add_data(&st->gpiochip, st);
+}
+
+static void ad5592r_gpio_cleanup(struct ad5592r_state *st)
+{
+ if (st->gpio_map)
+ gpiochip_remove(&st->gpiochip);
+}
+
+static int ad5592r_reset(struct ad5592r_state *st)
+{
+ struct gpio_desc *gpio;
+
+ gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+
+ if (gpio) {
+ udelay(1);
+ gpiod_set_value(gpio, 1);
+ } else {
+ mutex_lock(&st->lock);
+ /* Writing this magic value resets the device */
+ st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac);
+ mutex_unlock(&st->lock);
+ }
+
+ udelay(250);
+
+ return 0;
+}
+
+static int ad5592r_get_vref(struct ad5592r_state *st)
+{
+ int ret;
+
+ if (st->reg) {
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ return ret;
+
+ return ret / 1000;
+ } else {
+ return 2500;
+ }
+}
+
+static int ad5592r_set_channel_modes(struct ad5592r_state *st)
+{
+ const struct ad5592r_rw_ops *ops = st->ops;
+ int ret;
+ unsigned i;
+ u8 pulldown = 0, tristate = 0, dac = 0, adc = 0;
+ u16 read_back;
+
+ for (i = 0; i < st->num_channels; i++) {
+ switch (st->channel_modes[i]) {
+ case CH_MODE_DAC:
+ dac |= BIT(i);
+ break;
+
+ case CH_MODE_ADC:
+ adc |= BIT(i);
+ break;
+
+ case CH_MODE_DAC_AND_ADC:
+ dac |= BIT(i);
+ adc |= BIT(i);
+ break;
+
+ case CH_MODE_GPIO:
+ st->gpio_map |= BIT(i);
+ st->gpio_in |= BIT(i); /* Default to input */
+ break;
+
+ case CH_MODE_UNUSED:
+ default:
+ switch (st->channel_offstate[i]) {
+ case CH_OFFSTATE_OUT_TRISTATE:
+ tristate |= BIT(i);
+ break;
+
+ case CH_OFFSTATE_OUT_LOW:
+ st->gpio_out |= BIT(i);
+ break;
+
+ case CH_OFFSTATE_OUT_HIGH:
+ st->gpio_out |= BIT(i);
+ st->gpio_val |= BIT(i);
+ break;
+
+ case CH_OFFSTATE_PULLDOWN:
+ default:
+ pulldown |= BIT(i);
+ break;
+ }
+ }
+ }
+
+ mutex_lock(&st->lock);
+
+ /* Pull down unused pins to GND */
+ ret = ops->reg_write(st, AD5592R_REG_PULLDOWN, pulldown);
+ if (ret)
+ goto err_unlock;
+
+ ret = ops->reg_write(st, AD5592R_REG_TRISTATE, tristate);
+ if (ret)
+ goto err_unlock;
+
+ /* Configure pins that we use */
+ ret = ops->reg_write(st, AD5592R_REG_DAC_EN, dac);
+ if (ret)
+ goto err_unlock;
+
+ ret = ops->reg_write(st, AD5592R_REG_ADC_EN, adc);
+ if (ret)
+ goto err_unlock;
+
+ ret = ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
+ if (ret)
+ goto err_unlock;
+
+ ret = ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
+ if (ret)
+ goto err_unlock;
+
+ ret = ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
+ if (ret)
+ goto err_unlock;
+
+ /* Verify that we can read back at least one register */
+ ret = ops->reg_read(st, AD5592R_REG_ADC_EN, &read_back);
+ if (!ret && (read_back & 0xff) != adc)
+ ret = -EIO;
+
+err_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ad5592r_reset_channel_modes(struct ad5592r_state *st)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(st->channel_modes); i++)
+ st->channel_modes[i] = CH_MODE_UNUSED;
+
+ return ad5592r_set_channel_modes(st);
+}
+
+static int ad5592r_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad5592r_state *st = iio_priv(iio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ if (!chan->output)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = st->ops->write_dac(st, chan->channel, val);
+ if (!ret)
+ st->cached_dac[chan->channel] = val;
+ mutex_unlock(&st->lock);
+ return ret;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLTAGE) {
+ bool gain;
+
+ if (val == st->scale_avail[0][0] &&
+ val2 == st->scale_avail[0][1])
+ gain = false;
+ else if (val == st->scale_avail[1][0] &&
+ val2 == st->scale_avail[1][1])
+ gain = true;
+ else
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+
+ ret = st->ops->reg_read(st, AD5592R_REG_CTRL,
+ &st->cached_gp_ctrl);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ if (chan->output) {
+ if (gain)
+ st->cached_gp_ctrl |=
+ AD5592R_REG_CTRL_DAC_RANGE;
+ else
+ st->cached_gp_ctrl &=
+ ~AD5592R_REG_CTRL_DAC_RANGE;
+ } else {
+ if (gain)
+ st->cached_gp_ctrl |=
+ AD5592R_REG_CTRL_ADC_RANGE;
+ else
+ st->cached_gp_ctrl &=
+ ~AD5592R_REG_CTRL_ADC_RANGE;
+ }
+
+ ret = st->ops->reg_write(st, AD5592R_REG_CTRL,
+ st->cached_gp_ctrl);
+ mutex_unlock(&st->lock);
+
+ return ret;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad5592r_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ad5592r_state *st = iio_priv(iio_dev);
+ u16 read_val;
+ int ret, mult;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ if (!chan->output) {
+ mutex_lock(&st->lock);
+ ret = st->ops->read_adc(st, chan->channel, &read_val);
+ mutex_unlock(&st->lock);
+ if (ret)
+ return ret;
+
+ if ((read_val >> 12 & 0x7) != (chan->channel & 0x7)) {
+ dev_err(st->dev, "Error while reading channel %u\n",
+ chan->channel);
+ return -EIO;
+ }
+
+ read_val &= GENMASK(11, 0);
+
+ } else {
+ mutex_lock(&st->lock);
+ read_val = st->cached_dac[chan->channel];
+ mutex_unlock(&st->lock);
+ }
+
+ dev_dbg(st->dev, "Channel %u read: 0x%04hX\n",
+ chan->channel, read_val);
+
+ *val = (int) read_val;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = ad5592r_get_vref(st);
+
+ if (chan->type == IIO_TEMP) {
+ s64 tmp = *val * (3767897513LL / 25LL);
+ *val = div_s64_rem(tmp, 1000000000LL, val2);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ mutex_lock(&st->lock);
+
+ if (chan->output)
+ mult = !!(st->cached_gp_ctrl &
+ AD5592R_REG_CTRL_DAC_RANGE);
+ else
+ mult = !!(st->cached_gp_ctrl &
+ AD5592R_REG_CTRL_ADC_RANGE);
+
+ mutex_unlock(&st->lock);
+
+ *val *= ++mult;
+
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ ret = ad5592r_get_vref(st);
+
+ mutex_lock(&st->lock);
+
+ if (st->cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE)
+ *val = (-34365 * 25) / ret;
+ else
+ *val = (-75365 * 25) / ret;
+
+ mutex_unlock(&st->lock);
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5592r_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;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5592r_info = {
+ .read_raw = ad5592r_read_raw,
+ .write_raw = ad5592r_write_raw,
+ .write_raw_get_fmt = ad5592r_write_raw_get_fmt,
+};
+
+static ssize_t ad5592r_show_scale_available(struct iio_dev *iio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad5592r_state *st = iio_priv(iio_dev);
+
+ return sprintf(buf, "%d.%09u %d.%09u\n",
+ st->scale_avail[0][0], st->scale_avail[0][1],
+ st->scale_avail[1][0], st->scale_avail[1][1]);
+}
+
+static const struct iio_chan_spec_ext_info ad5592r_ext_info[] = {
+ {
+ .name = "scale_available",
+ .read = ad5592r_show_scale_available,
+ .shared = IIO_SHARED_BY_TYPE,
+ },
+ {},
+};
+
+static void ad5592r_setup_channel(struct iio_dev *iio_dev,
+ struct iio_chan_spec *chan, bool output, unsigned id)
+{
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->output = output;
+ chan->channel = id;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 12;
+ chan->scan_type.storagebits = 16;
+ chan->ext_info = ad5592r_ext_info;
+}
+
+static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
+{
+ struct ad5592r_state *st = iio_priv(iio_dev);
+ unsigned i, curr_channel = 0,
+ num_channels = st->num_channels;
+ struct iio_chan_spec *channels;
+ struct fwnode_handle *child;
+ u32 reg, tmp;
+ int ret;
+
+ device_for_each_child_node(st->dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret || reg >= ARRAY_SIZE(st->channel_modes))
+ continue;
+
+ ret = fwnode_property_read_u32(child, "adi,mode", &tmp);
+ if (!ret)
+ st->channel_modes[reg] = tmp;
+
+ ret = fwnode_property_read_u32(child, "adi,off-state", &tmp);
+ if (!ret)
+ st->channel_offstate[reg] = tmp;
+ }
+
+ channels = devm_kcalloc(st->dev,
+ 1 + 2 * num_channels, sizeof(*channels),
+ GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < num_channels; i++) {
+ switch (st->channel_modes[i]) {
+ case CH_MODE_DAC:
+ ad5592r_setup_channel(iio_dev, &channels[curr_channel],
+ true, i);
+ curr_channel++;
+ break;
+
+ case CH_MODE_ADC:
+ ad5592r_setup_channel(iio_dev, &channels[curr_channel],
+ false, i);
+ curr_channel++;
+ break;
+
+ case CH_MODE_DAC_AND_ADC:
+ ad5592r_setup_channel(iio_dev, &channels[curr_channel],
+ true, i);
+ curr_channel++;
+ ad5592r_setup_channel(iio_dev, &channels[curr_channel],
+ false, i);
+ curr_channel++;
+ break;
+
+ default:
+ continue;
+ }
+ }
+
+ channels[curr_channel].type = IIO_TEMP;
+ channels[curr_channel].channel = 8;
+ channels[curr_channel].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET);
+ curr_channel++;
+
+ iio_dev->num_channels = curr_channel;
+ iio_dev->channels = channels;
+
+ return 0;
+}
+
+static void ad5592r_init_scales(struct ad5592r_state *st, int vref_mV)
+{
+ s64 tmp = (s64)vref_mV * 1000000000LL >> 12;
+
+ st->scale_avail[0][0] =
+ div_s64_rem(tmp, 1000000000LL, &st->scale_avail[0][1]);
+ st->scale_avail[1][0] =
+ div_s64_rem(tmp * 2, 1000000000LL, &st->scale_avail[1][1]);
+}
+
+int ad5592r_probe(struct device *dev, const char *name,
+ const struct ad5592r_rw_ops *ops)
+{
+ struct iio_dev *iio_dev;
+ struct ad5592r_state *st;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(iio_dev);
+ st->dev = dev;
+ st->ops = ops;
+ st->num_channels = 8;
+ dev_set_drvdata(dev, iio_dev);
+
+ st->reg = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(st->reg)) {
+ if ((PTR_ERR(st->reg) != -ENODEV) && dev->of_node)
+ return PTR_ERR(st->reg);
+
+ st->reg = NULL;
+ } else {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+ }
+
+ iio_dev->name = name;
+ iio_dev->info = &ad5592r_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+
+ mutex_init(&st->lock);
+
+ ad5592r_init_scales(st, ad5592r_get_vref(st));
+
+ ret = ad5592r_reset(st);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ops->reg_write(st, AD5592R_REG_PD,
+ (st->reg == NULL) ? AD5592R_REG_PD_EN_REF : 0);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad5592r_alloc_channels(iio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad5592r_set_channel_modes(st);
+ if (ret)
+ goto error_reset_ch_modes;
+
+ ret = iio_device_register(iio_dev);
+ if (ret)
+ goto error_reset_ch_modes;
+
+ ret = ad5592r_gpio_init(st);
+ if (ret)
+ goto error_dev_unregister;
+
+ return 0;
+
+error_dev_unregister:
+ iio_device_unregister(iio_dev);
+
+error_reset_ch_modes:
+ ad5592r_reset_channel_modes(st);
+
+error_disable_reg:
+ if (st->reg)
+ regulator_disable(st->reg);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad5592r_probe);
+
+int ad5592r_remove(struct device *dev)
+{
+ struct iio_dev *iio_dev = dev_get_drvdata(dev);
+ struct ad5592r_state *st = iio_priv(iio_dev);
+
+ iio_device_unregister(iio_dev);
+ ad5592r_reset_channel_modes(st);
+ ad5592r_gpio_cleanup(st);
+
+ if (st->reg)
+ regulator_disable(st->reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad5592r_remove);
+
+MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h
new file mode 100644
index 000000000..23dac2f1f
--- /dev/null
+++ b/drivers/iio/dac/ad5592r-base.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AD5592R / AD5593R Digital <-> Analog converters driver
+ *
+ * Copyright 2015-2016 Analog Devices Inc.
+ * Author: Paul Cercueil <paul.cercueil@analog.com>
+ */
+
+#ifndef __DRIVERS_IIO_DAC_AD5592R_BASE_H__
+#define __DRIVERS_IIO_DAC_AD5592R_BASE_H__
+
+#include <linux/types.h>
+#include <linux/cache.h>
+#include <linux/mutex.h>
+#include <linux/gpio/driver.h>
+
+struct device;
+struct ad5592r_state;
+
+enum ad5592r_registers {
+ AD5592R_REG_NOOP = 0x0,
+ AD5592R_REG_DAC_READBACK = 0x1,
+ AD5592R_REG_ADC_SEQ = 0x2,
+ AD5592R_REG_CTRL = 0x3,
+ AD5592R_REG_ADC_EN = 0x4,
+ AD5592R_REG_DAC_EN = 0x5,
+ AD5592R_REG_PULLDOWN = 0x6,
+ AD5592R_REG_LDAC = 0x7,
+ AD5592R_REG_GPIO_OUT_EN = 0x8,
+ AD5592R_REG_GPIO_SET = 0x9,
+ AD5592R_REG_GPIO_IN_EN = 0xA,
+ AD5592R_REG_PD = 0xB,
+ AD5592R_REG_OPEN_DRAIN = 0xC,
+ AD5592R_REG_TRISTATE = 0xD,
+ AD5592R_REG_RESET = 0xF,
+};
+
+#define AD5592R_REG_PD_EN_REF BIT(9)
+#define AD5592R_REG_CTRL_ADC_RANGE BIT(5)
+#define AD5592R_REG_CTRL_DAC_RANGE BIT(4)
+
+struct ad5592r_rw_ops {
+ int (*write_dac)(struct ad5592r_state *st, unsigned chan, u16 value);
+ int (*read_adc)(struct ad5592r_state *st, unsigned chan, u16 *value);
+ int (*reg_write)(struct ad5592r_state *st, u8 reg, u16 value);
+ int (*reg_read)(struct ad5592r_state *st, u8 reg, u16 *value);
+ int (*gpio_read)(struct ad5592r_state *st, u8 *value);
+};
+
+struct ad5592r_state {
+ struct device *dev;
+ struct regulator *reg;
+ struct gpio_chip gpiochip;
+ struct mutex gpio_lock; /* Protect cached gpio_out, gpio_val, etc. */
+ struct mutex lock;
+ unsigned int num_channels;
+ const struct ad5592r_rw_ops *ops;
+ int scale_avail[2][2];
+ u16 cached_dac[8];
+ u16 cached_gp_ctrl;
+ u8 channel_modes[8];
+ u8 channel_offstate[8];
+ u8 gpio_map;
+ u8 gpio_out;
+ u8 gpio_in;
+ u8 gpio_val;
+
+ __be16 spi_msg ____cacheline_aligned;
+ __be16 spi_msg_nop;
+};
+
+int ad5592r_probe(struct device *dev, const char *name,
+ const struct ad5592r_rw_ops *ops);
+int ad5592r_remove(struct device *dev);
+
+#endif /* __DRIVERS_IIO_DAC_AD5592R_BASE_H__ */
diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c
new file mode 100644
index 000000000..41f651500
--- /dev/null
+++ b/drivers/iio/dac/ad5592r.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5592R Digital <-> Analog converters driver
+ *
+ * Copyright 2015-2016 Analog Devices Inc.
+ * Author: Paul Cercueil <paul.cercueil@analog.com>
+ */
+
+#include "ad5592r-base.h"
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+
+#define AD5592R_GPIO_READBACK_EN BIT(10)
+#define AD5592R_LDAC_READBACK_EN BIT(6)
+
+static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
+{
+ struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+ struct spi_transfer t = {
+ .tx_buf = &st->spi_msg_nop,
+ .rx_buf = buf,
+ .len = 2
+ };
+
+ st->spi_msg_nop = 0; /* NOP */
+
+ return spi_sync_transfer(spi, &t, 1);
+}
+
+static int ad5592r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
+{
+ struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+
+ st->spi_msg = cpu_to_be16(BIT(15) | (chan << 12) | value);
+
+ return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
+}
+
+static int ad5592r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
+{
+ struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+ int ret;
+
+ st->spi_msg = cpu_to_be16((AD5592R_REG_ADC_SEQ << 11) | BIT(chan));
+
+ ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
+ if (ret)
+ return ret;
+
+ /*
+ * Invalid data:
+ * See Figure 40. Single-Channel ADC Conversion Sequence
+ */
+ ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
+ if (ret)
+ return ret;
+
+ ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
+ if (ret)
+ return ret;
+
+ *value = be16_to_cpu(st->spi_msg);
+
+ return 0;
+}
+
+static int ad5592r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
+{
+ struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+
+ st->spi_msg = cpu_to_be16((reg << 11) | value);
+
+ return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
+}
+
+static int ad5592r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
+{
+ struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
+ int ret;
+
+ st->spi_msg = cpu_to_be16((AD5592R_REG_LDAC << 11) |
+ AD5592R_LDAC_READBACK_EN | (reg << 2));
+
+ ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
+ if (ret)
+ return ret;
+
+ ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
+ if (ret)
+ return ret;
+
+ *value = be16_to_cpu(st->spi_msg);
+
+ return 0;
+}
+
+static int ad5592r_gpio_read(struct ad5592r_state *st, u8 *value)
+{
+ int ret;
+
+ ret = ad5592r_reg_write(st, AD5592R_REG_GPIO_IN_EN,
+ AD5592R_GPIO_READBACK_EN | st->gpio_in);
+ if (ret)
+ return ret;
+
+ ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
+ if (ret)
+ return ret;
+
+ *value = (u8) be16_to_cpu(st->spi_msg);
+
+ return 0;
+}
+
+static const struct ad5592r_rw_ops ad5592r_rw_ops = {
+ .write_dac = ad5592r_write_dac,
+ .read_adc = ad5592r_read_adc,
+ .reg_write = ad5592r_reg_write,
+ .reg_read = ad5592r_reg_read,
+ .gpio_read = ad5592r_gpio_read,
+};
+
+static int ad5592r_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return ad5592r_probe(&spi->dev, id->name, &ad5592r_rw_ops);
+}
+
+static int ad5592r_spi_remove(struct spi_device *spi)
+{
+ return ad5592r_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5592r_spi_ids[] = {
+ { .name = "ad5592r", },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5592r_spi_ids);
+
+static const struct of_device_id ad5592r_of_match[] = {
+ { .compatible = "adi,ad5592r", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ad5592r_of_match);
+
+static const struct acpi_device_id ad5592r_acpi_match[] = {
+ {"ADS5592", },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match);
+
+static struct spi_driver ad5592r_spi_driver = {
+ .driver = {
+ .name = "ad5592r",
+ .of_match_table = ad5592r_of_match,
+ .acpi_match_table = ad5592r_acpi_match,
+ },
+ .probe = ad5592r_spi_probe,
+ .remove = ad5592r_spi_remove,
+ .id_table = ad5592r_spi_ids,
+};
+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");
diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c
new file mode 100644
index 000000000..4cc855c78
--- /dev/null
+++ b/drivers/iio/dac/ad5593r.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5593R Digital <-> Analog converters driver
+ *
+ * Copyright 2015-2016 Analog Devices Inc.
+ * Author: Paul Cercueil <paul.cercueil@analog.com>
+ */
+
+#include "ad5592r-base.h"
+
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+
+#include <asm/unaligned.h>
+
+#define AD5593R_MODE_CONF (0 << 4)
+#define AD5593R_MODE_DAC_WRITE (1 << 4)
+#define AD5593R_MODE_ADC_READBACK (4 << 4)
+#define AD5593R_MODE_DAC_READBACK (5 << 4)
+#define AD5593R_MODE_GPIO_READBACK (6 << 4)
+#define AD5593R_MODE_REG_READBACK (7 << 4)
+
+static int ad5593r_read_word(struct i2c_client *i2c, u8 reg, u16 *value)
+{
+ int ret;
+ u8 buf[2];
+
+ ret = i2c_smbus_write_byte(i2c, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(i2c, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ *value = get_unaligned_be16(buf);
+
+ return 0;
+}
+
+static int ad5593r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+
+ return i2c_smbus_write_word_swapped(i2c,
+ AD5593R_MODE_DAC_WRITE | chan, value);
+}
+
+static int ad5593r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+ s32 val;
+
+ val = i2c_smbus_write_word_swapped(i2c,
+ AD5593R_MODE_CONF | AD5592R_REG_ADC_SEQ, BIT(chan));
+ if (val < 0)
+ return (int) val;
+
+ return ad5593r_read_word(i2c, AD5593R_MODE_ADC_READBACK, value);
+}
+
+static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+
+ return i2c_smbus_write_word_swapped(i2c,
+ AD5593R_MODE_CONF | reg, value);
+}
+
+static int ad5593r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+
+ return ad5593r_read_word(i2c, AD5593R_MODE_REG_READBACK | reg, value);
+}
+
+static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+ u16 val;
+ int ret;
+
+ ret = ad5593r_read_word(i2c, AD5593R_MODE_GPIO_READBACK, &val);
+ if (ret)
+ return ret;
+
+ *value = (u8) val;
+
+ return 0;
+}
+
+static const struct ad5592r_rw_ops ad5593r_rw_ops = {
+ .write_dac = ad5593r_write_dac,
+ .read_adc = ad5593r_read_adc,
+ .reg_write = ad5593r_reg_write,
+ .reg_read = ad5593r_reg_read,
+ .gpio_read = ad5593r_gpio_read,
+};
+
+static int ad5593r_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5592r_probe(&i2c->dev, id->name, &ad5593r_rw_ops);
+}
+
+static int ad5593r_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5592r_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5593r_i2c_ids[] = {
+ { .name = "ad5593r", },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ad5593r_i2c_ids);
+
+static const struct of_device_id ad5593r_of_match[] = {
+ { .compatible = "adi,ad5593r", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ad5593r_of_match);
+
+static const struct acpi_device_id ad5593r_acpi_match[] = {
+ {"ADS5593", },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, ad5593r_acpi_match);
+
+static struct i2c_driver ad5593r_driver = {
+ .driver = {
+ .name = "ad5593r",
+ .of_match_table = ad5593r_of_match,
+ .acpi_match_table = ad5593r_acpi_match,
+ },
+ .probe = ad5593r_i2c_probe,
+ .remove = ad5593r_i2c_remove,
+ .id_table = ad5593r_i2c_ids,
+};
+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");
diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h
new file mode 100644
index 000000000..13964f3a2
--- /dev/null
+++ b/drivers/iio/dac/ad5624r.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AD5624R SPI DAC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ */
+#ifndef SPI_AD5624R_H_
+#define SPI_AD5624R_H_
+
+#define AD5624R_DAC_CHANNELS 4
+
+#define AD5624R_ADDR_DAC0 0x0
+#define AD5624R_ADDR_DAC1 0x1
+#define AD5624R_ADDR_DAC2 0x2
+#define AD5624R_ADDR_DAC3 0x3
+#define AD5624R_ADDR_ALL_DAC 0x7
+
+#define AD5624R_CMD_WRITE_INPUT_N 0x0
+#define AD5624R_CMD_UPDATE_DAC_N 0x1
+#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
+#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define AD5624R_CMD_POWERDOWN_DAC 0x4
+#define AD5624R_CMD_RESET 0x5
+#define AD5624R_CMD_LDAC_SETUP 0x6
+#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7
+
+#define AD5624R_LDAC_PWRDN_NONE 0x0
+#define AD5624R_LDAC_PWRDN_1K 0x1
+#define AD5624R_LDAC_PWRDN_100K 0x2
+#define AD5624R_LDAC_PWRDN_3STATE 0x3
+
+/**
+ * struct ad5624r_chip_info - chip specific information
+ * @channels: channel spec for the DAC
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ */
+
+struct ad5624r_chip_info {
+ const struct iio_chan_spec *channels;
+ u16 int_vref_mv;
+};
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @indio_dev: the industrial I/O device
+ * @us: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @pwr_down_mask power down mask
+ * @pwr_down_mode current power down mode
+ */
+
+struct ad5624r_state {
+ struct spi_device *us;
+ const struct ad5624r_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned pwr_down_mask;
+ unsigned pwr_down_mode;
+};
+
+/**
+ * ad5624r_supported_device_ids:
+ * The AD5624/44/64 parts are available in different
+ * fixed internal reference voltage options.
+ */
+
+enum ad5624r_supported_device_ids {
+ ID_AD5624R3,
+ ID_AD5644R3,
+ ID_AD5664R3,
+ ID_AD5624R5,
+ ID_AD5644R5,
+ ID_AD5664R5,
+};
+
+#endif /* SPI_AD5624R_H_ */
diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c
new file mode 100644
index 000000000..ab4997bfd
--- /dev/null
+++ b/drivers/iio/dac/ad5624r_spi.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <asm/unaligned.h>
+
+#include "ad5624r.h"
+
+static int ad5624r_spi_write(struct spi_device *spi,
+ u8 cmd, u8 addr, u16 val, u8 shift)
+{
+ u32 data;
+ u8 msg[3];
+
+ /*
+ * The input shift register is 24 bits wide. The first two bits are
+ * don't care bits. The next three are the command bits, C2 to C0,
+ * followed by the 3-bit DAC address, A2 to A0, and then the
+ * 16-, 14-, 12-bit data-word. The data-word comprises the 16-,
+ * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits,
+ * for the AD5664R, AD5644R, and AD5624R, respectively.
+ */
+ data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << shift);
+ put_unaligned_be24(data, &msg[0]);
+
+ return spi_write(spi, msg, sizeof(msg));
+}
+
+static int ad5624r_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static int ad5624r_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ return ad5624r_spi_write(st->us,
+ AD5624R_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val,
+ chan->scan_type.shift);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const char * const ad5624r_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state"
+};
+
+static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode;
+}
+
+static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode;
+
+ return 0;
+}
+
+static const struct iio_enum ad5624r_powerdown_mode_enum = {
+ .items = ad5624r_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5624r_powerdown_modes),
+ .get = ad5624r_get_powerdown_mode,
+ .set = ad5624r_set_powerdown_mode,
+};
+
+static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ !!(st->pwr_down_mask & (1 << chan->channel)));
+}
+
+static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ st->pwr_down_mask |= (1 << chan->channel);
+ else
+ st->pwr_down_mask &= ~(1 << chan->channel);
+
+ ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0,
+ (st->pwr_down_mode << 4) |
+ st->pwr_down_mask, 16);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_info ad5624r_info = {
+ .write_raw = ad5624r_write_raw,
+ .read_raw = ad5624r_read_raw,
+};
+
+static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5624r_read_dac_powerdown,
+ .write = ad5624r_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
+ &ad5624r_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum),
+ { },
+};
+
+#define AD5624R_CHANNEL(_chan, _bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (_chan), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ }, \
+ .ext_info = ad5624r_ext_info, \
+}
+
+#define DECLARE_AD5624R_CHANNELS(_name, _bits) \
+ const struct iio_chan_spec _name##_channels[] = { \
+ AD5624R_CHANNEL(0, _bits), \
+ AD5624R_CHANNEL(1, _bits), \
+ AD5624R_CHANNEL(2, _bits), \
+ AD5624R_CHANNEL(3, _bits), \
+}
+
+static DECLARE_AD5624R_CHANNELS(ad5624r, 12);
+static DECLARE_AD5624R_CHANNELS(ad5644r, 14);
+static DECLARE_AD5624R_CHANNELS(ad5664r, 16);
+
+static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = {
+ [ID_AD5624R3] = {
+ .channels = ad5624r_channels,
+ .int_vref_mv = 1250,
+ },
+ [ID_AD5624R5] = {
+ .channels = ad5624r_channels,
+ .int_vref_mv = 2500,
+ },
+ [ID_AD5644R3] = {
+ .channels = ad5644r_channels,
+ .int_vref_mv = 1250,
+ },
+ [ID_AD5644R5] = {
+ .channels = ad5644r_channels,
+ .int_vref_mv = 2500,
+ },
+ [ID_AD5664R3] = {
+ .channels = ad5664r_channels,
+ .int_vref_mv = 1250,
+ },
+ [ID_AD5664R5] = {
+ .channels = ad5664r_channels,
+ .int_vref_mv = 2500,
+ },
+};
+
+static int ad5624r_probe(struct spi_device *spi)
+{
+ struct ad5624r_state *st;
+ struct iio_dev *indio_dev;
+ int ret, voltage_uv = 0;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ } else {
+ if (PTR_ERR(st->reg) != -ENODEV)
+ return PTR_ERR(st->reg);
+ /* Backwards compatibility. This naming is not correct */
+ st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ st->chip_info =
+ &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ st->vref_mv = st->chip_info->int_vref_mv;
+
+ st->us = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5624r_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = AD5624R_DAC_CHANNELS;
+
+ ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0,
+ !!voltage_uv, 16);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ad5624r_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5624r_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5624r_id[] = {
+ {"ad5624r3", ID_AD5624R3},
+ {"ad5644r3", ID_AD5644R3},
+ {"ad5664r3", ID_AD5664R3},
+ {"ad5624r5", ID_AD5624R5},
+ {"ad5644r5", ID_AD5644R5},
+ {"ad5664r5", ID_AD5664R5},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5624r_id);
+
+static struct spi_driver ad5624r_driver = {
+ .driver = {
+ .name = "ad5624r",
+ },
+ .probe = ad5624r_probe,
+ .remove = ad5624r_remove,
+ .id_table = ad5624r_id,
+};
+module_spi_driver(ad5624r_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
new file mode 100644
index 000000000..0188ded51
--- /dev/null
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
+ * AD5681R, AD5682R, AD5683, AD5683R, AD5684,
+ * AD5684R, AD5685R, AD5686, AD5686R
+ * Digital to analog converters driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#include "ad5686.h"
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+static int ad5686_spi_write(struct ad5686_state *st,
+ u8 cmd, u8 addr, u16 val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ u8 tx_len, *buf;
+
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) |
+ val);
+ buf = &st->data[0].d8[0];
+ tx_len = 2;
+ break;
+ case AD5683_REGMAP:
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
+ AD5683_DATA(val));
+ buf = &st->data[0].d8[1];
+ tx_len = 3;
+ break;
+ case AD5686_REGMAP:
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
+ AD5686_ADDR(addr) |
+ val);
+ buf = &st->data[0].d8[1];
+ tx_len = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return spi_write(spi, buf, tx_len);
+}
+
+static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
+{
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[2].d8[1],
+ .len = 3,
+ },
+ };
+ struct spi_device *spi = to_spi_device(st->dev);
+ u8 cmd = 0;
+ int ret;
+
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ return -ENOTSUPP;
+ case AD5683_REGMAP:
+ cmd = AD5686_CMD_READBACK_ENABLE_V2;
+ break;
+ case AD5686_REGMAP:
+ cmd = AD5686_CMD_READBACK_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
+ AD5686_ADDR(addr));
+ st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
+
+ ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be32_to_cpu(st->data[2].d32);
+}
+
+static int ad5686_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return ad5686_probe(&spi->dev, id->driver_data, id->name,
+ ad5686_spi_write, ad5686_spi_read);
+}
+
+static int ad5686_spi_remove(struct spi_device *spi)
+{
+ return ad5686_remove(&spi->dev);
+}
+
+static const struct spi_device_id ad5686_spi_id[] = {
+ {"ad5310r", ID_AD5310R},
+ {"ad5672r", ID_AD5672R},
+ {"ad5674r", ID_AD5674R},
+ {"ad5676", ID_AD5676},
+ {"ad5676r", ID_AD5676R},
+ {"ad5679r", ID_AD5679R},
+ {"ad5681r", ID_AD5681R},
+ {"ad5682r", ID_AD5682R},
+ {"ad5683", ID_AD5683},
+ {"ad5683r", ID_AD5683R},
+ {"ad5684", ID_AD5684},
+ {"ad5684r", ID_AD5684R},
+ {"ad5685", ID_AD5685R}, /* Does not exist */
+ {"ad5685r", ID_AD5685R},
+ {"ad5686", ID_AD5686},
+ {"ad5686r", ID_AD5686R},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5686_spi_id);
+
+static struct spi_driver ad5686_spi_driver = {
+ .driver = {
+ .name = "ad5686",
+ },
+ .probe = ad5686_spi_probe,
+ .remove = ad5686_spi_remove,
+ .id_table = ad5686_spi_id,
+};
+
+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");
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
new file mode 100644
index 000000000..148d9541f
--- /dev/null
+++ b/drivers/iio/dac/ad5686.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD5686R, AD5685R, AD5684R Digital to analog converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "ad5686.h"
+
+static const char * const ad5686_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state"
+};
+
+static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
+}
+
+static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
+ st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));
+
+ return 0;
+}
+
+static const struct iio_enum ad5686_powerdown_mode_enum = {
+ .items = ad5686_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5686_powerdown_modes),
+ .get = ad5686_get_powerdown_mode,
+ .set = ad5686_set_powerdown_mode,
+};
+
+static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", !!(st->pwr_down_mask &
+ (0x3 << (chan->channel * 2))));
+}
+
+static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf,
+ size_t len)
+{
+ bool readin;
+ int ret;
+ struct ad5686_state *st = iio_priv(indio_dev);
+ unsigned int val, ref_bit_msk;
+ u8 shift, address = 0;
+
+ ret = strtobool(buf, &readin);
+ if (ret)
+ return ret;
+
+ if (readin)
+ st->pwr_down_mask |= (0x3 << (chan->channel * 2));
+ else
+ st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
+
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ shift = 9;
+ ref_bit_msk = AD5310_REF_BIT_MSK;
+ break;
+ case AD5683_REGMAP:
+ shift = 13;
+ ref_bit_msk = AD5683_REF_BIT_MSK;
+ break;
+ case AD5686_REGMAP:
+ shift = 0;
+ ref_bit_msk = 0;
+ /* AD5674R/AD5679R have 16 channels and 2 powerdown registers */
+ if (chan->channel > 0x7)
+ address = 0x8;
+ break;
+ case AD5693_REGMAP:
+ shift = 13;
+ ref_bit_msk = AD5693_REF_BIT_MSK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = ((st->pwr_down_mask & st->pwr_down_mode) << shift);
+ if (!st->use_internal_vref)
+ val |= ref_bit_msk;
+
+ ret = st->write(st, AD5686_CMD_POWERDOWN_DAC,
+ address, val >> (address * 2));
+
+ return ret ? ret : len;
+}
+
+static int ad5686_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ ret = st->read(st, chan->address);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ *val = (ret >> chan->scan_type.shift) &
+ GENMASK(chan->scan_type.realbits - 1, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static int ad5686_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5686_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val > (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = st->write(st,
+ AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address,
+ val << chan->scan_type.shift);
+ mutex_unlock(&st->lock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad5686_info = {
+ .read_raw = ad5686_read_raw,
+ .write_raw = ad5686_write_raw,
+};
+
+static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5686_read_dac_powerdown,
+ .write = ad5686_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum),
+ { },
+};
+
+#define AD5868_CHANNEL(chan, addr, bits, _shift) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .address = addr, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = (_shift), \
+ }, \
+ .ext_info = ad5686_ext_info, \
+}
+
+#define DECLARE_AD5693_CHANNELS(name, bits, _shift) \
+static const struct iio_chan_spec name[] = { \
+ AD5868_CHANNEL(0, 0, bits, _shift), \
+}
+
+#define DECLARE_AD5686_CHANNELS(name, bits, _shift) \
+static const struct iio_chan_spec name[] = { \
+ AD5868_CHANNEL(0, 1, bits, _shift), \
+ AD5868_CHANNEL(1, 2, bits, _shift), \
+ AD5868_CHANNEL(2, 4, bits, _shift), \
+ AD5868_CHANNEL(3, 8, bits, _shift), \
+}
+
+#define DECLARE_AD5676_CHANNELS(name, bits, _shift) \
+static const struct iio_chan_spec name[] = { \
+ AD5868_CHANNEL(0, 0, bits, _shift), \
+ AD5868_CHANNEL(1, 1, bits, _shift), \
+ AD5868_CHANNEL(2, 2, bits, _shift), \
+ AD5868_CHANNEL(3, 3, bits, _shift), \
+ AD5868_CHANNEL(4, 4, bits, _shift), \
+ AD5868_CHANNEL(5, 5, bits, _shift), \
+ AD5868_CHANNEL(6, 6, bits, _shift), \
+ AD5868_CHANNEL(7, 7, bits, _shift), \
+}
+
+#define DECLARE_AD5679_CHANNELS(name, bits, _shift) \
+static const struct iio_chan_spec name[] = { \
+ AD5868_CHANNEL(0, 0, bits, _shift), \
+ AD5868_CHANNEL(1, 1, bits, _shift), \
+ AD5868_CHANNEL(2, 2, bits, _shift), \
+ AD5868_CHANNEL(3, 3, bits, _shift), \
+ AD5868_CHANNEL(4, 4, bits, _shift), \
+ AD5868_CHANNEL(5, 5, bits, _shift), \
+ AD5868_CHANNEL(6, 6, bits, _shift), \
+ AD5868_CHANNEL(7, 7, bits, _shift), \
+ AD5868_CHANNEL(8, 8, bits, _shift), \
+ AD5868_CHANNEL(9, 9, bits, _shift), \
+ AD5868_CHANNEL(10, 10, bits, _shift), \
+ AD5868_CHANNEL(11, 11, bits, _shift), \
+ AD5868_CHANNEL(12, 12, bits, _shift), \
+ AD5868_CHANNEL(13, 13, bits, _shift), \
+ AD5868_CHANNEL(14, 14, bits, _shift), \
+ AD5868_CHANNEL(15, 15, bits, _shift), \
+}
+
+DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2);
+DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
+DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
+DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4);
+DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
+DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0);
+DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4);
+DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2);
+DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0);
+DECLARE_AD5693_CHANNELS(ad5693_channels, 16, 0);
+DECLARE_AD5693_CHANNELS(ad5692r_channels, 14, 2);
+DECLARE_AD5693_CHANNELS(ad5691r_channels, 12, 4);
+
+static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
+ [ID_AD5310R] = {
+ .channels = ad5310r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5310_REGMAP,
+ },
+ [ID_AD5311R] = {
+ .channels = ad5311r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5693_REGMAP,
+ },
+ [ID_AD5671R] = {
+ .channels = ad5672_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 8,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5672R] = {
+ .channels = ad5672_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 8,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5674R] = {
+ .channels = ad5674r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 16,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5675R] = {
+ .channels = ad5676_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 8,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5676] = {
+ .channels = ad5676_channels,
+ .num_channels = 8,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5676R] = {
+ .channels = ad5676_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 8,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5679R] = {
+ .channels = ad5679r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 16,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5681R] = {
+ .channels = ad5691r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5683_REGMAP,
+ },
+ [ID_AD5682R] = {
+ .channels = ad5692r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5683_REGMAP,
+ },
+ [ID_AD5683] = {
+ .channels = ad5693_channels,
+ .num_channels = 1,
+ .regmap_type = AD5683_REGMAP,
+ },
+ [ID_AD5683R] = {
+ .channels = ad5693_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5683_REGMAP,
+ },
+ [ID_AD5684] = {
+ .channels = ad5684_channels,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5684R] = {
+ .channels = ad5684_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5685R] = {
+ .channels = ad5685r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5686] = {
+ .channels = ad5686_channels,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5686R] = {
+ .channels = ad5686_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5691R] = {
+ .channels = ad5691r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5693_REGMAP,
+ },
+ [ID_AD5692R] = {
+ .channels = ad5692r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5693_REGMAP,
+ },
+ [ID_AD5693] = {
+ .channels = ad5693_channels,
+ .num_channels = 1,
+ .regmap_type = AD5693_REGMAP,
+ },
+ [ID_AD5693R] = {
+ .channels = ad5693_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5693_REGMAP,
+ },
+ [ID_AD5694] = {
+ .channels = ad5684_channels,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5694R] = {
+ .channels = ad5684_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5696] = {
+ .channels = ad5686_channels,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+ [ID_AD5696R] = {
+ .channels = ad5686_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 4,
+ .regmap_type = AD5686_REGMAP,
+ },
+};
+
+int ad5686_probe(struct device *dev,
+ enum ad5686_supported_device_ids chip_type,
+ const char *name, ad5686_write_func write,
+ ad5686_read_func read)
+{
+ struct ad5686_state *st;
+ struct iio_dev *indio_dev;
+ unsigned int val, ref_bit_msk;
+ u8 cmd;
+ int ret, i, voltage_uv = 0;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->dev = dev;
+ st->write = write;
+ st->read = read;
+
+ st->reg = devm_regulator_get_optional(dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_disable_reg;
+
+ voltage_uv = ret;
+ }
+
+ st->chip_info = &ad5686_chip_info_tbl[chip_type];
+
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ st->vref_mv = st->chip_info->int_vref_mv;
+
+ /* Set all the power down mode for all channels to 1K pulldown */
+ for (i = 0; i < st->chip_info->num_channels; i++)
+ st->pwr_down_mode |= (0x01 << (i * 2));
+
+ indio_dev->name = name;
+ indio_dev->info = &ad5686_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ mutex_init(&st->lock);
+
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ cmd = AD5686_CMD_CONTROL_REG;
+ ref_bit_msk = AD5310_REF_BIT_MSK;
+ st->use_internal_vref = !voltage_uv;
+ break;
+ case AD5683_REGMAP:
+ cmd = AD5686_CMD_CONTROL_REG;
+ ref_bit_msk = AD5683_REF_BIT_MSK;
+ st->use_internal_vref = !voltage_uv;
+ break;
+ case AD5686_REGMAP:
+ cmd = AD5686_CMD_INTERNAL_REFER_SETUP;
+ ref_bit_msk = 0;
+ break;
+ case AD5693_REGMAP:
+ cmd = AD5686_CMD_CONTROL_REG;
+ ref_bit_msk = AD5693_REF_BIT_MSK;
+ st->use_internal_vref = !voltage_uv;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_disable_reg;
+ }
+
+ val = (voltage_uv | ref_bit_msk);
+
+ ret = st->write(st, cmd, 0, !!val);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad5686_probe);
+
+int ad5686_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad5686_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad5686_remove);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
new file mode 100644
index 000000000..a15f29705
--- /dev/null
+++ b/drivers/iio/dac/ad5686.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of AD5686 DAC driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#ifndef __DRIVERS_IIO_DAC_AD5686_H__
+#define __DRIVERS_IIO_DAC_AD5686_H__
+
+#include <linux/types.h>
+#include <linux/cache.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+
+#define AD5310_CMD(x) ((x) << 12)
+
+#define AD5683_DATA(x) ((x) << 4)
+
+#define AD5686_ADDR(x) ((x) << 16)
+#define AD5686_CMD(x) ((x) << 20)
+
+#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
+#define AD5686_ADDR_ALL_DAC 0xF
+
+#define AD5686_CMD_NOOP 0x0
+#define AD5686_CMD_WRITE_INPUT_N 0x1
+#define AD5686_CMD_UPDATE_DAC_N 0x2
+#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define AD5686_CMD_POWERDOWN_DAC 0x4
+#define AD5686_CMD_LDAC_MASK 0x5
+#define AD5686_CMD_RESET 0x6
+#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
+#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
+#define AD5686_CMD_READBACK_ENABLE 0x9
+
+#define AD5686_LDAC_PWRDN_NONE 0x0
+#define AD5686_LDAC_PWRDN_1K 0x1
+#define AD5686_LDAC_PWRDN_100K 0x2
+#define AD5686_LDAC_PWRDN_3STATE 0x3
+
+#define AD5686_CMD_CONTROL_REG 0x4
+#define AD5686_CMD_READBACK_ENABLE_V2 0x5
+
+#define AD5310_REF_BIT_MSK BIT(8)
+#define AD5683_REF_BIT_MSK BIT(12)
+#define AD5693_REF_BIT_MSK BIT(12)
+
+/**
+ * ad5686_supported_device_ids:
+ */
+enum ad5686_supported_device_ids {
+ ID_AD5310R,
+ ID_AD5311R,
+ ID_AD5671R,
+ ID_AD5672R,
+ ID_AD5674R,
+ ID_AD5675R,
+ ID_AD5676,
+ ID_AD5676R,
+ ID_AD5679R,
+ ID_AD5681R,
+ ID_AD5682R,
+ ID_AD5683,
+ ID_AD5683R,
+ ID_AD5684,
+ ID_AD5684R,
+ ID_AD5685R,
+ ID_AD5686,
+ ID_AD5686R,
+ ID_AD5691R,
+ ID_AD5692R,
+ ID_AD5693,
+ ID_AD5693R,
+ ID_AD5694,
+ ID_AD5694R,
+ ID_AD5695R,
+ ID_AD5696,
+ ID_AD5696R,
+};
+
+enum ad5686_regmap_type {
+ AD5310_REGMAP,
+ AD5683_REGMAP,
+ AD5686_REGMAP,
+ AD5693_REGMAP
+};
+
+struct ad5686_state;
+
+typedef int (*ad5686_write_func)(struct ad5686_state *st,
+ u8 cmd, u8 addr, u16 val);
+
+typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr);
+
+/**
+ * struct ad5686_chip_info - chip specific information
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ * @num_channels: number of channels
+ * @channel: channel specification
+ * @regmap_type: register map layout variant
+ */
+
+struct ad5686_chip_info {
+ u16 int_vref_mv;
+ unsigned int num_channels;
+ const struct iio_chan_spec *channels;
+ enum ad5686_regmap_type regmap_type;
+};
+
+/**
+ * struct ad5446_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ * @pwr_down_mask: power down mask
+ * @pwr_down_mode: current power down mode
+ * @use_internal_vref: set to true if the internal reference voltage is used
+ * @lock lock to protect the data buffer during regmap ops
+ * @data: spi transfer buffers
+ */
+
+struct ad5686_state {
+ struct device *dev;
+ const struct ad5686_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned int pwr_down_mask;
+ unsigned int pwr_down_mode;
+ ad5686_write_func write;
+ ad5686_read_func read;
+ bool use_internal_vref;
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ union {
+ __be32 d32;
+ __be16 d16;
+ u8 d8[4];
+ } data[3] ____cacheline_aligned;
+};
+
+
+int ad5686_probe(struct device *dev,
+ enum ad5686_supported_device_ids chip_type,
+ const char *name, ad5686_write_func write,
+ ad5686_read_func read);
+
+int ad5686_remove(struct device *dev);
+
+
+#endif /* __DRIVERS_IIO_DAC_AD5686_H__ */
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
new file mode 100644
index 000000000..ccf794cae
--- /dev/null
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R,
+ * AD5694, AD5694R, AD5695R, AD5696, AD5696R
+ * Digital to analog converters driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#include "ad5686.h"
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+static int ad5686_i2c_read(struct ad5686_state *st, u8 addr)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+ struct i2c_msg msg[2] = {
+ {
+ .addr = i2c->addr,
+ .flags = i2c->flags,
+ .len = 3,
+ .buf = &st->data[0].d8[1],
+ },
+ {
+ .addr = i2c->addr,
+ .flags = i2c->flags | I2C_M_RD,
+ .len = 2,
+ .buf = (char *)&st->data[0].d16,
+ },
+ };
+ int ret;
+
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) |
+ AD5686_ADDR(addr) |
+ 0x00);
+
+ ret = i2c_transfer(i2c->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->data[0].d16);
+}
+
+static int ad5686_i2c_write(struct ad5686_state *st,
+ u8 cmd, u8 addr, u16 val)
+{
+ struct i2c_client *i2c = to_i2c_client(st->dev);
+ int ret;
+
+ st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr)
+ | val);
+
+ ret = i2c_master_send(i2c, &st->data[0].d8[1], 3);
+ if (ret < 0)
+ return ret;
+
+ return (ret != 3) ? -EIO : 0;
+}
+
+static int ad5686_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5686_probe(&i2c->dev, id->driver_data, id->name,
+ ad5686_i2c_write, ad5686_i2c_read);
+}
+
+static int ad5686_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5686_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5686_i2c_id[] = {
+ {"ad5311r", ID_AD5311R},
+ {"ad5671r", ID_AD5671R},
+ {"ad5675r", ID_AD5675R},
+ {"ad5691r", ID_AD5691R},
+ {"ad5692r", ID_AD5692R},
+ {"ad5693", ID_AD5693},
+ {"ad5693r", ID_AD5693R},
+ {"ad5694", ID_AD5694},
+ {"ad5694r", ID_AD5694R},
+ {"ad5695r", ID_AD5695R},
+ {"ad5696", ID_AD5696},
+ {"ad5696r", ID_AD5696R},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id);
+
+static struct i2c_driver ad5686_i2c_driver = {
+ .driver = {
+ .name = "ad5696",
+ },
+ .probe = ad5686_i2c_probe,
+ .remove = ad5686_i2c_remove,
+ .id_table = ad5686_i2c_id,
+};
+
+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");
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
new file mode 100644
index 000000000..0df28acf0
--- /dev/null
+++ b/drivers/iio/dac/ad5755.c
@@ -0,0 +1,806 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/platform_data/ad5755.h>
+
+#define AD5755_NUM_CHANNELS 4
+
+#define AD5755_ADDR(x) ((x) << 16)
+
+#define AD5755_WRITE_REG_DATA(chan) (chan)
+#define AD5755_WRITE_REG_GAIN(chan) (0x08 | (chan))
+#define AD5755_WRITE_REG_OFFSET(chan) (0x10 | (chan))
+#define AD5755_WRITE_REG_CTRL(chan) (0x1c | (chan))
+
+#define AD5755_READ_REG_DATA(chan) (chan)
+#define AD5755_READ_REG_CTRL(chan) (0x4 | (chan))
+#define AD5755_READ_REG_GAIN(chan) (0x8 | (chan))
+#define AD5755_READ_REG_OFFSET(chan) (0xc | (chan))
+#define AD5755_READ_REG_CLEAR(chan) (0x10 | (chan))
+#define AD5755_READ_REG_SLEW(chan) (0x14 | (chan))
+#define AD5755_READ_REG_STATUS 0x18
+#define AD5755_READ_REG_MAIN 0x19
+#define AD5755_READ_REG_DC_DC 0x1a
+
+#define AD5755_CTRL_REG_SLEW 0x0
+#define AD5755_CTRL_REG_MAIN 0x1
+#define AD5755_CTRL_REG_DAC 0x2
+#define AD5755_CTRL_REG_DC_DC 0x3
+#define AD5755_CTRL_REG_SW 0x4
+
+#define AD5755_READ_FLAG 0x800000
+
+#define AD5755_NOOP 0x1CE000
+
+#define AD5755_DAC_INT_EN BIT(8)
+#define AD5755_DAC_CLR_EN BIT(7)
+#define AD5755_DAC_OUT_EN BIT(6)
+#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR BIT(5)
+#define AD5755_DAC_DC_DC_EN BIT(4)
+#define AD5755_DAC_VOLTAGE_OVERRANGE_EN BIT(3)
+
+#define AD5755_DC_DC_MAXV 0
+#define AD5755_DC_DC_FREQ_SHIFT 2
+#define AD5755_DC_DC_PHASE_SHIFT 4
+#define AD5755_EXT_DC_DC_COMP_RES BIT(6)
+
+#define AD5755_SLEW_STEP_SIZE_SHIFT 0
+#define AD5755_SLEW_RATE_SHIFT 3
+#define AD5755_SLEW_ENABLE BIT(12)
+
+/**
+ * struct ad5755_chip_info - chip specific information
+ * @channel_template: channel specification
+ * @calib_shift: shift for the calibration data registers
+ * @has_voltage_out: whether the chip has voltage outputs
+ */
+struct ad5755_chip_info {
+ const struct iio_chan_spec channel_template;
+ unsigned int calib_shift;
+ bool has_voltage_out;
+};
+
+/**
+ * struct ad5755_state - driver instance specific data
+ * @spi: spi device the driver is attached to
+ * @chip_info: chip model specific constants, available modes etc
+ * @pwr_down: bitmask which contains hether a channel is powered down or not
+ * @ctrl: software shadow of the channel ctrl registers
+ * @channels: iio channel spec for the device
+ * @lock: lock to protect the data buffer during SPI ops
+ * @data: spi transfer buffers
+ */
+struct ad5755_state {
+ struct spi_device *spi;
+ const struct ad5755_chip_info *chip_info;
+ unsigned int pwr_down;
+ unsigned int ctrl[AD5755_NUM_CHANNELS];
+ struct iio_chan_spec channels[AD5755_NUM_CHANNELS];
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5755_type {
+ ID_AD5755,
+ ID_AD5757,
+ ID_AD5735,
+ ID_AD5737,
+};
+
+#ifdef CONFIG_OF
+static const int ad5755_dcdc_freq_table[][2] = {
+ { 250000, AD5755_DC_DC_FREQ_250kHZ },
+ { 410000, AD5755_DC_DC_FREQ_410kHZ },
+ { 650000, AD5755_DC_DC_FREQ_650kHZ }
+};
+
+static const int ad5755_dcdc_maxv_table[][2] = {
+ { 23000000, AD5755_DC_DC_MAXV_23V },
+ { 24500000, AD5755_DC_DC_MAXV_24V5 },
+ { 27000000, AD5755_DC_DC_MAXV_27V },
+ { 29500000, AD5755_DC_DC_MAXV_29V5 },
+};
+
+static const int ad5755_slew_rate_table[][2] = {
+ { 64000, AD5755_SLEW_RATE_64k },
+ { 32000, AD5755_SLEW_RATE_32k },
+ { 16000, AD5755_SLEW_RATE_16k },
+ { 8000, AD5755_SLEW_RATE_8k },
+ { 4000, AD5755_SLEW_RATE_4k },
+ { 2000, AD5755_SLEW_RATE_2k },
+ { 1000, AD5755_SLEW_RATE_1k },
+ { 500, AD5755_SLEW_RATE_500 },
+ { 250, AD5755_SLEW_RATE_250 },
+ { 125, AD5755_SLEW_RATE_125 },
+ { 64, AD5755_SLEW_RATE_64 },
+ { 32, AD5755_SLEW_RATE_32 },
+ { 16, AD5755_SLEW_RATE_16 },
+ { 8, AD5755_SLEW_RATE_8 },
+ { 4, AD5755_SLEW_RATE_4 },
+ { 0, AD5755_SLEW_RATE_0_5 },
+};
+
+static const int ad5755_slew_step_table[][2] = {
+ { 256, AD5755_SLEW_STEP_SIZE_256 },
+ { 128, AD5755_SLEW_STEP_SIZE_128 },
+ { 64, AD5755_SLEW_STEP_SIZE_64 },
+ { 32, AD5755_SLEW_STEP_SIZE_32 },
+ { 16, AD5755_SLEW_STEP_SIZE_16 },
+ { 4, AD5755_SLEW_STEP_SIZE_4 },
+ { 2, AD5755_SLEW_STEP_SIZE_2 },
+ { 1, AD5755_SLEW_STEP_SIZE_1 },
+};
+#endif
+
+static int ad5755_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int val)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev,
+ unsigned int channel, unsigned int reg, unsigned int val)
+{
+ return ad5755_write_unlocked(indio_dev,
+ AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val);
+}
+
+static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad5755_write_unlocked(indio_dev, reg, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
+ unsigned int reg, unsigned int val)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&st->lock);
+
+ st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16));
+ st->data[1].d32 = cpu_to_be32(AD5755_NOOP);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev,
+ unsigned int channel, unsigned int set, unsigned int clr)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->ctrl[channel] |= set;
+ st->ctrl[channel] &= ~clr;
+
+ ret = ad5755_write_ctrl_unlocked(indio_dev, channel,
+ AD5755_CTRL_REG_DAC, st->ctrl[channel]);
+
+ return ret;
+}
+
+static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev,
+ unsigned int channel, bool pwr_down)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int mask = BIT(channel);
+
+ mutex_lock(&st->lock);
+
+ if ((bool)(st->pwr_down & mask) == pwr_down)
+ goto out_unlock;
+
+ if (!pwr_down) {
+ st->pwr_down &= ~mask;
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0);
+ udelay(200);
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ AD5755_DAC_OUT_EN, 0);
+ } else {
+ st->pwr_down |= mask;
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ 0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN |
+ AD5755_DAC_DC_DC_EN);
+ }
+
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ return 0;
+}
+
+static const int ad5755_min_max_table[][2] = {
+ [AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 },
+ [AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 },
+ [AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 },
+ [AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 },
+ [AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 },
+ [AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 },
+ [AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 },
+};
+
+static void ad5755_get_min_max(struct ad5755_state *st,
+ struct iio_chan_spec const *chan, int *min, int *max)
+{
+ enum ad5755_mode mode = st->ctrl[chan->channel] & 7;
+ *min = ad5755_min_max_table[mode][0];
+ *max = ad5755_min_max_table[mode][1];
+}
+
+static inline int ad5755_get_offset(struct ad5755_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int min, max;
+
+ ad5755_get_min_max(st, chan, &min, &max);
+ return (min * (1 << chan->scan_type.realbits)) / (max - min);
+}
+
+static int ad5755_chan_reg_info(struct ad5755_state *st,
+ struct iio_chan_spec const *chan, long info, bool write,
+ unsigned int *reg, unsigned int *shift, unsigned int *offset)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (write)
+ *reg = AD5755_WRITE_REG_DATA(chan->address);
+ else
+ *reg = AD5755_READ_REG_DATA(chan->address);
+ *shift = chan->scan_type.shift;
+ *offset = 0;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (write)
+ *reg = AD5755_WRITE_REG_OFFSET(chan->address);
+ else
+ *reg = AD5755_READ_REG_OFFSET(chan->address);
+ *shift = st->chip_info->calib_shift;
+ *offset = 32768;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (write)
+ *reg = AD5755_WRITE_REG_GAIN(chan->address);
+ else
+ *reg = AD5755_READ_REG_GAIN(chan->address);
+ *shift = st->chip_info->calib_shift;
+ *offset = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad5755_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int reg, shift, offset;
+ int min, max;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ ad5755_get_min_max(st, chan, &min, &max);
+ *val = max - min;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = ad5755_get_offset(st, chan);
+ return IIO_VAL_INT;
+ default:
+ ret = ad5755_chan_reg_info(st, chan, info, false,
+ &reg, &shift, &offset);
+ if (ret)
+ return ret;
+
+ ret = ad5755_read(indio_dev, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = (ret - offset) >> shift;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5755_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int shift, reg, offset;
+ int ret;
+
+ ret = ad5755_chan_reg_info(st, chan, info, true,
+ &reg, &shift, &offset);
+ if (ret)
+ return ret;
+
+ val <<= shift;
+ val += offset;
+
+ if (val < 0 || val > 0xffff)
+ return -EINVAL;
+
+ return ad5755_write(indio_dev, reg, val);
+}
+
+static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ (bool)(st->pwr_down & (1 << chan->channel)));
+}
+
+static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ struct iio_chan_spec const *chan, const char *buf, size_t len)
+{
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down);
+ return ret ? ret : len;
+}
+
+static const struct iio_info ad5755_info = {
+ .read_raw = ad5755_read_raw,
+ .write_raw = ad5755_write_raw,
+};
+
+static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5755_read_powerdown,
+ .write = ad5755_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { },
+};
+
+#define AD5755_CHANNEL(_bits) { \
+ .indexed = 1, \
+ .output = 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), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ }, \
+ .ext_info = ad5755_ext_info, \
+}
+
+static const struct ad5755_chip_info ad5755_chip_info_tbl[] = {
+ [ID_AD5735] = {
+ .channel_template = AD5755_CHANNEL(14),
+ .has_voltage_out = true,
+ .calib_shift = 4,
+ },
+ [ID_AD5737] = {
+ .channel_template = AD5755_CHANNEL(14),
+ .has_voltage_out = false,
+ .calib_shift = 4,
+ },
+ [ID_AD5755] = {
+ .channel_template = AD5755_CHANNEL(16),
+ .has_voltage_out = true,
+ .calib_shift = 0,
+ },
+ [ID_AD5757] = {
+ .channel_template = AD5755_CHANNEL(16),
+ .has_voltage_out = false,
+ .calib_shift = 0,
+ },
+};
+
+static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
+{
+ switch (mode) {
+ case AD5755_MODE_VOLTAGE_0V_5V:
+ case AD5755_MODE_VOLTAGE_0V_10V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+ return st->chip_info->has_voltage_out;
+ case AD5755_MODE_CURRENT_4mA_20mA:
+ case AD5755_MODE_CURRENT_0mA_20mA:
+ case AD5755_MODE_CURRENT_0mA_24mA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int ad5755_setup_pdata(struct iio_dev *indio_dev,
+ const struct ad5755_platform_data *pdata)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ unsigned int i;
+ int ret;
+
+ if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE ||
+ pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ ||
+ pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5)
+ return -EINVAL;
+
+ val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV;
+ val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT;
+ val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT;
+ if (pdata->ext_dc_dc_compenstation_resistor)
+ val |= AD5755_EXT_DC_DC_COMP_RES;
+
+ ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+ val = pdata->dac[i].slew.step_size <<
+ AD5755_SLEW_STEP_SIZE_SHIFT;
+ val |= pdata->dac[i].slew.rate <<
+ AD5755_SLEW_RATE_SHIFT;
+ if (pdata->dac[i].slew.enable)
+ val |= AD5755_SLEW_ENABLE;
+
+ ret = ad5755_write_ctrl(indio_dev, i,
+ AD5755_CTRL_REG_SLEW, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+ if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
+ return -EINVAL;
+
+ val = 0;
+ if (!pdata->dac[i].ext_current_sense_resistor)
+ val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
+ if (pdata->dac[i].enable_voltage_overrange)
+ val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
+ val |= pdata->dac[i].mode;
+
+ ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool ad5755_is_voltage_mode(enum ad5755_mode mode)
+{
+ switch (mode) {
+ case AD5755_MODE_VOLTAGE_0V_5V:
+ case AD5755_MODE_VOLTAGE_0V_10V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int ad5755_init_channels(struct iio_dev *indio_dev,
+ const struct ad5755_platform_data *pdata)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels = st->channels;
+ unsigned int i;
+
+ for (i = 0; i < AD5755_NUM_CHANNELS; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = i;
+ if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode))
+ channels[i].type = IIO_VOLTAGE;
+ else
+ channels[i].type = IIO_CURRENT;
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+#define AD5755_DEFAULT_DAC_PDATA { \
+ .mode = AD5755_MODE_CURRENT_4mA_20mA, \
+ .ext_current_sense_resistor = true, \
+ .enable_voltage_overrange = false, \
+ .slew = { \
+ .enable = false, \
+ .rate = AD5755_SLEW_RATE_64k, \
+ .step_size = AD5755_SLEW_STEP_SIZE_1, \
+ }, \
+ }
+
+static const struct ad5755_platform_data ad5755_default_pdata = {
+ .ext_dc_dc_compenstation_resistor = false,
+ .dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE,
+ .dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ,
+ .dc_dc_maxv = AD5755_DC_DC_MAXV_23V,
+ .dac = {
+ [0] = AD5755_DEFAULT_DAC_PDATA,
+ [1] = AD5755_DEFAULT_DAC_PDATA,
+ [2] = AD5755_DEFAULT_DAC_PDATA,
+ [3] = AD5755_DEFAULT_DAC_PDATA,
+ },
+};
+
+#ifdef CONFIG_OF
+static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *pp;
+ struct ad5755_platform_data *pdata;
+ unsigned int tmp;
+ unsigned int tmparray[3];
+ int devnr, i;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->ext_dc_dc_compenstation_resistor =
+ of_property_read_bool(np, "adi,ext-dc-dc-compenstation-resistor");
+
+ if (!of_property_read_u32(np, "adi,dc-dc-phase", &tmp))
+ pdata->dc_dc_phase = tmp;
+ else
+ pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE;
+
+ pdata->dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ;
+ if (!of_property_read_u32(np, "adi,dc-dc-freq-hz", &tmp)) {
+ for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_freq_table); i++) {
+ if (tmp == ad5755_dcdc_freq_table[i][0]) {
+ pdata->dc_dc_freq = ad5755_dcdc_freq_table[i][1];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(ad5755_dcdc_freq_table))
+ dev_err(dev,
+ "adi,dc-dc-freq out of range selecting 410kHz\n");
+ }
+
+ pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V;
+ if (!of_property_read_u32(np, "adi,dc-dc-max-microvolt", &tmp)) {
+ for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_maxv_table); i++) {
+ if (tmp == ad5755_dcdc_maxv_table[i][0]) {
+ pdata->dc_dc_maxv = ad5755_dcdc_maxv_table[i][1];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table))
+ dev_err(dev,
+ "adi,dc-dc-maxv out of range selecting 23V\n");
+ }
+
+ devnr = 0;
+ for_each_child_of_node(np, pp) {
+ if (devnr >= AD5755_NUM_CHANNELS) {
+ dev_err(dev,
+ "There are too many channels defined in DT\n");
+ goto error_out;
+ }
+
+ if (!of_property_read_u32(pp, "adi,mode", &tmp))
+ pdata->dac[devnr].mode = tmp;
+ else
+ pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA;
+
+ pdata->dac[devnr].ext_current_sense_resistor =
+ of_property_read_bool(pp, "adi,ext-current-sense-resistor");
+
+ pdata->dac[devnr].enable_voltage_overrange =
+ of_property_read_bool(pp, "adi,enable-voltage-overrange");
+
+ if (!of_property_read_u32_array(pp, "adi,slew", tmparray, 3)) {
+ pdata->dac[devnr].slew.enable = tmparray[0];
+
+ pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k;
+ for (i = 0; i < ARRAY_SIZE(ad5755_slew_rate_table); i++) {
+ if (tmparray[1] == ad5755_slew_rate_table[i][0]) {
+ pdata->dac[devnr].slew.rate =
+ ad5755_slew_rate_table[i][1];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(ad5755_slew_rate_table))
+ dev_err(dev,
+ "channel %d slew rate out of range selecting 64kHz\n",
+ devnr);
+
+ pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1;
+ for (i = 0; i < ARRAY_SIZE(ad5755_slew_step_table); i++) {
+ if (tmparray[2] == ad5755_slew_step_table[i][0]) {
+ pdata->dac[devnr].slew.step_size =
+ ad5755_slew_step_table[i][1];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(ad5755_slew_step_table))
+ dev_err(dev,
+ "channel %d slew step size out of range selecting 1 LSB\n",
+ devnr);
+ } else {
+ pdata->dac[devnr].slew.enable = false;
+ pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k;
+ pdata->dac[devnr].slew.step_size =
+ AD5755_SLEW_STEP_SIZE_1;
+ }
+ devnr++;
+ }
+
+ return pdata;
+
+ error_out:
+ devm_kfree(dev, pdata);
+ return NULL;
+}
+#else
+static
+struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static int ad5755_probe(struct spi_device *spi)
+{
+ enum ad5755_type type = spi_get_device_id(spi)->driver_data;
+ const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct iio_dev *indio_dev;
+ struct ad5755_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5755_chip_info_tbl[type];
+ st->spi = spi;
+ st->pwr_down = 0xf;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5755_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = AD5755_NUM_CHANNELS;
+
+ mutex_init(&st->lock);
+
+ if (spi->dev.of_node)
+ pdata = ad5755_parse_dt(&spi->dev);
+ else
+ pdata = spi->dev.platform_data;
+
+ if (!pdata) {
+ dev_warn(&spi->dev, "no platform data? using default\n");
+ pdata = &ad5755_default_pdata;
+ }
+
+ ret = ad5755_init_channels(indio_dev, pdata);
+ if (ret)
+ return ret;
+
+ ret = ad5755_setup_pdata(indio_dev, pdata);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad5755_id[] = {
+ { "ad5755", ID_AD5755 },
+ { "ad5755-1", ID_AD5755 },
+ { "ad5757", ID_AD5757 },
+ { "ad5735", ID_AD5735 },
+ { "ad5737", ID_AD5737 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5755_id);
+
+static const struct of_device_id ad5755_of_match[] = {
+ { .compatible = "adi,ad5755" },
+ { .compatible = "adi,ad5755-1" },
+ { .compatible = "adi,ad5757" },
+ { .compatible = "adi,ad5735" },
+ { .compatible = "adi,ad5737" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad5755_of_match);
+
+static struct spi_driver ad5755_driver = {
+ .driver = {
+ .name = "ad5755",
+ },
+ .probe = ad5755_probe,
+ .id_table = ad5755_id,
+};
+module_spi_driver(ad5755_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c
new file mode 100644
index 000000000..bd9ac8359
--- /dev/null
+++ b/drivers/iio/dac/ad5758.c
@@ -0,0 +1,905 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD5758 Digital to analog converters driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ *
+ * TODO: Currently CRC is not supported in this driver
+ */
+#include <linux/bsearch.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* AD5758 registers definition */
+#define AD5758_NOP 0x00
+#define AD5758_DAC_INPUT 0x01
+#define AD5758_DAC_OUTPUT 0x02
+#define AD5758_CLEAR_CODE 0x03
+#define AD5758_USER_GAIN 0x04
+#define AD5758_USER_OFFSET 0x05
+#define AD5758_DAC_CONFIG 0x06
+#define AD5758_SW_LDAC 0x07
+#define AD5758_KEY 0x08
+#define AD5758_GP_CONFIG1 0x09
+#define AD5758_GP_CONFIG2 0x0A
+#define AD5758_DCDC_CONFIG1 0x0B
+#define AD5758_DCDC_CONFIG2 0x0C
+#define AD5758_WDT_CONFIG 0x0F
+#define AD5758_DIGITAL_DIAG_CONFIG 0x10
+#define AD5758_ADC_CONFIG 0x11
+#define AD5758_FAULT_PIN_CONFIG 0x12
+#define AD5758_TWO_STAGE_READBACK_SELECT 0x13
+#define AD5758_DIGITAL_DIAG_RESULTS 0x14
+#define AD5758_ANALOG_DIAG_RESULTS 0x15
+#define AD5758_STATUS 0x16
+#define AD5758_CHIP_ID 0x17
+#define AD5758_FREQ_MONITOR 0x18
+#define AD5758_DEVICE_ID_0 0x19
+#define AD5758_DEVICE_ID_1 0x1A
+#define AD5758_DEVICE_ID_2 0x1B
+#define AD5758_DEVICE_ID_3 0x1C
+
+/* AD5758_DAC_CONFIG */
+#define AD5758_DAC_CONFIG_RANGE_MSK GENMASK(3, 0)
+#define AD5758_DAC_CONFIG_RANGE_MODE(x) (((x) & 0xF) << 0)
+#define AD5758_DAC_CONFIG_INT_EN_MSK BIT(5)
+#define AD5758_DAC_CONFIG_INT_EN_MODE(x) (((x) & 0x1) << 5)
+#define AD5758_DAC_CONFIG_OUT_EN_MSK BIT(6)
+#define AD5758_DAC_CONFIG_OUT_EN_MODE(x) (((x) & 0x1) << 6)
+#define AD5758_DAC_CONFIG_SR_EN_MSK BIT(8)
+#define AD5758_DAC_CONFIG_SR_EN_MODE(x) (((x) & 0x1) << 8)
+#define AD5758_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9)
+#define AD5758_DAC_CONFIG_SR_CLOCK_MODE(x) (((x) & 0xF) << 9)
+#define AD5758_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13)
+#define AD5758_DAC_CONFIG_SR_STEP_MODE(x) (((x) & 0x7) << 13)
+
+/* AD5758_KEY */
+#define AD5758_KEY_CODE_RESET_1 0x15FA
+#define AD5758_KEY_CODE_RESET_2 0xAF51
+#define AD5758_KEY_CODE_SINGLE_ADC_CONV 0x1ADC
+#define AD5758_KEY_CODE_RESET_WDT 0x0D06
+#define AD5758_KEY_CODE_CALIB_MEM_REFRESH 0xFCBA
+
+/* AD5758_DCDC_CONFIG1 */
+#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MSK GENMASK(4, 0)
+#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0)
+#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5)
+#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5)
+
+/* AD5758_DCDC_CONFIG2 */
+#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1)
+#define AD5758_DCDC_CONFIG2_ILIMIT_MODE(x) (((x) & 0x7) << 1)
+#define AD5758_DCDC_CONFIG2_INTR_SAT_3WI_MSK BIT(11)
+#define AD5758_DCDC_CONFIG2_BUSY_3WI_MSK BIT(12)
+
+/* AD5758_DIGITAL_DIAG_RESULTS */
+#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15)
+
+/* AD5758_ADC_CONFIG */
+#define AD5758_ADC_CONFIG_PPC_BUF_EN(x) (((x) & 0x1) << 11)
+#define AD5758_ADC_CONFIG_PPC_BUF_MSK BIT(11)
+
+#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F))
+
+#define AD5758_FULL_SCALE_MICRO 65535000000ULL
+
+struct ad5758_range {
+ int reg;
+ int min;
+ int max;
+};
+
+/**
+ * struct ad5758_state - driver instance specific data
+ * @spi: spi_device
+ * @lock: mutex lock
+ * @gpio_reset: gpio descriptor for the reset line
+ * @out_range: struct which stores the output range
+ * @dc_dc_mode: variable which stores the mode of operation
+ * @dc_dc_ilim: variable which stores the dc-to-dc converter current limit
+ * @slew_time: variable which stores the target slew time
+ * @pwr_down: variable which contains whether a channel is powered down or not
+ * @d32: spi transfer buffers
+ */
+struct ad5758_state {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct gpio_desc *gpio_reset;
+ struct ad5758_range out_range;
+ unsigned int dc_dc_mode;
+ unsigned int dc_dc_ilim;
+ unsigned int slew_time;
+ bool pwr_down;
+ __be32 d32[3];
+};
+
+/*
+ * Output ranges corresponding to bits [3:0] from DAC_CONFIG register
+ * 0000: 0 V to 5 V voltage range
+ * 0001: 0 V to 10 V voltage range
+ * 0010: ±5 V voltage range
+ * 0011: ±10 V voltage range
+ * 1000: 0 mA to 20 mA current range
+ * 1001: 0 mA to 24 mA current range
+ * 1010: 4 mA to 20 mA current range
+ * 1011: ±20 mA current range
+ * 1100: ±24 mA current range
+ * 1101: -1 mA to +22 mA current range
+ */
+enum ad5758_output_range {
+ AD5758_RANGE_0V_5V,
+ AD5758_RANGE_0V_10V,
+ AD5758_RANGE_PLUSMINUS_5V,
+ AD5758_RANGE_PLUSMINUS_10V,
+ AD5758_RANGE_0mA_20mA = 8,
+ AD5758_RANGE_0mA_24mA,
+ AD5758_RANGE_4mA_24mA,
+ AD5758_RANGE_PLUSMINUS_20mA,
+ AD5758_RANGE_PLUSMINUS_24mA,
+ AD5758_RANGE_MINUS_1mA_PLUS_22mA,
+};
+
+enum ad5758_dc_dc_mode {
+ AD5758_DCDC_MODE_POWER_OFF,
+ AD5758_DCDC_MODE_DPC_CURRENT,
+ AD5758_DCDC_MODE_DPC_VOLTAGE,
+ AD5758_DCDC_MODE_PPC_CURRENT,
+};
+
+static const struct ad5758_range ad5758_voltage_range[] = {
+ { AD5758_RANGE_0V_5V, 0, 5000000 },
+ { AD5758_RANGE_0V_10V, 0, 10000000 },
+ { AD5758_RANGE_PLUSMINUS_5V, -5000000, 5000000 },
+ { AD5758_RANGE_PLUSMINUS_10V, -10000000, 10000000 }
+};
+
+static const struct ad5758_range ad5758_current_range[] = {
+ { AD5758_RANGE_0mA_20mA, 0, 20000},
+ { AD5758_RANGE_0mA_24mA, 0, 24000 },
+ { AD5758_RANGE_4mA_24mA, 4, 24000 },
+ { AD5758_RANGE_PLUSMINUS_20mA, -20000, 20000 },
+ { AD5758_RANGE_PLUSMINUS_24mA, -24000, 24000 },
+ { AD5758_RANGE_MINUS_1mA_PLUS_22mA, -1000, 22000 },
+};
+
+static const int ad5758_sr_clk[16] = {
+ 240000, 200000, 150000, 128000, 64000, 32000, 16000, 8000, 4000, 2000,
+ 1000, 512, 256, 128, 64, 16
+};
+
+static const int ad5758_sr_step[8] = {
+ 4, 12, 64, 120, 256, 500, 1820, 2048
+};
+
+static const int ad5758_dc_dc_ilim[6] = {
+ 150000, 200000, 250000, 300000, 350000, 400000
+};
+
+static int ad5758_spi_reg_read(struct ad5758_state *st, unsigned int addr)
+{
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d32[0],
+ .len = 4,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->d32[1],
+ .rx_buf = &st->d32[2],
+ .len = 4,
+ },
+ };
+ int ret;
+
+ st->d32[0] = cpu_to_be32(
+ (AD5758_WR_FLAG_MSK(AD5758_TWO_STAGE_READBACK_SELECT) << 24) |
+ (addr << 8));
+ st->d32[1] = cpu_to_be32(AD5758_WR_FLAG_MSK(AD5758_NOP) << 24);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return (be32_to_cpu(st->d32[2]) >> 8) & 0xFFFF;
+}
+
+static int ad5758_spi_reg_write(struct ad5758_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ st->d32[0] = cpu_to_be32((AD5758_WR_FLAG_MSK(addr) << 24) |
+ ((val & 0xFFFF) << 8));
+
+ return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
+}
+
+static int ad5758_spi_write_mask(struct ad5758_state *st,
+ unsigned int addr,
+ unsigned long int mask,
+ unsigned int val)
+{
+ int regval;
+
+ regval = ad5758_spi_reg_read(st, addr);
+ if (regval < 0)
+ return regval;
+
+ regval &= ~mask;
+ regval |= val;
+
+ return ad5758_spi_reg_write(st, addr, regval);
+}
+
+static int cmpfunc(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static int ad5758_find_closest_match(const int *array,
+ unsigned int size, int val)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= array[i])
+ return i;
+ }
+
+ return size - 1;
+}
+
+static int ad5758_wait_for_task_complete(struct ad5758_state *st,
+ unsigned int reg,
+ unsigned int mask)
+{
+ unsigned int timeout;
+ int ret;
+
+ timeout = 10;
+ do {
+ ret = ad5758_spi_reg_read(st, reg);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & mask))
+ return 0;
+
+ usleep_range(100, 1000);
+ } while (--timeout);
+
+ dev_err(&st->spi->dev,
+ "Error reading bit 0x%x in 0x%x register\n", mask, reg);
+
+ return -EIO;
+}
+
+static int ad5758_calib_mem_refresh(struct ad5758_state *st)
+{
+ int ret;
+
+ ret = ad5758_spi_reg_write(st, AD5758_KEY,
+ AD5758_KEY_CODE_CALIB_MEM_REFRESH);
+ if (ret < 0) {
+ dev_err(&st->spi->dev,
+ "Failed to initiate a calibration memory refresh\n");
+ return ret;
+ }
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
+ AD5758_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5758_soft_reset(struct ad5758_state *st)
+{
+ int ret;
+
+ ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_1);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_2);
+
+ /* Perform a software reset and wait at least 100us */
+ usleep_range(100, 1000);
+
+ return ret;
+}
+
+static int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st,
+ enum ad5758_dc_dc_mode mode)
+{
+ int ret;
+
+ /*
+ * The ENABLE_PPC_BUFFERS bit must be set prior to enabling PPC current
+ * mode.
+ */
+ if (mode == AD5758_DCDC_MODE_PPC_CURRENT) {
+ ret = ad5758_spi_write_mask(st, AD5758_ADC_CONFIG,
+ AD5758_ADC_CONFIG_PPC_BUF_MSK,
+ AD5758_ADC_CONFIG_PPC_BUF_EN(1));
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
+ AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
+ AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
+ * This allows the 3-wire interface communication to complete.
+ */
+ ret = ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
+ AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
+ if (ret < 0)
+ return ret;
+
+ st->dc_dc_mode = mode;
+
+ return ret;
+}
+
+static int ad5758_set_dc_dc_ilim(struct ad5758_state *st, unsigned int ilim)
+{
+ int ret;
+
+ ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG2,
+ AD5758_DCDC_CONFIG2_ILIMIT_MSK,
+ AD5758_DCDC_CONFIG2_ILIMIT_MODE(ilim));
+ if (ret < 0)
+ return ret;
+ /*
+ * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
+ * This allows the 3-wire interface communication to complete.
+ */
+ return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
+ AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
+}
+
+static int ad5758_slew_rate_set(struct ad5758_state *st,
+ unsigned int sr_clk_idx,
+ unsigned int sr_step_idx)
+{
+ unsigned int mode;
+ unsigned long int mask;
+ int ret;
+
+ mask = AD5758_DAC_CONFIG_SR_EN_MSK |
+ AD5758_DAC_CONFIG_SR_CLOCK_MSK |
+ AD5758_DAC_CONFIG_SR_STEP_MSK;
+ mode = AD5758_DAC_CONFIG_SR_EN_MODE(1) |
+ AD5758_DAC_CONFIG_SR_STEP_MODE(sr_step_idx) |
+ AD5758_DAC_CONFIG_SR_CLOCK_MODE(sr_clk_idx);
+
+ ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, mask, mode);
+ if (ret < 0)
+ return ret;
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
+ AD5758_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5758_slew_rate_config(struct ad5758_state *st)
+{
+ unsigned int sr_clk_idx, sr_step_idx;
+ int i, res;
+ s64 diff_new, diff_old;
+ u64 sr_step, calc_slew_time;
+
+ sr_clk_idx = 0;
+ sr_step_idx = 0;
+ diff_old = S64_MAX;
+ /*
+ * The slew time can be determined by using the formula:
+ * Slew Time = (Full Scale Out / (Step Size x Update Clk Freq))
+ * where Slew time is expressed in microseconds
+ * Given the desired slew time, the following algorithm determines the
+ * best match for the step size and the update clock frequency.
+ */
+ for (i = 0; i < ARRAY_SIZE(ad5758_sr_clk); i++) {
+ /*
+ * Go through each valid update clock freq and determine a raw
+ * value for the step size by using the formula:
+ * Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
+ */
+ sr_step = AD5758_FULL_SCALE_MICRO;
+ do_div(sr_step, ad5758_sr_clk[i]);
+ do_div(sr_step, st->slew_time);
+ /*
+ * After a raw value for step size was determined, find the
+ * closest valid match
+ */
+ res = ad5758_find_closest_match(ad5758_sr_step,
+ ARRAY_SIZE(ad5758_sr_step),
+ sr_step);
+ /* Calculate the slew time */
+ calc_slew_time = AD5758_FULL_SCALE_MICRO;
+ do_div(calc_slew_time, ad5758_sr_step[res]);
+ do_div(calc_slew_time, ad5758_sr_clk[i]);
+ /*
+ * Determine with how many microseconds the calculated slew time
+ * is different from the desired slew time and store the diff
+ * for the next iteration
+ */
+ diff_new = abs(st->slew_time - calc_slew_time);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ sr_clk_idx = i;
+ sr_step_idx = res;
+ }
+ }
+
+ return ad5758_slew_rate_set(st, sr_clk_idx, sr_step_idx);
+}
+
+static int ad5758_set_out_range(struct ad5758_state *st, int range)
+{
+ int ret;
+
+ ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
+ AD5758_DAC_CONFIG_RANGE_MSK,
+ AD5758_DAC_CONFIG_RANGE_MODE(range));
+ if (ret < 0)
+ return ret;
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
+ AD5758_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
+{
+ int ret;
+
+ ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
+ AD5758_DAC_CONFIG_INT_EN_MSK,
+ AD5758_DAC_CONFIG_INT_EN_MODE(enable));
+ if (ret < 0)
+ return ret;
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
+ AD5758_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5758_reset(struct ad5758_state *st)
+{
+ if (st->gpio_reset) {
+ gpiod_set_value(st->gpio_reset, 0);
+ usleep_range(100, 1000);
+ gpiod_set_value(st->gpio_reset, 1);
+ usleep_range(100, 1000);
+
+ return 0;
+ } else {
+ /* Perform a software reset */
+ return ad5758_soft_reset(st);
+ }
+}
+
+static int ad5758_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad5758_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = ad5758_spi_reg_read(st, reg);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = ad5758_spi_reg_write(st, reg, writeval);
+ }
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5758_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad5758_state *st = iio_priv(indio_dev);
+ int max, min, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ ret = ad5758_spi_reg_read(st, AD5758_DAC_INPUT);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ min = st->out_range.min;
+ max = st->out_range.max;
+ *val = (max - min) / 1000;
+ *val2 = 16;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ min = st->out_range.min;
+ max = st->out_range.max;
+ *val = ((min * (1 << 16)) / (max - min)) / 1000;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5758_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad5758_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ ret = ad5758_spi_reg_write(st, AD5758_DAC_INPUT, val);
+ mutex_unlock(&st->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ad5758_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t priv,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad5758_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t priv,
+ struct iio_chan_spec const *chan,
+ const char *buf, size_t len)
+{
+ struct ad5758_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ unsigned int dac_config_mode, val;
+ unsigned long int dac_config_msk;
+ int ret;
+
+ ret = kstrtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ if (pwr_down)
+ val = 0;
+ else
+ val = 1;
+
+ dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
+ AD5758_DAC_CONFIG_INT_EN_MODE(val);
+ dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK |
+ AD5758_DAC_CONFIG_INT_EN_MSK;
+
+ ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
+ dac_config_msk,
+ dac_config_mode);
+ if (ret < 0)
+ goto err_unlock;
+
+ st->pwr_down = pwr_down;
+
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_info ad5758_info = {
+ .read_raw = ad5758_read_raw,
+ .write_raw = ad5758_write_raw,
+ .debugfs_reg_access = &ad5758_reg_access,
+};
+
+static const struct iio_chan_spec_ext_info ad5758_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5758_read_powerdown,
+ .write = ad5758_write_powerdown,
+ .shared = IIO_SHARED_BY_TYPE,
+ },
+ { }
+};
+
+#define AD5758_DAC_CHAN(_chan_type) { \
+ .type = (_chan_type), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .indexed = 1, \
+ .output = 1, \
+ .ext_info = ad5758_ext_info, \
+}
+
+static const struct iio_chan_spec ad5758_voltage_ch[] = {
+ AD5758_DAC_CHAN(IIO_VOLTAGE)
+};
+
+static const struct iio_chan_spec ad5758_current_ch[] = {
+ AD5758_DAC_CHAN(IIO_CURRENT)
+};
+
+static bool ad5758_is_valid_mode(enum ad5758_dc_dc_mode mode)
+{
+ switch (mode) {
+ case AD5758_DCDC_MODE_DPC_CURRENT:
+ case AD5758_DCDC_MODE_DPC_VOLTAGE:
+ case AD5758_DCDC_MODE_PPC_CURRENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int ad5758_crc_disable(struct ad5758_state *st)
+{
+ unsigned int mask;
+
+ mask = (AD5758_WR_FLAG_MSK(AD5758_DIGITAL_DIAG_CONFIG) << 24) | 0x5C3A;
+ st->d32[0] = cpu_to_be32(mask);
+
+ return spi_write(st->spi, &st->d32[0], 4);
+}
+
+static int ad5758_find_out_range(struct ad5758_state *st,
+ const struct ad5758_range *range,
+ unsigned int size,
+ int min, int max)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if ((min == range[i].min) && (max == range[i].max)) {
+ st->out_range.reg = range[i].reg;
+ st->out_range.min = range[i].min;
+ st->out_range.max = range[i].max;
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ad5758_parse_dt(struct ad5758_state *st)
+{
+ unsigned int tmp, tmparray[2], size;
+ const struct ad5758_range *range;
+ int *index, ret;
+
+ st->dc_dc_ilim = 0;
+ ret = device_property_read_u32(&st->spi->dev,
+ "adi,dc-dc-ilim-microamp", &tmp);
+ if (ret) {
+ dev_dbg(&st->spi->dev,
+ "Missing \"dc-dc-ilim-microamp\" property\n");
+ } else {
+ index = bsearch(&tmp, ad5758_dc_dc_ilim,
+ ARRAY_SIZE(ad5758_dc_dc_ilim),
+ sizeof(int), cmpfunc);
+ if (!index)
+ dev_dbg(&st->spi->dev, "dc-dc-ilim out of range\n");
+ else
+ st->dc_dc_ilim = index - ad5758_dc_dc_ilim;
+ }
+
+ ret = device_property_read_u32(&st->spi->dev, "adi,dc-dc-mode",
+ &st->dc_dc_mode);
+ if (ret) {
+ dev_err(&st->spi->dev, "Missing \"dc-dc-mode\" property\n");
+ return ret;
+ }
+
+ if (!ad5758_is_valid_mode(st->dc_dc_mode))
+ return -EINVAL;
+
+ if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE) {
+ ret = device_property_read_u32_array(&st->spi->dev,
+ "adi,range-microvolt",
+ tmparray, 2);
+ if (ret) {
+ dev_err(&st->spi->dev,
+ "Missing \"range-microvolt\" property\n");
+ return ret;
+ }
+ range = ad5758_voltage_range;
+ size = ARRAY_SIZE(ad5758_voltage_range);
+ } else {
+ ret = device_property_read_u32_array(&st->spi->dev,
+ "adi,range-microamp",
+ tmparray, 2);
+ if (ret) {
+ dev_err(&st->spi->dev,
+ "Missing \"range-microamp\" property\n");
+ return ret;
+ }
+ range = ad5758_current_range;
+ size = ARRAY_SIZE(ad5758_current_range);
+ }
+
+ ret = ad5758_find_out_range(st, range, size, tmparray[0], tmparray[1]);
+ if (ret) {
+ dev_err(&st->spi->dev, "range invalid\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(&st->spi->dev, "adi,slew-time-us", &tmp);
+ if (ret) {
+ dev_dbg(&st->spi->dev, "Missing \"slew-time-us\" property\n");
+ st->slew_time = 0;
+ } else {
+ st->slew_time = tmp;
+ }
+
+ return 0;
+}
+
+static int ad5758_init(struct ad5758_state *st)
+{
+ int regval, ret;
+
+ st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
+
+ /* Disable CRC checks */
+ ret = ad5758_crc_disable(st);
+ if (ret < 0)
+ return ret;
+
+ /* Perform a reset */
+ ret = ad5758_reset(st);
+ if (ret < 0)
+ return ret;
+
+ /* Disable CRC checks */
+ ret = ad5758_crc_disable(st);
+ if (ret < 0)
+ return ret;
+
+ /* Perform a calibration memory refresh */
+ ret = ad5758_calib_mem_refresh(st);
+ if (ret < 0)
+ return ret;
+
+ regval = ad5758_spi_reg_read(st, AD5758_DIGITAL_DIAG_RESULTS);
+ if (regval < 0)
+ return regval;
+
+ /* Clear all the error flags */
+ ret = ad5758_spi_reg_write(st, AD5758_DIGITAL_DIAG_RESULTS, regval);
+ if (ret < 0)
+ return ret;
+
+ /* Set the dc-to-dc current limit */
+ ret = ad5758_set_dc_dc_ilim(st, st->dc_dc_ilim);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the dc-to-dc controller mode */
+ ret = ad5758_set_dc_dc_conv_mode(st, st->dc_dc_mode);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the output range */
+ ret = ad5758_set_out_range(st, st->out_range.reg);
+ if (ret < 0)
+ return ret;
+
+ /* Enable Slew Rate Control, set the slew rate clock and step */
+ if (st->slew_time) {
+ ret = ad5758_slew_rate_config(st);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Power up the DAC and internal (INT) amplifiers */
+ ret = ad5758_internal_buffers_en(st, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Enable VIOUT */
+ return ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
+ AD5758_DAC_CONFIG_OUT_EN_MSK,
+ AD5758_DAC_CONFIG_OUT_EN_MODE(1));
+}
+
+static int ad5758_probe(struct spi_device *spi)
+{
+ struct ad5758_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ mutex_init(&st->lock);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5758_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = 1;
+
+ ret = ad5758_parse_dt(st);
+ if (ret < 0)
+ return ret;
+
+ if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE)
+ indio_dev->channels = ad5758_voltage_ch;
+ else
+ indio_dev->channels = ad5758_current_ch;
+
+ ret = ad5758_init(st);
+ if (ret < 0) {
+ dev_err(&spi->dev, "AD5758 init failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&st->spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad5758_id[] = {
+ { "ad5758", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5758_id);
+
+static const struct of_device_id ad5758_of_match[] = {
+ { .compatible = "adi,ad5758" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad5758_of_match);
+
+static struct spi_driver ad5758_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ad5758_of_match,
+ },
+ .probe = ad5758_probe,
+ .id_table = ad5758_id,
+};
+
+module_spi_driver(ad5758_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5758 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c
new file mode 100644
index 000000000..e37e095e9
--- /dev/null
+++ b/drivers/iio/dac/ad5761.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
+ *
+ * Copyright 2016 Qtechnology A/S
+ * 2016 Ricardo Ribalda <ribalda@kernel.org>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_data/ad5761.h>
+
+#define AD5761_ADDR(addr) ((addr & 0xf) << 16)
+#define AD5761_ADDR_NOOP 0x0
+#define AD5761_ADDR_DAC_WRITE 0x3
+#define AD5761_ADDR_CTRL_WRITE_REG 0x4
+#define AD5761_ADDR_SW_DATA_RESET 0x7
+#define AD5761_ADDR_DAC_READ 0xb
+#define AD5761_ADDR_CTRL_READ_REG 0xc
+#define AD5761_ADDR_SW_FULL_RESET 0xf
+
+#define AD5761_CTRL_USE_INTVREF BIT(5)
+#define AD5761_CTRL_ETS BIT(6)
+
+/**
+ * struct ad5761_chip_info - chip specific information
+ * @int_vref: Value of the internal reference voltage in mV - 0 if external
+ * reference voltage is used
+ * @channel: channel specification
+*/
+
+struct ad5761_chip_info {
+ unsigned long int_vref;
+ const struct iio_chan_spec channel;
+};
+
+struct ad5761_range_params {
+ int m;
+ int c;
+};
+
+enum ad5761_supported_device_ids {
+ ID_AD5721,
+ ID_AD5721R,
+ ID_AD5761,
+ ID_AD5761R,
+};
+
+/**
+ * struct ad5761_state - driver instance specific data
+ * @spi: spi_device
+ * @vref_reg: reference voltage regulator
+ * @use_intref: true when the internal voltage reference is used
+ * @vref: actual voltage reference in mVolts
+ * @range: output range mode used
+ * @lock: lock to protect the data buffer during SPI ops
+ * @data: cache aligned spi buffer
+ */
+struct ad5761_state {
+ struct spi_device *spi;
+ struct regulator *vref_reg;
+ struct mutex lock;
+
+ bool use_intref;
+ int vref;
+ enum ad5761_voltage_range range;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[3] ____cacheline_aligned;
+};
+
+static const struct ad5761_range_params ad5761_range_params[] = {
+ [AD5761_VOLTAGE_RANGE_M10V_10V] = {
+ .m = 80,
+ .c = 40,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_10V] = {
+ .m = 40,
+ .c = 0,
+ },
+ [AD5761_VOLTAGE_RANGE_M5V_5V] = {
+ .m = 40,
+ .c = 20,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_5V] = {
+ .m = 20,
+ .c = 0,
+ },
+ [AD5761_VOLTAGE_RANGE_M2V5_7V5] = {
+ .m = 40,
+ .c = 10,
+ },
+ [AD5761_VOLTAGE_RANGE_M3V_3V] = {
+ .m = 24,
+ .c = 12,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_16V] = {
+ .m = 64,
+ .c = 0,
+ },
+ [AD5761_VOLTAGE_RANGE_0V_20V] = {
+ .m = 80,
+ .c = 0,
+ },
+};
+
+static int _ad5761_spi_write(struct ad5761_state *st, u8 addr, u16 val)
+{
+ st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val)
+{
+ struct ad5761_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = _ad5761_spi_write(st, addr, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val)
+{
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ .cs_change = true,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[2].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ },
+ };
+
+ st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr));
+ st->data[1].d32 = cpu_to_be32(AD5761_ADDR(AD5761_ADDR_NOOP));
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+
+ *val = be32_to_cpu(st->data[2].d32);
+
+ return ret;
+}
+
+static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val)
+{
+ struct ad5761_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = _ad5761_spi_read(st, addr, val);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5761_spi_set_range(struct ad5761_state *st,
+ enum ad5761_voltage_range range)
+{
+ u16 aux;
+ int ret;
+
+ aux = (range & 0x7) | AD5761_CTRL_ETS;
+
+ if (st->use_intref)
+ aux |= AD5761_CTRL_USE_INTVREF;
+
+ ret = _ad5761_spi_write(st, AD5761_ADDR_SW_FULL_RESET, 0);
+ if (ret)
+ return ret;
+
+ ret = _ad5761_spi_write(st, AD5761_ADDR_CTRL_WRITE_REG, aux);
+ if (ret)
+ return ret;
+
+ st->range = range;
+
+ return 0;
+}
+
+static int ad5761_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct ad5761_state *st;
+ int ret;
+ u16 aux;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5761_spi_read(indio_dev, AD5761_ADDR_DAC_READ, &aux);
+ if (ret)
+ return ret;
+ *val = aux >> chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ st = iio_priv(indio_dev);
+ *val = st->vref * ad5761_range_params[st->range].m;
+ *val /= 10;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ st = iio_priv(indio_dev);
+ *val = -(1 << chan->scan_type.realbits);
+ *val *= ad5761_range_params[st->range].c;
+ *val /= ad5761_range_params[st->range].m;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5761_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ u16 aux;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if (val2 || (val << chan->scan_type.shift) > 0xffff || val < 0)
+ return -EINVAL;
+
+ aux = val << chan->scan_type.shift;
+
+ return ad5761_spi_write(indio_dev, AD5761_ADDR_DAC_WRITE, aux);
+}
+
+static const struct iio_info ad5761_info = {
+ .read_raw = &ad5761_read_raw,
+ .write_raw = &ad5761_write_raw,
+};
+
+#define AD5761_CHAN(_bits) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ }, \
+}
+
+static const struct ad5761_chip_info ad5761_chip_infos[] = {
+ [ID_AD5721] = {
+ .int_vref = 0,
+ .channel = AD5761_CHAN(12),
+ },
+ [ID_AD5721R] = {
+ .int_vref = 2500,
+ .channel = AD5761_CHAN(12),
+ },
+ [ID_AD5761] = {
+ .int_vref = 0,
+ .channel = AD5761_CHAN(16),
+ },
+ [ID_AD5761R] = {
+ .int_vref = 2500,
+ .channel = AD5761_CHAN(16),
+ },
+};
+
+static int ad5761_get_vref(struct ad5761_state *st,
+ const struct ad5761_chip_info *chip_info)
+{
+ int ret;
+
+ st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref");
+ if (PTR_ERR(st->vref_reg) == -ENODEV) {
+ /* Use Internal regulator */
+ if (!chip_info->int_vref) {
+ dev_err(&st->spi->dev,
+ "Voltage reference not found\n");
+ return -EIO;
+ }
+
+ st->use_intref = true;
+ st->vref = chip_info->int_vref;
+ return 0;
+ }
+
+ if (IS_ERR(st->vref_reg)) {
+ dev_err(&st->spi->dev,
+ "Error getting voltage reference regulator\n");
+ return PTR_ERR(st->vref_reg);
+ }
+
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(&st->spi->dev,
+ "Failed to enable voltage reference\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(st->vref_reg);
+ if (ret < 0) {
+ dev_err(&st->spi->dev,
+ "Failed to get voltage reference value\n");
+ goto disable_regulator_vref;
+ }
+
+ if (ret < 2000000 || ret > 3000000) {
+ dev_warn(&st->spi->dev,
+ "Invalid external voltage ref. value %d uV\n", ret);
+ ret = -EIO;
+ goto disable_regulator_vref;
+ }
+
+ st->vref = ret / 1000;
+ st->use_intref = false;
+
+ return 0;
+
+disable_regulator_vref:
+ regulator_disable(st->vref_reg);
+ st->vref_reg = NULL;
+ return ret;
+}
+
+static int ad5761_probe(struct spi_device *spi)
+{
+ struct iio_dev *iio_dev;
+ struct ad5761_state *st;
+ int ret;
+ const struct ad5761_chip_info *chip_info =
+ &ad5761_chip_infos[spi_get_device_id(spi)->driver_data];
+ enum ad5761_voltage_range voltage_range = AD5761_VOLTAGE_RANGE_0V_5V;
+ struct ad5761_platform_data *pdata = dev_get_platdata(&spi->dev);
+
+ iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(iio_dev);
+
+ st->spi = spi;
+ spi_set_drvdata(spi, iio_dev);
+
+ ret = ad5761_get_vref(st, chip_info);
+ if (ret)
+ return ret;
+
+ if (pdata)
+ voltage_range = pdata->voltage_range;
+
+ mutex_init(&st->lock);
+
+ ret = ad5761_spi_set_range(st, voltage_range);
+ if (ret)
+ goto disable_regulator_err;
+
+ iio_dev->info = &ad5761_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = &chip_info->channel;
+ iio_dev->num_channels = 1;
+ iio_dev->name = spi_get_device_id(st->spi)->name;
+ ret = iio_device_register(iio_dev);
+ if (ret)
+ goto disable_regulator_err;
+
+ return 0;
+
+disable_regulator_err:
+ if (!IS_ERR_OR_NULL(st->vref_reg))
+ regulator_disable(st->vref_reg);
+
+ return ret;
+}
+
+static int ad5761_remove(struct spi_device *spi)
+{
+ struct iio_dev *iio_dev = spi_get_drvdata(spi);
+ struct ad5761_state *st = iio_priv(iio_dev);
+
+ iio_device_unregister(iio_dev);
+
+ if (!IS_ERR_OR_NULL(st->vref_reg))
+ regulator_disable(st->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5761_id[] = {
+ {"ad5721", ID_AD5721},
+ {"ad5721r", ID_AD5721R},
+ {"ad5761", ID_AD5761},
+ {"ad5761r", ID_AD5761R},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5761_id);
+
+static struct spi_driver ad5761_driver = {
+ .driver = {
+ .name = "ad5761",
+ },
+ .probe = ad5761_probe,
+ .remove = ad5761_remove,
+ .id_table = ad5761_id,
+};
+module_spi_driver(ad5761_driver);
+
+MODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
+MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c
new file mode 100644
index 000000000..ae089b914
--- /dev/null
+++ b/drivers/iio/dac/ad5764.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
+ * Digital to Analog Converters driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AD5764_REG_SF_NOP 0x0
+#define AD5764_REG_SF_CONFIG 0x1
+#define AD5764_REG_SF_CLEAR 0x4
+#define AD5764_REG_SF_LOAD 0x5
+#define AD5764_REG_DATA(x) ((2 << 3) | (x))
+#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x))
+#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x))
+#define AD5764_REG_OFFSET(x) ((5 << 3) | (x))
+
+#define AD5764_NUM_CHANNELS 4
+
+/**
+ * struct ad5764_chip_info - chip specific information
+ * @int_vref: Value of the internal reference voltage in uV - 0 if external
+ * reference voltage is used
+ * @channels: channel specification
+*/
+struct ad5764_chip_info {
+ unsigned long int_vref;
+ const struct iio_chan_spec *channels;
+};
+
+/**
+ * struct ad5764_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip info
+ * @vref_reg: vref supply regulators
+ * @lock: lock to protect the data buffer during SPI ops
+ * @data: spi transfer buffers
+ */
+
+struct ad5764_state {
+ struct spi_device *spi;
+ const struct ad5764_chip_info *chip_info;
+ struct regulator_bulk_data vref_reg[2];
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5764_type {
+ ID_AD5744,
+ ID_AD5744R,
+ ID_AD5764,
+ ID_AD5764R,
+};
+
+#define AD5764_CHANNEL(_chan, _bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .address = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ }, \
+}
+
+#define DECLARE_AD5764_CHANNELS(_name, _bits) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD5764_CHANNEL(0, (_bits)), \
+ AD5764_CHANNEL(1, (_bits)), \
+ AD5764_CHANNEL(2, (_bits)), \
+ AD5764_CHANNEL(3, (_bits)), \
+};
+
+static DECLARE_AD5764_CHANNELS(ad5764, 16);
+static DECLARE_AD5764_CHANNELS(ad5744, 14);
+
+static const struct ad5764_chip_info ad5764_chip_infos[] = {
+ [ID_AD5744] = {
+ .int_vref = 0,
+ .channels = ad5744_channels,
+ },
+ [ID_AD5744R] = {
+ .int_vref = 5000000,
+ .channels = ad5744_channels,
+ },
+ [ID_AD5764] = {
+ .int_vref = 0,
+ .channels = ad5764_channels,
+ },
+ [ID_AD5764R] = {
+ .int_vref = 5000000,
+ .channels = ad5764_channels,
+ },
+};
+
+static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ struct ad5764_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ ret = spi_write(st->spi, &st->data[0].d8[1], 3);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int *val)
+{
+ struct ad5764_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&st->lock);
+
+ st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret >= 0)
+ *val = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return AD5764_REG_DATA(chan->address);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return AD5764_REG_OFFSET(chan->address);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return AD5764_REG_FINE_GAIN(chan->address);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad5764_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ const int max_val = (1 << chan->scan_type.realbits);
+ unsigned int reg;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= max_val || val < 0)
+ return -EINVAL;
+ val <<= chan->scan_type.shift;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val >= 128 || val < -128)
+ return -EINVAL;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val >= 32 || val < -32)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg = ad5764_chan_info_to_reg(chan, info);
+ return ad5764_write(indio_dev, reg, (u16)val);
+}
+
+static int ad5764_get_channel_vref(struct ad5764_state *st,
+ unsigned int channel)
+{
+ if (st->chip_info->int_vref)
+ return st->chip_info->int_vref;
+ else
+ return regulator_get_voltage(st->vref_reg[channel / 2].consumer);
+}
+
+static int ad5764_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad5764_state *st = iio_priv(indio_dev);
+ unsigned int reg;
+ int vref;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ reg = AD5764_REG_DATA(chan->address);
+ ret = ad5764_read(indio_dev, reg, val);
+ if (ret < 0)
+ return ret;
+ *val >>= chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ reg = AD5764_REG_OFFSET(chan->address);
+ ret = ad5764_read(indio_dev, reg, val);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(*val, 7);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ reg = AD5764_REG_FINE_GAIN(chan->address);
+ ret = ad5764_read(indio_dev, reg, val);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(*val, 5);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* vout = 4 * vref + ((dac_code / 65536) - 0.5) */
+ vref = ad5764_get_channel_vref(st, chan->channel);
+ if (vref < 0)
+ return vref;
+
+ *val = vref * 4 / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -(1 << chan->scan_type.realbits) / 2;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad5764_info = {
+ .read_raw = ad5764_read_raw,
+ .write_raw = ad5764_write_raw,
+};
+
+static int ad5764_probe(struct spi_device *spi)
+{
+ enum ad5764_type type = spi_get_device_id(spi)->driver_data;
+ struct iio_dev *indio_dev;
+ struct ad5764_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+ st->chip_info = &ad5764_chip_infos[type];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5764_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = AD5764_NUM_CHANNELS;
+ indio_dev->channels = st->chip_info->channels;
+
+ mutex_init(&st->lock);
+
+ if (st->chip_info->int_vref == 0) {
+ st->vref_reg[0].supply = "vrefAB";
+ st->vref_reg[1].supply = "vrefCD";
+
+ ret = devm_regulator_bulk_get(&st->spi->dev,
+ ARRAY_SIZE(st->vref_reg), st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to request vref regulators: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg),
+ st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vref regulators: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ if (st->chip_info->int_vref == 0)
+ regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+ return ret;
+}
+
+static int ad5764_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5764_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (st->chip_info->int_vref == 0)
+ regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5764_ids[] = {
+ { "ad5744", ID_AD5744 },
+ { "ad5744r", ID_AD5744R },
+ { "ad5764", ID_AD5764 },
+ { "ad5764r", ID_AD5764R },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad5764_ids);
+
+static struct spi_driver ad5764_driver = {
+ .driver = {
+ .name = "ad5764",
+ },
+ .probe = ad5764_probe,
+ .remove = ad5764_remove,
+ .id_table = ad5764_ids,
+};
+module_spi_driver(ad5764_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c
new file mode 100644
index 000000000..56d8bd2dd
--- /dev/null
+++ b/drivers/iio/dac/ad5770r.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD5770R Digital to analog converters driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define ADI_SPI_IF_CONFIG_A 0x00
+#define ADI_SPI_IF_CONFIG_B 0x01
+#define ADI_SPI_IF_DEVICE_CONFIG 0x02
+#define ADI_SPI_IF_CHIP_TYPE 0x03
+#define ADI_SPI_IF_PRODUCT_ID_L 0x04
+#define ADI_SPI_IF_PRODUCT_ID_H 0x05
+#define ADI_SPI_IF_CHIP_GRADE 0x06
+#define ADI_SPI_IF_SCRACTH_PAD 0x0A
+#define ADI_SPI_IF_SPI_REVISION 0x0B
+#define ADI_SPI_IF_SPI_VENDOR_L 0x0C
+#define ADI_SPI_IF_SPI_VENDOR_H 0x0D
+#define ADI_SPI_IF_SPI_STREAM_MODE 0x0E
+#define ADI_SPI_IF_CONFIG_C 0x10
+#define ADI_SPI_IF_STATUS_A 0x11
+
+/* ADI_SPI_IF_CONFIG_A */
+#define ADI_SPI_IF_SW_RESET_MSK (BIT(0) | BIT(7))
+#define ADI_SPI_IF_SW_RESET_SEL(x) ((x) & ADI_SPI_IF_SW_RESET_MSK)
+#define ADI_SPI_IF_ADDR_ASC_MSK (BIT(2) | BIT(5))
+#define ADI_SPI_IF_ADDR_ASC_SEL(x) (((x) << 2) & ADI_SPI_IF_ADDR_ASC_MSK)
+
+/* ADI_SPI_IF_CONFIG_B */
+#define ADI_SPI_IF_SINGLE_INS_MSK BIT(7)
+#define ADI_SPI_IF_SINGLE_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x)
+#define ADI_SPI_IF_SHORT_INS_MSK BIT(7)
+#define ADI_SPI_IF_SHORT_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x)
+
+/* ADI_SPI_IF_CONFIG_C */
+#define ADI_SPI_IF_STRICT_REG_MSK BIT(5)
+#define ADI_SPI_IF_STRICT_REG_GET(x) FIELD_GET(ADI_SPI_IF_STRICT_REG_MSK, x)
+
+/* AD5770R configuration registers */
+#define AD5770R_CHANNEL_CONFIG 0x14
+#define AD5770R_OUTPUT_RANGE(ch) (0x15 + (ch))
+#define AD5770R_FILTER_RESISTOR(ch) (0x1D + (ch))
+#define AD5770R_REFERENCE 0x1B
+#define AD5770R_DAC_LSB(ch) (0x26 + 2 * (ch))
+#define AD5770R_DAC_MSB(ch) (0x27 + 2 * (ch))
+#define AD5770R_CH_SELECT 0x34
+#define AD5770R_CH_ENABLE 0x44
+
+/* AD5770R_CHANNEL_CONFIG */
+#define AD5770R_CFG_CH0_SINK_EN(x) (((x) & 0x1) << 7)
+#define AD5770R_CFG_SHUTDOWN_B(x, ch) (((x) & 0x1) << (ch))
+
+/* AD5770R_OUTPUT_RANGE */
+#define AD5770R_RANGE_OUTPUT_SCALING(x) (((x) & GENMASK(5, 0)) << 2)
+#define AD5770R_RANGE_MODE(x) ((x) & GENMASK(1, 0))
+
+/* AD5770R_REFERENCE */
+#define AD5770R_REF_RESISTOR_SEL(x) (((x) & 0x1) << 2)
+#define AD5770R_REF_SEL(x) ((x) & GENMASK(1, 0))
+
+/* AD5770R_CH_ENABLE */
+#define AD5770R_CH_SET(x, ch) (((x) & 0x1) << (ch))
+
+#define AD5770R_MAX_CHANNELS 6
+#define AD5770R_MAX_CH_MODES 14
+#define AD5770R_LOW_VREF_mV 1250
+#define AD5770R_HIGH_VREF_mV 2500
+
+enum ad5770r_ch0_modes {
+ AD5770R_CH0_0_300 = 0,
+ AD5770R_CH0_NEG_60_0,
+ AD5770R_CH0_NEG_60_300
+};
+
+enum ad5770r_ch1_modes {
+ AD5770R_CH1_0_140_LOW_HEAD = 1,
+ AD5770R_CH1_0_140_LOW_NOISE,
+ AD5770R_CH1_0_250
+};
+
+enum ad5770r_ch2_5_modes {
+ AD5770R_CH_LOW_RANGE = 0,
+ AD5770R_CH_HIGH_RANGE
+};
+
+enum ad5770r_ref_v {
+ AD5770R_EXT_2_5_V = 0,
+ AD5770R_INT_1_25_V_OUT_ON,
+ AD5770R_EXT_1_25_V,
+ AD5770R_INT_1_25_V_OUT_OFF
+};
+
+enum ad5770r_output_filter_resistor {
+ AD5770R_FILTER_60_OHM = 0x0,
+ AD5770R_FILTER_5_6_KOHM = 0x5,
+ AD5770R_FILTER_11_2_KOHM,
+ AD5770R_FILTER_22_2_KOHM,
+ AD5770R_FILTER_44_4_KOHM,
+ AD5770R_FILTER_104_KOHM,
+};
+
+struct ad5770r_out_range {
+ u8 out_scale;
+ u8 out_range_mode;
+};
+
+/**
+ * struct ad5770R_state - driver instance specific data
+ * @spi: spi_device
+ * @regmap: regmap
+ * @vref_reg: fixed regulator for reference configuration
+ * @gpio_reset: gpio descriptor
+ * @output_mode: array contains channels output ranges
+ * @vref: reference value
+ * @ch_pwr_down: powerdown flags
+ * @internal_ref: internal reference flag
+ * @external_res: external 2.5k resistor flag
+ * @transf_buf: cache aligned buffer for spi read/write
+ */
+struct ad5770r_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator *vref_reg;
+ struct gpio_desc *gpio_reset;
+ struct ad5770r_out_range output_mode[AD5770R_MAX_CHANNELS];
+ int vref;
+ bool ch_pwr_down[AD5770R_MAX_CHANNELS];
+ bool internal_ref;
+ bool external_res;
+ u8 transf_buf[2] ____cacheline_aligned;
+};
+
+static const struct regmap_config ad5770r_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = BIT(7),
+};
+
+struct ad5770r_output_modes {
+ unsigned int ch;
+ u8 mode;
+ int min;
+ int max;
+};
+
+static struct ad5770r_output_modes ad5770r_rng_tbl[] = {
+ { 0, AD5770R_CH0_0_300, 0, 300 },
+ { 0, AD5770R_CH0_NEG_60_0, -60, 0 },
+ { 0, AD5770R_CH0_NEG_60_300, -60, 300 },
+ { 1, AD5770R_CH1_0_140_LOW_HEAD, 0, 140 },
+ { 1, AD5770R_CH1_0_140_LOW_NOISE, 0, 140 },
+ { 1, AD5770R_CH1_0_250, 0, 250 },
+ { 2, AD5770R_CH_LOW_RANGE, 0, 55 },
+ { 2, AD5770R_CH_HIGH_RANGE, 0, 150 },
+ { 3, AD5770R_CH_LOW_RANGE, 0, 45 },
+ { 3, AD5770R_CH_HIGH_RANGE, 0, 100 },
+ { 4, AD5770R_CH_LOW_RANGE, 0, 45 },
+ { 4, AD5770R_CH_HIGH_RANGE, 0, 100 },
+ { 5, AD5770R_CH_LOW_RANGE, 0, 45 },
+ { 5, AD5770R_CH_HIGH_RANGE, 0, 100 },
+};
+
+static const unsigned int ad5770r_filter_freqs[] = {
+ 153, 357, 715, 1400, 2800, 262000,
+};
+
+static const unsigned int ad5770r_filter_reg_vals[] = {
+ AD5770R_FILTER_104_KOHM,
+ AD5770R_FILTER_44_4_KOHM,
+ AD5770R_FILTER_22_2_KOHM,
+ AD5770R_FILTER_11_2_KOHM,
+ AD5770R_FILTER_5_6_KOHM,
+ AD5770R_FILTER_60_OHM
+};
+
+static int ad5770r_set_output_mode(struct ad5770r_state *st,
+ const struct ad5770r_out_range *out_mode,
+ int channel)
+{
+ unsigned int regval;
+
+ regval = AD5770R_RANGE_OUTPUT_SCALING(out_mode->out_scale) |
+ AD5770R_RANGE_MODE(out_mode->out_range_mode);
+
+ return regmap_write(st->regmap,
+ AD5770R_OUTPUT_RANGE(channel), regval);
+}
+
+static int ad5770r_set_reference(struct ad5770r_state *st)
+{
+ unsigned int regval;
+
+ regval = AD5770R_REF_RESISTOR_SEL(st->external_res);
+
+ if (st->internal_ref) {
+ regval |= AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF);
+ } else {
+ switch (st->vref) {
+ case AD5770R_LOW_VREF_mV:
+ regval |= AD5770R_REF_SEL(AD5770R_EXT_1_25_V);
+ break;
+ case AD5770R_HIGH_VREF_mV:
+ regval |= AD5770R_REF_SEL(AD5770R_EXT_2_5_V);
+ break;
+ default:
+ regval = AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF);
+ break;
+ }
+ }
+
+ return regmap_write(st->regmap, AD5770R_REFERENCE, regval);
+}
+
+static int ad5770r_soft_reset(struct ad5770r_state *st)
+{
+ return regmap_write(st->regmap, ADI_SPI_IF_CONFIG_A,
+ ADI_SPI_IF_SW_RESET_SEL(1));
+}
+
+static int ad5770r_reset(struct ad5770r_state *st)
+{
+ /* Perform software reset if no GPIO provided */
+ if (!st->gpio_reset)
+ return ad5770r_soft_reset(st);
+
+ gpiod_set_value_cansleep(st->gpio_reset, 0);
+ usleep_range(10, 20);
+ gpiod_set_value_cansleep(st->gpio_reset, 1);
+
+ /* data must not be written during reset timeframe */
+ usleep_range(100, 200);
+
+ return 0;
+}
+
+static int ad5770r_get_range(struct ad5770r_state *st,
+ int ch, int *min, int *max)
+{
+ int i;
+ u8 tbl_ch, tbl_mode, out_range;
+
+ out_range = st->output_mode[ch].out_range_mode;
+
+ for (i = 0; i < AD5770R_MAX_CH_MODES; i++) {
+ tbl_ch = ad5770r_rng_tbl[i].ch;
+ tbl_mode = ad5770r_rng_tbl[i].mode;
+ if (tbl_ch == ch && tbl_mode == out_range) {
+ *min = ad5770r_rng_tbl[i].min;
+ *max = ad5770r_rng_tbl[i].max;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ad5770r_get_filter_freq(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *freq)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned int regval, i;
+
+ ret = regmap_read(st->regmap,
+ AD5770R_FILTER_RESISTOR(chan->channel), &regval);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ad5770r_filter_reg_vals); i++)
+ if (regval == ad5770r_filter_reg_vals[i])
+ break;
+ if (i == ARRAY_SIZE(ad5770r_filter_reg_vals))
+ return -EINVAL;
+
+ *freq = ad5770r_filter_freqs[i];
+
+ return IIO_VAL_INT;
+}
+
+static int ad5770r_set_filter_freq(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int freq)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+ unsigned int regval, i;
+
+ for (i = 0; i < ARRAY_SIZE(ad5770r_filter_freqs); i++)
+ if (ad5770r_filter_freqs[i] >= freq)
+ break;
+ if (i == ARRAY_SIZE(ad5770r_filter_freqs))
+ return -EINVAL;
+
+ regval = ad5770r_filter_reg_vals[i];
+
+ return regmap_write(st->regmap, AD5770R_FILTER_RESISTOR(chan->channel),
+ regval);
+}
+
+static int ad5770r_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+ int max, min, ret;
+ u16 buf16;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_bulk_read(st->regmap,
+ chan->address,
+ st->transf_buf, 2);
+ if (ret)
+ return 0;
+
+ buf16 = st->transf_buf[0] + (st->transf_buf[1] << 8);
+ *val = buf16 >> 2;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = ad5770r_get_range(st, chan->channel, &min, &max);
+ if (ret < 0)
+ return ret;
+ *val = max - min;
+ /* There is no sign bit. (negative current is mapped from 0)
+ * (sourced/sinked) current = raw * scale + offset
+ * where offset in case of CH0 can be negative.
+ */
+ *val2 = 14;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return ad5770r_get_filter_freq(indio_dev, chan, val);
+ case IIO_CHAN_INFO_OFFSET:
+ ret = ad5770r_get_range(st, chan->channel, &min, &max);
+ if (ret < 0)
+ return ret;
+ *val = min;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5770r_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ st->transf_buf[0] = ((u16)val >> 6);
+ st->transf_buf[1] = (val & GENMASK(5, 0)) << 2;
+ return regmap_bulk_write(st->regmap, chan->address,
+ st->transf_buf, 2);
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return ad5770r_set_filter_freq(indio_dev, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5770r_read_freq_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_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *type = IIO_VAL_INT;
+ *vals = ad5770r_filter_freqs;
+ *length = ARRAY_SIZE(ad5770r_filter_freqs);
+ return IIO_AVAIL_LIST;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5770r_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static const struct iio_info ad5770r_info = {
+ .read_raw = ad5770r_read_raw,
+ .write_raw = ad5770r_write_raw,
+ .read_avail = ad5770r_read_freq_avail,
+ .debugfs_reg_access = &ad5770r_reg_access,
+};
+
+static int ad5770r_store_output_range(struct ad5770r_state *st,
+ int min, int max, int index)
+{
+ int i;
+
+ for (i = 0; i < AD5770R_MAX_CH_MODES; i++) {
+ if (ad5770r_rng_tbl[i].ch != index)
+ continue;
+ if (ad5770r_rng_tbl[i].min != min ||
+ ad5770r_rng_tbl[i].max != max)
+ continue;
+ st->output_mode[index].out_range_mode = ad5770r_rng_tbl[i].mode;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t ad5770r_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->ch_pwr_down[chan->channel]);
+}
+
+static ssize_t ad5770r_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad5770r_state *st = iio_priv(indio_dev);
+ unsigned int regval;
+ unsigned int mask;
+ bool readin;
+ int ret;
+
+ ret = kstrtobool(buf, &readin);
+ if (ret)
+ return ret;
+
+ readin = !readin;
+
+ regval = AD5770R_CFG_SHUTDOWN_B(readin, chan->channel);
+ if (chan->channel == 0 &&
+ st->output_mode[0].out_range_mode > AD5770R_CH0_0_300) {
+ regval |= AD5770R_CFG_CH0_SINK_EN(readin);
+ mask = BIT(chan->channel) + BIT(7);
+ } else {
+ mask = BIT(chan->channel);
+ }
+ ret = regmap_update_bits(st->regmap, AD5770R_CHANNEL_CONFIG, mask,
+ regval);
+ if (ret)
+ return ret;
+
+ regval = AD5770R_CH_SET(readin, chan->channel);
+ ret = regmap_update_bits(st->regmap, AD5770R_CH_ENABLE,
+ BIT(chan->channel), regval);
+ if (ret)
+ return ret;
+
+ st->ch_pwr_down[chan->channel] = !readin;
+
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info ad5770r_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5770r_read_dac_powerdown,
+ .write = ad5770r_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { }
+};
+
+#define AD5770R_IDAC_CHANNEL(index, reg) { \
+ .type = IIO_CURRENT, \
+ .address = reg, \
+ .indexed = 1, \
+ .channel = index, \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .ext_info = ad5770r_ext_info, \
+}
+
+static const struct iio_chan_spec ad5770r_channels[] = {
+ AD5770R_IDAC_CHANNEL(0, AD5770R_DAC_MSB(0)),
+ AD5770R_IDAC_CHANNEL(1, AD5770R_DAC_MSB(1)),
+ AD5770R_IDAC_CHANNEL(2, AD5770R_DAC_MSB(2)),
+ AD5770R_IDAC_CHANNEL(3, AD5770R_DAC_MSB(3)),
+ AD5770R_IDAC_CHANNEL(4, AD5770R_DAC_MSB(4)),
+ AD5770R_IDAC_CHANNEL(5, AD5770R_DAC_MSB(5)),
+};
+
+static int ad5770r_channel_config(struct ad5770r_state *st)
+{
+ int ret, tmp[2], min, max;
+ unsigned int num;
+ struct fwnode_handle *child;
+
+ num = device_get_child_node_count(&st->spi->dev);
+ if (num != AD5770R_MAX_CHANNELS)
+ return -EINVAL;
+
+ device_for_each_child_node(&st->spi->dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &num);
+ if (ret)
+ goto err_child_out;
+ if (num >= AD5770R_MAX_CHANNELS) {
+ ret = -EINVAL;
+ goto err_child_out;
+ }
+
+ ret = fwnode_property_read_u32_array(child,
+ "adi,range-microamp",
+ tmp, 2);
+ if (ret)
+ goto err_child_out;
+
+ min = tmp[0] / 1000;
+ max = tmp[1] / 1000;
+ ret = ad5770r_store_output_range(st, min, max, num);
+ if (ret)
+ goto err_child_out;
+ }
+
+ return 0;
+
+err_child_out:
+ fwnode_handle_put(child);
+ return ret;
+}
+
+static int ad5770r_init(struct ad5770r_state *st)
+{
+ int ret, i;
+
+ st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
+
+ /* Perform a reset */
+ ret = ad5770r_reset(st);
+ if (ret)
+ return ret;
+
+ /* Set output range */
+ ret = ad5770r_channel_config(st);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AD5770R_MAX_CHANNELS; i++) {
+ ret = ad5770r_set_output_mode(st, &st->output_mode[i], i);
+ if (ret)
+ return ret;
+ }
+
+ st->external_res = fwnode_property_read_bool(st->spi->dev.fwnode,
+ "adi,external-resistor");
+
+ ret = ad5770r_set_reference(st);
+ if (ret)
+ return ret;
+
+ /* Set outputs off */
+ ret = regmap_write(st->regmap, AD5770R_CHANNEL_CONFIG, 0x00);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD5770R_CH_ENABLE, 0x00);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AD5770R_MAX_CHANNELS; i++)
+ st->ch_pwr_down[i] = true;
+
+ return ret;
+}
+
+static void ad5770r_disable_regulator(void *data)
+{
+ struct ad5770r_state *st = data;
+
+ regulator_disable(st->vref_reg);
+}
+
+static int ad5770r_probe(struct spi_device *spi)
+{
+ struct ad5770r_state *st;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ regmap = devm_regmap_init_spi(spi, &ad5770r_spi_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+ st->regmap = regmap;
+
+ st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->vref_reg)) {
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev,
+ "Failed to enable vref regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev,
+ ad5770r_disable_regulator,
+ st);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_get_voltage(st->vref_reg);
+ if (ret < 0)
+ return ret;
+
+ st->vref = ret / 1000;
+ } else {
+ if (PTR_ERR(st->vref_reg) == -ENODEV) {
+ st->vref = AD5770R_LOW_VREF_mV;
+ st->internal_ref = true;
+ } else {
+ return PTR_ERR(st->vref_reg);
+ }
+ }
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5770r_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad5770r_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad5770r_channels);
+
+ ret = ad5770r_init(st);
+ if (ret < 0) {
+ dev_err(&spi->dev, "AD5770R init failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&st->spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad5770r_of_id[] = {
+ { .compatible = "adi,ad5770r", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ad5770r_of_id);
+
+static const struct spi_device_id ad5770r_id[] = {
+ { "ad5770r", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, ad5770r_id);
+
+static struct spi_driver ad5770r_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ad5770r_of_id,
+ },
+ .probe = ad5770r_probe,
+ .id_table = ad5770r_id,
+};
+
+module_spi_driver(ad5770r_driver);
+
+MODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5770R IDAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c
new file mode 100644
index 000000000..e3ffa4b9f
--- /dev/null
+++ b/drivers/iio/dac/ad5791.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog
+ * Converter
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/dac/ad5791.h>
+
+#define AD5791_DAC_MASK GENMASK(19, 0)
+
+#define AD5791_CMD_READ BIT(23)
+#define AD5791_CMD_WRITE 0
+#define AD5791_ADDR(addr) ((addr) << 20)
+
+/* Registers */
+#define AD5791_ADDR_NOOP 0
+#define AD5791_ADDR_DAC0 1
+#define AD5791_ADDR_CTRL 2
+#define AD5791_ADDR_CLRCODE 3
+#define AD5791_ADDR_SW_CTRL 4
+
+/* Control Register */
+#define AD5791_CTRL_RBUF BIT(1)
+#define AD5791_CTRL_OPGND BIT(2)
+#define AD5791_CTRL_DACTRI BIT(3)
+#define AD5791_CTRL_BIN2SC BIT(4)
+#define AD5791_CTRL_SDODIS BIT(5)
+#define AD5761_CTRL_LINCOMP(x) ((x) << 6)
+
+#define AD5791_LINCOMP_0_10 0
+#define AD5791_LINCOMP_10_12 1
+#define AD5791_LINCOMP_12_16 2
+#define AD5791_LINCOMP_16_19 3
+#define AD5791_LINCOMP_19_20 12
+
+#define AD5780_LINCOMP_0_10 0
+#define AD5780_LINCOMP_10_20 12
+
+/* Software Control Register */
+#define AD5791_SWCTRL_LDAC BIT(0)
+#define AD5791_SWCTRL_CLR BIT(1)
+#define AD5791_SWCTRL_RESET BIT(2)
+
+#define AD5791_DAC_PWRDN_6K 0
+#define AD5791_DAC_PWRDN_3STATE 1
+
+/**
+ * struct ad5791_chip_info - chip specific information
+ * @get_lin_comp: function pointer to the device specific function
+ */
+
+struct ad5791_chip_info {
+ int (*get_lin_comp) (unsigned int span);
+};
+
+/**
+ * struct ad5791_state - driver instance specific data
+ * @spi: spi_device
+ * @reg_vdd: positive supply regulator
+ * @reg_vss: negative supply regulator
+ * @chip_info: chip model specific constants
+ * @vref_mv: actual reference voltage used
+ * @vref_neg_mv: voltage of the negative supply
+ * @ctrl: control regster cache
+ * @pwr_down_mode: current power down mode
+ * @pwr_down: true if device is powered down
+ * @data: spi transfer buffers
+ */
+struct ad5791_state {
+ struct spi_device *spi;
+ struct regulator *reg_vdd;
+ struct regulator *reg_vss;
+ const struct ad5791_chip_info *chip_info;
+ unsigned short vref_mv;
+ unsigned int vref_neg_mv;
+ unsigned ctrl;
+ unsigned pwr_down_mode;
+ bool pwr_down;
+
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[3] ____cacheline_aligned;
+};
+
+enum ad5791_supported_device_ids {
+ ID_AD5760,
+ ID_AD5780,
+ ID_AD5781,
+ ID_AD5791,
+};
+
+static int ad5791_spi_write(struct ad5791_state *st, u8 addr, u32 val)
+{
+ st->data[0].d32 = cpu_to_be32(AD5791_CMD_WRITE |
+ AD5791_ADDR(addr) |
+ (val & AD5791_DAC_MASK));
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5791_spi_read(struct ad5791_state *st, u8 addr, u32 *val)
+{
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[2].d8[1],
+ .bits_per_word = 8,
+ .len = 3,
+ },
+ };
+
+ st->data[0].d32 = cpu_to_be32(AD5791_CMD_READ |
+ AD5791_ADDR(addr));
+ st->data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP));
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+
+ *val = be32_to_cpu(st->data[2].d32);
+
+ return ret;
+}
+
+static const char * const ad5791_powerdown_modes[] = {
+ "6kohm_to_gnd",
+ "three_state",
+};
+
+static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ return st->pwr_down_mode;
+}
+
+static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int mode)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ st->pwr_down_mode = mode;
+
+ return 0;
+}
+
+static const struct iio_enum ad5791_powerdown_mode_enum = {
+ .items = ad5791_powerdown_modes,
+ .num_items = ARRAY_SIZE(ad5791_powerdown_modes),
+ .get = ad5791_get_powerdown_mode,
+ .set = ad5791_set_powerdown_mode,
+};
+
+static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (!pwr_down) {
+ st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
+ } else {
+ if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K)
+ st->ctrl |= AD5791_CTRL_OPGND;
+ else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE)
+ st->ctrl |= AD5791_CTRL_DACTRI;
+ }
+ st->pwr_down = pwr_down;
+
+ ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl);
+
+ return ret ? ret : len;
+}
+
+static int ad5791_get_lin_comp(unsigned int span)
+{
+ if (span <= 10000)
+ return AD5791_LINCOMP_0_10;
+ else if (span <= 12000)
+ return AD5791_LINCOMP_10_12;
+ else if (span <= 16000)
+ return AD5791_LINCOMP_12_16;
+ else if (span <= 19000)
+ return AD5791_LINCOMP_16_19;
+ else
+ return AD5791_LINCOMP_19_20;
+}
+
+static int ad5780_get_lin_comp(unsigned int span)
+{
+ if (span <= 10000)
+ return AD5780_LINCOMP_0_10;
+ else
+ return AD5780_LINCOMP_10_20;
+}
+static const struct ad5791_chip_info ad5791_chip_info_tbl[] = {
+ [ID_AD5760] = {
+ .get_lin_comp = ad5780_get_lin_comp,
+ },
+ [ID_AD5780] = {
+ .get_lin_comp = ad5780_get_lin_comp,
+ },
+ [ID_AD5781] = {
+ .get_lin_comp = ad5791_get_lin_comp,
+ },
+ [ID_AD5791] = {
+ .get_lin_comp = ad5791_get_lin_comp,
+ },
+};
+
+static int ad5791_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+ u64 val64;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad5791_spi_read(st, chan->address, val);
+ if (ret)
+ return ret;
+ *val &= AD5791_DAC_MASK;
+ *val >>= chan->scan_type.shift;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = (1 << chan->scan_type.realbits) - 1;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_OFFSET:
+ val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits);
+ do_div(val64, st->vref_mv);
+ *val = -val64;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+};
+
+static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {
+ {
+ .name = "powerdown",
+ .shared = IIO_SHARED_BY_TYPE,
+ .read = ad5791_read_dac_powerdown,
+ .write = ad5791_write_dac_powerdown,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
+ &ad5791_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum),
+ { },
+};
+
+#define AD5791_CHAN(bits, _shift) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .address = AD5791_ADDR_DAC0, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 24, \
+ .shift = (_shift), \
+ }, \
+ .ext_info = ad5791_ext_info, \
+}
+
+static const struct iio_chan_spec ad5791_channels[] = {
+ [ID_AD5760] = AD5791_CHAN(16, 4),
+ [ID_AD5780] = AD5791_CHAN(18, 2),
+ [ID_AD5781] = AD5791_CHAN(18, 2),
+ [ID_AD5791] = AD5791_CHAN(20, 0)
+};
+
+static int ad5791_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ val &= GENMASK(chan->scan_type.realbits - 1, 0);
+ val <<= chan->scan_type.shift;
+
+ return ad5791_spi_write(st, chan->address, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad5791_info = {
+ .read_raw = &ad5791_read_raw,
+ .write_raw = &ad5791_write_raw,
+};
+
+static int ad5791_probe(struct spi_device *spi)
+{
+ struct ad5791_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad5791_state *st;
+ int ret, pos_voltage_uv = 0, neg_voltage_uv = 0;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ st->reg_vdd = devm_regulator_get(&spi->dev, "vdd");
+ if (!IS_ERR(st->reg_vdd)) {
+ ret = regulator_enable(st->reg_vdd);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg_vdd);
+ if (ret < 0)
+ goto error_disable_reg_pos;
+
+ pos_voltage_uv = ret;
+ }
+
+ st->reg_vss = devm_regulator_get(&spi->dev, "vss");
+ if (!IS_ERR(st->reg_vss)) {
+ ret = regulator_enable(st->reg_vss);
+ if (ret)
+ goto error_disable_reg_pos;
+
+ ret = regulator_get_voltage(st->reg_vss);
+ if (ret < 0)
+ goto error_disable_reg_neg;
+
+ neg_voltage_uv = ret;
+ }
+
+ st->pwr_down = true;
+ st->spi = spi;
+
+ if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) {
+ st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000;
+ st->vref_neg_mv = neg_voltage_uv / 1000;
+ } else if (pdata) {
+ st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv;
+ st->vref_neg_mv = pdata->vref_neg_mv;
+ } else {
+ dev_warn(&spi->dev, "reference voltage unspecified\n");
+ }
+
+ ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET);
+ if (ret)
+ goto error_disable_reg_neg;
+
+ st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi)
+ ->driver_data];
+
+
+ st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv))
+ | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) |
+ AD5791_CTRL_BIN2SC;
+
+ ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl |
+ AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
+ if (ret)
+ goto error_disable_reg_neg;
+
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->info = &ad5791_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels
+ = &ad5791_channels[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = 1;
+ indio_dev->name = spi_get_device_id(st->spi)->name;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg_neg;
+
+ return 0;
+
+error_disable_reg_neg:
+ if (!IS_ERR(st->reg_vss))
+ regulator_disable(st->reg_vss);
+error_disable_reg_pos:
+ if (!IS_ERR(st->reg_vdd))
+ regulator_disable(st->reg_vdd);
+ return ret;
+}
+
+static int ad5791_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad5791_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg_vdd))
+ regulator_disable(st->reg_vdd);
+
+ if (!IS_ERR(st->reg_vss))
+ regulator_disable(st->reg_vss);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5791_id[] = {
+ {"ad5760", ID_AD5760},
+ {"ad5780", ID_AD5780},
+ {"ad5781", ID_AD5781},
+ {"ad5790", ID_AD5791},
+ {"ad5791", ID_AD5791},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5791_id);
+
+static struct spi_driver ad5791_driver = {
+ .driver = {
+ .name = "ad5791",
+ },
+ .probe = ad5791_probe,
+ .remove = ad5791_remove,
+ .id_table = ad5791_id,
+};
+module_spi_driver(ad5791_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
new file mode 100644
index 000000000..2e46def9d
--- /dev/null
+++ b/drivers/iio/dac/ad7303.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7303 Digital to analog converters driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/platform_data/ad7303.h>
+
+#define AD7303_CFG_EXTERNAL_VREF BIT(15)
+#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
+#define AD7303_CFG_ADDR_OFFSET 10
+
+#define AD7303_CMD_UPDATE_DAC (0x3 << 8)
+
+/**
+ * struct ad7303_state - driver instance specific data
+ * @spi: the device for this driver instance
+ * @config: cached config register value
+ * @dac_cache: current DAC raw value (chip does not support readback)
+ * @vdd_reg: reference to VDD regulator
+ * @vref_reg: reference to VREF regulator
+ * @lock: protect writes and cache updates
+ * @data: spi transfer buffer
+ */
+
+struct ad7303_state {
+ struct spi_device *spi;
+ uint16_t config;
+ uint8_t dac_cache[2];
+
+ struct regulator *vdd_reg;
+ struct regulator *vref_reg;
+
+ struct mutex lock;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 data ____cacheline_aligned;
+};
+
+static int ad7303_write(struct ad7303_state *st, unsigned int chan,
+ uint8_t val)
+{
+ st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC |
+ (chan << AD7303_CFG_ADDR_OFFSET) |
+ st->config | val);
+
+ return spi_write(st->spi, &st->data, sizeof(st->data));
+}
+
+static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (bool)(st->config &
+ AD7303_CFG_POWER_DOWN(chan->channel)));
+}
+
+static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+
+ if (pwr_down)
+ st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
+ else
+ st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel);
+
+ /* There is no noop cmd which allows us to only update the powerdown
+ * mode, so just write one of the DAC channels again */
+ ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
+
+ mutex_unlock(&st->lock);
+ return len;
+}
+
+static int ad7303_get_vref(struct ad7303_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int ret;
+
+ if (st->config & AD7303_CFG_EXTERNAL_VREF)
+ return regulator_get_voltage(st->vref_reg);
+
+ ret = regulator_get_voltage(st->vdd_reg);
+ if (ret < 0)
+ return ret;
+ return ret / 2;
+}
+
+static int ad7303_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+ int vref_uv;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ *val = st->dac_cache[chan->channel];
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ vref_uv = ad7303_get_vref(st, chan);
+ if (vref_uv < 0)
+ return vref_uv;
+
+ *val = 2 * vref_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int ad7303_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad7303_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = ad7303_write(st, chan->address, val);
+ if (ret == 0)
+ st->dac_cache[chan->channel] = val;
+ mutex_unlock(&st->lock);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info ad7303_info = {
+ .read_raw = ad7303_read_raw,
+ .write_raw = ad7303_write_raw,
+};
+
+static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad7303_read_dac_powerdown,
+ .write = ad7303_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { },
+};
+
+#define AD7303_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (chan), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ .shift = 0, \
+ }, \
+ .ext_info = ad7303_ext_info, \
+}
+
+static const struct iio_chan_spec ad7303_channels[] = {
+ AD7303_CHANNEL(0),
+ AD7303_CHANNEL(1),
+};
+
+static int ad7303_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct ad7303_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ mutex_init(&st->lock);
+
+ st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd");
+ if (IS_ERR(st->vdd_reg))
+ return PTR_ERR(st->vdd_reg);
+
+ ret = regulator_enable(st->vdd_reg);
+ if (ret)
+ return ret;
+
+ st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF");
+ if (IS_ERR(st->vref_reg)) {
+ ret = PTR_ERR(st->vref_reg);
+ if (ret != -ENODEV)
+ goto err_disable_vdd_reg;
+ st->vref_reg = NULL;
+ }
+
+ if (st->vref_reg) {
+ ret = regulator_enable(st->vref_reg);
+ if (ret)
+ goto err_disable_vdd_reg;
+
+ st->config |= AD7303_CFG_EXTERNAL_VREF;
+ }
+
+ indio_dev->name = id->name;
+ indio_dev->info = &ad7303_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7303_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7303_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_disable_vref_reg;
+
+ return 0;
+
+err_disable_vref_reg:
+ if (st->vref_reg)
+ regulator_disable(st->vref_reg);
+err_disable_vdd_reg:
+ regulator_disable(st->vdd_reg);
+ return ret;
+}
+
+static int ad7303_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7303_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (st->vref_reg)
+ regulator_disable(st->vref_reg);
+ regulator_disable(st->vdd_reg);
+
+ return 0;
+}
+
+static const struct of_device_id ad7303_spi_of_match[] = {
+ { .compatible = "adi,ad7303", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
+
+static const struct spi_device_id ad7303_spi_ids[] = {
+ { "ad7303", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
+
+static struct spi_driver ad7303_driver = {
+ .driver = {
+ .name = "ad7303",
+ .of_match_table = ad7303_spi_of_match,
+ },
+ .probe = ad7303_probe,
+ .remove = ad7303_remove,
+ .id_table = ad7303_spi_ids,
+};
+module_spi_driver(ad7303_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c
new file mode 100644
index 000000000..6354b7c8f
--- /dev/null
+++ b/drivers/iio/dac/ad8801.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IIO DAC driver for Analog Devices AD8801 DAC
+ *
+ * Copyright (C) 2016 Gwenhael Goavec-Merou
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+
+#define AD8801_CFG_ADDR_OFFSET 8
+
+enum ad8801_device_ids {
+ ID_AD8801,
+ ID_AD8803,
+};
+
+struct ad8801_state {
+ struct spi_device *spi;
+ unsigned char dac_cache[8]; /* Value write on each channel */
+ unsigned int vrefh_mv;
+ unsigned int vrefl_mv;
+ struct regulator *vrefh_reg;
+ struct regulator *vrefl_reg;
+
+ __be16 data ____cacheline_aligned;
+};
+
+static int ad8801_spi_write(struct ad8801_state *state,
+ u8 channel, unsigned char value)
+{
+ state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value);
+ return spi_write(state->spi, &state->data, sizeof(state->data));
+}
+
+static int ad8801_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad8801_state *state = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= 256 || val < 0)
+ return -EINVAL;
+
+ ret = ad8801_spi_write(state, chan->channel, val);
+ if (ret == 0)
+ state->dac_cache[chan->channel] = val;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ad8801_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad8801_state *state = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ *val = state->dac_cache[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = state->vrefh_mv - state->vrefl_mv;
+ *val2 = 8;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = state->vrefl_mv;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad8801_info = {
+ .read_raw = ad8801_read_raw,
+ .write_raw = ad8801_write_raw,
+};
+
+#define AD8801_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec ad8801_channels[] = {
+ AD8801_CHANNEL(0),
+ AD8801_CHANNEL(1),
+ AD8801_CHANNEL(2),
+ AD8801_CHANNEL(3),
+ AD8801_CHANNEL(4),
+ AD8801_CHANNEL(5),
+ AD8801_CHANNEL(6),
+ AD8801_CHANNEL(7),
+};
+
+static int ad8801_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad8801_state *state;
+ const struct spi_device_id *id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ state->spi = spi;
+ id = spi_get_device_id(spi);
+
+ state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh");
+ if (IS_ERR(state->vrefh_reg)) {
+ dev_err(&spi->dev, "Vrefh regulator not specified\n");
+ return PTR_ERR(state->vrefh_reg);
+ }
+
+ ret = regulator_enable(state->vrefh_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(state->vrefh_reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n",
+ ret);
+ goto error_disable_vrefh_reg;
+ }
+ state->vrefh_mv = ret / 1000;
+
+ if (id->driver_data == ID_AD8803) {
+ state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl");
+ if (IS_ERR(state->vrefl_reg)) {
+ dev_err(&spi->dev, "Vrefl regulator not specified\n");
+ ret = PTR_ERR(state->vrefl_reg);
+ goto error_disable_vrefh_reg;
+ }
+
+ ret = regulator_enable(state->vrefl_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n",
+ ret);
+ goto error_disable_vrefh_reg;
+ }
+
+ ret = regulator_get_voltage(state->vrefl_reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n",
+ ret);
+ goto error_disable_vrefl_reg;
+ }
+ state->vrefl_mv = ret / 1000;
+ } else {
+ state->vrefl_mv = 0;
+ state->vrefl_reg = NULL;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->info = &ad8801_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad8801_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad8801_channels);
+ indio_dev->name = id->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n",
+ ret);
+ goto error_disable_vrefl_reg;
+ }
+
+ return 0;
+
+error_disable_vrefl_reg:
+ if (state->vrefl_reg)
+ regulator_disable(state->vrefl_reg);
+error_disable_vrefh_reg:
+ regulator_disable(state->vrefh_reg);
+ return ret;
+}
+
+static int ad8801_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad8801_state *state = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (state->vrefl_reg)
+ regulator_disable(state->vrefl_reg);
+ regulator_disable(state->vrefh_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad8801_ids[] = {
+ {"ad8801", ID_AD8801},
+ {"ad8803", ID_AD8803},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad8801_ids);
+
+static struct spi_driver ad8801_driver = {
+ .driver = {
+ .name = "ad8801",
+ },
+ .probe = ad8801_probe,
+ .remove = ad8801_remove,
+ .id_table = ad8801_ids,
+};
+module_spi_driver(ad8801_driver);
+
+MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>");
+MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c
new file mode 100644
index 000000000..77a6916b3
--- /dev/null
+++ b/drivers/iio/dac/cio-dac.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IIO driver for the Measurement Computing CIO-DAC
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * This driver supports the following Measurement Computing devices: CIO-DAC16,
+ * CIO-DAC06, and PC104-DAC06.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#define CIO_DAC_NUM_CHAN 16
+
+#define CIO_DAC_CHAN(chan) { \
+ .type = IIO_VOLTAGE, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .indexed = 1, \
+ .output = 1 \
+}
+
+#define CIO_DAC_EXTENT 32
+
+static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)];
+static unsigned int num_cio_dac;
+module_param_hw_array(base, uint, ioport, &num_cio_dac, 0);
+MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses");
+
+/**
+ * struct cio_dac_iio - IIO device private data structure
+ * @chan_out_states: channels' output states
+ * @base: base port address of the IIO device
+ */
+struct cio_dac_iio {
+ int chan_out_states[CIO_DAC_NUM_CHAN];
+ unsigned int base;
+};
+
+static int cio_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct cio_dac_iio *const priv = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ *val = priv->chan_out_states[chan->channel];
+
+ return IIO_VAL_INT;
+}
+
+static int cio_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct cio_dac_iio *const priv = iio_priv(indio_dev);
+ const unsigned int chan_addr_offset = 2 * chan->channel;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ /* DAC can only accept up to a 12-bit value */
+ if ((unsigned int)val > 4095)
+ return -EINVAL;
+
+ priv->chan_out_states[chan->channel] = val;
+ outw(val, priv->base + chan_addr_offset);
+
+ return 0;
+}
+
+static const struct iio_info cio_dac_info = {
+ .read_raw = cio_dac_read_raw,
+ .write_raw = cio_dac_write_raw
+};
+
+static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = {
+ CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3),
+ CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7),
+ CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11),
+ CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15)
+};
+
+static int cio_dac_probe(struct device *dev, unsigned int id)
+{
+ struct iio_dev *indio_dev;
+ struct cio_dac_iio *priv;
+ unsigned int i;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT,
+ dev_name(dev))) {
+ dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n",
+ base[id], base[id] + CIO_DAC_EXTENT);
+ return -EBUSY;
+ }
+
+ indio_dev->info = &cio_dac_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = cio_dac_channels;
+ indio_dev->num_channels = CIO_DAC_NUM_CHAN;
+ indio_dev->name = dev_name(dev);
+
+ priv = iio_priv(indio_dev);
+ priv->base = base[id];
+
+ /* initialize DAC outputs to 0V */
+ for (i = 0; i < 32; i += 2)
+ outw(0, base[id] + i);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct isa_driver cio_dac_driver = {
+ .probe = cio_dac_probe,
+ .driver = {
+ .name = "cio-dac"
+ }
+};
+
+module_isa_driver(cio_dac_driver, num_cio_dac);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c
new file mode 100644
index 000000000..5d1819448
--- /dev/null
+++ b/drivers/iio/dac/dpot-dac.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO DAC emulation driver using a digital potentiometer
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ */
+
+/*
+ * It is assumed that the dpot is used as a voltage divider between the
+ * current dpot wiper setting and the maximum resistance of the dpot. The
+ * divided voltage is provided by a vref regulator.
+ *
+ * .------.
+ * .-----------. | |
+ * | vref |--' .---.
+ * | regulator |--. | |
+ * '-----------' | | d |
+ * | | p |
+ * | | o | wiper
+ * | | t |<---------+
+ * | | |
+ * | '---' dac output voltage
+ * | |
+ * '------+------------+
+ */
+
+#include <linux/err.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+struct dpot_dac {
+ struct regulator *vref;
+ struct iio_channel *dpot;
+ u32 max_ohms;
+};
+
+static const struct iio_chan_spec dpot_dac_iio_channel = {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
+ .output = 1,
+ .indexed = 1,
+};
+
+static int dpot_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct dpot_dac *dac = iio_priv(indio_dev);
+ int ret;
+ unsigned long long tmp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return iio_read_channel_raw(dac->dpot, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = iio_read_channel_scale(dac->dpot, val, val2);
+ switch (ret) {
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = *val * 1000000000LL;
+ do_div(tmp, dac->max_ohms);
+ tmp *= regulator_get_voltage(dac->vref) / 1000;
+ do_div(tmp, 1000000000LL);
+ *val = tmp;
+ return ret;
+ case IIO_VAL_INT:
+ /*
+ * Convert integer scale to fractional scale by
+ * setting the denominator (val2) to one...
+ */
+ *val2 = 1;
+ ret = IIO_VAL_FRACTIONAL;
+ /* ...and fall through. Say it again for GCC. */
+ fallthrough;
+ case IIO_VAL_FRACTIONAL:
+ *val *= regulator_get_voltage(dac->vref) / 1000;
+ *val2 *= dac->max_ohms;
+ break;
+ }
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int dpot_dac_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct dpot_dac *dac = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *type = IIO_VAL_INT;
+ return iio_read_avail_channel_raw(dac->dpot, vals, length);
+ }
+
+ return -EINVAL;
+}
+
+static int dpot_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct dpot_dac *dac = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return iio_write_channel_raw(dac->dpot, val);
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info dpot_dac_info = {
+ .read_raw = dpot_dac_read_raw,
+ .read_avail = dpot_dac_read_avail,
+ .write_raw = dpot_dac_write_raw,
+};
+
+static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
+{
+ struct device *dev = &indio_dev->dev;
+ struct dpot_dac *dac = iio_priv(indio_dev);
+ unsigned long long tmp;
+ int ret;
+ int val;
+ int val2;
+ int max;
+
+ ret = iio_read_max_channel_raw(dac->dpot, &max);
+ if (ret < 0) {
+ dev_err(dev, "dpot does not indicate its raw maximum value\n");
+ return ret;
+ }
+
+ switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
+ case IIO_VAL_INT:
+ return max * val;
+ case IIO_VAL_FRACTIONAL:
+ tmp = (unsigned long long)max * val;
+ do_div(tmp, val2);
+ return tmp;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = val * 1000000000LL * max >> val2;
+ do_div(tmp, 1000000000LL);
+ return tmp;
+ default:
+ dev_err(dev, "dpot has a scale that is too weird\n");
+ }
+
+ return -EINVAL;
+}
+
+static int dpot_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct dpot_dac *dac;
+ enum iio_chan_type type;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ dac = iio_priv(indio_dev);
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->info = &dpot_dac_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &dpot_dac_iio_channel;
+ indio_dev->num_channels = 1;
+
+ dac->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(dac->vref))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref),
+ "failed to get vref regulator\n");
+
+ dac->dpot = devm_iio_channel_get(dev, "dpot");
+ if (IS_ERR(dac->dpot))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dac->dpot),
+ "failed to get dpot input channel\n");
+
+ ret = iio_get_channel_type(dac->dpot, &type);
+ if (ret < 0)
+ return ret;
+
+ if (type != IIO_RESISTANCE) {
+ dev_err(dev, "dpot is of the wrong type\n");
+ return -EINVAL;
+ }
+
+ ret = dpot_dac_channel_max_ohms(indio_dev);
+ if (ret < 0)
+ return ret;
+ dac->max_ohms = ret;
+
+ ret = regulator_enable(dac->vref);
+ if (ret) {
+ dev_err(dev, "failed to enable the vref regulator\n");
+ return ret;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "failed to register iio device\n");
+ goto disable_reg;
+ }
+
+ return 0;
+
+disable_reg:
+ regulator_disable(dac->vref);
+ return ret;
+}
+
+static int dpot_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct dpot_dac *dac = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(dac->vref);
+
+ return 0;
+}
+
+static const struct of_device_id dpot_dac_match[] = {
+ { .compatible = "dpot-dac" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dpot_dac_match);
+
+static struct platform_driver dpot_dac_driver = {
+ .probe = dpot_dac_probe,
+ .remove = dpot_dac_remove,
+ .driver = {
+ .name = "iio-dpot-dac",
+ .of_match_table = dpot_dac_match,
+ },
+};
+module_platform_driver(dpot_dac_driver);
+
+MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c
new file mode 100644
index 000000000..79527fbc2
--- /dev/null
+++ b/drivers/iio/dac/ds4424.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Maxim Integrated
+ * 7-bit, Multi-Channel Sink/Source Current DAC Driver
+ * Copyright (C) 2017 Maxim Integrated
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/consumer.h>
+
+#define DS4422_MAX_DAC_CHANNELS 2
+#define DS4424_MAX_DAC_CHANNELS 4
+
+#define DS4424_DAC_ADDR(chan) ((chan) + 0xf8)
+#define DS4424_SOURCE_I 1
+#define DS4424_SINK_I 0
+
+#define DS4424_CHANNEL(chan) { \
+ .type = IIO_CURRENT, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+/*
+ * DS4424 DAC control register 8 bits
+ * [7] 0: to sink; 1: to source
+ * [6:0] steps to sink/source
+ * bit[7] looks like a sign bit, but the value of the register is
+ * not a two's complement code considering the bit[6:0] is a absolute
+ * distance from the zero point.
+ */
+union ds4424_raw_data {
+ struct {
+ u8 dx:7;
+ u8 source_bit:1;
+ };
+ u8 bits;
+};
+
+enum ds4424_device_ids {
+ ID_DS4422,
+ ID_DS4424,
+};
+
+struct ds4424_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ uint8_t save[DS4424_MAX_DAC_CHANNELS];
+ struct regulator *vcc_reg;
+ uint8_t raw[DS4424_MAX_DAC_CHANNELS];
+};
+
+static const struct iio_chan_spec ds4424_channels[] = {
+ DS4424_CHANNEL(0),
+ DS4424_CHANNEL(1),
+ DS4424_CHANNEL(2),
+ DS4424_CHANNEL(3),
+};
+
+static int ds4424_get_value(struct iio_dev *indio_dev,
+ int *val, int channel)
+{
+ struct ds4424_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_read_byte_data(data->client, DS4424_DAC_ADDR(channel));
+ if (ret < 0)
+ goto fail;
+
+ *val = ret;
+
+fail:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int ds4424_set_value(struct iio_dev *indio_dev,
+ int val, struct iio_chan_spec const *chan)
+{
+ struct ds4424_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_write_byte_data(data->client,
+ DS4424_DAC_ADDR(chan->channel), val);
+ if (ret < 0)
+ goto fail;
+
+ data->raw[chan->channel] = val;
+
+fail:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int ds4424_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ union ds4424_raw_data raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ds4424_get_value(indio_dev, val, chan->channel);
+ if (ret < 0) {
+ pr_err("%s : ds4424_get_value returned %d\n",
+ __func__, ret);
+ return ret;
+ }
+ raw.bits = *val;
+ *val = raw.dx;
+ if (raw.source_bit == DS4424_SINK_I)
+ *val = -*val;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ds4424_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ union ds4424_raw_data raw;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < S8_MIN || val > S8_MAX)
+ return -EINVAL;
+
+ if (val > 0) {
+ raw.source_bit = DS4424_SOURCE_I;
+ raw.dx = val;
+ } else {
+ raw.source_bit = DS4424_SINK_I;
+ raw.dx = -val;
+ }
+
+ return ds4424_set_value(indio_dev, raw.bits, chan);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ds4424_verify_chip(struct iio_dev *indio_dev)
+{
+ int ret, val;
+
+ ret = ds4424_get_value(indio_dev, &val, 0);
+ if (ret < 0)
+ dev_err(&indio_dev->dev,
+ "%s failed. ret: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int __maybe_unused ds4424_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ds4424_data *data = iio_priv(indio_dev);
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ data->save[i] = data->raw[i];
+ ret = ds4424_set_value(indio_dev, 0,
+ &indio_dev->channels[i]);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+
+static int __maybe_unused ds4424_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ds4424_data *data = iio_priv(indio_dev);
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = ds4424_set_value(indio_dev, data->save[i],
+ &indio_dev->channels[i]);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume);
+
+static const struct iio_info ds4424_info = {
+ .read_raw = ds4424_read_raw,
+ .write_raw = ds4424_write_raw,
+};
+
+static int ds4424_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds4424_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio dev alloc failed.\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ indio_dev->name = id->name;
+
+ data->vcc_reg = devm_regulator_get(&client->dev, "vcc");
+ if (IS_ERR(data->vcc_reg)) {
+ dev_err(&client->dev,
+ "Failed to get vcc-supply regulator. err: %ld\n",
+ PTR_ERR(data->vcc_reg));
+ return PTR_ERR(data->vcc_reg);
+ }
+
+ mutex_init(&data->lock);
+ ret = regulator_enable(data->vcc_reg);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Unable to enable the regulator.\n");
+ return ret;
+ }
+
+ usleep_range(1000, 1200);
+ ret = ds4424_verify_chip(indio_dev);
+ if (ret < 0)
+ goto fail;
+
+ switch (id->driver_data) {
+ case ID_DS4422:
+ indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS;
+ break;
+ case ID_DS4424:
+ indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS;
+ break;
+ default:
+ dev_err(&client->dev,
+ "ds4424: Invalid chip id.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ indio_dev->channels = ds4424_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ds4424_info;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "iio_device_register failed. ret: %d\n", ret);
+ goto fail;
+ }
+
+ return ret;
+
+fail:
+ regulator_disable(data->vcc_reg);
+ return ret;
+}
+
+static int ds4424_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ds4424_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(data->vcc_reg);
+
+ return 0;
+}
+
+static const struct i2c_device_id ds4424_id[] = {
+ { "ds4422", ID_DS4422 },
+ { "ds4424", ID_DS4424 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, ds4424_id);
+
+static const struct of_device_id ds4424_of_match[] = {
+ { .compatible = "maxim,ds4422" },
+ { .compatible = "maxim,ds4424" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, ds4424_of_match);
+
+static struct i2c_driver ds4424_driver = {
+ .driver = {
+ .name = "ds4424",
+ .of_match_table = ds4424_of_match,
+ .pm = &ds4424_pm_ops,
+ },
+ .probe = ds4424_probe,
+ .remove = ds4424_remove,
+ .id_table = ds4424_id,
+};
+module_i2c_driver(ds4424_driver);
+
+MODULE_DESCRIPTION("Maxim DS4424 DAC Driver");
+MODULE_AUTHOR("Ismail H. Kose <ismail.kose@maximintegrated.com>");
+MODULE_AUTHOR("Vishal Sood <vishal.sood@maximintegrated.com>");
+MODULE_AUTHOR("David Jung <david.jung@maximintegrated.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c
new file mode 100644
index 000000000..9e38607a1
--- /dev/null
+++ b/drivers/iio/dac/lpc18xx_dac.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IIO DAC driver for NXP LPC18xx DAC
+ *
+ * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
+ *
+ * UNSUPPORTED hardware features:
+ * - Interrupts
+ * - DMA
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/* LPC18XX DAC registers and bits */
+#define LPC18XX_DAC_CR 0x000
+#define LPC18XX_DAC_CR_VALUE_SHIFT 6
+#define LPC18XX_DAC_CR_VALUE_MASK 0x3ff
+#define LPC18XX_DAC_CR_BIAS BIT(16)
+#define LPC18XX_DAC_CTRL 0x004
+#define LPC18XX_DAC_CTRL_DMA_ENA BIT(3)
+
+struct lpc18xx_dac {
+ struct regulator *vref;
+ void __iomem *base;
+ struct mutex lock;
+ struct clk *clk;
+};
+
+static const struct iio_chan_spec lpc18xx_dac_iio_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int lpc18xx_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lpc18xx_dac *dac = iio_priv(indio_dev);
+ u32 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ reg = readl(dac->base + LPC18XX_DAC_CR);
+ *val = reg >> LPC18XX_DAC_CR_VALUE_SHIFT;
+ *val &= LPC18XX_DAC_CR_VALUE_MASK;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(dac->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static int lpc18xx_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct lpc18xx_dac *dac = iio_priv(indio_dev);
+ u32 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val > LPC18XX_DAC_CR_VALUE_MASK)
+ return -EINVAL;
+
+ reg = LPC18XX_DAC_CR_BIAS;
+ reg |= val << LPC18XX_DAC_CR_VALUE_SHIFT;
+
+ mutex_lock(&dac->lock);
+ writel(reg, dac->base + LPC18XX_DAC_CR);
+ writel(LPC18XX_DAC_CTRL_DMA_ENA, dac->base + LPC18XX_DAC_CTRL);
+ mutex_unlock(&dac->lock);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lpc18xx_dac_info = {
+ .read_raw = lpc18xx_dac_read_raw,
+ .write_raw = lpc18xx_dac_write_raw,
+};
+
+static int lpc18xx_dac_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct lpc18xx_dac *dac;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ dac = iio_priv(indio_dev);
+ mutex_init(&dac->lock);
+
+ dac->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dac->base))
+ return PTR_ERR(dac->base);
+
+ dac->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dac->clk)) {
+ dev_err(&pdev->dev, "error getting clock\n");
+ return PTR_ERR(dac->clk);
+ }
+
+ dac->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(dac->vref)) {
+ dev_err(&pdev->dev, "error getting regulator\n");
+ return PTR_ERR(dac->vref);
+ }
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &lpc18xx_dac_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = lpc18xx_dac_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lpc18xx_dac_iio_channels);
+
+ ret = regulator_enable(dac->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable regulator\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dac->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable clock\n");
+ goto dis_reg;
+ }
+
+ writel(0, dac->base + LPC18XX_DAC_CTRL);
+ writel(0, dac->base + LPC18XX_DAC_CR);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ goto dis_clk;
+ }
+
+ return 0;
+
+dis_clk:
+ clk_disable_unprepare(dac->clk);
+dis_reg:
+ regulator_disable(dac->vref);
+ return ret;
+}
+
+static int lpc18xx_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct lpc18xx_dac *dac = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ writel(0, dac->base + LPC18XX_DAC_CTRL);
+ clk_disable_unprepare(dac->clk);
+ regulator_disable(dac->vref);
+
+ return 0;
+}
+
+static const struct of_device_id lpc18xx_dac_match[] = {
+ { .compatible = "nxp,lpc1850-dac" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_dac_match);
+
+static struct platform_driver lpc18xx_dac_driver = {
+ .probe = lpc18xx_dac_probe,
+ .remove = lpc18xx_dac_remove,
+ .driver = {
+ .name = "lpc18xx-dac",
+ .of_match_table = lpc18xx_dac_match,
+ },
+};
+module_platform_driver(lpc18xx_dac_driver);
+
+MODULE_DESCRIPTION("LPC18xx DAC driver");
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c
new file mode 100644
index 000000000..dc1018854
--- /dev/null
+++ b/drivers/iio/dac/ltc1660.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Linear Technology LTC1665/LTC1660, 8 channels DAC
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#define LTC1660_REG_WAKE 0x0
+#define LTC1660_REG_DAC_A 0x1
+#define LTC1660_REG_DAC_B 0x2
+#define LTC1660_REG_DAC_C 0x3
+#define LTC1660_REG_DAC_D 0x4
+#define LTC1660_REG_DAC_E 0x5
+#define LTC1660_REG_DAC_F 0x6
+#define LTC1660_REG_DAC_G 0x7
+#define LTC1660_REG_DAC_H 0x8
+#define LTC1660_REG_SLEEP 0xe
+
+#define LTC1660_NUM_CHANNELS 8
+
+static const struct regmap_config ltc1660_regmap_config = {
+ .reg_bits = 4,
+ .val_bits = 12,
+};
+
+enum ltc1660_supported_device_ids {
+ ID_LTC1660,
+ ID_LTC1665,
+};
+
+struct ltc1660_priv {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator *vref_reg;
+ unsigned int value[LTC1660_NUM_CHANNELS];
+ unsigned int vref_mv;
+};
+
+static int ltc1660_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct ltc1660_priv *priv = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = priv->value[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(priv->vref_reg);
+ if (*val < 0) {
+ dev_err(&priv->spi->dev, "failed to read vref regulator: %d\n",
+ *val);
+ return *val;
+ }
+
+ /* Convert to mV */
+ *val /= 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc1660_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ltc1660_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val2 != 0)
+ return -EINVAL;
+
+ if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
+ return -EINVAL;
+
+ ret = regmap_write(priv->regmap, chan->channel,
+ (val << chan->scan_type.shift));
+ if (!ret)
+ priv->value[chan->channel] = val;
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define LTC1660_CHAN(chan, bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 12 - (bits), \
+ }, \
+}
+
+#define LTC1660_OCTAL_CHANNELS(bits) { \
+ LTC1660_CHAN(LTC1660_REG_DAC_A, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_B, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_C, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_D, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_E, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_F, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_G, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_H, bits), \
+}
+
+static const struct iio_chan_spec ltc1660_channels[][LTC1660_NUM_CHANNELS] = {
+ [ID_LTC1660] = LTC1660_OCTAL_CHANNELS(10),
+ [ID_LTC1665] = LTC1660_OCTAL_CHANNELS(8),
+};
+
+static const struct iio_info ltc1660_info = {
+ .read_raw = &ltc1660_read_raw,
+ .write_raw = &ltc1660_write_raw,
+};
+
+static int __maybe_unused ltc1660_suspend(struct device *dev)
+{
+ struct ltc1660_priv *priv = iio_priv(spi_get_drvdata(
+ to_spi_device(dev)));
+ return regmap_write(priv->regmap, LTC1660_REG_SLEEP, 0x00);
+}
+
+static int __maybe_unused ltc1660_resume(struct device *dev)
+{
+ struct ltc1660_priv *priv = iio_priv(spi_get_drvdata(
+ to_spi_device(dev)));
+ return regmap_write(priv->regmap, LTC1660_REG_WAKE, 0x00);
+}
+static SIMPLE_DEV_PM_OPS(ltc1660_pm_ops, ltc1660_suspend, ltc1660_resume);
+
+static int ltc1660_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ltc1660_priv *priv;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->regmap = devm_regmap_init_spi(spi, &ltc1660_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&spi->dev, "failed to register spi regmap %ld\n",
+ PTR_ERR(priv->regmap));
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->vref_reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(priv->vref_reg)) {
+ dev_err(&spi->dev, "vref regulator not specified\n");
+ return PTR_ERR(priv->vref_reg);
+ }
+
+ ret = regulator_enable(priv->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "failed to enable vref regulator: %d\n",
+ ret);
+ return ret;
+ }
+
+ priv->spi = spi;
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->info = &ltc1660_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ltc1660_channels[id->driver_data];
+ indio_dev->num_channels = LTC1660_NUM_CHANNELS;
+ indio_dev->name = id->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register iio device: %d\n",
+ ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ regulator_disable(priv->vref_reg);
+
+ return ret;
+}
+
+static int ltc1660_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ltc1660_priv *priv = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(priv->vref_reg);
+
+ return 0;
+}
+
+static const struct of_device_id ltc1660_dt_ids[] = {
+ { .compatible = "lltc,ltc1660", .data = (void *)ID_LTC1660 },
+ { .compatible = "lltc,ltc1665", .data = (void *)ID_LTC1665 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ltc1660_dt_ids);
+
+static const struct spi_device_id ltc1660_id[] = {
+ {"ltc1660", ID_LTC1660},
+ {"ltc1665", ID_LTC1665},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, ltc1660_id);
+
+static struct spi_driver ltc1660_driver = {
+ .driver = {
+ .name = "ltc1660",
+ .of_match_table = ltc1660_dt_ids,
+ .pm = &ltc1660_pm_ops,
+ },
+ .probe = ltc1660_probe,
+ .remove = ltc1660_remove,
+ .id_table = ltc1660_id,
+};
+module_spi_driver(ltc1660_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("Linear Technology LTC1660/LTC1665 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c
new file mode 100644
index 000000000..4002ed086
--- /dev/null
+++ b/drivers/iio/dac/ltc2632.c
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * LTC2632 Digital to analog convertors spi driver
+ *
+ * Copyright 2017 Maxime Roussin-Bélanger
+ * expanded by Silvan Murer <silvan.murer@gmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/unaligned.h>
+
+#define LTC2632_CMD_WRITE_INPUT_N 0x0
+#define LTC2632_CMD_UPDATE_DAC_N 0x1
+#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
+#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N 0x3
+#define LTC2632_CMD_POWERDOWN_DAC_N 0x4
+#define LTC2632_CMD_POWERDOWN_CHIP 0x5
+#define LTC2632_CMD_INTERNAL_REFER 0x6
+#define LTC2632_CMD_EXTERNAL_REFER 0x7
+
+/**
+ * struct ltc2632_chip_info - chip specific information
+ * @channels: channel spec for the DAC
+ * @num_channels: DAC channel count of the chip
+ * @vref_mv: internal reference voltage
+ */
+struct ltc2632_chip_info {
+ const struct iio_chan_spec *channels;
+ const size_t num_channels;
+ const int vref_mv;
+};
+
+/**
+ * struct ltc2632_state - driver instance specific data
+ * @spi_dev: pointer to the spi_device struct
+ * @powerdown_cache_mask: used to show current channel powerdown state
+ * @vref_mv: used reference voltage (internal or external)
+ * @vref_reg: regulator for the reference voltage
+ */
+struct ltc2632_state {
+ struct spi_device *spi_dev;
+ unsigned int powerdown_cache_mask;
+ int vref_mv;
+ struct regulator *vref_reg;
+};
+
+enum ltc2632_supported_device_ids {
+ ID_LTC2632L12,
+ ID_LTC2632L10,
+ ID_LTC2632L8,
+ ID_LTC2632H12,
+ ID_LTC2632H10,
+ ID_LTC2632H8,
+ ID_LTC2634L12,
+ ID_LTC2634L10,
+ ID_LTC2634L8,
+ ID_LTC2634H12,
+ ID_LTC2634H10,
+ ID_LTC2634H8,
+ ID_LTC2636L12,
+ ID_LTC2636L10,
+ ID_LTC2636L8,
+ ID_LTC2636H12,
+ ID_LTC2636H10,
+ ID_LTC2636H8,
+};
+
+static int ltc2632_spi_write(struct spi_device *spi,
+ u8 cmd, u8 addr, u16 val, u8 shift)
+{
+ u32 data;
+ u8 msg[3];
+
+ /*
+ * The input shift register is 24 bits wide.
+ * The next four are the command bits, C3 to C0,
+ * followed by the 4-bit DAC address, A3 to A0, and then the
+ * 12-, 10-, 8-bit data-word. The data-word comprises the 12-,
+ * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits.
+ */
+ data = (cmd << 20) | (addr << 16) | (val << shift);
+ put_unaligned_be24(data, &msg[0]);
+
+ return spi_write(spi, msg, sizeof(msg));
+}
+
+static int ltc2632_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ const struct ltc2632_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static int ltc2632_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ltc2632_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
+ return -EINVAL;
+
+ return ltc2632_spi_write(st->spi_dev,
+ LTC2632_CMD_WRITE_INPUT_N_UPDATE_N,
+ chan->address, val,
+ chan->scan_type.shift);
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ltc2632_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ !!(st->powerdown_cache_mask & (1 << chan->channel)));
+}
+
+static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf,
+ size_t len)
+{
+ bool pwr_down;
+ int ret;
+ struct ltc2632_state *st = iio_priv(indio_dev);
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ if (pwr_down)
+ st->powerdown_cache_mask |= (1 << chan->channel);
+ else
+ st->powerdown_cache_mask &= ~(1 << chan->channel);
+
+ ret = ltc2632_spi_write(st->spi_dev,
+ LTC2632_CMD_POWERDOWN_DAC_N,
+ chan->channel, 0, 0);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_info ltc2632_info = {
+ .write_raw = ltc2632_write_raw,
+ .read_raw = ltc2632_read_raw,
+};
+
+static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ltc2632_read_dac_powerdown,
+ .write = ltc2632_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { },
+};
+
+#define LTC2632_CHANNEL(_chan, _bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (_chan), \
+ .scan_type = { \
+ .realbits = (_bits), \
+ .shift = 16 - (_bits), \
+ }, \
+ .ext_info = ltc2632_ext_info, \
+}
+
+#define DECLARE_LTC2632_CHANNELS(_name, _bits) \
+ const struct iio_chan_spec _name ## _channels[] = { \
+ LTC2632_CHANNEL(0, _bits), \
+ LTC2632_CHANNEL(1, _bits), \
+ LTC2632_CHANNEL(2, _bits), \
+ LTC2632_CHANNEL(3, _bits), \
+ LTC2632_CHANNEL(4, _bits), \
+ LTC2632_CHANNEL(5, _bits), \
+ LTC2632_CHANNEL(6, _bits), \
+ LTC2632_CHANNEL(7, _bits), \
+ }
+
+static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12);
+static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10);
+static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8);
+
+static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = {
+ [ID_LTC2632L12] = {
+ .channels = ltc2632x12_channels,
+ .num_channels = 2,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2632L10] = {
+ .channels = ltc2632x10_channels,
+ .num_channels = 2,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2632L8] = {
+ .channels = ltc2632x8_channels,
+ .num_channels = 2,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2632H12] = {
+ .channels = ltc2632x12_channels,
+ .num_channels = 2,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2632H10] = {
+ .channels = ltc2632x10_channels,
+ .num_channels = 2,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2632H8] = {
+ .channels = ltc2632x8_channels,
+ .num_channels = 2,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2634L12] = {
+ .channels = ltc2632x12_channels,
+ .num_channels = 4,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2634L10] = {
+ .channels = ltc2632x10_channels,
+ .num_channels = 4,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2634L8] = {
+ .channels = ltc2632x8_channels,
+ .num_channels = 4,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2634H12] = {
+ .channels = ltc2632x12_channels,
+ .num_channels = 4,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2634H10] = {
+ .channels = ltc2632x10_channels,
+ .num_channels = 4,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2634H8] = {
+ .channels = ltc2632x8_channels,
+ .num_channels = 4,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2636L12] = {
+ .channels = ltc2632x12_channels,
+ .num_channels = 8,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2636L10] = {
+ .channels = ltc2632x10_channels,
+ .num_channels = 8,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2636L8] = {
+ .channels = ltc2632x8_channels,
+ .num_channels = 8,
+ .vref_mv = 2500,
+ },
+ [ID_LTC2636H12] = {
+ .channels = ltc2632x12_channels,
+ .num_channels = 8,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2636H10] = {
+ .channels = ltc2632x10_channels,
+ .num_channels = 8,
+ .vref_mv = 4096,
+ },
+ [ID_LTC2636H8] = {
+ .channels = ltc2632x8_channels,
+ .num_channels = 8,
+ .vref_mv = 4096,
+ },
+};
+
+static int ltc2632_probe(struct spi_device *spi)
+{
+ struct ltc2632_state *st;
+ struct iio_dev *indio_dev;
+ struct ltc2632_chip_info *chip_info;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ spi_set_drvdata(spi, indio_dev);
+ st->spi_dev = spi;
+
+ chip_info = (struct ltc2632_chip_info *)
+ spi_get_device_id(spi)->driver_data;
+
+ st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (PTR_ERR(st->vref_reg) == -ENODEV) {
+ /* use internal reference voltage */
+ st->vref_reg = NULL;
+ st->vref_mv = chip_info->vref_mv;
+
+ ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER,
+ 0, 0, 0);
+ if (ret) {
+ dev_err(&spi->dev,
+ "Set internal reference command failed, %d\n",
+ ret);
+ return ret;
+ }
+ } else if (IS_ERR(st->vref_reg)) {
+ dev_err(&spi->dev,
+ "Error getting voltage reference regulator\n");
+ return PTR_ERR(st->vref_reg);
+ } else {
+ /* use external reference voltage */
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev,
+ "enable reference regulator failed, %d\n",
+ ret);
+ return ret;
+ }
+ st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000;
+
+ ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER,
+ 0, 0, 0);
+ if (ret) {
+ dev_err(&spi->dev,
+ "Set external reference command failed, %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name
+ : spi_get_device_id(spi)->name;
+ indio_dev->info = &ltc2632_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
+
+ return iio_device_register(indio_dev);
+}
+
+static int ltc2632_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ltc2632_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (st->vref_reg)
+ regulator_disable(st->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ltc2632_id[] = {
+ { "ltc2632-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L12] },
+ { "ltc2632-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L10] },
+ { "ltc2632-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L8] },
+ { "ltc2632-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H12] },
+ { "ltc2632-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H10] },
+ { "ltc2632-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H8] },
+ { "ltc2634-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634L12] },
+ { "ltc2634-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634L10] },
+ { "ltc2634-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634L8] },
+ { "ltc2634-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634H12] },
+ { "ltc2634-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634H10] },
+ { "ltc2634-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2634H8] },
+ { "ltc2636-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636L12] },
+ { "ltc2636-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636L10] },
+ { "ltc2636-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636L8] },
+ { "ltc2636-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636H12] },
+ { "ltc2636-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636H10] },
+ { "ltc2636-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2636H8] },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ltc2632_id);
+
+static const struct of_device_id ltc2632_of_match[] = {
+ {
+ .compatible = "lltc,ltc2632-l12",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2632L12]
+ }, {
+ .compatible = "lltc,ltc2632-l10",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2632L10]
+ }, {
+ .compatible = "lltc,ltc2632-l8",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2632L8]
+ }, {
+ .compatible = "lltc,ltc2632-h12",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2632H12]
+ }, {
+ .compatible = "lltc,ltc2632-h10",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2632H10]
+ }, {
+ .compatible = "lltc,ltc2632-h8",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2632H8]
+ }, {
+ .compatible = "lltc,ltc2634-l12",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2634L12]
+ }, {
+ .compatible = "lltc,ltc2634-l10",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2634L10]
+ }, {
+ .compatible = "lltc,ltc2634-l8",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2634L8]
+ }, {
+ .compatible = "lltc,ltc2634-h12",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2634H12]
+ }, {
+ .compatible = "lltc,ltc2634-h10",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2634H10]
+ }, {
+ .compatible = "lltc,ltc2634-h8",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2634H8]
+ }, {
+ .compatible = "lltc,ltc2636-l12",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2636L12]
+ }, {
+ .compatible = "lltc,ltc2636-l10",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2636L10]
+ }, {
+ .compatible = "lltc,ltc2636-l8",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2636L8]
+ }, {
+ .compatible = "lltc,ltc2636-h12",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2636H12]
+ }, {
+ .compatible = "lltc,ltc2636-h10",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2636H10]
+ }, {
+ .compatible = "lltc,ltc2636-h8",
+ .data = &ltc2632_chip_info_tbl[ID_LTC2636H8]
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ltc2632_of_match);
+
+static struct spi_driver ltc2632_driver = {
+ .driver = {
+ .name = "ltc2632",
+ .of_match_table = of_match_ptr(ltc2632_of_match),
+ },
+ .probe = ltc2632_probe,
+ .remove = ltc2632_remove,
+ .id_table = ltc2632_id,
+};
+module_spi_driver(ltc2632_driver);
+
+MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
+MODULE_DESCRIPTION("LTC2632 DAC SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
new file mode 100644
index 000000000..225b1a374
--- /dev/null
+++ b/drivers/iio/dac/m62332.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * m62332.c - Support for Mitsubishi m62332 DAC
+ *
+ * Copyright (c) 2014 Dmitry Eremin-Solenikov
+ *
+ * Based on max517 driver:
+ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+#include <linux/regulator/consumer.h>
+
+#define M62332_CHANNELS 2
+
+struct m62332_data {
+ struct i2c_client *client;
+ 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)
+{
+ struct m62332_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ u8 outbuf[2];
+ int res;
+
+ if (val == data->raw[channel])
+ return 0;
+
+ outbuf[0] = channel;
+ outbuf[1] = val;
+
+ mutex_lock(&data->mutex);
+
+ if (val) {
+ res = regulator_enable(data->vcc);
+ if (res)
+ goto out;
+ }
+
+ res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf));
+ if (res >= 0 && res != ARRAY_SIZE(outbuf))
+ res = -EIO;
+ if (res < 0)
+ goto out;
+
+ data->raw[channel] = val;
+
+ if (!val)
+ regulator_disable(data->vcc);
+
+ mutex_unlock(&data->mutex);
+
+ return 0;
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return res;
+}
+
+static int m62332_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct m62332_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ /* Corresponds to Vref / 2^(bits) */
+ ret = regulator_get_voltage(data->vcc);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000; /* mV */
+ *val2 = 8;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_RAW:
+ *val = data->raw[chan->channel];
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 1;
+
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int m62332_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ return m62332_set_value(indio_dev, val, chan->channel);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int m62332_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct m62332_data *data = iio_priv(indio_dev);
+ int ret;
+
+ data->save[0] = data->raw[0];
+ data->save[1] = data->raw[1];
+
+ ret = m62332_set_value(indio_dev, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ return m62332_set_value(indio_dev, 0, 1);
+}
+
+static int m62332_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct m62332_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = m62332_set_value(indio_dev, data->save[0], 0);
+ if (ret < 0)
+ return ret;
+
+ 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 const struct iio_info m62332_info = {
+ .read_raw = m62332_read_raw,
+ .write_raw = m62332_write_raw,
+};
+
+#define M62332_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .datasheet_name = "CH" #chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec m62332_channels[M62332_CHANNELS] = {
+ M62332_CHANNEL(0),
+ M62332_CHANNEL(1)
+};
+
+static int m62332_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct m62332_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ mutex_init(&data->mutex);
+
+ data->vcc = devm_regulator_get(&client->dev, "VCC");
+ if (IS_ERR(data->vcc))
+ return PTR_ERR(data->vcc);
+
+ indio_dev->num_channels = ARRAY_SIZE(m62332_channels);
+ indio_dev->channels = m62332_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &m62332_info;
+
+ ret = iio_map_array_register(indio_dev, client->dev.platform_data);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ iio_map_array_unregister(indio_dev);
+
+ return ret;
+}
+
+static int m62332_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+ m62332_set_value(indio_dev, 0, 0);
+ m62332_set_value(indio_dev, 0, 1);
+
+ return 0;
+}
+
+static const struct i2c_device_id m62332_id[] = {
+ { "m62332", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, m62332_id);
+
+static struct i2c_driver m62332_driver = {
+ .driver = {
+ .name = "m62332",
+ .pm = M62332_PM_OPS,
+ },
+ .probe = m62332_probe,
+ .remove = m62332_remove,
+ .id_table = m62332_id,
+};
+module_i2c_driver(m62332_driver);
+
+MODULE_AUTHOR("Dmitry Eremin-Solenikov");
+MODULE_DESCRIPTION("M62332 8-bit DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c
new file mode 100644
index 000000000..daa60386b
--- /dev/null
+++ b/drivers/iio/dac/max517.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max517.c - Support for Maxim MAX517, MAX518 and MAX519
+ *
+ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/dac/max517.h>
+
+#define MAX517_DRV_NAME "max517"
+
+/* Commands */
+#define COMMAND_CHANNEL0 0x00
+#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */
+#define COMMAND_PD 0x08 /* Power Down */
+
+enum max517_device_ids {
+ ID_MAX517,
+ ID_MAX518,
+ ID_MAX519,
+ ID_MAX520,
+ ID_MAX521,
+};
+
+struct max517_data {
+ struct i2c_client *client;
+ unsigned short vref_mv[8];
+};
+
+/*
+ * channel: bit 0: channel 1
+ * bit 1: channel 2
+ * (this way, it's possible to set both channels at once)
+ */
+static int max517_set_value(struct iio_dev *indio_dev,
+ long val, int channel)
+{
+ struct max517_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ u8 outbuf[2];
+ int res;
+
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ outbuf[0] = channel;
+ outbuf[1] = val;
+
+ res = i2c_master_send(client, outbuf, 2);
+ if (res < 0)
+ return res;
+ else if (res != 2)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int max517_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct max517_data *data = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ /* Corresponds to Vref / 2^(bits) */
+ *val = data->vref_mv[chan->channel];
+ *val2 = 8;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int max517_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max517_set_value(indio_dev, val, chan->channel);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int __maybe_unused max517_suspend(struct device *dev)
+{
+ u8 outbuf = COMMAND_PD;
+
+ return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
+}
+
+static int __maybe_unused max517_resume(struct device *dev)
+{
+ u8 outbuf = 0;
+
+ return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
+}
+
+static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume);
+
+static const struct iio_info max517_info = {
+ .read_raw = max517_read_raw,
+ .write_raw = max517_write_raw,
+};
+
+#define MAX517_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec max517_channels[] = {
+ MAX517_CHANNEL(0),
+ MAX517_CHANNEL(1),
+ MAX517_CHANNEL(2),
+ MAX517_CHANNEL(3),
+ MAX517_CHANNEL(4),
+ MAX517_CHANNEL(5),
+ MAX517_CHANNEL(6),
+ MAX517_CHANNEL(7),
+};
+
+static int max517_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max517_data *data;
+ struct iio_dev *indio_dev;
+ struct max517_platform_data *platform_data = client->dev.platform_data;
+ int chan;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ switch (id->driver_data) {
+ case ID_MAX521:
+ indio_dev->num_channels = 8;
+ break;
+ case ID_MAX520:
+ indio_dev->num_channels = 4;
+ break;
+ case ID_MAX519:
+ case ID_MAX518:
+ indio_dev->num_channels = 2;
+ break;
+ default: /* single channel for MAX517 */
+ indio_dev->num_channels = 1;
+ break;
+ }
+ indio_dev->channels = max517_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &max517_info;
+
+ /*
+ * Reference voltage on MAX518 and default is 5V, else take vref_mv
+ * from platform_data
+ */
+ for (chan = 0; chan < indio_dev->num_channels; chan++) {
+ if (id->driver_data == ID_MAX518 || !platform_data)
+ data->vref_mv[chan] = 5000; /* mV */
+ else
+ data->vref_mv[chan] = platform_data->vref_mv[chan];
+ }
+
+ return iio_device_register(indio_dev);
+}
+
+static int max517_remove(struct i2c_client *client)
+{
+ iio_device_unregister(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id max517_id[] = {
+ { "max517", ID_MAX517 },
+ { "max518", ID_MAX518 },
+ { "max519", ID_MAX519 },
+ { "max520", ID_MAX520 },
+ { "max521", ID_MAX521 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max517_id);
+
+static struct i2c_driver max517_driver = {
+ .driver = {
+ .name = MAX517_DRV_NAME,
+ .pm = &max517_pm_ops,
+ },
+ .probe = max517_probe,
+ .remove = max517_remove,
+ .id_table = max517_id,
+};
+module_i2c_driver(max517_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("MAX517/518/519/520/521 8-bit DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c
new file mode 100644
index 000000000..d6bb24db4
--- /dev/null
+++ b/drivers/iio/dac/max5821.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0-only
+ /*
+ * iio/dac/max5821.c
+ * Copyright (C) 2014 Philippe Reynes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+#define MAX5821_MAX_DAC_CHANNELS 2
+
+/* command bytes */
+#define MAX5821_LOAD_DAC_A_IN_REG_B 0x00
+#define MAX5821_LOAD_DAC_B_IN_REG_A 0x10
+#define MAX5821_EXTENDED_COMMAND_MODE 0xf0
+#define MAX5821_READ_DAC_A_COMMAND 0xf1
+#define MAX5821_READ_DAC_B_COMMAND 0xf2
+
+#define MAX5821_EXTENDED_POWER_UP 0x00
+#define MAX5821_EXTENDED_POWER_DOWN_MODE0 0x01
+#define MAX5821_EXTENDED_POWER_DOWN_MODE1 0x02
+#define MAX5821_EXTENDED_POWER_DOWN_MODE2 0x03
+#define MAX5821_EXTENDED_DAC_A 0x04
+#define MAX5821_EXTENDED_DAC_B 0x08
+
+enum max5821_device_ids {
+ ID_MAX5821,
+};
+
+struct max5821_data {
+ struct i2c_client *client;
+ struct regulator *vref_reg;
+ unsigned short vref_mv;
+ bool powerdown[MAX5821_MAX_DAC_CHANNELS];
+ u8 powerdown_mode[MAX5821_MAX_DAC_CHANNELS];
+ struct mutex lock;
+};
+
+static const char * const max5821_powerdown_modes[] = {
+ "three_state",
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+};
+
+enum {
+ MAX5821_THREE_STATE,
+ MAX5821_1KOHM_TO_GND,
+ MAX5821_100KOHM_TO_GND
+};
+
+static int max5821_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct max5821_data *st = iio_priv(indio_dev);
+
+ return st->powerdown_mode[chan->channel];
+}
+
+static int max5821_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct max5821_data *st = iio_priv(indio_dev);
+
+ st->powerdown_mode[chan->channel] = mode;
+
+ return 0;
+}
+
+static const struct iio_enum max5821_powerdown_mode_enum = {
+ .items = max5821_powerdown_modes,
+ .num_items = ARRAY_SIZE(max5821_powerdown_modes),
+ .get = max5821_get_powerdown_mode,
+ .set = max5821_set_powerdown_mode,
+};
+
+static ssize_t max5821_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct max5821_data *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->powerdown[chan->channel]);
+}
+
+static int max5821_sync_powerdown_mode(struct max5821_data *data,
+ const struct iio_chan_spec *chan)
+{
+ u8 outbuf[2];
+
+ outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE;
+
+ if (chan->channel == 0)
+ outbuf[1] = MAX5821_EXTENDED_DAC_A;
+ else
+ outbuf[1] = MAX5821_EXTENDED_DAC_B;
+
+ if (data->powerdown[chan->channel])
+ outbuf[1] |= data->powerdown_mode[chan->channel] + 1;
+ else
+ outbuf[1] |= MAX5821_EXTENDED_POWER_UP;
+
+ return i2c_master_send(data->client, outbuf, 2);
+}
+
+static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct max5821_data *data = iio_priv(indio_dev);
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ data->powerdown[chan->channel] = powerdown;
+
+ ret = max5821_sync_powerdown_mode(data, chan);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info max5821_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = max5821_read_dac_powerdown,
+ .write = max5821_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &max5821_powerdown_mode_enum),
+ { },
+};
+
+#define MAX5821_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = max5821_ext_info, \
+}
+
+static const struct iio_chan_spec max5821_channels[] = {
+ MAX5821_CHANNEL(0),
+ MAX5821_CHANNEL(1)
+};
+
+static const u8 max5821_read_dac_command[] = {
+ MAX5821_READ_DAC_A_COMMAND,
+ MAX5821_READ_DAC_B_COMMAND
+};
+
+static const u8 max5821_load_dac_command[] = {
+ MAX5821_LOAD_DAC_A_IN_REG_B,
+ MAX5821_LOAD_DAC_B_IN_REG_A
+};
+
+static int max5821_get_value(struct iio_dev *indio_dev,
+ int *val, int channel)
+{
+ struct max5821_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ u8 outbuf[1];
+ u8 inbuf[2];
+ int ret;
+
+ if ((channel != 0) && (channel != 1))
+ return -EINVAL;
+
+ outbuf[0] = max5821_read_dac_command[channel];
+
+ mutex_lock(&data->lock);
+
+ ret = i2c_master_send(client, outbuf, 1);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ return ret;
+ } else if (ret != 1) {
+ mutex_unlock(&data->lock);
+ return -EIO;
+ }
+
+ ret = i2c_master_recv(client, inbuf, 2);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ return ret;
+ } else if (ret != 2) {
+ mutex_unlock(&data->lock);
+ return -EIO;
+ }
+
+ mutex_unlock(&data->lock);
+
+ *val = ((inbuf[0] & 0x0f) << 6) | (inbuf[1] >> 2);
+
+ return IIO_VAL_INT;
+}
+
+static int max5821_set_value(struct iio_dev *indio_dev,
+ int val, int channel)
+{
+ struct max5821_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ u8 outbuf[2];
+ int ret;
+
+ if ((val < 0) || (val > 1023))
+ return -EINVAL;
+
+ if ((channel != 0) && (channel != 1))
+ return -EINVAL;
+
+ outbuf[0] = max5821_load_dac_command[channel];
+ outbuf[0] |= val >> 6;
+ outbuf[1] = (val & 0x3f) << 2;
+
+ ret = i2c_master_send(client, outbuf, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int max5821_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max5821_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return max5821_get_value(indio_dev, val, chan->channel);
+ case IIO_CHAN_INFO_SCALE:
+ *val = data->vref_mv;
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max5821_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ if (val2 != 0)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return max5821_set_value(indio_dev, val, chan->channel);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int __maybe_unused max5821_suspend(struct device *dev)
+{
+ u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
+ MAX5821_EXTENDED_DAC_A |
+ MAX5821_EXTENDED_DAC_B |
+ MAX5821_EXTENDED_POWER_DOWN_MODE2 };
+
+ return i2c_master_send(to_i2c_client(dev), outbuf, 2);
+}
+
+static int __maybe_unused max5821_resume(struct device *dev)
+{
+ u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
+ MAX5821_EXTENDED_DAC_A |
+ MAX5821_EXTENDED_DAC_B |
+ MAX5821_EXTENDED_POWER_UP };
+
+ return i2c_master_send(to_i2c_client(dev), outbuf, 2);
+}
+
+static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume);
+
+static const struct iio_info max5821_info = {
+ .read_raw = max5821_read_raw,
+ .write_raw = max5821_write_raw,
+};
+
+static int max5821_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max5821_data *data;
+ struct iio_dev *indio_dev;
+ u32 tmp;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ mutex_init(&data->lock);
+
+ /* max5821 start in powerdown mode 100Kohm to ground */
+ for (tmp = 0; tmp < MAX5821_MAX_DAC_CHANNELS; tmp++) {
+ data->powerdown[tmp] = true;
+ data->powerdown_mode[tmp] = MAX5821_100KOHM_TO_GND;
+ }
+
+ data->vref_reg = devm_regulator_get(&client->dev, "vref");
+ if (IS_ERR(data->vref_reg)) {
+ ret = PTR_ERR(data->vref_reg);
+ dev_err(&client->dev,
+ "Failed to get vref regulator: %d\n", ret);
+ goto error_free_reg;
+ }
+
+ ret = regulator_enable(data->vref_reg);
+ if (ret) {
+ dev_err(&client->dev,
+ "Failed to enable vref regulator: %d\n", ret);
+ goto error_free_reg;
+ }
+
+ ret = regulator_get_voltage(data->vref_reg);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to get voltage on regulator: %d\n", ret);
+ goto error_disable_reg;
+ }
+
+ data->vref_mv = ret / 1000;
+
+ indio_dev->name = id->name;
+ indio_dev->num_channels = ARRAY_SIZE(max5821_channels);
+ indio_dev->channels = max5821_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &max5821_info;
+
+ return iio_device_register(indio_dev);
+
+error_disable_reg:
+ regulator_disable(data->vref_reg);
+
+error_free_reg:
+
+ return ret;
+}
+
+static int max5821_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct max5821_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(data->vref_reg);
+
+ return 0;
+}
+
+static const struct i2c_device_id max5821_id[] = {
+ { "max5821", ID_MAX5821 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max5821_id);
+
+static const struct of_device_id max5821_of_match[] = {
+ { .compatible = "maxim,max5821" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max5821_of_match);
+
+static struct i2c_driver max5821_driver = {
+ .driver = {
+ .name = "max5821",
+ .of_match_table = max5821_of_match,
+ .pm = &max5821_pm_ops,
+ },
+ .probe = max5821_probe,
+ .remove = max5821_remove,
+ .id_table = max5821_id,
+};
+module_i2c_driver(max5821_driver);
+
+MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
+MODULE_DESCRIPTION("MAX5821 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
new file mode 100644
index 000000000..0c0e726ae
--- /dev/null
+++ b/drivers/iio/dac/mcp4725.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mcp4725.c - Support for Microchip MCP4725/6
+ *
+ * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * Based on max517 by Roland Stigge <stigge@antcom.de>
+ *
+ * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC)
+ * (7-bit I2C slave address 0x60, the three LSBs can be configured in
+ * hardware)
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/iio/dac/mcp4725.h>
+
+#define MCP4725_DRV_NAME "mcp4725"
+
+#define MCP472X_REF_VDD 0x00
+#define MCP472X_REF_VREF_UNBUFFERED 0x02
+#define MCP472X_REF_VREF_BUFFERED 0x03
+
+struct mcp4725_data {
+ struct i2c_client *client;
+ int id;
+ unsigned ref_mode;
+ bool vref_buffered;
+ u16 dac_value;
+ bool powerdown;
+ unsigned powerdown_mode;
+ struct regulator *vdd_reg;
+ struct regulator *vref_reg;
+};
+
+static int __maybe_unused mcp4725_suspend(struct device *dev)
+{
+ struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev)));
+ u8 outbuf[2];
+ int ret;
+
+ outbuf[0] = (data->powerdown_mode + 1) << 4;
+ outbuf[1] = 0;
+ data->powerdown = true;
+
+ ret = i2c_master_send(data->client, outbuf, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+ return 0;
+}
+
+static int __maybe_unused mcp4725_resume(struct device *dev)
+{
+ struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev)));
+ u8 outbuf[2];
+ int ret;
+
+ /* restore previous DAC value */
+ outbuf[0] = (data->dac_value >> 8) & 0xf;
+ outbuf[1] = data->dac_value & 0xff;
+ data->powerdown = false;
+
+ ret = i2c_master_send(data->client, outbuf, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+ return 0;
+}
+static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume);
+
+static ssize_t mcp4725_store_eeprom(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ int tries = 20;
+ u8 inoutbuf[3];
+ bool state;
+ int ret;
+
+ ret = strtobool(buf, &state);
+ if (ret < 0)
+ return ret;
+
+ if (!state)
+ return 0;
+
+ inoutbuf[0] = 0x60; /* write EEPROM */
+ inoutbuf[0] |= data->ref_mode << 3;
+ inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0;
+ inoutbuf[1] = data->dac_value >> 4;
+ inoutbuf[2] = (data->dac_value & 0xf) << 4;
+
+ ret = i2c_master_send(data->client, inoutbuf, 3);
+ if (ret < 0)
+ return ret;
+ else if (ret != 3)
+ return -EIO;
+
+ /* wait for write complete, takes up to 50ms */
+ while (tries--) {
+ msleep(20);
+ ret = i2c_master_recv(data->client, inoutbuf, 3);
+ if (ret < 0)
+ return ret;
+ else if (ret != 3)
+ return -EIO;
+
+ if (inoutbuf[0] & 0x80)
+ break;
+ }
+
+ if (tries < 0) {
+ dev_err(&data->client->dev,
+ "mcp4725_store_eeprom() failed, incomplete\n");
+ return -EIO;
+ }
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, NULL, mcp4725_store_eeprom, 0);
+
+static struct attribute *mcp4725_attributes[] = {
+ &iio_dev_attr_store_eeprom.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group mcp4725_attribute_group = {
+ .attrs = mcp4725_attributes,
+};
+
+static const char * const mcp4725_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "500kohm_to_gnd"
+};
+
+static const char * const mcp4726_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "125kohm_to_gnd",
+ "640kohm_to_gnd"
+};
+
+static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+
+ return data->powerdown_mode;
+}
+
+static int mcp4725_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned mode)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+
+ data->powerdown_mode = mode;
+
+ return 0;
+}
+
+static ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", data->powerdown);
+}
+
+static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ bool state;
+ int ret;
+
+ ret = strtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ if (state)
+ ret = mcp4725_suspend(&data->client->dev);
+ else
+ ret = mcp4725_resume(&data->client->dev);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+enum chip_id {
+ MCP4725,
+ MCP4726,
+};
+
+static const struct iio_enum mcp472x_powerdown_mode_enum[] = {
+ [MCP4725] = {
+ .items = mcp4725_powerdown_modes,
+ .num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
+ .get = mcp4725_get_powerdown_mode,
+ .set = mcp4725_set_powerdown_mode,
+ },
+ [MCP4726] = {
+ .items = mcp4726_powerdown_modes,
+ .num_items = ARRAY_SIZE(mcp4726_powerdown_modes),
+ .get = mcp4725_get_powerdown_mode,
+ .set = mcp4725_set_powerdown_mode,
+ },
+};
+
+static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = mcp4725_read_powerdown,
+ .write = mcp4725_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE,
+ &mcp472x_powerdown_mode_enum[MCP4725]),
+ IIO_ENUM_AVAILABLE("powerdown_mode",
+ &mcp472x_powerdown_mode_enum[MCP4725]),
+ { },
+};
+
+static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = mcp4725_read_powerdown,
+ .write = mcp4725_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE,
+ &mcp472x_powerdown_mode_enum[MCP4726]),
+ IIO_ENUM_AVAILABLE("powerdown_mode",
+ &mcp472x_powerdown_mode_enum[MCP4726]),
+ { },
+};
+
+static const struct iio_chan_spec mcp472x_channel[] = {
+ [MCP4725] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = mcp4725_ext_info,
+ },
+ [MCP4726] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = mcp4726_ext_info,
+ },
+};
+
+static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ u8 outbuf[2];
+ int ret;
+
+ if (val >= (1 << 12) || val < 0)
+ return -EINVAL;
+
+ outbuf[0] = (val >> 8) & 0xf;
+ outbuf[1] = val & 0xff;
+
+ ret = i2c_master_send(data->client, outbuf, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int mcp4726_set_cfg(struct iio_dev *indio_dev)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ u8 outbuf[3];
+ int ret;
+
+ outbuf[0] = 0x40;
+ outbuf[0] |= data->ref_mode << 3;
+ if (data->powerdown)
+ outbuf[0] |= data->powerdown << 1;
+ outbuf[1] = data->dac_value >> 4;
+ outbuf[2] = (data->dac_value & 0xf) << 4;
+
+ ret = i2c_master_send(data->client, outbuf, 3);
+ if (ret < 0)
+ return ret;
+ else if (ret != 3)
+ return -EIO;
+ else
+ return 0;
+}
+
+static int mcp4725_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = data->dac_value;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (data->ref_mode == MCP472X_REF_VDD)
+ ret = regulator_get_voltage(data->vdd_reg);
+ else
+ ret = regulator_get_voltage(data->vref_reg);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static int mcp4725_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp4725_set_value(indio_dev, val);
+ data->dac_value = val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info mcp4725_info = {
+ .read_raw = mcp4725_read_raw,
+ .write_raw = mcp4725_write_raw,
+ .attrs = &mcp4725_attribute_group,
+};
+
+static int mcp4725_probe_dt(struct device *dev,
+ struct mcp4725_platform_data *pdata)
+{
+ /* check if is the vref-supply defined */
+ pdata->use_vref = device_property_read_bool(dev, "vref-supply");
+ pdata->vref_buffered =
+ device_property_read_bool(dev, "microchip,vref-buffered");
+
+ return 0;
+}
+
+static int mcp4725_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcp4725_data *data;
+ struct iio_dev *indio_dev;
+ struct mcp4725_platform_data *pdata, pdata_dt;
+ u8 inbuf[4];
+ u8 pd;
+ u8 ref;
+ int err;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ if (dev_fwnode(&client->dev))
+ data->id = (enum chip_id)device_get_match_data(&client->dev);
+ else
+ data->id = id->driver_data;
+ pdata = dev_get_platdata(&client->dev);
+
+ if (!pdata) {
+ err = mcp4725_probe_dt(&client->dev, &pdata_dt);
+ if (err) {
+ dev_err(&client->dev,
+ "invalid platform or devicetree data");
+ return err;
+ }
+ pdata = &pdata_dt;
+ }
+
+ if (data->id == MCP4725 && pdata->use_vref) {
+ dev_err(&client->dev,
+ "external reference is unavailable on MCP4725");
+ return -EINVAL;
+ }
+
+ if (!pdata->use_vref && pdata->vref_buffered) {
+ dev_err(&client->dev,
+ "buffering is unavailable on the internal reference");
+ return -EINVAL;
+ }
+
+ if (!pdata->use_vref)
+ data->ref_mode = MCP472X_REF_VDD;
+ else
+ data->ref_mode = pdata->vref_buffered ?
+ MCP472X_REF_VREF_BUFFERED :
+ MCP472X_REF_VREF_UNBUFFERED;
+
+ data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->vdd_reg))
+ return PTR_ERR(data->vdd_reg);
+
+ err = regulator_enable(data->vdd_reg);
+ if (err)
+ return err;
+
+ if (pdata->use_vref) {
+ data->vref_reg = devm_regulator_get(&client->dev, "vref");
+ if (IS_ERR(data->vref_reg)) {
+ err = PTR_ERR(data->vref_reg);
+ goto err_disable_vdd_reg;
+ }
+
+ err = regulator_enable(data->vref_reg);
+ if (err)
+ goto err_disable_vdd_reg;
+ }
+
+ indio_dev->name = id->name;
+ indio_dev->info = &mcp4725_info;
+ indio_dev->channels = &mcp472x_channel[id->driver_data];
+ indio_dev->num_channels = 1;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* read current DAC value and settings */
+ err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
+
+ if (err < 0) {
+ dev_err(&client->dev, "failed to read DAC value");
+ goto err_disable_vref_reg;
+ }
+ pd = (inbuf[0] >> 1) & 0x3;
+ data->powerdown = pd > 0;
+ data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
+ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
+ if (data->id == MCP4726)
+ ref = (inbuf[3] >> 3) & 0x3;
+
+ if (data->id == MCP4726 && ref != data->ref_mode) {
+ dev_info(&client->dev,
+ "voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
+ data->ref_mode, ref, data->ref_mode);
+ err = mcp4726_set_cfg(indio_dev);
+ if (err < 0)
+ goto err_disable_vref_reg;
+ }
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto err_disable_vref_reg;
+
+ return 0;
+
+err_disable_vref_reg:
+ if (data->vref_reg)
+ regulator_disable(data->vref_reg);
+
+err_disable_vdd_reg:
+ regulator_disable(data->vdd_reg);
+
+ return err;
+}
+
+static int mcp4725_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mcp4725_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (data->vref_reg)
+ regulator_disable(data->vref_reg);
+ regulator_disable(data->vdd_reg);
+
+ return 0;
+}
+
+static const struct i2c_device_id mcp4725_id[] = {
+ { "mcp4725", MCP4725 },
+ { "mcp4726", MCP4726 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp4725_id);
+
+static const struct of_device_id mcp4725_of_match[] = {
+ {
+ .compatible = "microchip,mcp4725",
+ .data = (void *)MCP4725
+ },
+ {
+ .compatible = "microchip,mcp4726",
+ .data = (void *)MCP4726
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp4725_of_match);
+
+static struct i2c_driver mcp4725_driver = {
+ .driver = {
+ .name = MCP4725_DRV_NAME,
+ .of_match_table = mcp4725_of_match,
+ .pm = &mcp4725_pm_ops,
+ },
+ .probe = mcp4725_probe,
+ .remove = mcp4725_remove,
+ .id_table = mcp4725_id,
+};
+module_i2c_driver(mcp4725_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c
new file mode 100644
index 000000000..c4e430b40
--- /dev/null
+++ b/drivers/iio/dac/mcp4922.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * mcp4922.c
+ *
+ * Driver for Microchip Digital to Analog Converters.
+ * Supports MCP4902, MCP4912, and MCP4922.
+ *
+ * Copyright (c) 2014 EMAC Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/bitops.h>
+
+#define MCP4922_NUM_CHANNELS 2
+
+enum mcp4922_supported_device_ids {
+ ID_MCP4902,
+ ID_MCP4912,
+ ID_MCP4922,
+};
+
+struct mcp4922_state {
+ struct spi_device *spi;
+ unsigned int value[MCP4922_NUM_CHANNELS];
+ unsigned int vref_mv;
+ struct regulator *vref_reg;
+ u8 mosi[2] ____cacheline_aligned;
+};
+
+#define MCP4922_CHAN(chan, bits) { \
+ .type = IIO_VOLTAGE, \
+ .output = 1, \
+ .indexed = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 12 - (bits), \
+ }, \
+}
+
+static int mcp4922_spi_write(struct mcp4922_state *state, u8 addr, u32 val)
+{
+ state->mosi[1] = val & 0xff;
+ state->mosi[0] = (addr == 0) ? 0x00 : 0x80;
+ state->mosi[0] |= 0x30 | ((val >> 8) & 0x0f);
+
+ return spi_write(state->spi, state->mosi, 2);
+}
+
+static int mcp4922_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct mcp4922_state *state = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = state->value[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = state->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp4922_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct mcp4922_state *state = iio_priv(indio_dev);
+ int ret;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
+ return -EINVAL;
+ val <<= chan->scan_type.shift;
+
+ ret = mcp4922_spi_write(state, chan->channel, val);
+ if (!ret)
+ state->value[chan->channel] = val;
+ return ret;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec mcp4922_channels[3][MCP4922_NUM_CHANNELS] = {
+ [ID_MCP4902] = { MCP4922_CHAN(0, 8), MCP4922_CHAN(1, 8) },
+ [ID_MCP4912] = { MCP4922_CHAN(0, 10), MCP4922_CHAN(1, 10) },
+ [ID_MCP4922] = { MCP4922_CHAN(0, 12), MCP4922_CHAN(1, 12) },
+};
+
+static const struct iio_info mcp4922_info = {
+ .read_raw = &mcp4922_read_raw,
+ .write_raw = &mcp4922_write_raw,
+};
+
+static int mcp4922_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp4922_state *state;
+ const struct spi_device_id *id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ state->spi = spi;
+ state->vref_reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(state->vref_reg)) {
+ dev_err(&spi->dev, "Vref regulator not specified\n");
+ return PTR_ERR(state->vref_reg);
+ }
+
+ ret = regulator_enable(state->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vref regulator: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(state->vref_reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to read vref regulator: %d\n",
+ ret);
+ goto error_disable_reg;
+ }
+ state->vref_mv = ret / 1000;
+
+ spi_set_drvdata(spi, indio_dev);
+ id = spi_get_device_id(spi);
+ indio_dev->info = &mcp4922_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mcp4922_channels[id->driver_data];
+ indio_dev->num_channels = MCP4922_NUM_CHANNELS;
+ indio_dev->name = id->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n",
+ ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ regulator_disable(state->vref_reg);
+
+ return ret;
+}
+
+static int mcp4922_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct mcp4922_state *state;
+
+ iio_device_unregister(indio_dev);
+ state = iio_priv(indio_dev);
+ regulator_disable(state->vref_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id mcp4922_id[] = {
+ {"mcp4902", ID_MCP4902},
+ {"mcp4912", ID_MCP4912},
+ {"mcp4922", ID_MCP4922},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, mcp4922_id);
+
+static struct spi_driver mcp4922_driver = {
+ .driver = {
+ .name = "mcp4922",
+ },
+ .probe = mcp4922_probe,
+ .remove = mcp4922_remove,
+ .id_table = mcp4922_id,
+};
+module_spi_driver(mcp4922_driver);
+
+MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
+MODULE_DESCRIPTION("Microchip MCP4902, MCP4912, MCP4922 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c
new file mode 100644
index 000000000..906436780
--- /dev/null
+++ b/drivers/iio/dac/stm32-dac-core.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of STM32 DAC driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include "stm32-dac-core.h"
+
+/**
+ * struct stm32_dac_priv - stm32 DAC core private data
+ * @pclk: peripheral clock common for all DACs
+ * @vref: regulator reference
+ * @common: Common data for all DAC instances
+ */
+struct stm32_dac_priv {
+ struct clk *pclk;
+ struct regulator *vref;
+ struct stm32_dac_common common;
+};
+
+/**
+ * struct stm32_dac_cfg - DAC configuration
+ * @has_hfsel: DAC has high frequency control
+ */
+struct stm32_dac_cfg {
+ bool has_hfsel;
+};
+
+static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
+{
+ return container_of(com, struct stm32_dac_priv, common);
+}
+
+static const struct regmap_config stm32_dac_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x3fc,
+};
+
+static int stm32_dac_core_hw_start(struct device *dev)
+{
+ struct stm32_dac_common *common = dev_get_drvdata(dev);
+ struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
+ int ret;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret < 0) {
+ dev_err(dev, "pclk enable failed: %d\n", ret);
+ goto err_regulator_disable;
+ }
+
+ return 0;
+
+err_regulator_disable:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static void stm32_dac_core_hw_stop(struct device *dev)
+{
+ struct stm32_dac_common *common = dev_get_drvdata(dev);
+ struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
+
+ clk_disable_unprepare(priv->pclk);
+ regulator_disable(priv->vref);
+}
+
+static int stm32_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct stm32_dac_cfg *cfg;
+ struct stm32_dac_priv *priv;
+ struct regmap *regmap;
+ struct resource *res;
+ void __iomem *mmio;
+ struct reset_control *rst;
+ int ret;
+
+ if (!dev->of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, &priv->common);
+
+ cfg = (const struct stm32_dac_cfg *)
+ of_match_device(dev->driver->of_match_table, dev)->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio,
+ &stm32_dac_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ priv->common.regmap = regmap;
+
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk)) {
+ ret = PTR_ERR(priv->pclk);
+ dev_err(dev, "pclk get failed\n");
+ return ret;
+ }
+
+ priv->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(priv->vref)) {
+ ret = PTR_ERR(priv->vref);
+ dev_err(dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_dac_core_hw_start(dev);
+ if (ret)
+ goto err_pm_stop;
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref get voltage failed, %d\n", ret);
+ goto err_hw_stop;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ rst = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (rst) {
+ if (IS_ERR(rst)) {
+ ret = dev_err_probe(dev, PTR_ERR(rst), "reset get failed\n");
+ goto err_hw_stop;
+ }
+
+ reset_control_assert(rst);
+ udelay(2);
+ reset_control_deassert(rst);
+ }
+
+ if (cfg && cfg->has_hfsel) {
+ /* When clock speed is higher than 80MHz, set HFSEL */
+ priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
+ ret = regmap_update_bits(regmap, STM32_DAC_CR,
+ STM32H7_DAC_CR_HFSEL,
+ priv->common.hfsel ?
+ STM32H7_DAC_CR_HFSEL : 0);
+ if (ret)
+ goto err_hw_stop;
+ }
+
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to populate DT children\n");
+ goto err_hw_stop;
+ }
+
+ pm_runtime_put(dev);
+
+ return 0;
+
+err_hw_stop:
+ stm32_dac_core_hw_stop(dev);
+err_pm_stop:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ return ret;
+}
+
+static int stm32_dac_remove(struct platform_device *pdev)
+{
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ stm32_dac_core_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused 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);
+ int ret;
+
+ if (priv->common.hfsel) {
+ /* restore hfsel (maybe lost under low power state) */
+ ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR,
+ STM32H7_DAC_CR_HFSEL,
+ STM32H7_DAC_CR_HFSEL);
+ if (ret)
+ return ret;
+ }
+
+ return pm_runtime_force_resume(dev);
+}
+
+static int __maybe_unused 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)
+{
+ 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)
+};
+
+static const struct stm32_dac_cfg stm32h7_dac_cfg = {
+ .has_hfsel = true,
+};
+
+static const struct of_device_id stm32_dac_of_match[] = {
+ {
+ .compatible = "st,stm32f4-dac-core",
+ }, {
+ .compatible = "st,stm32h7-dac-core",
+ .data = (void *)&stm32h7_dac_cfg,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
+
+static struct platform_driver stm32_dac_driver = {
+ .probe = stm32_dac_probe,
+ .remove = stm32_dac_remove,
+ .driver = {
+ .name = "stm32-dac-core",
+ .of_match_table = stm32_dac_of_match,
+ .pm = &stm32_dac_core_pm_ops,
+ },
+};
+module_platform_driver(stm32_dac_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 DAC core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-dac-core");
diff --git a/drivers/iio/dac/stm32-dac-core.h b/drivers/iio/dac/stm32-dac-core.h
new file mode 100644
index 000000000..d3b415fb9
--- /dev/null
+++ b/drivers/iio/dac/stm32-dac-core.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of STM32 DAC driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ */
+
+#ifndef __STM32_DAC_CORE_H
+#define __STM32_DAC_CORE_H
+
+#include <linux/regmap.h>
+
+/* STM32 DAC registers */
+#define STM32_DAC_CR 0x00
+#define STM32_DAC_DHR12R1 0x08
+#define STM32_DAC_DHR12R2 0x14
+#define STM32_DAC_DOR1 0x2C
+#define STM32_DAC_DOR2 0x30
+
+/* STM32_DAC_CR bit fields */
+#define STM32_DAC_CR_EN1 BIT(0)
+#define STM32H7_DAC_CR_HFSEL BIT(15)
+#define STM32_DAC_CR_EN2 BIT(16)
+
+/**
+ * struct stm32_dac_common - stm32 DAC driver common data (for all instances)
+ * @regmap: DAC registers shared via regmap
+ * @vref_mv: reference voltage (mv)
+ * @hfsel: high speed bus clock selected
+ */
+struct stm32_dac_common {
+ struct regmap *regmap;
+ int vref_mv;
+ bool hfsel;
+};
+
+#endif
diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
new file mode 100644
index 000000000..12dec68c1
--- /dev/null
+++ b/drivers/iio/dac/stm32-dac.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of STM32 DAC driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Amelie Delaunay <amelie.delaunay@st.com>
+ * Fabrice Gasnier <fabrice.gasnier@st.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "stm32-dac-core.h"
+
+#define STM32_DAC_CHANNEL_1 1
+#define STM32_DAC_CHANNEL_2 2
+#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
+
+#define STM32_DAC_AUTO_SUSPEND_DELAY_MS 2000
+
+/**
+ * struct stm32_dac - private data of DAC driver
+ * @common: reference to DAC common data
+ * @lock: lock to protect against potential races when reading
+ * and update CR, to keep it in sync with pm_runtime
+ */
+struct stm32_dac {
+ struct stm32_dac_common *common;
+ struct mutex lock;
+};
+
+static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+ u32 en, val;
+ int ret;
+
+ ret = regmap_read(dac->common->regmap, STM32_DAC_CR, &val);
+ if (ret < 0)
+ return ret;
+ if (STM32_DAC_IS_CHAN_1(channel))
+ en = FIELD_GET(STM32_DAC_CR_EN1, val);
+ else
+ en = FIELD_GET(STM32_DAC_CR_EN2, val);
+
+ return !!en;
+}
+
+static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
+ bool enable)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
+ u32 en = enable ? msk : 0;
+ int ret;
+
+ /* already enabled / disabled ? */
+ mutex_lock(&dac->lock);
+ ret = stm32_dac_is_enabled(indio_dev, ch);
+ if (ret < 0 || enable == !!ret) {
+ mutex_unlock(&dac->lock);
+ return ret < 0 ? ret : 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ mutex_unlock(&dac->lock);
+ return ret;
+ }
+ }
+
+ ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
+ mutex_unlock(&dac->lock);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "%s failed\n", en ?
+ "Enable" : "Disable");
+ goto err_put_pm;
+ }
+
+ /*
+ * When HFSEL is set, it is not allowed to write the DHRx register
+ * during 8 clock cycles after the ENx bit is set. It is not allowed
+ * to make software/hardware trigger during this period either.
+ */
+ if (en && dac->common->hfsel)
+ udelay(1);
+
+ if (!enable) {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
+ return 0;
+
+err_put_pm:
+ if (enable) {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
+ return ret;
+}
+
+static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
+{
+ int ret;
+
+ if (STM32_DAC_IS_CHAN_1(channel))
+ ret = regmap_read(dac->common->regmap, STM32_DAC_DOR1, val);
+ else
+ ret = regmap_read(dac->common->regmap, STM32_DAC_DOR2, val);
+
+ return ret ? ret : IIO_VAL_INT;
+}
+
+static int stm32_dac_set_value(struct stm32_dac *dac, int channel, int val)
+{
+ int ret;
+
+ if (STM32_DAC_IS_CHAN_1(channel))
+ ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R1, val);
+ else
+ ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R2, val);
+
+ return ret;
+}
+
+static int stm32_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return stm32_dac_get_value(dac, chan->channel, val);
+ case IIO_CHAN_INFO_SCALE:
+ *val = dac->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int stm32_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return stm32_dac_set_value(dac, chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int stm32_dac_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_dac *dac = iio_priv(indio_dev);
+
+ if (!readval)
+ return regmap_write(dac->common->regmap, reg, writeval);
+ else
+ return regmap_read(dac->common->regmap, reg, readval);
+}
+
+static const struct iio_info stm32_dac_iio_info = {
+ .read_raw = stm32_dac_read_raw,
+ .write_raw = stm32_dac_write_raw,
+ .debugfs_reg_access = stm32_dac_debugfs_reg_access,
+};
+
+static const char * const stm32_dac_powerdown_modes[] = {
+ "three_state",
+};
+
+static int stm32_dac_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ return 0;
+}
+
+static int stm32_dac_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int type)
+{
+ return 0;
+}
+
+static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ int ret = stm32_dac_is_enabled(indio_dev, chan->channel);
+
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret ? 0 : 1);
+}
+
+static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ ret = stm32_dac_set_enable_state(indio_dev, chan->channel, !powerdown);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static const struct iio_enum stm32_dac_powerdown_mode_en = {
+ .items = stm32_dac_powerdown_modes,
+ .num_items = ARRAY_SIZE(stm32_dac_powerdown_modes),
+ .get = stm32_dac_get_powerdown_mode,
+ .set = stm32_dac_set_powerdown_mode,
+};
+
+static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = stm32_dac_read_powerdown,
+ .write = stm32_dac_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en),
+ {},
+};
+
+#define STM32_DAC_CHANNEL(chan, name) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ /* scan_index is always 0 as num_channels is 1 */ \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .datasheet_name = name, \
+ .ext_info = stm32_dac_ext_info \
+}
+
+static const struct iio_chan_spec stm32_dac_channels[] = {
+ STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_1, "out1"),
+ STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_2, "out2"),
+};
+
+static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
+{
+ struct device_node *np = indio_dev->dev.of_node;
+ unsigned int i;
+ u32 channel;
+ int ret;
+
+ ret = of_property_read_u32(np, "reg", &channel);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to read reg property\n");
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stm32_dac_channels); i++) {
+ if (stm32_dac_channels[i].channel == channel)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32_dac_channels)) {
+ dev_err(&indio_dev->dev, "Invalid reg property\n");
+ return -EINVAL;
+ }
+
+ indio_dev->channels = &stm32_dac_channels[i];
+ /*
+ * Expose only one channel here, as they can be used independently,
+ * with separate trigger. Then separate IIO devices are instantiated
+ * to manage this.
+ */
+ indio_dev->num_channels = 1;
+
+ return 0;
+};
+
+static int stm32_dac_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct stm32_dac *dac;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
+ if (!indio_dev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, indio_dev);
+
+ dac = iio_priv(indio_dev);
+ dac->common = dev_get_drvdata(pdev->dev.parent);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_dac_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ mutex_init(&dac->lock);
+
+ ret = stm32_dac_chan_of_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /* Get stm32-dac-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_DAC_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_pm_put;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ return ret;
+}
+
+static int stm32_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ iio_device_unregister(indio_dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dac_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int channel = indio_dev->channels[0].channel;
+ int ret;
+
+ /* Ensure DAC is disabled before suspend */
+ ret = stm32_dac_is_enabled(indio_dev, channel);
+ if (ret)
+ return ret < 0 ? ret : -EBUSY;
+
+ 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 const struct of_device_id stm32_dac_of_match[] = {
+ { .compatible = "st,stm32-dac", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
+
+static struct platform_driver stm32_dac_driver = {
+ .probe = stm32_dac_probe,
+ .remove = stm32_dac_remove,
+ .driver = {
+ .name = "stm32-dac",
+ .of_match_table = stm32_dac_of_match,
+ .pm = &stm32_dac_pm_ops,
+ },
+};
+module_platform_driver(stm32_dac_driver);
+
+MODULE_ALIAS("platform:stm32-dac");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c
new file mode 100644
index 000000000..de33c1fc6
--- /dev/null
+++ b/drivers/iio/dac/ti-dac082s085.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ti-dac082s085.c - Texas Instruments 8/10/12-bit 2/4-channel DAC driver
+ *
+ * Copyright (C) 2017 KUNBUS GmbH
+ *
+ * https://www.ti.com/lit/ds/symlink/dac082s085.pdf
+ * https://www.ti.com/lit/ds/symlink/dac102s085.pdf
+ * https://www.ti.com/lit/ds/symlink/dac122s085.pdf
+ * https://www.ti.com/lit/ds/symlink/dac084s085.pdf
+ * https://www.ti.com/lit/ds/symlink/dac104s085.pdf
+ * https://www.ti.com/lit/ds/symlink/dac124s085.pdf
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+enum { dual_8bit, dual_10bit, dual_12bit, quad_8bit, quad_10bit, quad_12bit };
+
+struct ti_dac_spec {
+ u8 num_channels;
+ u8 resolution;
+};
+
+static const struct ti_dac_spec ti_dac_spec[] = {
+ [dual_8bit] = { .num_channels = 2, .resolution = 8 },
+ [dual_10bit] = { .num_channels = 2, .resolution = 10 },
+ [dual_12bit] = { .num_channels = 2, .resolution = 12 },
+ [quad_8bit] = { .num_channels = 4, .resolution = 8 },
+ [quad_10bit] = { .num_channels = 4, .resolution = 10 },
+ [quad_12bit] = { .num_channels = 4, .resolution = 12 },
+};
+
+/**
+ * struct ti_dac_chip - TI DAC chip
+ * @lock: protects write sequences
+ * @vref: regulator generating Vref
+ * @mesg: SPI message to perform a write
+ * @xfer: SPI transfer used by @mesg
+ * @val: cached value of each output
+ * @powerdown: whether the chip is powered down
+ * @powerdown_mode: selected by the user
+ * @resolution: resolution of the chip
+ * @buf: buffer for @xfer
+ */
+struct ti_dac_chip {
+ struct mutex lock;
+ struct regulator *vref;
+ struct spi_message mesg;
+ struct spi_transfer xfer;
+ u16 val[4];
+ bool powerdown;
+ u8 powerdown_mode;
+ u8 resolution;
+ u8 buf[2] ____cacheline_aligned;
+};
+
+#define WRITE_NOT_UPDATE(chan) (0x00 | (chan) << 6)
+#define WRITE_AND_UPDATE(chan) (0x10 | (chan) << 6)
+#define WRITE_ALL_UPDATE 0x20
+#define POWERDOWN(mode) (0x30 | ((mode) + 1) << 6)
+
+static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 cmd, u16 val)
+{
+ u8 shift = 12 - ti_dac->resolution;
+
+ ti_dac->buf[0] = cmd | (val >> (8 - shift));
+ ti_dac->buf[1] = (val << shift) & 0xff;
+ return spi_sync(ti_dac->mesg.spi, &ti_dac->mesg);
+}
+
+static const char * const ti_dac_powerdown_modes[] = {
+ "2.5kohm_to_gnd", "100kohm_to_gnd", "three_state",
+};
+
+static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ return ti_dac->powerdown_mode;
+}
+
+static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (ti_dac->powerdown_mode == mode)
+ return 0;
+
+ mutex_lock(&ti_dac->lock);
+ if (ti_dac->powerdown) {
+ ret = ti_dac_cmd(ti_dac, POWERDOWN(mode), 0);
+ if (ret)
+ goto out;
+ }
+ ti_dac->powerdown_mode = mode;
+
+out:
+ mutex_unlock(&ti_dac->lock);
+ return ret;
+}
+
+static const struct iio_enum ti_dac_powerdown_mode = {
+ .items = ti_dac_powerdown_modes,
+ .num_items = ARRAY_SIZE(ti_dac_powerdown_modes),
+ .get = ti_dac_get_powerdown_mode,
+ .set = ti_dac_set_powerdown_mode,
+};
+
+static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", ti_dac->powerdown);
+}
+
+static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ if (ti_dac->powerdown == powerdown)
+ return len;
+
+ mutex_lock(&ti_dac->lock);
+ if (powerdown)
+ ret = ti_dac_cmd(ti_dac, POWERDOWN(ti_dac->powerdown_mode), 0);
+ else
+ ret = ti_dac_cmd(ti_dac, WRITE_AND_UPDATE(0), ti_dac->val[0]);
+ if (!ret)
+ ti_dac->powerdown = powerdown;
+ mutex_unlock(&ti_dac->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ti_dac_read_powerdown,
+ .write = ti_dac_write_powerdown,
+ .shared = IIO_SHARED_BY_TYPE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode),
+ { },
+};
+
+#define TI_DAC_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .channel = (chan), \
+ .address = (chan), \
+ .indexed = true, \
+ .output = true, \
+ .datasheet_name = (const char[]){ 'A' + (chan), 0 }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = ti_dac_ext_info, \
+}
+
+static const struct iio_chan_spec ti_dac_channels[] = {
+ TI_DAC_CHANNEL(0),
+ TI_DAC_CHANNEL(1),
+ TI_DAC_CHANNEL(2),
+ TI_DAC_CHANNEL(3),
+};
+
+static int ti_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = ti_dac->val[chan->channel];
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(ti_dac->vref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = ti_dac->resolution;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ti_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (ti_dac->val[chan->channel] == val)
+ return 0;
+
+ if (val >= (1 << ti_dac->resolution) || val < 0)
+ return -EINVAL;
+
+ if (ti_dac->powerdown)
+ return -EBUSY;
+
+ mutex_lock(&ti_dac->lock);
+ ret = ti_dac_cmd(ti_dac, WRITE_AND_UPDATE(chan->channel), val);
+ if (!ret)
+ ti_dac->val[chan->channel] = val;
+ mutex_unlock(&ti_dac->lock);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info ti_dac_info = {
+ .read_raw = ti_dac_read_raw,
+ .write_raw = ti_dac_write_raw,
+ .write_raw_get_fmt = ti_dac_write_raw_get_fmt,
+};
+
+static int ti_dac_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct ti_dac_spec *spec;
+ struct ti_dac_chip *ti_dac;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &ti_dac_info;
+ indio_dev->name = spi->modalias;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ti_dac_channels;
+ spi_set_drvdata(spi, indio_dev);
+
+ ti_dac = iio_priv(indio_dev);
+ ti_dac->xfer.tx_buf = &ti_dac->buf;
+ ti_dac->xfer.len = sizeof(ti_dac->buf);
+ spi_message_init_with_transfers(&ti_dac->mesg, &ti_dac->xfer, 1);
+ ti_dac->mesg.spi = spi;
+
+ spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = spec->num_channels;
+ ti_dac->resolution = spec->resolution;
+
+ ti_dac->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(ti_dac->vref))
+ return PTR_ERR(ti_dac->vref);
+
+ ret = regulator_enable(ti_dac->vref);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&ti_dac->lock);
+
+ ret = ti_dac_cmd(ti_dac, WRITE_ALL_UPDATE, 0);
+ if (ret) {
+ dev_err(dev, "failed to initialize outputs to 0\n");
+ goto err;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ mutex_destroy(&ti_dac->lock);
+ regulator_disable(ti_dac->vref);
+ return ret;
+}
+
+static int ti_dac_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_destroy(&ti_dac->lock);
+ regulator_disable(ti_dac->vref);
+
+ return 0;
+}
+
+static const struct of_device_id ti_dac_of_id[] = {
+ { .compatible = "ti,dac082s085" },
+ { .compatible = "ti,dac102s085" },
+ { .compatible = "ti,dac122s085" },
+ { .compatible = "ti,dac084s085" },
+ { .compatible = "ti,dac104s085" },
+ { .compatible = "ti,dac124s085" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_dac_of_id);
+
+static const struct spi_device_id ti_dac_spi_id[] = {
+ { "dac082s085", dual_8bit },
+ { "dac102s085", dual_10bit },
+ { "dac122s085", dual_12bit },
+ { "dac084s085", quad_8bit },
+ { "dac104s085", quad_10bit },
+ { "dac124s085", quad_12bit },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ti_dac_spi_id);
+
+static struct spi_driver ti_dac_driver = {
+ .driver = {
+ .name = "ti-dac082s085",
+ .of_match_table = ti_dac_of_id,
+ },
+ .probe = ti_dac_probe,
+ .remove = ti_dac_remove,
+ .id_table = ti_dac_spi_id,
+};
+module_spi_driver(ti_dac_driver);
+
+MODULE_AUTHOR("Lukas Wunner <lukas@wunner.de>");
+MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 2/4-channel DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c
new file mode 100644
index 000000000..c0714cb1e
--- /dev/null
+++ b/drivers/iio/dac/ti-dac5571.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver
+ *
+ * Copyright (C) 2018 Prevas A/S
+ *
+ * https://www.ti.com/lit/ds/symlink/dac5571.pdf
+ * https://www.ti.com/lit/ds/symlink/dac6571.pdf
+ * https://www.ti.com/lit/ds/symlink/dac7571.pdf
+ * https://www.ti.com/lit/ds/symlink/dac5574.pdf
+ * https://www.ti.com/lit/ds/symlink/dac6574.pdf
+ * https://www.ti.com/lit/ds/symlink/dac7574.pdf
+ * https://www.ti.com/lit/ds/symlink/dac5573.pdf
+ * https://www.ti.com/lit/ds/symlink/dac6573.pdf
+ * https://www.ti.com/lit/ds/symlink/dac7573.pdf
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regulator/consumer.h>
+
+enum chip_id {
+ single_8bit, single_10bit, single_12bit,
+ quad_8bit, quad_10bit, quad_12bit
+};
+
+struct dac5571_spec {
+ u8 num_channels;
+ u8 resolution;
+};
+
+static const struct dac5571_spec dac5571_spec[] = {
+ [single_8bit] = {.num_channels = 1, .resolution = 8},
+ [single_10bit] = {.num_channels = 1, .resolution = 10},
+ [single_12bit] = {.num_channels = 1, .resolution = 12},
+ [quad_8bit] = {.num_channels = 4, .resolution = 8},
+ [quad_10bit] = {.num_channels = 4, .resolution = 10},
+ [quad_12bit] = {.num_channels = 4, .resolution = 12},
+};
+
+struct dac5571_data {
+ struct i2c_client *client;
+ int id;
+ struct mutex lock;
+ struct regulator *vref;
+ u16 val[4];
+ bool powerdown[4];
+ u8 powerdown_mode[4];
+ struct dac5571_spec const *spec;
+ int (*dac5571_cmd)(struct dac5571_data *data, int channel, u16 val);
+ int (*dac5571_pwrdwn)(struct dac5571_data *data, int channel, u8 pwrdwn);
+ u8 buf[3] ____cacheline_aligned;
+};
+
+#define DAC5571_POWERDOWN(mode) ((mode) + 1)
+#define DAC5571_POWERDOWN_FLAG BIT(0)
+#define DAC5571_CHANNEL_SELECT 1
+#define DAC5571_LOADMODE_DIRECT BIT(4)
+#define DAC5571_SINGLE_PWRDWN_BITS 4
+#define DAC5571_QUAD_PWRDWN_BITS 6
+
+static int dac5571_cmd_single(struct dac5571_data *data, int channel, u16 val)
+{
+ unsigned int shift;
+
+ shift = 12 - data->spec->resolution;
+ data->buf[1] = val << shift;
+ data->buf[0] = val >> (8 - shift);
+
+ if (i2c_master_send(data->client, data->buf, 2) != 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val)
+{
+ unsigned int shift;
+
+ shift = 16 - data->spec->resolution;
+ data->buf[2] = val << shift;
+ data->buf[1] = (val >> (8 - shift));
+ data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) |
+ DAC5571_LOADMODE_DIRECT;
+
+ if (i2c_master_send(data->client, data->buf, 3) != 3)
+ return -EIO;
+
+ return 0;
+}
+
+static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn)
+{
+ data->buf[1] = 0;
+ data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS;
+
+ if (i2c_master_send(data->client, data->buf, 2) != 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn)
+{
+ data->buf[2] = 0;
+ data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS;
+ data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) |
+ DAC5571_LOADMODE_DIRECT | DAC5571_POWERDOWN_FLAG;
+
+ if (i2c_master_send(data->client, data->buf, 3) != 3)
+ return -EIO;
+
+ return 0;
+}
+
+static const char *const dac5571_powerdown_modes[] = {
+ "1kohm_to_gnd", "100kohm_to_gnd", "three_state",
+};
+
+static int dac5571_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct dac5571_data *data = iio_priv(indio_dev);
+
+ return data->powerdown_mode[chan->channel];
+}
+
+static int dac5571_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct dac5571_data *data = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (data->powerdown_mode[chan->channel] == mode)
+ return 0;
+
+ mutex_lock(&data->lock);
+ if (data->powerdown[chan->channel]) {
+ ret = data->dac5571_pwrdwn(data, chan->channel,
+ DAC5571_POWERDOWN(mode));
+ if (ret)
+ goto out;
+ }
+ data->powerdown_mode[chan->channel] = mode;
+
+ out:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static const struct iio_enum dac5571_powerdown_mode = {
+ .items = dac5571_powerdown_modes,
+ .num_items = ARRAY_SIZE(dac5571_powerdown_modes),
+ .get = dac5571_get_powerdown_mode,
+ .set = dac5571_set_powerdown_mode,
+};
+
+static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct dac5571_data *data = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", data->powerdown[chan->channel]);
+}
+
+static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct dac5571_data *data = iio_priv(indio_dev);
+ bool powerdown;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ if (data->powerdown[chan->channel] == powerdown)
+ return len;
+
+ mutex_lock(&data->lock);
+ if (powerdown)
+ ret = data->dac5571_pwrdwn(data, chan->channel,
+ DAC5571_POWERDOWN(data->powerdown_mode[chan->channel]));
+ else
+ ret = data->dac5571_cmd(data, chan->channel,
+ data->val[chan->channel]);
+ if (ret)
+ goto out;
+
+ data->powerdown[chan->channel] = powerdown;
+
+ out:
+ mutex_unlock(&data->lock);
+
+ return ret ? ret : len;
+}
+
+
+static const struct iio_chan_spec_ext_info dac5571_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = dac5571_read_powerdown,
+ .write = dac5571_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &dac5571_powerdown_mode),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode),
+ {},
+};
+
+#define dac5571_CHANNEL(chan, name) { \
+ .type = IIO_VOLTAGE, \
+ .channel = (chan), \
+ .address = (chan), \
+ .indexed = true, \
+ .output = true, \
+ .datasheet_name = name, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = dac5571_ext_info, \
+}
+
+static const struct iio_chan_spec dac5571_channels[] = {
+ dac5571_CHANNEL(0, "A"),
+ dac5571_CHANNEL(1, "B"),
+ dac5571_CHANNEL(2, "C"),
+ dac5571_CHANNEL(3, "D"),
+};
+
+static int dac5571_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct dac5571_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = data->val[chan->channel];
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(data->vref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = data->spec->resolution;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dac5571_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct dac5571_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (data->val[chan->channel] == val)
+ return 0;
+
+ if (val >= (1 << data->spec->resolution) || val < 0)
+ return -EINVAL;
+
+ if (data->powerdown[chan->channel])
+ return -EBUSY;
+
+ mutex_lock(&data->lock);
+ ret = data->dac5571_cmd(data, chan->channel, val);
+ if (ret == 0)
+ data->val[chan->channel] = val;
+ mutex_unlock(&data->lock);
+ return ret;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dac5571_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info dac5571_info = {
+ .read_raw = dac5571_read_raw,
+ .write_raw = dac5571_write_raw,
+ .write_raw_get_fmt = dac5571_write_raw_get_fmt,
+};
+
+static int dac5571_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ const struct dac5571_spec *spec;
+ struct dac5571_data *data;
+ struct iio_dev *indio_dev;
+ int ret, i;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ indio_dev->info = &dac5571_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dac5571_channels;
+
+ spec = &dac5571_spec[id->driver_data];
+ indio_dev->num_channels = spec->num_channels;
+ data->spec = spec;
+
+ data->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(data->vref))
+ return PTR_ERR(data->vref);
+
+ ret = regulator_enable(data->vref);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&data->lock);
+
+ switch (spec->num_channels) {
+ case 1:
+ data->dac5571_cmd = dac5571_cmd_single;
+ data->dac5571_pwrdwn = dac5571_pwrdwn_single;
+ break;
+ case 4:
+ data->dac5571_cmd = dac5571_cmd_quad;
+ data->dac5571_pwrdwn = dac5571_pwrdwn_quad;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < spec->num_channels; i++) {
+ ret = data->dac5571_cmd(data, i, 0);
+ if (ret) {
+ dev_err(dev, "failed to initialize channel %d to 0\n", i);
+ goto err;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err;
+
+ return 0;
+
+ err:
+ regulator_disable(data->vref);
+ return ret;
+}
+
+static int dac5571_remove(struct i2c_client *i2c)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+ struct dac5571_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(data->vref);
+
+ return 0;
+}
+
+static const struct of_device_id dac5571_of_id[] = {
+ {.compatible = "ti,dac5571"},
+ {.compatible = "ti,dac6571"},
+ {.compatible = "ti,dac7571"},
+ {.compatible = "ti,dac5574"},
+ {.compatible = "ti,dac6574"},
+ {.compatible = "ti,dac7574"},
+ {.compatible = "ti,dac5573"},
+ {.compatible = "ti,dac6573"},
+ {.compatible = "ti,dac7573"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, dac5571_of_id);
+
+static const struct i2c_device_id dac5571_id[] = {
+ {"dac5571", single_8bit},
+ {"dac6571", single_10bit},
+ {"dac7571", single_12bit},
+ {"dac5574", quad_8bit},
+ {"dac6574", quad_10bit},
+ {"dac7574", quad_12bit},
+ {"dac5573", quad_8bit},
+ {"dac6573", quad_10bit},
+ {"dac7573", quad_12bit},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, dac5571_id);
+
+static struct i2c_driver dac5571_driver = {
+ .driver = {
+ .name = "ti-dac5571",
+ .of_match_table = dac5571_of_id,
+ },
+ .probe = dac5571_probe,
+ .remove = dac5571_remove,
+ .id_table = dac5571_id,
+};
+module_i2c_driver(dac5571_driver);
+
+MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
+MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c
new file mode 100644
index 000000000..63171e42f
--- /dev/null
+++ b/drivers/iio/dac/ti-dac7311.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ti-dac7311.c - Texas Instruments 8/10/12-bit 1-channel DAC driver
+ *
+ * Copyright (C) 2018 CMC NV
+ *
+ * https://www.ti.com/lit/ds/symlink/dac7311.pdf
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+enum {
+ ID_DAC5311 = 0,
+ ID_DAC6311,
+ ID_DAC7311,
+};
+
+enum {
+ POWER_1KOHM_TO_GND = 0,
+ POWER_100KOHM_TO_GND,
+ POWER_TRI_STATE,
+};
+
+struct ti_dac_spec {
+ u8 resolution;
+};
+
+static const struct ti_dac_spec ti_dac_spec[] = {
+ [ID_DAC5311] = { .resolution = 8 },
+ [ID_DAC6311] = { .resolution = 10 },
+ [ID_DAC7311] = { .resolution = 12 },
+};
+
+/**
+ * struct ti_dac_chip - TI DAC chip
+ * @lock: protects write sequences
+ * @vref: regulator generating Vref
+ * @spi: SPI device to send data to the device
+ * @val: cached value
+ * @powerdown: whether the chip is powered down
+ * @powerdown_mode: selected by the user
+ * @resolution: resolution of the chip
+ * @buf: buffer for transfer data
+ */
+struct ti_dac_chip {
+ struct mutex lock;
+ struct regulator *vref;
+ struct spi_device *spi;
+ u16 val;
+ bool powerdown;
+ u8 powerdown_mode;
+ u8 resolution;
+ u8 buf[2] ____cacheline_aligned;
+};
+
+static u8 ti_dac_get_power(struct ti_dac_chip *ti_dac, bool powerdown)
+{
+ if (powerdown)
+ return ti_dac->powerdown_mode + 1;
+
+ return 0;
+}
+
+static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 power, u16 val)
+{
+ u8 shift = 14 - ti_dac->resolution;
+
+ ti_dac->buf[0] = (val << shift) & 0xFF;
+ ti_dac->buf[1] = (power << 6) | (val >> (8 - shift));
+ return spi_write(ti_dac->spi, ti_dac->buf, 2);
+}
+
+static const char * const ti_dac_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state",
+};
+
+static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ return ti_dac->powerdown_mode;
+}
+
+static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ ti_dac->powerdown_mode = mode;
+ return 0;
+}
+
+static const struct iio_enum ti_dac_powerdown_mode = {
+ .items = ti_dac_powerdown_modes,
+ .num_items = ARRAY_SIZE(ti_dac_powerdown_modes),
+ .get = ti_dac_get_powerdown_mode,
+ .set = ti_dac_set_powerdown_mode,
+};
+
+static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", ti_dac->powerdown);
+}
+
+static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ bool powerdown;
+ u8 power;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ power = ti_dac_get_power(ti_dac, powerdown);
+
+ mutex_lock(&ti_dac->lock);
+ ret = ti_dac_cmd(ti_dac, power, 0);
+ if (!ret)
+ ti_dac->powerdown = powerdown;
+ mutex_unlock(&ti_dac->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ti_dac_read_powerdown,
+ .write = ti_dac_write_powerdown,
+ .shared = IIO_SHARED_BY_TYPE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode),
+ { },
+};
+
+#define TI_DAC_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .channel = (chan), \
+ .output = true, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = ti_dac_ext_info, \
+}
+
+static const struct iio_chan_spec ti_dac_channels[] = {
+ TI_DAC_CHANNEL(0),
+};
+
+static int ti_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = ti_dac->val;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(ti_dac->vref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = ti_dac->resolution;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static int ti_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ u8 power = ti_dac_get_power(ti_dac, ti_dac->powerdown);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (ti_dac->val == val)
+ return 0;
+
+ if (val >= (1 << ti_dac->resolution) || val < 0)
+ return -EINVAL;
+
+ if (ti_dac->powerdown)
+ return -EBUSY;
+
+ mutex_lock(&ti_dac->lock);
+ ret = ti_dac_cmd(ti_dac, power, val);
+ if (!ret)
+ ti_dac->val = val;
+ mutex_unlock(&ti_dac->lock);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info ti_dac_info = {
+ .read_raw = ti_dac_read_raw,
+ .write_raw = ti_dac_write_raw,
+ .write_raw_get_fmt = ti_dac_write_raw_get_fmt,
+};
+
+static int ti_dac_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct ti_dac_spec *spec;
+ struct ti_dac_chip *ti_dac;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac));
+ if (!indio_dev) {
+ dev_err(dev, "can not allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ spi->mode = SPI_MODE_1;
+ spi->bits_per_word = 16;
+ spi_setup(spi);
+
+ indio_dev->info = &ti_dac_info;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ti_dac_channels;
+ spi_set_drvdata(spi, indio_dev);
+
+ ti_dac = iio_priv(indio_dev);
+ ti_dac->powerdown = false;
+ ti_dac->spi = spi;
+
+ spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = 1;
+ ti_dac->resolution = spec->resolution;
+
+ ti_dac->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(ti_dac->vref)) {
+ dev_err(dev, "error to get regulator\n");
+ return PTR_ERR(ti_dac->vref);
+ }
+
+ ret = regulator_enable(ti_dac->vref);
+ if (ret < 0) {
+ dev_err(dev, "can not enable regulator\n");
+ return ret;
+ }
+
+ mutex_init(&ti_dac->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "fail to register iio device: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mutex_destroy(&ti_dac->lock);
+ regulator_disable(ti_dac->vref);
+ return ret;
+}
+
+static int ti_dac_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_destroy(&ti_dac->lock);
+ regulator_disable(ti_dac->vref);
+ return 0;
+}
+
+static const struct of_device_id ti_dac_of_id[] = {
+ { .compatible = "ti,dac5311" },
+ { .compatible = "ti,dac6311" },
+ { .compatible = "ti,dac7311" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_dac_of_id);
+
+static const struct spi_device_id ti_dac_spi_id[] = {
+ { "dac5311", ID_DAC5311 },
+ { "dac6311", ID_DAC6311 },
+ { "dac7311", ID_DAC7311 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ti_dac_spi_id);
+
+static struct spi_driver ti_dac_driver = {
+ .driver = {
+ .name = "ti-dac7311",
+ .of_match_table = ti_dac_of_id,
+ },
+ .probe = ti_dac_probe,
+ .remove = ti_dac_remove,
+ .id_table = ti_dac_spi_id,
+};
+module_spi_driver(ti_dac_driver);
+
+MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
+MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1-channel DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c
new file mode 100644
index 000000000..4c0f4b5e9
--- /dev/null
+++ b/drivers/iio/dac/ti-dac7612.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
+ *
+ * Copyright 2019 Qtechnology A/S
+ * 2019 Ricardo Ribalda <ribalda@kernel.org>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+
+#define DAC7612_RESOLUTION 12
+#define DAC7612_ADDRESS 4
+#define DAC7612_START 5
+
+struct dac7612 {
+ struct spi_device *spi;
+ struct gpio_desc *loaddacs;
+ uint16_t cache[2];
+
+ /*
+ * Lock to protect the state of the device from potential concurrent
+ * write accesses from userspace. The write operation requires an
+ * SPI write, then toggling of a GPIO, so the lock aims to protect
+ * the sanity of the entire sequence of operation.
+ */
+ struct mutex lock;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ uint8_t data[2] ____cacheline_aligned;
+};
+
+static int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val)
+{
+ int ret;
+
+ priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS);
+ priv->data[0] |= val >> 8;
+ priv->data[1] = val & 0xff;
+
+ priv->cache[channel] = val;
+
+ ret = spi_write(priv->spi, priv->data, sizeof(priv->data));
+ if (ret)
+ return ret;
+
+ gpiod_set_value(priv->loaddacs, 1);
+ gpiod_set_value(priv->loaddacs, 0);
+
+ return 0;
+}
+
+#define dac7612_CHANNEL(chan, name) { \
+ .type = IIO_VOLTAGE, \
+ .channel = (chan), \
+ .indexed = 1, \
+ .output = 1, \
+ .datasheet_name = name, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec dac7612_channels[] = {
+ dac7612_CHANNEL(0, "OUTA"),
+ dac7612_CHANNEL(1, "OUTB"),
+};
+
+static int dac7612_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct dac7612 *priv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ priv = iio_priv(iio_dev);
+ *val = priv->cache[chan->channel];
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dac7612_write_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct dac7612 *priv = iio_priv(iio_dev);
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2)
+ return -EINVAL;
+
+ if (val == priv->cache[chan->channel])
+ return 0;
+
+ mutex_lock(&priv->lock);
+ ret = dac7612_cmd_single(priv, chan->channel, val);
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static const struct iio_info dac7612_info = {
+ .read_raw = dac7612_read_raw,
+ .write_raw = dac7612_write_raw,
+};
+
+static int dac7612_probe(struct spi_device *spi)
+{
+ struct iio_dev *iio_dev;
+ struct dac7612 *priv;
+ int i;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ /*
+ * LOADDACS pin can be controlled by the driver or externally.
+ * When controlled by the driver, the DAC value is updated after
+ * every write.
+ * When the driver does not control the PIN, the user or an external
+ * event can change the value of all DACs by pulsing down the LOADDACs
+ * pin.
+ */
+ priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->loaddacs))
+ return PTR_ERR(priv->loaddacs);
+ priv->spi = spi;
+ spi_set_drvdata(spi, iio_dev);
+ iio_dev->info = &dac7612_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = dac7612_channels;
+ iio_dev->num_channels = ARRAY_SIZE(priv->cache);
+ iio_dev->name = spi_get_device_id(spi)->name;
+
+ mutex_init(&priv->lock);
+
+ for (i = 0; i < ARRAY_SIZE(priv->cache); i++) {
+ ret = dac7612_cmd_single(priv, i, 0);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, iio_dev);
+}
+
+static const struct spi_device_id dac7612_id[] = {
+ {"ti-dac7612"},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, dac7612_id);
+
+static const struct of_device_id dac7612_of_match[] = {
+ { .compatible = "ti,dac7612" },
+ { .compatible = "ti,dac7612u" },
+ { .compatible = "ti,dac7612ub" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dac7612_of_match);
+
+static struct spi_driver dac7612_driver = {
+ .driver = {
+ .name = "ti-dac7612",
+ .of_match_table = dac7612_of_match,
+ },
+ .probe = dac7612_probe,
+ .id_table = dac7612_id,
+};
+module_spi_driver(dac7612_driver);
+
+MODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
+MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c
new file mode 100644
index 000000000..636b4009f
--- /dev/null
+++ b/drivers/iio/dac/vf610_dac.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale Vybrid vf610 DAC driver
+ *
+ * Copyright 2016 Toradex AG
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define VF610_DACx_STATCTRL 0x20
+
+#define VF610_DAC_DACEN BIT(15)
+#define VF610_DAC_DACRFS BIT(14)
+#define VF610_DAC_LPEN BIT(11)
+
+#define VF610_DAC_DAT0(x) ((x) & 0xFFF)
+
+enum vf610_conversion_mode_sel {
+ VF610_DAC_CONV_HIGH_POWER,
+ VF610_DAC_CONV_LOW_POWER,
+};
+
+struct vf610_dac {
+ struct clk *clk;
+ struct device *dev;
+ enum vf610_conversion_mode_sel conv_mode;
+ void __iomem *regs;
+ struct mutex lock;
+};
+
+static void vf610_dac_init(struct vf610_dac *info)
+{
+ int val;
+
+ info->conv_mode = VF610_DAC_CONV_LOW_POWER;
+ val = VF610_DAC_DACEN | VF610_DAC_DACRFS |
+ VF610_DAC_LPEN;
+ writel(val, info->regs + VF610_DACx_STATCTRL);
+}
+
+static void vf610_dac_exit(struct vf610_dac *info)
+{
+ int val;
+
+ val = readl(info->regs + VF610_DACx_STATCTRL);
+ val &= ~VF610_DAC_DACEN;
+ writel(val, info->regs + VF610_DACx_STATCTRL);
+}
+
+static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+ int val;
+
+ mutex_lock(&info->lock);
+ info->conv_mode = mode;
+ val = readl(info->regs + VF610_DACx_STATCTRL);
+ if (mode)
+ val |= VF610_DAC_LPEN;
+ else
+ val &= ~VF610_DAC_LPEN;
+ writel(val, info->regs + VF610_DACx_STATCTRL);
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ return info->conv_mode;
+}
+
+static const char * const vf610_conv_modes[] = { "high-power", "low-power" };
+
+static const struct iio_enum vf610_conversion_mode = {
+ .items = vf610_conv_modes,
+ .num_items = ARRAY_SIZE(vf610_conv_modes),
+ .get = vf610_get_conversion_mode,
+ .set = vf610_set_conversion_mode,
+};
+
+static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
+ IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR,
+ &vf610_conversion_mode),
+ {},
+};
+
+#define VF610_DAC_CHAN(_chan_type) { \
+ .type = (_chan_type), \
+ .output = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = vf610_ext_info, \
+}
+
+static const struct iio_chan_spec vf610_dac_iio_channels[] = {
+ VF610_DAC_CHAN(IIO_VOLTAGE),
+};
+
+static int vf610_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = VF610_DAC_DAT0(readl(info->regs));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * DACRFS is always 1 for valid reference and typical
+ * reference voltage as per Vybrid datasheet is 3.3V
+ * from section 9.1.2.1 of Vybrid datasheet
+ */
+ *val = 3300 /* mV */;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vf610_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2,
+ long mask)
+{
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&info->lock);
+ writel(VF610_DAC_DAT0(val), info->regs);
+ mutex_unlock(&info->lock);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info vf610_dac_iio_info = {
+ .read_raw = &vf610_read_raw,
+ .write_raw = &vf610_write_raw,
+};
+
+static const struct of_device_id vf610_dac_match[] = {
+ { .compatible = "fsl,vf610-dac", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_dac_match);
+
+static int vf610_dac_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct vf610_dac *info;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct vf610_dac));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = &pdev->dev;
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->clk = devm_clk_get(&pdev->dev, "dac");
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "Failed getting clock, err = %ld\n",
+ PTR_ERR(info->clk));
+ return PTR_ERR(info->clk);
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &vf610_dac_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = vf610_dac_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels);
+
+ mutex_init(&info->lock);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock\n");
+ return ret;
+ }
+
+ vf610_dac_init(info);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device\n");
+ goto error_iio_device_register;
+ }
+
+ return 0;
+
+error_iio_device_register:
+ vf610_dac_exit(info);
+ clk_disable_unprepare(info->clk);
+
+ return ret;
+}
+
+static int vf610_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ vf610_dac_exit(info);
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vf610_dac_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct vf610_dac *info = iio_priv(indio_dev);
+
+ vf610_dac_exit(info);
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+static int vf610_dac_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct vf610_dac *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+
+ vf610_dac_init(info);
+
+ return 0;
+}
+#endif
+
+static 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,
+ .remove = vf610_dac_remove,
+ .driver = {
+ .name = "vf610-dac",
+ .of_match_table = vf610_dac_match,
+ .pm = &vf610_dac_pm_ops,
+ },
+};
+module_platform_driver(vf610_dac_driver);
+
+MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
+MODULE_DESCRIPTION("Freescale VF610 DAC driver");
+MODULE_LICENSE("GPL v2");