1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * LTC2632 Digital to analog convertors spi driver |
4 | * |
5 | * Copyright 2017 Maxime Roussin-BĂ©langer |
6 | * expanded by Silvan Murer <silvan.murer@gmail.com> |
7 | */ |
8 | |
9 | #include <linux/device.h> |
10 | #include <linux/spi/spi.h> |
11 | #include <linux/module.h> |
12 | #include <linux/iio/iio.h> |
13 | #include <linux/property.h> |
14 | #include <linux/regulator/consumer.h> |
15 | |
16 | #include <asm/unaligned.h> |
17 | |
18 | #define LTC2632_CMD_WRITE_INPUT_N 0x0 |
19 | #define LTC2632_CMD_UPDATE_DAC_N 0x1 |
20 | #define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 |
21 | #define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N 0x3 |
22 | #define LTC2632_CMD_POWERDOWN_DAC_N 0x4 |
23 | #define LTC2632_CMD_POWERDOWN_CHIP 0x5 |
24 | #define LTC2632_CMD_INTERNAL_REFER 0x6 |
25 | #define LTC2632_CMD_EXTERNAL_REFER 0x7 |
26 | |
27 | /** |
28 | * struct ltc2632_chip_info - chip specific information |
29 | * @channels: channel spec for the DAC |
30 | * @num_channels: DAC channel count of the chip |
31 | * @vref_mv: internal reference voltage |
32 | */ |
33 | struct ltc2632_chip_info { |
34 | const struct iio_chan_spec *channels; |
35 | const size_t num_channels; |
36 | const int vref_mv; |
37 | }; |
38 | |
39 | /** |
40 | * struct ltc2632_state - driver instance specific data |
41 | * @spi_dev: pointer to the spi_device struct |
42 | * @powerdown_cache_mask: used to show current channel powerdown state |
43 | * @vref_mv: used reference voltage (internal or external) |
44 | * @vref_reg: regulator for the reference voltage |
45 | */ |
46 | struct ltc2632_state { |
47 | struct spi_device *spi_dev; |
48 | unsigned int powerdown_cache_mask; |
49 | int vref_mv; |
50 | struct regulator *vref_reg; |
51 | }; |
52 | |
53 | enum ltc2632_supported_device_ids { |
54 | ID_LTC2632L12, |
55 | ID_LTC2632L10, |
56 | ID_LTC2632L8, |
57 | ID_LTC2632H12, |
58 | ID_LTC2632H10, |
59 | ID_LTC2632H8, |
60 | ID_LTC2634L12, |
61 | ID_LTC2634L10, |
62 | ID_LTC2634L8, |
63 | ID_LTC2634H12, |
64 | ID_LTC2634H10, |
65 | ID_LTC2634H8, |
66 | ID_LTC2636L12, |
67 | ID_LTC2636L10, |
68 | ID_LTC2636L8, |
69 | ID_LTC2636H12, |
70 | ID_LTC2636H10, |
71 | ID_LTC2636H8, |
72 | }; |
73 | |
74 | static int ltc2632_spi_write(struct spi_device *spi, |
75 | u8 cmd, u8 addr, u16 val, u8 shift) |
76 | { |
77 | u32 data; |
78 | u8 msg[3]; |
79 | |
80 | /* |
81 | * The input shift register is 24 bits wide. |
82 | * The next four are the command bits, C3 to C0, |
83 | * followed by the 4-bit DAC address, A3 to A0, and then the |
84 | * 12-, 10-, 8-bit data-word. The data-word comprises the 12-, |
85 | * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits. |
86 | */ |
87 | data = (cmd << 20) | (addr << 16) | (val << shift); |
88 | put_unaligned_be24(val: data, p: &msg[0]); |
89 | |
90 | return spi_write(spi, buf: msg, len: sizeof(msg)); |
91 | } |
92 | |
93 | static int ltc2632_read_raw(struct iio_dev *indio_dev, |
94 | struct iio_chan_spec const *chan, |
95 | int *val, |
96 | int *val2, |
97 | long m) |
98 | { |
99 | const struct ltc2632_state *st = iio_priv(indio_dev); |
100 | |
101 | switch (m) { |
102 | case IIO_CHAN_INFO_SCALE: |
103 | *val = st->vref_mv; |
104 | *val2 = chan->scan_type.realbits; |
105 | return IIO_VAL_FRACTIONAL_LOG2; |
106 | } |
107 | return -EINVAL; |
108 | } |
109 | |
110 | static int ltc2632_write_raw(struct iio_dev *indio_dev, |
111 | struct iio_chan_spec const *chan, |
112 | int val, |
113 | int val2, |
114 | long mask) |
115 | { |
116 | struct ltc2632_state *st = iio_priv(indio_dev); |
117 | |
118 | switch (mask) { |
119 | case IIO_CHAN_INFO_RAW: |
120 | if (val >= (1 << chan->scan_type.realbits) || val < 0) |
121 | return -EINVAL; |
122 | |
123 | return ltc2632_spi_write(spi: st->spi_dev, |
124 | LTC2632_CMD_WRITE_INPUT_N_UPDATE_N, |
125 | addr: chan->address, val, |
126 | shift: chan->scan_type.shift); |
127 | default: |
128 | return -EINVAL; |
129 | } |
130 | } |
131 | |
132 | static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev, |
133 | uintptr_t private, |
134 | const struct iio_chan_spec *chan, |
135 | char *buf) |
136 | { |
137 | struct ltc2632_state *st = iio_priv(indio_dev); |
138 | |
139 | return sysfs_emit(buf, fmt: "%d\n" , |
140 | !!(st->powerdown_cache_mask & (1 << chan->channel))); |
141 | } |
142 | |
143 | static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev, |
144 | uintptr_t private, |
145 | const struct iio_chan_spec *chan, |
146 | const char *buf, |
147 | size_t len) |
148 | { |
149 | bool pwr_down; |
150 | int ret; |
151 | struct ltc2632_state *st = iio_priv(indio_dev); |
152 | |
153 | ret = kstrtobool(s: buf, res: &pwr_down); |
154 | if (ret) |
155 | return ret; |
156 | |
157 | if (pwr_down) |
158 | st->powerdown_cache_mask |= (1 << chan->channel); |
159 | else |
160 | st->powerdown_cache_mask &= ~(1 << chan->channel); |
161 | |
162 | ret = ltc2632_spi_write(spi: st->spi_dev, |
163 | LTC2632_CMD_POWERDOWN_DAC_N, |
164 | addr: chan->channel, val: 0, shift: 0); |
165 | |
166 | return ret ? ret : len; |
167 | } |
168 | |
169 | static const struct iio_info ltc2632_info = { |
170 | .write_raw = ltc2632_write_raw, |
171 | .read_raw = ltc2632_read_raw, |
172 | }; |
173 | |
174 | static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { |
175 | { |
176 | .name = "powerdown" , |
177 | .read = ltc2632_read_dac_powerdown, |
178 | .write = ltc2632_write_dac_powerdown, |
179 | .shared = IIO_SEPARATE, |
180 | }, |
181 | { }, |
182 | }; |
183 | |
184 | #define LTC2632_CHANNEL(_chan, _bits) { \ |
185 | .type = IIO_VOLTAGE, \ |
186 | .indexed = 1, \ |
187 | .output = 1, \ |
188 | .channel = (_chan), \ |
189 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
190 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
191 | .address = (_chan), \ |
192 | .scan_type = { \ |
193 | .realbits = (_bits), \ |
194 | .shift = 16 - (_bits), \ |
195 | }, \ |
196 | .ext_info = ltc2632_ext_info, \ |
197 | } |
198 | |
199 | #define DECLARE_LTC2632_CHANNELS(_name, _bits) \ |
200 | const struct iio_chan_spec _name ## _channels[] = { \ |
201 | LTC2632_CHANNEL(0, _bits), \ |
202 | LTC2632_CHANNEL(1, _bits), \ |
203 | LTC2632_CHANNEL(2, _bits), \ |
204 | LTC2632_CHANNEL(3, _bits), \ |
205 | LTC2632_CHANNEL(4, _bits), \ |
206 | LTC2632_CHANNEL(5, _bits), \ |
207 | LTC2632_CHANNEL(6, _bits), \ |
208 | LTC2632_CHANNEL(7, _bits), \ |
209 | } |
210 | |
211 | static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); |
212 | static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10); |
213 | static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); |
214 | |
215 | static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { |
216 | [ID_LTC2632L12] = { |
217 | .channels = ltc2632x12_channels, |
218 | .num_channels = 2, |
219 | .vref_mv = 2500, |
220 | }, |
221 | [ID_LTC2632L10] = { |
222 | .channels = ltc2632x10_channels, |
223 | .num_channels = 2, |
224 | .vref_mv = 2500, |
225 | }, |
226 | [ID_LTC2632L8] = { |
227 | .channels = ltc2632x8_channels, |
228 | .num_channels = 2, |
229 | .vref_mv = 2500, |
230 | }, |
231 | [ID_LTC2632H12] = { |
232 | .channels = ltc2632x12_channels, |
233 | .num_channels = 2, |
234 | .vref_mv = 4096, |
235 | }, |
236 | [ID_LTC2632H10] = { |
237 | .channels = ltc2632x10_channels, |
238 | .num_channels = 2, |
239 | .vref_mv = 4096, |
240 | }, |
241 | [ID_LTC2632H8] = { |
242 | .channels = ltc2632x8_channels, |
243 | .num_channels = 2, |
244 | .vref_mv = 4096, |
245 | }, |
246 | [ID_LTC2634L12] = { |
247 | .channels = ltc2632x12_channels, |
248 | .num_channels = 4, |
249 | .vref_mv = 2500, |
250 | }, |
251 | [ID_LTC2634L10] = { |
252 | .channels = ltc2632x10_channels, |
253 | .num_channels = 4, |
254 | .vref_mv = 2500, |
255 | }, |
256 | [ID_LTC2634L8] = { |
257 | .channels = ltc2632x8_channels, |
258 | .num_channels = 4, |
259 | .vref_mv = 2500, |
260 | }, |
261 | [ID_LTC2634H12] = { |
262 | .channels = ltc2632x12_channels, |
263 | .num_channels = 4, |
264 | .vref_mv = 4096, |
265 | }, |
266 | [ID_LTC2634H10] = { |
267 | .channels = ltc2632x10_channels, |
268 | .num_channels = 4, |
269 | .vref_mv = 4096, |
270 | }, |
271 | [ID_LTC2634H8] = { |
272 | .channels = ltc2632x8_channels, |
273 | .num_channels = 4, |
274 | .vref_mv = 4096, |
275 | }, |
276 | [ID_LTC2636L12] = { |
277 | .channels = ltc2632x12_channels, |
278 | .num_channels = 8, |
279 | .vref_mv = 2500, |
280 | }, |
281 | [ID_LTC2636L10] = { |
282 | .channels = ltc2632x10_channels, |
283 | .num_channels = 8, |
284 | .vref_mv = 2500, |
285 | }, |
286 | [ID_LTC2636L8] = { |
287 | .channels = ltc2632x8_channels, |
288 | .num_channels = 8, |
289 | .vref_mv = 2500, |
290 | }, |
291 | [ID_LTC2636H12] = { |
292 | .channels = ltc2632x12_channels, |
293 | .num_channels = 8, |
294 | .vref_mv = 4096, |
295 | }, |
296 | [ID_LTC2636H10] = { |
297 | .channels = ltc2632x10_channels, |
298 | .num_channels = 8, |
299 | .vref_mv = 4096, |
300 | }, |
301 | [ID_LTC2636H8] = { |
302 | .channels = ltc2632x8_channels, |
303 | .num_channels = 8, |
304 | .vref_mv = 4096, |
305 | }, |
306 | }; |
307 | |
308 | static int ltc2632_probe(struct spi_device *spi) |
309 | { |
310 | struct ltc2632_state *st; |
311 | struct iio_dev *indio_dev; |
312 | struct ltc2632_chip_info *chip_info; |
313 | int ret; |
314 | |
315 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
316 | if (!indio_dev) |
317 | return -ENOMEM; |
318 | |
319 | st = iio_priv(indio_dev); |
320 | |
321 | spi_set_drvdata(spi, data: indio_dev); |
322 | st->spi_dev = spi; |
323 | |
324 | chip_info = (struct ltc2632_chip_info *) |
325 | spi_get_device_id(sdev: spi)->driver_data; |
326 | |
327 | st->vref_reg = devm_regulator_get_optional(dev: &spi->dev, id: "vref" ); |
328 | if (PTR_ERR(ptr: st->vref_reg) == -ENODEV) { |
329 | /* use internal reference voltage */ |
330 | st->vref_reg = NULL; |
331 | st->vref_mv = chip_info->vref_mv; |
332 | |
333 | ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, |
334 | addr: 0, val: 0, shift: 0); |
335 | if (ret) { |
336 | dev_err(&spi->dev, |
337 | "Set internal reference command failed, %d\n" , |
338 | ret); |
339 | return ret; |
340 | } |
341 | } else if (IS_ERR(ptr: st->vref_reg)) { |
342 | dev_err(&spi->dev, |
343 | "Error getting voltage reference regulator\n" ); |
344 | return PTR_ERR(ptr: st->vref_reg); |
345 | } else { |
346 | /* use external reference voltage */ |
347 | ret = regulator_enable(regulator: st->vref_reg); |
348 | if (ret) { |
349 | dev_err(&spi->dev, |
350 | "enable reference regulator failed, %d\n" , |
351 | ret); |
352 | return ret; |
353 | } |
354 | st->vref_mv = regulator_get_voltage(regulator: st->vref_reg) / 1000; |
355 | |
356 | ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER, |
357 | addr: 0, val: 0, shift: 0); |
358 | if (ret) { |
359 | dev_err(&spi->dev, |
360 | "Set external reference command failed, %d\n" , |
361 | ret); |
362 | return ret; |
363 | } |
364 | } |
365 | |
366 | indio_dev->name = fwnode_get_name(dev_fwnode(&spi->dev)) ?: spi_get_device_id(sdev: spi)->name; |
367 | indio_dev->info = <c2632_info; |
368 | indio_dev->modes = INDIO_DIRECT_MODE; |
369 | indio_dev->channels = chip_info->channels; |
370 | indio_dev->num_channels = chip_info->num_channels; |
371 | |
372 | return iio_device_register(indio_dev); |
373 | } |
374 | |
375 | static void ltc2632_remove(struct spi_device *spi) |
376 | { |
377 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
378 | struct ltc2632_state *st = iio_priv(indio_dev); |
379 | |
380 | iio_device_unregister(indio_dev); |
381 | |
382 | if (st->vref_reg) |
383 | regulator_disable(regulator: st->vref_reg); |
384 | } |
385 | |
386 | static const struct spi_device_id ltc2632_id[] = { |
387 | { "ltc2632-l12" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L12] }, |
388 | { "ltc2632-l10" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L10] }, |
389 | { "ltc2632-l8" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L8] }, |
390 | { "ltc2632-h12" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, |
391 | { "ltc2632-h10" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, |
392 | { "ltc2632-h8" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, |
393 | { "ltc2634-l12" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L12] }, |
394 | { "ltc2634-l10" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L10] }, |
395 | { "ltc2634-l8" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634L8] }, |
396 | { "ltc2634-h12" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H12] }, |
397 | { "ltc2634-h10" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H10] }, |
398 | { "ltc2634-h8" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2634H8] }, |
399 | { "ltc2636-l12" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L12] }, |
400 | { "ltc2636-l10" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L10] }, |
401 | { "ltc2636-l8" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L8] }, |
402 | { "ltc2636-h12" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] }, |
403 | { "ltc2636-h10" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] }, |
404 | { "ltc2636-h8" , (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] }, |
405 | {} |
406 | }; |
407 | MODULE_DEVICE_TABLE(spi, ltc2632_id); |
408 | |
409 | static const struct of_device_id ltc2632_of_match[] = { |
410 | { |
411 | .compatible = "lltc,ltc2632-l12" , |
412 | .data = <c2632_chip_info_tbl[ID_LTC2632L12] |
413 | }, { |
414 | .compatible = "lltc,ltc2632-l10" , |
415 | .data = <c2632_chip_info_tbl[ID_LTC2632L10] |
416 | }, { |
417 | .compatible = "lltc,ltc2632-l8" , |
418 | .data = <c2632_chip_info_tbl[ID_LTC2632L8] |
419 | }, { |
420 | .compatible = "lltc,ltc2632-h12" , |
421 | .data = <c2632_chip_info_tbl[ID_LTC2632H12] |
422 | }, { |
423 | .compatible = "lltc,ltc2632-h10" , |
424 | .data = <c2632_chip_info_tbl[ID_LTC2632H10] |
425 | }, { |
426 | .compatible = "lltc,ltc2632-h8" , |
427 | .data = <c2632_chip_info_tbl[ID_LTC2632H8] |
428 | }, { |
429 | .compatible = "lltc,ltc2634-l12" , |
430 | .data = <c2632_chip_info_tbl[ID_LTC2634L12] |
431 | }, { |
432 | .compatible = "lltc,ltc2634-l10" , |
433 | .data = <c2632_chip_info_tbl[ID_LTC2634L10] |
434 | }, { |
435 | .compatible = "lltc,ltc2634-l8" , |
436 | .data = <c2632_chip_info_tbl[ID_LTC2634L8] |
437 | }, { |
438 | .compatible = "lltc,ltc2634-h12" , |
439 | .data = <c2632_chip_info_tbl[ID_LTC2634H12] |
440 | }, { |
441 | .compatible = "lltc,ltc2634-h10" , |
442 | .data = <c2632_chip_info_tbl[ID_LTC2634H10] |
443 | }, { |
444 | .compatible = "lltc,ltc2634-h8" , |
445 | .data = <c2632_chip_info_tbl[ID_LTC2634H8] |
446 | }, { |
447 | .compatible = "lltc,ltc2636-l12" , |
448 | .data = <c2632_chip_info_tbl[ID_LTC2636L12] |
449 | }, { |
450 | .compatible = "lltc,ltc2636-l10" , |
451 | .data = <c2632_chip_info_tbl[ID_LTC2636L10] |
452 | }, { |
453 | .compatible = "lltc,ltc2636-l8" , |
454 | .data = <c2632_chip_info_tbl[ID_LTC2636L8] |
455 | }, { |
456 | .compatible = "lltc,ltc2636-h12" , |
457 | .data = <c2632_chip_info_tbl[ID_LTC2636H12] |
458 | }, { |
459 | .compatible = "lltc,ltc2636-h10" , |
460 | .data = <c2632_chip_info_tbl[ID_LTC2636H10] |
461 | }, { |
462 | .compatible = "lltc,ltc2636-h8" , |
463 | .data = <c2632_chip_info_tbl[ID_LTC2636H8] |
464 | }, |
465 | {} |
466 | }; |
467 | MODULE_DEVICE_TABLE(of, ltc2632_of_match); |
468 | |
469 | static struct spi_driver ltc2632_driver = { |
470 | .driver = { |
471 | .name = "ltc2632" , |
472 | .of_match_table = ltc2632_of_match, |
473 | }, |
474 | .probe = ltc2632_probe, |
475 | .remove = ltc2632_remove, |
476 | .id_table = ltc2632_id, |
477 | }; |
478 | module_spi_driver(ltc2632_driver); |
479 | |
480 | MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>" ); |
481 | MODULE_DESCRIPTION("LTC2632 DAC SPI driver" ); |
482 | MODULE_LICENSE("GPL v2" ); |
483 | |