]>
Commit | Line | Data |
---|---|---|
cbbb8198 AT |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * AD5770R Digital to analog converters driver | |
4 | * | |
5 | * Copyright 2018 Analog Devices Inc. | |
6 | */ | |
7 | ||
8 | #include <linux/bits.h> | |
9 | #include <linux/delay.h> | |
10 | #include <linux/device.h> | |
11 | #include <linux/gpio/consumer.h> | |
12 | #include <linux/iio/iio.h> | |
13 | #include <linux/iio/sysfs.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/property.h> | |
17 | #include <linux/regmap.h> | |
18 | #include <linux/regulator/consumer.h> | |
19 | #include <linux/spi/spi.h> | |
20 | ||
21 | #define ADI_SPI_IF_CONFIG_A 0x00 | |
22 | #define ADI_SPI_IF_CONFIG_B 0x01 | |
23 | #define ADI_SPI_IF_DEVICE_CONFIG 0x02 | |
24 | #define ADI_SPI_IF_CHIP_TYPE 0x03 | |
25 | #define ADI_SPI_IF_PRODUCT_ID_L 0x04 | |
26 | #define ADI_SPI_IF_PRODUCT_ID_H 0x05 | |
27 | #define ADI_SPI_IF_CHIP_GRADE 0x06 | |
28 | #define ADI_SPI_IF_SCRACTH_PAD 0x0A | |
29 | #define ADI_SPI_IF_SPI_REVISION 0x0B | |
30 | #define ADI_SPI_IF_SPI_VENDOR_L 0x0C | |
31 | #define ADI_SPI_IF_SPI_VENDOR_H 0x0D | |
32 | #define ADI_SPI_IF_SPI_STREAM_MODE 0x0E | |
33 | #define ADI_SPI_IF_CONFIG_C 0x10 | |
34 | #define ADI_SPI_IF_STATUS_A 0x11 | |
35 | ||
36 | /* ADI_SPI_IF_CONFIG_A */ | |
37 | #define ADI_SPI_IF_SW_RESET_MSK (BIT(0) | BIT(7)) | |
38 | #define ADI_SPI_IF_SW_RESET_SEL(x) ((x) & ADI_SPI_IF_SW_RESET_MSK) | |
39 | #define ADI_SPI_IF_ADDR_ASC_MSK (BIT(2) | BIT(5)) | |
40 | #define ADI_SPI_IF_ADDR_ASC_SEL(x) (((x) << 2) & ADI_SPI_IF_ADDR_ASC_MSK) | |
41 | ||
42 | /* ADI_SPI_IF_CONFIG_B */ | |
43 | #define ADI_SPI_IF_SINGLE_INS_MSK BIT(7) | |
44 | #define ADI_SPI_IF_SINGLE_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x) | |
45 | #define ADI_SPI_IF_SHORT_INS_MSK BIT(7) | |
46 | #define ADI_SPI_IF_SHORT_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x) | |
47 | ||
48 | /* ADI_SPI_IF_CONFIG_C */ | |
49 | #define ADI_SPI_IF_STRICT_REG_MSK BIT(5) | |
50 | #define ADI_SPI_IF_STRICT_REG_GET(x) FIELD_GET(ADI_SPI_IF_STRICT_REG_MSK, x) | |
51 | ||
52 | /* AD5770R configuration registers */ | |
53 | #define AD5770R_CHANNEL_CONFIG 0x14 | |
54 | #define AD5770R_OUTPUT_RANGE(ch) (0x15 + (ch)) | |
55 | #define AD5770R_FILTER_RESISTOR(ch) (0x1D + (ch)) | |
56 | #define AD5770R_REFERENCE 0x1B | |
57 | #define AD5770R_DAC_LSB(ch) (0x26 + 2 * (ch)) | |
58 | #define AD5770R_DAC_MSB(ch) (0x27 + 2 * (ch)) | |
59 | #define AD5770R_CH_SELECT 0x34 | |
60 | #define AD5770R_CH_ENABLE 0x44 | |
61 | ||
62 | /* AD5770R_CHANNEL_CONFIG */ | |
63 | #define AD5770R_CFG_CH0_SINK_EN(x) (((x) & 0x1) << 7) | |
64 | #define AD5770R_CFG_SHUTDOWN_B(x, ch) (((x) & 0x1) << (ch)) | |
65 | ||
66 | /* AD5770R_OUTPUT_RANGE */ | |
67 | #define AD5770R_RANGE_OUTPUT_SCALING(x) (((x) & GENMASK(5, 0)) << 2) | |
68 | #define AD5770R_RANGE_MODE(x) ((x) & GENMASK(1, 0)) | |
69 | ||
70 | /* AD5770R_REFERENCE */ | |
71 | #define AD5770R_REF_RESISTOR_SEL(x) (((x) & 0x1) << 2) | |
72 | #define AD5770R_REF_SEL(x) ((x) & GENMASK(1, 0)) | |
73 | ||
74 | /* AD5770R_CH_ENABLE */ | |
75 | #define AD5770R_CH_SET(x, ch) (((x) & 0x1) << (ch)) | |
76 | ||
77 | #define AD5770R_MAX_CHANNELS 6 | |
78 | #define AD5770R_MAX_CH_MODES 14 | |
79 | #define AD5770R_LOW_VREF_mV 1250 | |
80 | #define AD5770R_HIGH_VREF_mV 2500 | |
81 | ||
82 | enum ad5770r_ch0_modes { | |
83 | AD5770R_CH0_0_300 = 0, | |
84 | AD5770R_CH0_NEG_60_0, | |
85 | AD5770R_CH0_NEG_60_300 | |
86 | }; | |
87 | ||
88 | enum ad5770r_ch1_modes { | |
89 | AD5770R_CH1_0_140_LOW_HEAD = 1, | |
90 | AD5770R_CH1_0_140_LOW_NOISE, | |
91 | AD5770R_CH1_0_250 | |
92 | }; | |
93 | ||
94 | enum ad5770r_ch2_5_modes { | |
95 | AD5770R_CH_LOW_RANGE = 0, | |
96 | AD5770R_CH_HIGH_RANGE | |
97 | }; | |
98 | ||
99 | enum ad5770r_ref_v { | |
100 | AD5770R_EXT_2_5_V = 0, | |
101 | AD5770R_INT_1_25_V_OUT_ON, | |
102 | AD5770R_EXT_1_25_V, | |
103 | AD5770R_INT_1_25_V_OUT_OFF | |
104 | }; | |
105 | ||
106 | enum ad5770r_output_filter_resistor { | |
107 | AD5770R_FILTER_60_OHM = 0x0, | |
108 | AD5770R_FILTER_5_6_KOHM = 0x5, | |
109 | AD5770R_FILTER_11_2_KOHM, | |
110 | AD5770R_FILTER_22_2_KOHM, | |
111 | AD5770R_FILTER_44_4_KOHM, | |
112 | AD5770R_FILTER_104_KOHM, | |
113 | }; | |
114 | ||
115 | struct ad5770r_out_range { | |
116 | u8 out_scale; | |
117 | u8 out_range_mode; | |
118 | }; | |
119 | ||
120 | /** | |
121 | * struct ad5770R_state - driver instance specific data | |
122 | * @spi: spi_device | |
123 | * @regmap: regmap | |
124 | * @vref_reg: fixed regulator for reference configuration | |
125 | * @gpio_reset: gpio descriptor | |
126 | * @output_mode: array contains channels output ranges | |
127 | * @vref: reference value | |
128 | * @ch_pwr_down: powerdown flags | |
129 | * @internal_ref: internal reference flag | |
130 | * @external_res: external 2.5k resistor flag | |
131 | * @transf_buf: cache aligned buffer for spi read/write | |
132 | */ | |
133 | struct ad5770r_state { | |
134 | struct spi_device *spi; | |
135 | struct regmap *regmap; | |
136 | struct regulator *vref_reg; | |
137 | struct gpio_desc *gpio_reset; | |
138 | struct ad5770r_out_range output_mode[AD5770R_MAX_CHANNELS]; | |
139 | int vref; | |
140 | bool ch_pwr_down[AD5770R_MAX_CHANNELS]; | |
141 | bool internal_ref; | |
142 | bool external_res; | |
143 | u8 transf_buf[2] ____cacheline_aligned; | |
144 | }; | |
145 | ||
146 | static const struct regmap_config ad5770r_spi_regmap_config = { | |
147 | .reg_bits = 8, | |
148 | .val_bits = 8, | |
149 | .read_flag_mask = BIT(7), | |
150 | }; | |
151 | ||
152 | struct ad5770r_output_modes { | |
153 | unsigned int ch; | |
154 | u8 mode; | |
155 | int min; | |
156 | int max; | |
157 | }; | |
158 | ||
159 | static struct ad5770r_output_modes ad5770r_rng_tbl[] = { | |
160 | { 0, AD5770R_CH0_0_300, 0, 300 }, | |
161 | { 0, AD5770R_CH0_NEG_60_0, -60, 0 }, | |
162 | { 0, AD5770R_CH0_NEG_60_300, -60, 300 }, | |
163 | { 1, AD5770R_CH1_0_140_LOW_HEAD, 0, 140 }, | |
164 | { 1, AD5770R_CH1_0_140_LOW_NOISE, 0, 140 }, | |
165 | { 1, AD5770R_CH1_0_250, 0, 250 }, | |
166 | { 2, AD5770R_CH_LOW_RANGE, 0, 55 }, | |
167 | { 2, AD5770R_CH_HIGH_RANGE, 0, 150 }, | |
168 | { 3, AD5770R_CH_LOW_RANGE, 0, 45 }, | |
169 | { 3, AD5770R_CH_HIGH_RANGE, 0, 100 }, | |
170 | { 4, AD5770R_CH_LOW_RANGE, 0, 45 }, | |
171 | { 4, AD5770R_CH_HIGH_RANGE, 0, 100 }, | |
172 | { 5, AD5770R_CH_LOW_RANGE, 0, 45 }, | |
173 | { 5, AD5770R_CH_HIGH_RANGE, 0, 100 }, | |
174 | }; | |
175 | ||
176 | static const unsigned int ad5770r_filter_freqs[] = { | |
177 | 153, 357, 715, 1400, 2800, 262000, | |
178 | }; | |
179 | ||
180 | static const unsigned int ad5770r_filter_reg_vals[] = { | |
181 | AD5770R_FILTER_104_KOHM, | |
182 | AD5770R_FILTER_44_4_KOHM, | |
183 | AD5770R_FILTER_22_2_KOHM, | |
184 | AD5770R_FILTER_11_2_KOHM, | |
185 | AD5770R_FILTER_5_6_KOHM, | |
186 | AD5770R_FILTER_60_OHM | |
187 | }; | |
188 | ||
189 | static int ad5770r_set_output_mode(struct ad5770r_state *st, | |
190 | const struct ad5770r_out_range *out_mode, | |
191 | int channel) | |
192 | { | |
193 | unsigned int regval; | |
194 | ||
195 | regval = AD5770R_RANGE_OUTPUT_SCALING(out_mode->out_scale) | | |
196 | AD5770R_RANGE_MODE(out_mode->out_range_mode); | |
197 | ||
198 | return regmap_write(st->regmap, | |
199 | AD5770R_OUTPUT_RANGE(channel), regval); | |
200 | } | |
201 | ||
202 | static int ad5770r_set_reference(struct ad5770r_state *st) | |
203 | { | |
204 | unsigned int regval; | |
205 | ||
206 | regval = AD5770R_REF_RESISTOR_SEL(st->external_res); | |
207 | ||
208 | if (st->internal_ref) { | |
209 | regval |= AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF); | |
210 | } else { | |
211 | switch (st->vref) { | |
212 | case AD5770R_LOW_VREF_mV: | |
213 | regval |= AD5770R_REF_SEL(AD5770R_EXT_1_25_V); | |
214 | break; | |
215 | case AD5770R_HIGH_VREF_mV: | |
216 | regval |= AD5770R_REF_SEL(AD5770R_EXT_2_5_V); | |
217 | break; | |
218 | default: | |
219 | regval = AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF); | |
220 | break; | |
221 | } | |
222 | } | |
223 | ||
224 | return regmap_write(st->regmap, AD5770R_REFERENCE, regval); | |
225 | } | |
226 | ||
227 | static int ad5770r_soft_reset(struct ad5770r_state *st) | |
228 | { | |
229 | return regmap_write(st->regmap, ADI_SPI_IF_CONFIG_A, | |
230 | ADI_SPI_IF_SW_RESET_SEL(1)); | |
231 | } | |
232 | ||
233 | static int ad5770r_reset(struct ad5770r_state *st) | |
234 | { | |
235 | /* Perform software reset if no GPIO provided */ | |
236 | if (!st->gpio_reset) | |
237 | return ad5770r_soft_reset(st); | |
238 | ||
239 | gpiod_set_value_cansleep(st->gpio_reset, 0); | |
240 | usleep_range(10, 20); | |
241 | gpiod_set_value_cansleep(st->gpio_reset, 1); | |
242 | ||
243 | /* data must not be written during reset timeframe */ | |
244 | usleep_range(100, 200); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | static int ad5770r_get_range(struct ad5770r_state *st, | |
250 | int ch, int *min, int *max) | |
251 | { | |
252 | int i; | |
253 | u8 tbl_ch, tbl_mode, out_range; | |
254 | ||
255 | out_range = st->output_mode[ch].out_range_mode; | |
256 | ||
257 | for (i = 0; i < AD5770R_MAX_CH_MODES; i++) { | |
258 | tbl_ch = ad5770r_rng_tbl[i].ch; | |
259 | tbl_mode = ad5770r_rng_tbl[i].mode; | |
260 | if (tbl_ch == ch && tbl_mode == out_range) { | |
261 | *min = ad5770r_rng_tbl[i].min; | |
262 | *max = ad5770r_rng_tbl[i].max; | |
263 | return 0; | |
264 | } | |
265 | } | |
266 | ||
267 | return -EINVAL; | |
268 | } | |
269 | ||
270 | static int ad5770r_get_filter_freq(struct iio_dev *indio_dev, | |
271 | const struct iio_chan_spec *chan, int *freq) | |
272 | { | |
273 | struct ad5770r_state *st = iio_priv(indio_dev); | |
274 | int ret; | |
275 | unsigned int regval, i; | |
276 | ||
277 | ret = regmap_read(st->regmap, | |
278 | AD5770R_FILTER_RESISTOR(chan->channel), ®val); | |
279 | if (ret < 0) | |
280 | return ret; | |
281 | ||
282 | for (i = 0; i < ARRAY_SIZE(ad5770r_filter_reg_vals); i++) | |
283 | if (regval == ad5770r_filter_reg_vals[i]) | |
284 | break; | |
285 | if (i == ARRAY_SIZE(ad5770r_filter_reg_vals)) | |
286 | return -EINVAL; | |
287 | ||
288 | *freq = ad5770r_filter_freqs[i]; | |
289 | ||
290 | return IIO_VAL_INT; | |
291 | } | |
292 | ||
293 | static int ad5770r_set_filter_freq(struct iio_dev *indio_dev, | |
294 | const struct iio_chan_spec *chan, | |
295 | unsigned int freq) | |
296 | { | |
297 | struct ad5770r_state *st = iio_priv(indio_dev); | |
298 | unsigned int regval, i; | |
299 | ||
300 | for (i = 0; i < ARRAY_SIZE(ad5770r_filter_freqs); i++) | |
301 | if (ad5770r_filter_freqs[i] >= freq) | |
302 | break; | |
303 | if (i == ARRAY_SIZE(ad5770r_filter_freqs)) | |
304 | return -EINVAL; | |
305 | ||
306 | regval = ad5770r_filter_reg_vals[i]; | |
307 | ||
308 | return regmap_write(st->regmap, AD5770R_FILTER_RESISTOR(chan->channel), | |
309 | regval); | |
310 | } | |
311 | ||
312 | static int ad5770r_read_raw(struct iio_dev *indio_dev, | |
313 | struct iio_chan_spec const *chan, | |
314 | int *val, int *val2, long info) | |
315 | { | |
316 | struct ad5770r_state *st = iio_priv(indio_dev); | |
317 | int max, min, ret; | |
318 | u16 buf16; | |
319 | ||
320 | switch (info) { | |
321 | case IIO_CHAN_INFO_RAW: | |
322 | ret = regmap_bulk_read(st->regmap, | |
323 | chan->address, | |
324 | st->transf_buf, 2); | |
325 | if (ret) | |
326 | return 0; | |
327 | ||
328 | buf16 = st->transf_buf[0] + (st->transf_buf[1] << 8); | |
329 | *val = buf16 >> 2; | |
330 | return IIO_VAL_INT; | |
331 | case IIO_CHAN_INFO_SCALE: | |
332 | ret = ad5770r_get_range(st, chan->channel, &min, &max); | |
333 | if (ret < 0) | |
334 | return ret; | |
335 | *val = max - min; | |
336 | /* There is no sign bit. (negative current is mapped from 0) | |
337 | * (sourced/sinked) current = raw * scale + offset | |
338 | * where offset in case of CH0 can be negative. | |
339 | */ | |
340 | *val2 = 14; | |
341 | return IIO_VAL_FRACTIONAL_LOG2; | |
342 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: | |
343 | return ad5770r_get_filter_freq(indio_dev, chan, val); | |
344 | case IIO_CHAN_INFO_OFFSET: | |
345 | ret = ad5770r_get_range(st, chan->channel, &min, &max); | |
346 | if (ret < 0) | |
347 | return ret; | |
348 | *val = min; | |
349 | return IIO_VAL_INT; | |
350 | default: | |
351 | return -EINVAL; | |
352 | } | |
353 | } | |
354 | ||
355 | static int ad5770r_write_raw(struct iio_dev *indio_dev, | |
356 | struct iio_chan_spec const *chan, | |
357 | int val, int val2, long info) | |
358 | { | |
359 | struct ad5770r_state *st = iio_priv(indio_dev); | |
360 | ||
361 | switch (info) { | |
362 | case IIO_CHAN_INFO_RAW: | |
363 | st->transf_buf[0] = ((u16)val >> 6); | |
364 | st->transf_buf[1] = (val & GENMASK(5, 0)) << 2; | |
365 | return regmap_bulk_write(st->regmap, chan->address, | |
366 | st->transf_buf, 2); | |
367 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: | |
368 | return ad5770r_set_filter_freq(indio_dev, chan, val); | |
369 | default: | |
370 | return -EINVAL; | |
371 | } | |
372 | } | |
373 | ||
374 | static int ad5770r_read_freq_avail(struct iio_dev *indio_dev, | |
375 | struct iio_chan_spec const *chan, | |
376 | const int **vals, int *type, int *length, | |
377 | long mask) | |
378 | { | |
379 | switch (mask) { | |
380 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: | |
381 | *type = IIO_VAL_INT; | |
382 | *vals = ad5770r_filter_freqs; | |
383 | *length = ARRAY_SIZE(ad5770r_filter_freqs); | |
384 | return IIO_AVAIL_LIST; | |
385 | } | |
386 | ||
387 | return -EINVAL; | |
388 | } | |
389 | ||
390 | static int ad5770r_reg_access(struct iio_dev *indio_dev, | |
391 | unsigned int reg, | |
392 | unsigned int writeval, | |
393 | unsigned int *readval) | |
394 | { | |
395 | struct ad5770r_state *st = iio_priv(indio_dev); | |
396 | ||
397 | if (readval) | |
398 | return regmap_read(st->regmap, reg, readval); | |
399 | else | |
400 | return regmap_write(st->regmap, reg, writeval); | |
401 | } | |
402 | ||
403 | static const struct iio_info ad5770r_info = { | |
404 | .read_raw = ad5770r_read_raw, | |
405 | .write_raw = ad5770r_write_raw, | |
406 | .read_avail = ad5770r_read_freq_avail, | |
407 | .debugfs_reg_access = &ad5770r_reg_access, | |
408 | }; | |
409 | ||
410 | static int ad5770r_store_output_range(struct ad5770r_state *st, | |
411 | int min, int max, int index) | |
412 | { | |
413 | int i; | |
414 | ||
415 | for (i = 0; i < AD5770R_MAX_CH_MODES; i++) { | |
416 | if (ad5770r_rng_tbl[i].ch != index) | |
417 | continue; | |
418 | if (ad5770r_rng_tbl[i].min != min || | |
419 | ad5770r_rng_tbl[i].max != max) | |
420 | continue; | |
421 | st->output_mode[index].out_range_mode = ad5770r_rng_tbl[i].mode; | |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
426 | return -EINVAL; | |
427 | } | |
428 | ||
429 | static ssize_t ad5770r_read_dac_powerdown(struct iio_dev *indio_dev, | |
430 | uintptr_t private, | |
431 | const struct iio_chan_spec *chan, | |
432 | char *buf) | |
433 | { | |
434 | struct ad5770r_state *st = iio_priv(indio_dev); | |
435 | ||
436 | return sprintf(buf, "%d\n", st->ch_pwr_down[chan->channel]); | |
437 | } | |
438 | ||
439 | static ssize_t ad5770r_write_dac_powerdown(struct iio_dev *indio_dev, | |
440 | uintptr_t private, | |
441 | const struct iio_chan_spec *chan, | |
442 | const char *buf, size_t len) | |
443 | { | |
444 | struct ad5770r_state *st = iio_priv(indio_dev); | |
445 | unsigned int regval; | |
446 | unsigned int mask; | |
447 | bool readin; | |
448 | int ret; | |
449 | ||
450 | ret = kstrtobool(buf, &readin); | |
451 | if (ret) | |
452 | return ret; | |
453 | ||
454 | readin = !readin; | |
455 | ||
456 | regval = AD5770R_CFG_SHUTDOWN_B(readin, chan->channel); | |
457 | if (chan->channel == 0 && | |
458 | st->output_mode[0].out_range_mode > AD5770R_CH0_0_300) { | |
459 | regval |= AD5770R_CFG_CH0_SINK_EN(readin); | |
460 | mask = BIT(chan->channel) + BIT(7); | |
461 | } else { | |
462 | mask = BIT(chan->channel); | |
463 | } | |
464 | ret = regmap_update_bits(st->regmap, AD5770R_CHANNEL_CONFIG, mask, | |
465 | regval); | |
466 | if (ret) | |
467 | return ret; | |
468 | ||
469 | regval = AD5770R_CH_SET(readin, chan->channel); | |
470 | ret = regmap_update_bits(st->regmap, AD5770R_CH_ENABLE, | |
471 | BIT(chan->channel), regval); | |
472 | if (ret) | |
473 | return ret; | |
474 | ||
475 | st->ch_pwr_down[chan->channel] = !readin; | |
476 | ||
477 | return len; | |
478 | } | |
479 | ||
480 | static const struct iio_chan_spec_ext_info ad5770r_ext_info[] = { | |
481 | { | |
482 | .name = "powerdown", | |
483 | .read = ad5770r_read_dac_powerdown, | |
484 | .write = ad5770r_write_dac_powerdown, | |
485 | .shared = IIO_SEPARATE, | |
486 | }, | |
487 | { } | |
488 | }; | |
489 | ||
490 | #define AD5770R_IDAC_CHANNEL(index, reg) { \ | |
491 | .type = IIO_CURRENT, \ | |
492 | .address = reg, \ | |
493 | .indexed = 1, \ | |
494 | .channel = index, \ | |
495 | .output = 1, \ | |
496 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
497 | BIT(IIO_CHAN_INFO_SCALE) | \ | |
498 | BIT(IIO_CHAN_INFO_OFFSET) | \ | |
499 | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ | |
500 | .info_mask_shared_by_type_available = \ | |
501 | BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ | |
502 | .ext_info = ad5770r_ext_info, \ | |
503 | } | |
504 | ||
505 | static const struct iio_chan_spec ad5770r_channels[] = { | |
506 | AD5770R_IDAC_CHANNEL(0, AD5770R_DAC_MSB(0)), | |
507 | AD5770R_IDAC_CHANNEL(1, AD5770R_DAC_MSB(1)), | |
508 | AD5770R_IDAC_CHANNEL(2, AD5770R_DAC_MSB(2)), | |
509 | AD5770R_IDAC_CHANNEL(3, AD5770R_DAC_MSB(3)), | |
510 | AD5770R_IDAC_CHANNEL(4, AD5770R_DAC_MSB(4)), | |
511 | AD5770R_IDAC_CHANNEL(5, AD5770R_DAC_MSB(5)), | |
512 | }; | |
513 | ||
514 | static int ad5770r_channel_config(struct ad5770r_state *st) | |
515 | { | |
516 | int ret, tmp[2], min, max; | |
517 | unsigned int num; | |
518 | struct fwnode_handle *child; | |
519 | ||
520 | num = device_get_child_node_count(&st->spi->dev); | |
521 | if (num != AD5770R_MAX_CHANNELS) | |
522 | return -EINVAL; | |
523 | ||
524 | device_for_each_child_node(&st->spi->dev, child) { | |
525 | ret = fwnode_property_read_u32(child, "num", &num); | |
526 | if (ret) | |
527 | return ret; | |
dd6230ba | 528 | if (num >= AD5770R_MAX_CHANNELS) |
cbbb8198 AT |
529 | return -EINVAL; |
530 | ||
531 | ret = fwnode_property_read_u32_array(child, | |
532 | "adi,range-microamp", | |
533 | tmp, 2); | |
534 | if (ret) | |
535 | return ret; | |
536 | ||
537 | min = tmp[0] / 1000; | |
538 | max = tmp[1] / 1000; | |
539 | ret = ad5770r_store_output_range(st, min, max, num); | |
540 | if (ret) | |
541 | return ret; | |
542 | } | |
543 | ||
544 | return ret; | |
545 | } | |
546 | ||
547 | static int ad5770r_init(struct ad5770r_state *st) | |
548 | { | |
549 | int ret, i; | |
550 | ||
551 | st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", | |
552 | GPIOD_OUT_HIGH); | |
553 | if (IS_ERR(st->gpio_reset)) | |
554 | return PTR_ERR(st->gpio_reset); | |
555 | ||
556 | /* Perform a reset */ | |
557 | ret = ad5770r_reset(st); | |
558 | if (ret) | |
559 | return ret; | |
560 | ||
561 | /* Set output range */ | |
562 | ret = ad5770r_channel_config(st); | |
563 | if (ret) | |
564 | return ret; | |
565 | ||
566 | for (i = 0; i < AD5770R_MAX_CHANNELS; i++) { | |
567 | ret = ad5770r_set_output_mode(st, &st->output_mode[i], i); | |
568 | if (ret) | |
569 | return ret; | |
570 | } | |
571 | ||
572 | st->external_res = fwnode_property_read_bool(st->spi->dev.fwnode, | |
573 | "adi,external-resistor"); | |
574 | ||
575 | ret = ad5770r_set_reference(st); | |
576 | if (ret) | |
577 | return ret; | |
578 | ||
579 | /* Set outputs off */ | |
580 | ret = regmap_write(st->regmap, AD5770R_CHANNEL_CONFIG, 0x00); | |
581 | if (ret) | |
582 | return ret; | |
583 | ||
584 | ret = regmap_write(st->regmap, AD5770R_CH_ENABLE, 0x00); | |
585 | if (ret) | |
586 | return ret; | |
587 | ||
588 | for (i = 0; i < AD5770R_MAX_CHANNELS; i++) | |
589 | st->ch_pwr_down[i] = true; | |
590 | ||
591 | return ret; | |
592 | } | |
593 | ||
594 | static void ad5770r_disable_regulator(void *data) | |
595 | { | |
596 | struct ad5770r_state *st = data; | |
597 | ||
598 | regulator_disable(st->vref_reg); | |
599 | } | |
600 | ||
601 | static int ad5770r_probe(struct spi_device *spi) | |
602 | { | |
603 | struct ad5770r_state *st; | |
604 | struct iio_dev *indio_dev; | |
605 | struct regmap *regmap; | |
606 | int ret; | |
607 | ||
608 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | |
609 | if (!indio_dev) | |
610 | return -ENOMEM; | |
611 | ||
612 | st = iio_priv(indio_dev); | |
613 | spi_set_drvdata(spi, indio_dev); | |
614 | ||
615 | st->spi = spi; | |
616 | ||
617 | regmap = devm_regmap_init_spi(spi, &ad5770r_spi_regmap_config); | |
618 | if (IS_ERR(regmap)) { | |
619 | dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", | |
620 | PTR_ERR(regmap)); | |
621 | return PTR_ERR(regmap); | |
622 | } | |
623 | st->regmap = regmap; | |
624 | ||
625 | st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); | |
626 | if (!IS_ERR(st->vref_reg)) { | |
627 | ret = regulator_enable(st->vref_reg); | |
628 | if (ret) { | |
629 | dev_err(&spi->dev, | |
630 | "Failed to enable vref regulators: %d\n", ret); | |
631 | return ret; | |
632 | } | |
633 | ||
634 | ret = devm_add_action_or_reset(&spi->dev, | |
635 | ad5770r_disable_regulator, | |
636 | st); | |
637 | if (ret < 0) | |
638 | return ret; | |
639 | ||
640 | ret = regulator_get_voltage(st->vref_reg); | |
641 | if (ret < 0) | |
642 | return ret; | |
643 | ||
644 | st->vref = ret / 1000; | |
645 | } else { | |
646 | if (PTR_ERR(st->vref_reg) == -ENODEV) { | |
647 | st->vref = AD5770R_LOW_VREF_mV; | |
648 | st->internal_ref = true; | |
649 | } else { | |
650 | return PTR_ERR(st->vref_reg); | |
651 | } | |
652 | } | |
653 | ||
654 | indio_dev->dev.parent = &spi->dev; | |
655 | indio_dev->name = spi_get_device_id(spi)->name; | |
656 | indio_dev->info = &ad5770r_info; | |
657 | indio_dev->modes = INDIO_DIRECT_MODE; | |
658 | indio_dev->channels = ad5770r_channels; | |
659 | indio_dev->num_channels = ARRAY_SIZE(ad5770r_channels); | |
660 | ||
661 | ret = ad5770r_init(st); | |
662 | if (ret < 0) { | |
663 | dev_err(&spi->dev, "AD5770R init failed\n"); | |
664 | return ret; | |
665 | } | |
666 | ||
667 | return devm_iio_device_register(&st->spi->dev, indio_dev); | |
668 | } | |
669 | ||
670 | static const struct of_device_id ad5770r_of_id[] = { | |
671 | { .compatible = "adi,ad5770r", }, | |
672 | {}, | |
673 | }; | |
674 | MODULE_DEVICE_TABLE(of, ad5770r_of_id); | |
675 | ||
676 | static const struct spi_device_id ad5770r_id[] = { | |
677 | { "ad5770r", 0 }, | |
678 | {}, | |
679 | }; | |
680 | MODULE_DEVICE_TABLE(spi, ad5770r_id); | |
681 | ||
682 | static struct spi_driver ad5770r_driver = { | |
683 | .driver = { | |
684 | .name = KBUILD_MODNAME, | |
685 | .of_match_table = ad5770r_of_id, | |
686 | }, | |
687 | .probe = ad5770r_probe, | |
688 | .id_table = ad5770r_id, | |
689 | }; | |
690 | ||
691 | module_spi_driver(ad5770r_driver); | |
692 | ||
693 | MODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>"); | |
694 | MODULE_DESCRIPTION("Analog Devices AD5770R IDAC"); | |
695 | MODULE_LICENSE("GPL v2"); |