]>
Commit | Line | Data |
---|---|---|
02b829f9 MRB |
1 | /* |
2 | * LTC2632 Digital to analog convertors spi driver | |
3 | * | |
3723c632 | 4 | * Copyright 2017 Maxime Roussin-BĂ©langer |
9ff1d500 | 5 | * expanded by Silvan Murer <[email protected]> |
02b829f9 MRB |
6 | * |
7 | * Licensed under the GPL-2. | |
8 | */ | |
9 | ||
10 | #include <linux/device.h> | |
11 | #include <linux/spi/spi.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/iio/iio.h> | |
9ff1d500 | 14 | #include <linux/regulator/consumer.h> |
02b829f9 MRB |
15 | |
16 | #define LTC2632_DAC_CHANNELS 2 | |
17 | ||
18 | #define LTC2632_ADDR_DAC0 0x0 | |
19 | #define LTC2632_ADDR_DAC1 0x1 | |
20 | ||
21 | #define LTC2632_CMD_WRITE_INPUT_N 0x0 | |
22 | #define LTC2632_CMD_UPDATE_DAC_N 0x1 | |
23 | #define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 | |
24 | #define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N 0x3 | |
25 | #define LTC2632_CMD_POWERDOWN_DAC_N 0x4 | |
26 | #define LTC2632_CMD_POWERDOWN_CHIP 0x5 | |
27 | #define LTC2632_CMD_INTERNAL_REFER 0x6 | |
28 | #define LTC2632_CMD_EXTERNAL_REFER 0x7 | |
29 | ||
30 | /** | |
31 | * struct ltc2632_chip_info - chip specific information | |
32 | * @channels: channel spec for the DAC | |
9ff1d500 | 33 | * @vref_mv: internal reference voltage |
02b829f9 MRB |
34 | */ |
35 | struct ltc2632_chip_info { | |
36 | const struct iio_chan_spec *channels; | |
37 | const int vref_mv; | |
38 | }; | |
39 | ||
40 | /** | |
41 | * struct ltc2632_state - driver instance specific data | |
42 | * @spi_dev: pointer to the spi_device struct | |
43 | * @powerdown_cache_mask used to show current channel powerdown state | |
9ff1d500 SM |
44 | * @vref_mv used reference voltage (internal or external) |
45 | * @vref_reg regulator for the reference voltage | |
02b829f9 MRB |
46 | */ |
47 | struct ltc2632_state { | |
48 | struct spi_device *spi_dev; | |
49 | unsigned int powerdown_cache_mask; | |
9ff1d500 SM |
50 | int vref_mv; |
51 | struct regulator *vref_reg; | |
02b829f9 MRB |
52 | }; |
53 | ||
54 | enum ltc2632_supported_device_ids { | |
55 | ID_LTC2632L12, | |
56 | ID_LTC2632L10, | |
57 | ID_LTC2632L8, | |
58 | ID_LTC2632H12, | |
59 | ID_LTC2632H10, | |
60 | ID_LTC2632H8, | |
61 | }; | |
62 | ||
63 | static int ltc2632_spi_write(struct spi_device *spi, | |
64 | u8 cmd, u8 addr, u16 val, u8 shift) | |
65 | { | |
66 | u32 data; | |
67 | u8 msg[3]; | |
68 | ||
69 | /* | |
70 | * The input shift register is 24 bits wide. | |
71 | * The next four are the command bits, C3 to C0, | |
72 | * followed by the 4-bit DAC address, A3 to A0, and then the | |
73 | * 12-, 10-, 8-bit data-word. The data-word comprises the 12-, | |
74 | * 10-, 8-bit input code followed by 4, 6, or 8 don't care bits. | |
75 | */ | |
76 | data = (cmd << 20) | (addr << 16) | (val << shift); | |
77 | msg[0] = data >> 16; | |
78 | msg[1] = data >> 8; | |
79 | msg[2] = data; | |
80 | ||
81 | return spi_write(spi, msg, sizeof(msg)); | |
82 | } | |
83 | ||
84 | static int ltc2632_read_raw(struct iio_dev *indio_dev, | |
85 | struct iio_chan_spec const *chan, | |
86 | int *val, | |
87 | int *val2, | |
88 | long m) | |
89 | { | |
02b829f9 | 90 | const struct ltc2632_state *st = iio_priv(indio_dev); |
02b829f9 MRB |
91 | |
92 | switch (m) { | |
93 | case IIO_CHAN_INFO_SCALE: | |
9ff1d500 | 94 | *val = st->vref_mv; |
02b829f9 MRB |
95 | *val2 = chan->scan_type.realbits; |
96 | return IIO_VAL_FRACTIONAL_LOG2; | |
97 | } | |
98 | return -EINVAL; | |
99 | } | |
100 | ||
101 | static int ltc2632_write_raw(struct iio_dev *indio_dev, | |
102 | struct iio_chan_spec const *chan, | |
103 | int val, | |
104 | int val2, | |
105 | long mask) | |
106 | { | |
107 | struct ltc2632_state *st = iio_priv(indio_dev); | |
108 | ||
109 | switch (mask) { | |
110 | case IIO_CHAN_INFO_RAW: | |
111 | if (val >= (1 << chan->scan_type.realbits) || val < 0) | |
112 | return -EINVAL; | |
113 | ||
114 | return ltc2632_spi_write(st->spi_dev, | |
115 | LTC2632_CMD_WRITE_INPUT_N_UPDATE_N, | |
116 | chan->address, val, | |
117 | chan->scan_type.shift); | |
118 | default: | |
119 | return -EINVAL; | |
120 | } | |
121 | } | |
122 | ||
123 | static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev, | |
124 | uintptr_t private, | |
125 | const struct iio_chan_spec *chan, | |
126 | char *buf) | |
127 | { | |
128 | struct ltc2632_state *st = iio_priv(indio_dev); | |
129 | ||
130 | return sprintf(buf, "%d\n", | |
131 | !!(st->powerdown_cache_mask & (1 << chan->channel))); | |
132 | } | |
133 | ||
134 | static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev, | |
135 | uintptr_t private, | |
136 | const struct iio_chan_spec *chan, | |
137 | const char *buf, | |
138 | size_t len) | |
139 | { | |
140 | bool pwr_down; | |
141 | int ret; | |
142 | struct ltc2632_state *st = iio_priv(indio_dev); | |
143 | ||
144 | ret = strtobool(buf, &pwr_down); | |
145 | if (ret) | |
146 | return ret; | |
147 | ||
148 | if (pwr_down) | |
149 | st->powerdown_cache_mask |= (1 << chan->channel); | |
150 | else | |
151 | st->powerdown_cache_mask &= ~(1 << chan->channel); | |
152 | ||
153 | ret = ltc2632_spi_write(st->spi_dev, | |
154 | LTC2632_CMD_POWERDOWN_DAC_N, | |
155 | chan->channel, 0, 0); | |
156 | ||
157 | return ret ? ret : len; | |
158 | } | |
159 | ||
160 | static const struct iio_info ltc2632_info = { | |
161 | .write_raw = ltc2632_write_raw, | |
162 | .read_raw = ltc2632_read_raw, | |
02b829f9 MRB |
163 | }; |
164 | ||
165 | static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { | |
166 | { | |
167 | .name = "powerdown", | |
168 | .read = ltc2632_read_dac_powerdown, | |
169 | .write = ltc2632_write_dac_powerdown, | |
170 | .shared = IIO_SEPARATE, | |
171 | }, | |
172 | { }, | |
173 | }; | |
174 | ||
175 | #define LTC2632_CHANNEL(_chan, _bits) { \ | |
176 | .type = IIO_VOLTAGE, \ | |
177 | .indexed = 1, \ | |
178 | .output = 1, \ | |
179 | .channel = (_chan), \ | |
180 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
181 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
182 | .address = (_chan), \ | |
183 | .scan_type = { \ | |
184 | .realbits = (_bits), \ | |
185 | .shift = 16 - (_bits), \ | |
186 | }, \ | |
187 | .ext_info = ltc2632_ext_info, \ | |
188 | } | |
189 | ||
190 | #define DECLARE_LTC2632_CHANNELS(_name, _bits) \ | |
191 | const struct iio_chan_spec _name ## _channels[] = { \ | |
192 | LTC2632_CHANNEL(0, _bits), \ | |
193 | LTC2632_CHANNEL(1, _bits), \ | |
194 | } | |
195 | ||
196 | static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12); | |
197 | static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10); | |
198 | static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8); | |
199 | ||
200 | static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12); | |
201 | static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10); | |
202 | static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8); | |
203 | ||
204 | static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { | |
205 | [ID_LTC2632L12] = { | |
206 | .channels = ltc2632l12_channels, | |
207 | .vref_mv = 2500, | |
208 | }, | |
209 | [ID_LTC2632L10] = { | |
210 | .channels = ltc2632l10_channels, | |
211 | .vref_mv = 2500, | |
212 | }, | |
213 | [ID_LTC2632L8] = { | |
214 | .channels = ltc2632l8_channels, | |
215 | .vref_mv = 2500, | |
216 | }, | |
217 | [ID_LTC2632H12] = { | |
218 | .channels = ltc2632h12_channels, | |
219 | .vref_mv = 4096, | |
220 | }, | |
221 | [ID_LTC2632H10] = { | |
222 | .channels = ltc2632h10_channels, | |
223 | .vref_mv = 4096, | |
224 | }, | |
225 | [ID_LTC2632H8] = { | |
226 | .channels = ltc2632h8_channels, | |
227 | .vref_mv = 4096, | |
228 | }, | |
229 | }; | |
230 | ||
231 | static int ltc2632_probe(struct spi_device *spi) | |
232 | { | |
233 | struct ltc2632_state *st; | |
234 | struct iio_dev *indio_dev; | |
235 | struct ltc2632_chip_info *chip_info; | |
236 | int ret; | |
237 | ||
238 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); | |
239 | if (!indio_dev) | |
240 | return -ENOMEM; | |
241 | ||
242 | st = iio_priv(indio_dev); | |
243 | ||
244 | spi_set_drvdata(spi, indio_dev); | |
245 | st->spi_dev = spi; | |
246 | ||
247 | chip_info = (struct ltc2632_chip_info *) | |
248 | spi_get_device_id(spi)->driver_data; | |
249 | ||
9ff1d500 SM |
250 | st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); |
251 | if (PTR_ERR(st->vref_reg) == -ENODEV) { | |
252 | /* use internal reference voltage */ | |
253 | st->vref_reg = NULL; | |
254 | st->vref_mv = chip_info->vref_mv; | |
255 | ||
256 | ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, | |
257 | 0, 0, 0); | |
258 | if (ret) { | |
259 | dev_err(&spi->dev, | |
260 | "Set internal reference command failed, %d\n", | |
261 | ret); | |
262 | return ret; | |
263 | } | |
264 | } else if (IS_ERR(st->vref_reg)) { | |
265 | dev_err(&spi->dev, | |
266 | "Error getting voltage reference regulator\n"); | |
267 | return PTR_ERR(st->vref_reg); | |
268 | } else { | |
269 | /* use external reference voltage */ | |
270 | ret = regulator_enable(st->vref_reg); | |
271 | if (ret) { | |
272 | dev_err(&spi->dev, | |
273 | "enable reference regulator failed, %d\n", | |
274 | ret); | |
275 | return ret; | |
276 | } | |
277 | st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000; | |
278 | ||
279 | ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER, | |
280 | 0, 0, 0); | |
281 | if (ret) { | |
282 | dev_err(&spi->dev, | |
283 | "Set external reference command failed, %d\n", | |
284 | ret); | |
285 | return ret; | |
286 | } | |
287 | } | |
288 | ||
02b829f9 MRB |
289 | indio_dev->dev.parent = &spi->dev; |
290 | indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name | |
291 | : spi_get_device_id(spi)->name; | |
292 | indio_dev->info = <c2632_info; | |
293 | indio_dev->modes = INDIO_DIRECT_MODE; | |
294 | indio_dev->channels = chip_info->channels; | |
295 | indio_dev->num_channels = LTC2632_DAC_CHANNELS; | |
296 | ||
9ff1d500 SM |
297 | return iio_device_register(indio_dev); |
298 | } | |
299 | ||
300 | static int ltc2632_remove(struct spi_device *spi) | |
301 | { | |
302 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
303 | struct ltc2632_state *st = iio_priv(indio_dev); | |
304 | ||
305 | iio_device_unregister(indio_dev); | |
306 | ||
307 | if (st->vref_reg) | |
308 | regulator_disable(st->vref_reg); | |
02b829f9 | 309 | |
9ff1d500 | 310 | return 0; |
02b829f9 MRB |
311 | } |
312 | ||
313 | static const struct spi_device_id ltc2632_id[] = { | |
314 | { "ltc2632-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L12] }, | |
315 | { "ltc2632-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L10] }, | |
316 | { "ltc2632-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L8] }, | |
317 | { "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, | |
318 | { "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, | |
319 | { "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, | |
320 | {} | |
321 | }; | |
322 | MODULE_DEVICE_TABLE(spi, ltc2632_id); | |
323 | ||
02b829f9 MRB |
324 | static const struct of_device_id ltc2632_of_match[] = { |
325 | { | |
326 | .compatible = "lltc,ltc2632-l12", | |
327 | .data = <c2632_chip_info_tbl[ID_LTC2632L12] | |
328 | }, { | |
329 | .compatible = "lltc,ltc2632-l10", | |
330 | .data = <c2632_chip_info_tbl[ID_LTC2632L10] | |
331 | }, { | |
332 | .compatible = "lltc,ltc2632-l8", | |
333 | .data = <c2632_chip_info_tbl[ID_LTC2632L8] | |
334 | }, { | |
335 | .compatible = "lltc,ltc2632-h12", | |
336 | .data = <c2632_chip_info_tbl[ID_LTC2632H12] | |
337 | }, { | |
338 | .compatible = "lltc,ltc2632-h10", | |
339 | .data = <c2632_chip_info_tbl[ID_LTC2632H10] | |
340 | }, { | |
341 | .compatible = "lltc,ltc2632-h8", | |
342 | .data = <c2632_chip_info_tbl[ID_LTC2632H8] | |
343 | }, | |
344 | {} | |
345 | }; | |
346 | MODULE_DEVICE_TABLE(of, ltc2632_of_match); | |
347 | ||
0f6a2165 SM |
348 | static struct spi_driver ltc2632_driver = { |
349 | .driver = { | |
350 | .name = "ltc2632", | |
351 | .of_match_table = of_match_ptr(ltc2632_of_match), | |
352 | }, | |
353 | .probe = ltc2632_probe, | |
9ff1d500 | 354 | .remove = ltc2632_remove, |
0f6a2165 SM |
355 | .id_table = ltc2632_id, |
356 | }; | |
357 | module_spi_driver(ltc2632_driver); | |
358 | ||
02b829f9 MRB |
359 | MODULE_AUTHOR("Maxime Roussin-Belanger <[email protected]>"); |
360 | MODULE_DESCRIPTION("LTC2632 DAC SPI driver"); | |
361 | MODULE_LICENSE("GPL v2"); |