xref: /linux/drivers/iio/dac/ad5706r.c (revision d2c9a99135da931377240942d44f3dea104cedb8)
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, &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