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] __aligned(IIO_DMA_MINALIGN); |
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(map: st->regmap, |
199 | AD5770R_OUTPUT_RANGE(channel), val: 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(map: st->regmap, AD5770R_REFERENCE, val: regval); |
225 | } |
226 | |
227 | static int ad5770r_soft_reset(struct ad5770r_state *st) |
228 | { |
229 | return regmap_write(map: 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(desc: st->gpio_reset, value: 0); |
240 | usleep_range(min: 10, max: 20); |
241 | gpiod_set_value_cansleep(desc: st->gpio_reset, value: 1); |
242 | |
243 | /* data must not be written during reset timeframe */ |
244 | usleep_range(min: 100, max: 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(map: st->regmap, |
278 | AD5770R_FILTER_RESISTOR(chan->channel), val: ®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(map: st->regmap, AD5770R_FILTER_RESISTOR(chan->channel), |
309 | val: 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(map: st->regmap, |
323 | reg: chan->address, |
324 | val: st->transf_buf, val_count: 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, ch: chan->channel, min: &min, max: &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, freq: val); |
344 | case IIO_CHAN_INFO_OFFSET: |
345 | ret = ad5770r_get_range(st, ch: chan->channel, min: &min, max: &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(map: st->regmap, reg: chan->address, |
366 | val: st->transf_buf, val_count: 2); |
367 | case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
368 | return ad5770r_set_filter_freq(indio_dev, chan, freq: 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(map: st->regmap, reg, val: readval); |
399 | else |
400 | return regmap_write(map: st->regmap, reg, val: 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 sysfs_emit(buf, fmt: "%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(s: buf, res: &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(map: st->regmap, AD5770R_CHANNEL_CONFIG, mask, |
465 | val: regval); |
466 | if (ret) |
467 | return ret; |
468 | |
469 | regval = AD5770R_CH_SET(readin, chan->channel); |
470 | ret = regmap_update_bits(map: st->regmap, AD5770R_CH_ENABLE, |
471 | BIT(chan->channel), val: 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(dev: &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(fwnode: child, propname: "reg" , val: &num); |
526 | if (ret) |
527 | goto err_child_out; |
528 | if (num >= AD5770R_MAX_CHANNELS) { |
529 | ret = -EINVAL; |
530 | goto err_child_out; |
531 | } |
532 | |
533 | ret = fwnode_property_read_u32_array(fwnode: child, |
534 | propname: "adi,range-microamp" , |
535 | val: tmp, nval: 2); |
536 | if (ret) |
537 | goto err_child_out; |
538 | |
539 | min = tmp[0] / 1000; |
540 | max = tmp[1] / 1000; |
541 | ret = ad5770r_store_output_range(st, min, max, index: num); |
542 | if (ret) |
543 | goto err_child_out; |
544 | } |
545 | |
546 | return 0; |
547 | |
548 | err_child_out: |
549 | fwnode_handle_put(fwnode: child); |
550 | return ret; |
551 | } |
552 | |
553 | static int ad5770r_init(struct ad5770r_state *st) |
554 | { |
555 | int ret, i; |
556 | |
557 | st->gpio_reset = devm_gpiod_get_optional(dev: &st->spi->dev, con_id: "reset" , |
558 | flags: GPIOD_OUT_HIGH); |
559 | if (IS_ERR(ptr: st->gpio_reset)) |
560 | return PTR_ERR(ptr: st->gpio_reset); |
561 | |
562 | /* Perform a reset */ |
563 | ret = ad5770r_reset(st); |
564 | if (ret) |
565 | return ret; |
566 | |
567 | /* Set output range */ |
568 | ret = ad5770r_channel_config(st); |
569 | if (ret) |
570 | return ret; |
571 | |
572 | for (i = 0; i < AD5770R_MAX_CHANNELS; i++) { |
573 | ret = ad5770r_set_output_mode(st, out_mode: &st->output_mode[i], channel: i); |
574 | if (ret) |
575 | return ret; |
576 | } |
577 | |
578 | st->external_res = fwnode_property_read_bool(fwnode: st->spi->dev.fwnode, |
579 | propname: "adi,external-resistor" ); |
580 | |
581 | ret = ad5770r_set_reference(st); |
582 | if (ret) |
583 | return ret; |
584 | |
585 | /* Set outputs off */ |
586 | ret = regmap_write(map: st->regmap, AD5770R_CHANNEL_CONFIG, val: 0x00); |
587 | if (ret) |
588 | return ret; |
589 | |
590 | ret = regmap_write(map: st->regmap, AD5770R_CH_ENABLE, val: 0x00); |
591 | if (ret) |
592 | return ret; |
593 | |
594 | for (i = 0; i < AD5770R_MAX_CHANNELS; i++) |
595 | st->ch_pwr_down[i] = true; |
596 | |
597 | return ret; |
598 | } |
599 | |
600 | static void ad5770r_disable_regulator(void *data) |
601 | { |
602 | struct ad5770r_state *st = data; |
603 | |
604 | regulator_disable(regulator: st->vref_reg); |
605 | } |
606 | |
607 | static int ad5770r_probe(struct spi_device *spi) |
608 | { |
609 | struct ad5770r_state *st; |
610 | struct iio_dev *indio_dev; |
611 | struct regmap *regmap; |
612 | int ret; |
613 | |
614 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
615 | if (!indio_dev) |
616 | return -ENOMEM; |
617 | |
618 | st = iio_priv(indio_dev); |
619 | spi_set_drvdata(spi, data: indio_dev); |
620 | |
621 | st->spi = spi; |
622 | |
623 | regmap = devm_regmap_init_spi(spi, &ad5770r_spi_regmap_config); |
624 | if (IS_ERR(ptr: regmap)) { |
625 | dev_err(&spi->dev, "Error initializing spi regmap: %ld\n" , |
626 | PTR_ERR(regmap)); |
627 | return PTR_ERR(ptr: regmap); |
628 | } |
629 | st->regmap = regmap; |
630 | |
631 | st->vref_reg = devm_regulator_get_optional(dev: &spi->dev, id: "vref" ); |
632 | if (!IS_ERR(ptr: st->vref_reg)) { |
633 | ret = regulator_enable(regulator: st->vref_reg); |
634 | if (ret) { |
635 | dev_err(&spi->dev, |
636 | "Failed to enable vref regulators: %d\n" , ret); |
637 | return ret; |
638 | } |
639 | |
640 | ret = devm_add_action_or_reset(&spi->dev, |
641 | ad5770r_disable_regulator, |
642 | st); |
643 | if (ret < 0) |
644 | return ret; |
645 | |
646 | ret = regulator_get_voltage(regulator: st->vref_reg); |
647 | if (ret < 0) |
648 | return ret; |
649 | |
650 | st->vref = ret / 1000; |
651 | } else { |
652 | if (PTR_ERR(ptr: st->vref_reg) == -ENODEV) { |
653 | st->vref = AD5770R_LOW_VREF_mV; |
654 | st->internal_ref = true; |
655 | } else { |
656 | return PTR_ERR(ptr: st->vref_reg); |
657 | } |
658 | } |
659 | |
660 | indio_dev->name = spi_get_device_id(sdev: spi)->name; |
661 | indio_dev->info = &ad5770r_info; |
662 | indio_dev->modes = INDIO_DIRECT_MODE; |
663 | indio_dev->channels = ad5770r_channels; |
664 | indio_dev->num_channels = ARRAY_SIZE(ad5770r_channels); |
665 | |
666 | ret = ad5770r_init(st); |
667 | if (ret < 0) { |
668 | dev_err(&spi->dev, "AD5770R init failed\n" ); |
669 | return ret; |
670 | } |
671 | |
672 | return devm_iio_device_register(&st->spi->dev, indio_dev); |
673 | } |
674 | |
675 | static const struct of_device_id ad5770r_of_id[] = { |
676 | { .compatible = "adi,ad5770r" , }, |
677 | {}, |
678 | }; |
679 | MODULE_DEVICE_TABLE(of, ad5770r_of_id); |
680 | |
681 | static const struct spi_device_id ad5770r_id[] = { |
682 | { "ad5770r" , 0 }, |
683 | {}, |
684 | }; |
685 | MODULE_DEVICE_TABLE(spi, ad5770r_id); |
686 | |
687 | static struct spi_driver ad5770r_driver = { |
688 | .driver = { |
689 | .name = KBUILD_MODNAME, |
690 | .of_match_table = ad5770r_of_id, |
691 | }, |
692 | .probe = ad5770r_probe, |
693 | .id_table = ad5770r_id, |
694 | }; |
695 | |
696 | module_spi_driver(ad5770r_driver); |
697 | |
698 | MODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>" ); |
699 | MODULE_DESCRIPTION("Analog Devices AD5770R IDAC" ); |
700 | MODULE_LICENSE("GPL v2" ); |
701 | |