1*081ce276SAlexis Czezar Torreno // SPDX-License-Identifier: GPL-2.0-only
2*081ce276SAlexis Czezar Torreno /*
3*081ce276SAlexis Czezar Torreno * AD5706R 16-bit Current Output Digital to Analog Converter
4*081ce276SAlexis Czezar Torreno *
5*081ce276SAlexis Czezar Torreno * Copyright 2026 Analog Devices Inc.
6*081ce276SAlexis Czezar Torreno */
7*081ce276SAlexis Czezar Torreno
8*081ce276SAlexis Czezar Torreno #include <linux/array_size.h>
9*081ce276SAlexis Czezar Torreno #include <linux/bits.h>
10*081ce276SAlexis Czezar Torreno #include <linux/dev_printk.h>
11*081ce276SAlexis Czezar Torreno #include <linux/err.h>
12*081ce276SAlexis Czezar Torreno #include <linux/iio/iio.h>
13*081ce276SAlexis Czezar Torreno #include <linux/module.h>
14*081ce276SAlexis Czezar Torreno #include <linux/regmap.h>
15*081ce276SAlexis Czezar Torreno #include <linux/spi/spi.h>
16*081ce276SAlexis Czezar Torreno #include <linux/types.h>
17*081ce276SAlexis Czezar Torreno #include <linux/unaligned.h>
18*081ce276SAlexis Czezar Torreno
19*081ce276SAlexis Czezar Torreno /* SPI frame layout */
20*081ce276SAlexis Czezar Torreno #define AD5706R_RD_MASK BIT(15)
21*081ce276SAlexis Czezar Torreno #define AD5706R_ADDR_MASK GENMASK(11, 0)
22*081ce276SAlexis Czezar Torreno
23*081ce276SAlexis Czezar Torreno /* Registers */
24*081ce276SAlexis Czezar Torreno #define AD5706R_REG_DAC_INPUT_A_CH(x) (0x60 + ((x) * 2))
25*081ce276SAlexis Czezar Torreno #define AD5706R_REG_DAC_DATA_READBACK_CH(x) (0x68 + ((x) * 2))
26*081ce276SAlexis Czezar Torreno
27*081ce276SAlexis Czezar Torreno #define AD5706R_DAC_RESOLUTION 16
28*081ce276SAlexis Czezar Torreno #define AD5706R_DAC_MAX_CODE GENMASK(15, 0)
29*081ce276SAlexis Czezar Torreno #define AD5706R_MULTIBYTE_REG_START 0x14
30*081ce276SAlexis Czezar Torreno #define AD5706R_MULTIBYTE_REG_END 0x71
31*081ce276SAlexis Czezar Torreno #define AD5706R_MAX_REG 0x77
32*081ce276SAlexis Czezar Torreno
33*081ce276SAlexis Czezar Torreno struct ad5706r_state {
34*081ce276SAlexis Czezar Torreno struct spi_device *spi;
35*081ce276SAlexis Czezar Torreno struct regmap *regmap;
36*081ce276SAlexis Czezar Torreno
37*081ce276SAlexis Czezar Torreno u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN);
38*081ce276SAlexis Czezar Torreno u8 rx_buf[4];
39*081ce276SAlexis Czezar Torreno };
40*081ce276SAlexis Czezar Torreno
ad5706r_reg_len(unsigned int reg)41*081ce276SAlexis Czezar Torreno static int ad5706r_reg_len(unsigned int reg)
42*081ce276SAlexis Czezar Torreno {
43*081ce276SAlexis Czezar Torreno if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END)
44*081ce276SAlexis Czezar Torreno return 2;
45*081ce276SAlexis Czezar Torreno
46*081ce276SAlexis Czezar Torreno return 1;
47*081ce276SAlexis Czezar Torreno }
48*081ce276SAlexis Czezar Torreno
ad5706r_regmap_write(void * context,const void * data,size_t count)49*081ce276SAlexis Czezar Torreno static int ad5706r_regmap_write(void *context, const void *data, size_t count)
50*081ce276SAlexis Czezar Torreno {
51*081ce276SAlexis Czezar Torreno struct ad5706r_state *st = context;
52*081ce276SAlexis Czezar Torreno unsigned int num_bytes;
53*081ce276SAlexis Czezar Torreno u16 reg, val;
54*081ce276SAlexis Czezar Torreno
55*081ce276SAlexis Czezar Torreno if (count != 4)
56*081ce276SAlexis Czezar Torreno return -EINVAL;
57*081ce276SAlexis Czezar Torreno
58*081ce276SAlexis Czezar Torreno reg = get_unaligned_be16(data);
59*081ce276SAlexis Czezar Torreno val = get_unaligned_be16(data + 2);
60*081ce276SAlexis Czezar Torreno num_bytes = ad5706r_reg_len(reg);
61*081ce276SAlexis Czezar Torreno
62*081ce276SAlexis Czezar Torreno struct spi_transfer xfer = {
63*081ce276SAlexis Czezar Torreno .tx_buf = st->tx_buf,
64*081ce276SAlexis Czezar Torreno .len = num_bytes + 2,
65*081ce276SAlexis Czezar Torreno };
66*081ce276SAlexis Czezar Torreno
67*081ce276SAlexis Czezar Torreno put_unaligned_be16(reg, &st->tx_buf[0]);
68*081ce276SAlexis Czezar Torreno
69*081ce276SAlexis Czezar Torreno if (num_bytes == 1)
70*081ce276SAlexis Czezar Torreno st->tx_buf[2] = (u8)val;
71*081ce276SAlexis Czezar Torreno else if (num_bytes == 2)
72*081ce276SAlexis Czezar Torreno put_unaligned_be16(val, &st->tx_buf[2]);
73*081ce276SAlexis Czezar Torreno else
74*081ce276SAlexis Czezar Torreno return -EINVAL;
75*081ce276SAlexis Czezar Torreno
76*081ce276SAlexis Czezar Torreno return spi_sync_transfer(st->spi, &xfer, 1);
77*081ce276SAlexis Czezar Torreno }
78*081ce276SAlexis Czezar Torreno
ad5706r_regmap_read(void * context,const void * reg_buf,size_t reg_size,void * val_buf,size_t val_size)79*081ce276SAlexis Czezar Torreno static int ad5706r_regmap_read(void *context, const void *reg_buf,
80*081ce276SAlexis Czezar Torreno size_t reg_size, void *val_buf, size_t val_size)
81*081ce276SAlexis Czezar Torreno {
82*081ce276SAlexis Czezar Torreno struct ad5706r_state *st = context;
83*081ce276SAlexis Czezar Torreno unsigned int num_bytes;
84*081ce276SAlexis Czezar Torreno u16 reg, cmd, val;
85*081ce276SAlexis Czezar Torreno int ret;
86*081ce276SAlexis Czezar Torreno
87*081ce276SAlexis Czezar Torreno if (reg_size != 2 || val_size != 2)
88*081ce276SAlexis Czezar Torreno return -EINVAL;
89*081ce276SAlexis Czezar Torreno
90*081ce276SAlexis Czezar Torreno reg = get_unaligned_be16(reg_buf);
91*081ce276SAlexis Czezar Torreno num_bytes = ad5706r_reg_len(reg);
92*081ce276SAlexis Czezar Torreno
93*081ce276SAlexis Czezar Torreno /* Full duplex, device responds immediately after command */
94*081ce276SAlexis Czezar Torreno struct spi_transfer xfer = {
95*081ce276SAlexis Czezar Torreno .tx_buf = st->tx_buf,
96*081ce276SAlexis Czezar Torreno .rx_buf = st->rx_buf,
97*081ce276SAlexis Czezar Torreno .len = 2 + num_bytes,
98*081ce276SAlexis Czezar Torreno };
99*081ce276SAlexis Czezar Torreno
100*081ce276SAlexis Czezar Torreno cmd = AD5706R_RD_MASK | (reg & AD5706R_ADDR_MASK);
101*081ce276SAlexis Czezar Torreno put_unaligned_be16(cmd, &st->tx_buf[0]);
102*081ce276SAlexis Czezar Torreno put_unaligned_be16(0, &st->tx_buf[2]);
103*081ce276SAlexis Czezar Torreno
104*081ce276SAlexis Czezar Torreno ret = spi_sync_transfer(st->spi, &xfer, 1);
105*081ce276SAlexis Czezar Torreno if (ret)
106*081ce276SAlexis Czezar Torreno return ret;
107*081ce276SAlexis Czezar Torreno
108*081ce276SAlexis Czezar Torreno /* Extract value from response (skip 2-byte command echo) */
109*081ce276SAlexis Czezar Torreno if (num_bytes == 1)
110*081ce276SAlexis Czezar Torreno val = st->rx_buf[2];
111*081ce276SAlexis Czezar Torreno else if (num_bytes == 2)
112*081ce276SAlexis Czezar Torreno val = get_unaligned_be16(&st->rx_buf[2]);
113*081ce276SAlexis Czezar Torreno else
114*081ce276SAlexis Czezar Torreno return -EINVAL;
115*081ce276SAlexis Czezar Torreno
116*081ce276SAlexis Czezar Torreno put_unaligned_be16(val, val_buf);
117*081ce276SAlexis Czezar Torreno
118*081ce276SAlexis Czezar Torreno return 0;
119*081ce276SAlexis Czezar Torreno }
120*081ce276SAlexis Czezar Torreno
ad5706r_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)121*081ce276SAlexis Czezar Torreno static int ad5706r_read_raw(struct iio_dev *indio_dev,
122*081ce276SAlexis Czezar Torreno struct iio_chan_spec const *chan,
123*081ce276SAlexis Czezar Torreno int *val, int *val2, long mask)
124*081ce276SAlexis Czezar Torreno {
125*081ce276SAlexis Czezar Torreno struct ad5706r_state *st = iio_priv(indio_dev);
126*081ce276SAlexis Czezar Torreno unsigned int reg, reg_val;
127*081ce276SAlexis Czezar Torreno int ret;
128*081ce276SAlexis Czezar Torreno
129*081ce276SAlexis Czezar Torreno switch (mask) {
130*081ce276SAlexis Czezar Torreno case IIO_CHAN_INFO_RAW:
131*081ce276SAlexis Czezar Torreno reg = AD5706R_REG_DAC_DATA_READBACK_CH(chan->channel);
132*081ce276SAlexis Czezar Torreno ret = regmap_read(st->regmap, reg, ®_val);
133*081ce276SAlexis Czezar Torreno if (ret)
134*081ce276SAlexis Czezar Torreno return ret;
135*081ce276SAlexis Czezar Torreno
136*081ce276SAlexis Czezar Torreno *val = reg_val;
137*081ce276SAlexis Czezar Torreno return IIO_VAL_INT;
138*081ce276SAlexis Czezar Torreno case IIO_CHAN_INFO_SCALE:
139*081ce276SAlexis Czezar Torreno *val = 50;
140*081ce276SAlexis Czezar Torreno *val2 = AD5706R_DAC_RESOLUTION;
141*081ce276SAlexis Czezar Torreno return IIO_VAL_FRACTIONAL_LOG2;
142*081ce276SAlexis Czezar Torreno default:
143*081ce276SAlexis Czezar Torreno return -EINVAL;
144*081ce276SAlexis Czezar Torreno }
145*081ce276SAlexis Czezar Torreno }
146*081ce276SAlexis Czezar Torreno
ad5706r_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)147*081ce276SAlexis Czezar Torreno static int ad5706r_write_raw(struct iio_dev *indio_dev,
148*081ce276SAlexis Czezar Torreno struct iio_chan_spec const *chan,
149*081ce276SAlexis Czezar Torreno int val, int val2, long mask)
150*081ce276SAlexis Czezar Torreno {
151*081ce276SAlexis Czezar Torreno struct ad5706r_state *st = iio_priv(indio_dev);
152*081ce276SAlexis Czezar Torreno unsigned int reg;
153*081ce276SAlexis Czezar Torreno
154*081ce276SAlexis Czezar Torreno switch (mask) {
155*081ce276SAlexis Czezar Torreno case IIO_CHAN_INFO_RAW:
156*081ce276SAlexis Czezar Torreno if (val < 0 || val > AD5706R_DAC_MAX_CODE)
157*081ce276SAlexis Czezar Torreno return -EINVAL;
158*081ce276SAlexis Czezar Torreno
159*081ce276SAlexis Czezar Torreno reg = AD5706R_REG_DAC_INPUT_A_CH(chan->channel);
160*081ce276SAlexis Czezar Torreno return regmap_write(st->regmap, reg, val);
161*081ce276SAlexis Czezar Torreno default:
162*081ce276SAlexis Czezar Torreno return -EINVAL;
163*081ce276SAlexis Czezar Torreno }
164*081ce276SAlexis Czezar Torreno }
165*081ce276SAlexis Czezar Torreno
166*081ce276SAlexis Czezar Torreno static const struct regmap_bus ad5706r_regmap_bus = {
167*081ce276SAlexis Czezar Torreno .write = ad5706r_regmap_write,
168*081ce276SAlexis Czezar Torreno .read = ad5706r_regmap_read,
169*081ce276SAlexis Czezar Torreno .reg_format_endian_default = REGMAP_ENDIAN_BIG,
170*081ce276SAlexis Czezar Torreno .val_format_endian_default = REGMAP_ENDIAN_BIG,
171*081ce276SAlexis Czezar Torreno };
172*081ce276SAlexis Czezar Torreno
173*081ce276SAlexis Czezar Torreno static const struct regmap_config ad5706r_regmap_config = {
174*081ce276SAlexis Czezar Torreno .reg_bits = 16,
175*081ce276SAlexis Czezar Torreno .val_bits = 16,
176*081ce276SAlexis Czezar Torreno .max_register = AD5706R_MAX_REG,
177*081ce276SAlexis Czezar Torreno };
178*081ce276SAlexis Czezar Torreno
179*081ce276SAlexis Czezar Torreno static const struct iio_info ad5706r_info = {
180*081ce276SAlexis Czezar Torreno .read_raw = ad5706r_read_raw,
181*081ce276SAlexis Czezar Torreno .write_raw = ad5706r_write_raw,
182*081ce276SAlexis Czezar Torreno };
183*081ce276SAlexis Czezar Torreno
184*081ce276SAlexis Czezar Torreno #define AD5706R_CHAN(_channel) { \
185*081ce276SAlexis Czezar Torreno .type = IIO_CURRENT, \
186*081ce276SAlexis Czezar Torreno .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
187*081ce276SAlexis Czezar Torreno BIT(IIO_CHAN_INFO_SCALE), \
188*081ce276SAlexis Czezar Torreno .output = 1, \
189*081ce276SAlexis Czezar Torreno .indexed = 1, \
190*081ce276SAlexis Czezar Torreno .channel = _channel, \
191*081ce276SAlexis Czezar Torreno }
192*081ce276SAlexis Czezar Torreno
193*081ce276SAlexis Czezar Torreno static const struct iio_chan_spec ad5706r_channels[] = {
194*081ce276SAlexis Czezar Torreno AD5706R_CHAN(0),
195*081ce276SAlexis Czezar Torreno AD5706R_CHAN(1),
196*081ce276SAlexis Czezar Torreno AD5706R_CHAN(2),
197*081ce276SAlexis Czezar Torreno AD5706R_CHAN(3),
198*081ce276SAlexis Czezar Torreno };
199*081ce276SAlexis Czezar Torreno
ad5706r_probe(struct spi_device * spi)200*081ce276SAlexis Czezar Torreno static int ad5706r_probe(struct spi_device *spi)
201*081ce276SAlexis Czezar Torreno {
202*081ce276SAlexis Czezar Torreno struct device *dev = &spi->dev;
203*081ce276SAlexis Czezar Torreno struct iio_dev *indio_dev;
204*081ce276SAlexis Czezar Torreno struct ad5706r_state *st;
205*081ce276SAlexis Czezar Torreno
206*081ce276SAlexis Czezar Torreno indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
207*081ce276SAlexis Czezar Torreno if (!indio_dev)
208*081ce276SAlexis Czezar Torreno return -ENOMEM;
209*081ce276SAlexis Czezar Torreno
210*081ce276SAlexis Czezar Torreno st = iio_priv(indio_dev);
211*081ce276SAlexis Czezar Torreno st->spi = spi;
212*081ce276SAlexis Czezar Torreno
213*081ce276SAlexis Czezar Torreno st->regmap = devm_regmap_init(dev, &ad5706r_regmap_bus,
214*081ce276SAlexis Czezar Torreno st, &ad5706r_regmap_config);
215*081ce276SAlexis Czezar Torreno if (IS_ERR(st->regmap))
216*081ce276SAlexis Czezar Torreno return dev_err_probe(dev, PTR_ERR(st->regmap),
217*081ce276SAlexis Czezar Torreno "Failed to init regmap\n");
218*081ce276SAlexis Czezar Torreno
219*081ce276SAlexis Czezar Torreno indio_dev->name = "ad5706r";
220*081ce276SAlexis Czezar Torreno indio_dev->info = &ad5706r_info;
221*081ce276SAlexis Czezar Torreno indio_dev->modes = INDIO_DIRECT_MODE;
222*081ce276SAlexis Czezar Torreno indio_dev->channels = ad5706r_channels;
223*081ce276SAlexis Czezar Torreno indio_dev->num_channels = ARRAY_SIZE(ad5706r_channels);
224*081ce276SAlexis Czezar Torreno
225*081ce276SAlexis Czezar Torreno return devm_iio_device_register(dev, indio_dev);
226*081ce276SAlexis Czezar Torreno }
227*081ce276SAlexis Czezar Torreno
228*081ce276SAlexis Czezar Torreno static const struct of_device_id ad5706r_of_match[] = {
229*081ce276SAlexis Czezar Torreno { .compatible = "adi,ad5706r" },
230*081ce276SAlexis Czezar Torreno { }
231*081ce276SAlexis Czezar Torreno };
232*081ce276SAlexis Czezar Torreno MODULE_DEVICE_TABLE(of, ad5706r_of_match);
233*081ce276SAlexis Czezar Torreno
234*081ce276SAlexis Czezar Torreno static const struct spi_device_id ad5706r_id[] = {
235*081ce276SAlexis Czezar Torreno { "ad5706r" },
236*081ce276SAlexis Czezar Torreno { }
237*081ce276SAlexis Czezar Torreno };
238*081ce276SAlexis Czezar Torreno MODULE_DEVICE_TABLE(spi, ad5706r_id);
239*081ce276SAlexis Czezar Torreno
240*081ce276SAlexis Czezar Torreno static struct spi_driver ad5706r_driver = {
241*081ce276SAlexis Czezar Torreno .driver = {
242*081ce276SAlexis Czezar Torreno .name = "ad5706r",
243*081ce276SAlexis Czezar Torreno .of_match_table = ad5706r_of_match,
244*081ce276SAlexis Czezar Torreno },
245*081ce276SAlexis Czezar Torreno .probe = ad5706r_probe,
246*081ce276SAlexis Czezar Torreno .id_table = ad5706r_id,
247*081ce276SAlexis Czezar Torreno };
248*081ce276SAlexis Czezar Torreno module_spi_driver(ad5706r_driver);
249*081ce276SAlexis Czezar Torreno
250*081ce276SAlexis Czezar Torreno MODULE_AUTHOR("Alexis Czezar Torreno <alexisczezar.torreno@analog.com>");
251*081ce276SAlexis Czezar Torreno MODULE_DESCRIPTION("AD5706R 16-bit Current Output DAC driver");
252*081ce276SAlexis Czezar Torreno MODULE_LICENSE("GPL");
253