]>
Commit | Line | Data |
---|---|---|
61011264 LW |
1 | /* |
2 | * ti-dac082s085.c - Texas Instruments 8/10/12-bit 2/4-channel DAC driver | |
3 | * | |
4 | * Copyright (C) 2017 KUNBUS GmbH | |
5 | * | |
6 | * http://www.ti.com/lit/ds/symlink/dac082s085.pdf | |
7 | * http://www.ti.com/lit/ds/symlink/dac102s085.pdf | |
8 | * http://www.ti.com/lit/ds/symlink/dac122s085.pdf | |
9 | * http://www.ti.com/lit/ds/symlink/dac084s085.pdf | |
10 | * http://www.ti.com/lit/ds/symlink/dac104s085.pdf | |
11 | * http://www.ti.com/lit/ds/symlink/dac124s085.pdf | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License (version 2) as | |
15 | * published by the Free Software Foundation. | |
16 | */ | |
17 | ||
18 | #include <linux/iio/iio.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/regulator/consumer.h> | |
21 | #include <linux/spi/spi.h> | |
22 | ||
f98677cf LW |
23 | enum { dual_8bit, dual_10bit, dual_12bit, quad_8bit, quad_10bit, quad_12bit }; |
24 | ||
25 | struct ti_dac_spec { | |
26 | u8 num_channels; | |
27 | u8 resolution; | |
28 | }; | |
29 | ||
30 | static const struct ti_dac_spec ti_dac_spec[] = { | |
31 | [dual_8bit] = { .num_channels = 2, .resolution = 8 }, | |
32 | [dual_10bit] = { .num_channels = 2, .resolution = 10 }, | |
33 | [dual_12bit] = { .num_channels = 2, .resolution = 12 }, | |
34 | [quad_8bit] = { .num_channels = 4, .resolution = 8 }, | |
35 | [quad_10bit] = { .num_channels = 4, .resolution = 10 }, | |
36 | [quad_12bit] = { .num_channels = 4, .resolution = 12 }, | |
37 | }; | |
38 | ||
61011264 LW |
39 | /** |
40 | * struct ti_dac_chip - TI DAC chip | |
41 | * @lock: protects write sequences | |
42 | * @vref: regulator generating Vref | |
43 | * @mesg: SPI message to perform a write | |
44 | * @xfer: SPI transfer used by @mesg | |
45 | * @val: cached value of each output | |
46 | * @powerdown: whether the chip is powered down | |
47 | * @powerdown_mode: selected by the user | |
48 | * @resolution: resolution of the chip | |
49 | * @buf: buffer for @xfer | |
50 | */ | |
51 | struct ti_dac_chip { | |
52 | struct mutex lock; | |
53 | struct regulator *vref; | |
54 | struct spi_message mesg; | |
55 | struct spi_transfer xfer; | |
56 | u16 val[4]; | |
57 | bool powerdown; | |
58 | u8 powerdown_mode; | |
59 | u8 resolution; | |
60 | u8 buf[2] ____cacheline_aligned; | |
61 | }; | |
62 | ||
63 | #define WRITE_NOT_UPDATE(chan) (0x00 | (chan) << 6) | |
64 | #define WRITE_AND_UPDATE(chan) (0x10 | (chan) << 6) | |
65 | #define WRITE_ALL_UPDATE 0x20 | |
66 | #define POWERDOWN(mode) (0x30 | ((mode) + 1) << 6) | |
67 | ||
68 | static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 cmd, u16 val) | |
69 | { | |
70 | u8 shift = 12 - ti_dac->resolution; | |
71 | ||
72 | ti_dac->buf[0] = cmd | (val >> (8 - shift)); | |
73 | ti_dac->buf[1] = (val << shift) & 0xff; | |
74 | return spi_sync(ti_dac->mesg.spi, &ti_dac->mesg); | |
75 | } | |
76 | ||
77 | static const char * const ti_dac_powerdown_modes[] = { | |
78 | "2.5kohm_to_gnd", "100kohm_to_gnd", "three_state", | |
79 | }; | |
80 | ||
81 | static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev, | |
82 | const struct iio_chan_spec *chan) | |
83 | { | |
84 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
85 | ||
86 | return ti_dac->powerdown_mode; | |
87 | } | |
88 | ||
89 | static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev, | |
90 | const struct iio_chan_spec *chan, | |
91 | unsigned int mode) | |
92 | { | |
93 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
94 | int ret = 0; | |
95 | ||
96 | if (ti_dac->powerdown_mode == mode) | |
97 | return 0; | |
98 | ||
99 | mutex_lock(&ti_dac->lock); | |
100 | if (ti_dac->powerdown) { | |
101 | ret = ti_dac_cmd(ti_dac, POWERDOWN(mode), 0); | |
102 | if (ret) | |
103 | goto out; | |
104 | } | |
105 | ti_dac->powerdown_mode = mode; | |
106 | ||
107 | out: | |
108 | mutex_unlock(&ti_dac->lock); | |
109 | return ret; | |
110 | } | |
111 | ||
112 | static const struct iio_enum ti_dac_powerdown_mode = { | |
113 | .items = ti_dac_powerdown_modes, | |
114 | .num_items = ARRAY_SIZE(ti_dac_powerdown_modes), | |
115 | .get = ti_dac_get_powerdown_mode, | |
116 | .set = ti_dac_set_powerdown_mode, | |
117 | }; | |
118 | ||
119 | static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev, | |
120 | uintptr_t private, | |
121 | const struct iio_chan_spec *chan, | |
122 | char *buf) | |
123 | { | |
124 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
125 | ||
126 | return sprintf(buf, "%d\n", ti_dac->powerdown); | |
127 | } | |
128 | ||
129 | static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev, | |
130 | uintptr_t private, | |
131 | const struct iio_chan_spec *chan, | |
132 | const char *buf, size_t len) | |
133 | { | |
134 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
135 | bool powerdown; | |
136 | int ret; | |
137 | ||
138 | ret = strtobool(buf, &powerdown); | |
139 | if (ret) | |
140 | return ret; | |
141 | ||
142 | if (ti_dac->powerdown == powerdown) | |
143 | return len; | |
144 | ||
145 | mutex_lock(&ti_dac->lock); | |
146 | if (powerdown) | |
147 | ret = ti_dac_cmd(ti_dac, POWERDOWN(ti_dac->powerdown_mode), 0); | |
148 | else | |
149 | ret = ti_dac_cmd(ti_dac, WRITE_AND_UPDATE(0), ti_dac->val[0]); | |
150 | if (!ret) | |
151 | ti_dac->powerdown = powerdown; | |
152 | mutex_unlock(&ti_dac->lock); | |
153 | ||
154 | return ret ? ret : len; | |
155 | } | |
156 | ||
157 | static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { | |
158 | { | |
159 | .name = "powerdown", | |
160 | .read = ti_dac_read_powerdown, | |
161 | .write = ti_dac_write_powerdown, | |
162 | .shared = IIO_SHARED_BY_TYPE, | |
163 | }, | |
164 | IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), | |
165 | IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), | |
166 | { }, | |
167 | }; | |
168 | ||
169 | #define TI_DAC_CHANNEL(chan) { \ | |
170 | .type = IIO_VOLTAGE, \ | |
171 | .channel = (chan), \ | |
172 | .address = (chan), \ | |
173 | .indexed = true, \ | |
174 | .output = true, \ | |
175 | .datasheet_name = (const char[]){ 'A' + (chan), 0 }, \ | |
176 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
177 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
178 | .ext_info = ti_dac_ext_info, \ | |
179 | } | |
180 | ||
181 | static const struct iio_chan_spec ti_dac_channels[] = { | |
182 | TI_DAC_CHANNEL(0), | |
183 | TI_DAC_CHANNEL(1), | |
184 | TI_DAC_CHANNEL(2), | |
185 | TI_DAC_CHANNEL(3), | |
186 | }; | |
187 | ||
188 | static int ti_dac_read_raw(struct iio_dev *indio_dev, | |
189 | struct iio_chan_spec const *chan, | |
190 | int *val, int *val2, long mask) | |
191 | { | |
192 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
193 | int ret; | |
194 | ||
195 | switch (mask) { | |
196 | case IIO_CHAN_INFO_RAW: | |
197 | *val = ti_dac->val[chan->channel]; | |
198 | ret = IIO_VAL_INT; | |
199 | break; | |
200 | ||
201 | case IIO_CHAN_INFO_SCALE: | |
202 | ret = regulator_get_voltage(ti_dac->vref); | |
203 | if (ret < 0) | |
204 | return ret; | |
205 | ||
206 | *val = ret / 1000; | |
207 | *val2 = ti_dac->resolution; | |
208 | ret = IIO_VAL_FRACTIONAL_LOG2; | |
209 | break; | |
210 | ||
211 | default: | |
212 | ret = -EINVAL; | |
213 | } | |
214 | ||
215 | return ret; | |
216 | } | |
217 | ||
218 | static int ti_dac_write_raw(struct iio_dev *indio_dev, | |
219 | struct iio_chan_spec const *chan, | |
220 | int val, int val2, long mask) | |
221 | { | |
222 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
223 | int ret; | |
224 | ||
225 | switch (mask) { | |
226 | case IIO_CHAN_INFO_RAW: | |
227 | if (ti_dac->val[chan->channel] == val) | |
228 | return 0; | |
229 | ||
230 | if (val >= (1 << ti_dac->resolution) || val < 0) | |
231 | return -EINVAL; | |
232 | ||
233 | if (ti_dac->powerdown) | |
234 | return -EBUSY; | |
235 | ||
236 | mutex_lock(&ti_dac->lock); | |
237 | ret = ti_dac_cmd(ti_dac, WRITE_AND_UPDATE(chan->channel), val); | |
238 | if (!ret) | |
239 | ti_dac->val[chan->channel] = val; | |
240 | mutex_unlock(&ti_dac->lock); | |
241 | break; | |
242 | ||
243 | default: | |
244 | ret = -EINVAL; | |
245 | } | |
246 | ||
247 | return ret; | |
248 | } | |
249 | ||
250 | static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev, | |
251 | struct iio_chan_spec const *chan, long mask) | |
252 | { | |
253 | return IIO_VAL_INT; | |
254 | } | |
255 | ||
256 | static const struct iio_info ti_dac_info = { | |
257 | .read_raw = ti_dac_read_raw, | |
258 | .write_raw = ti_dac_write_raw, | |
259 | .write_raw_get_fmt = ti_dac_write_raw_get_fmt, | |
260 | }; | |
261 | ||
262 | static int ti_dac_probe(struct spi_device *spi) | |
263 | { | |
264 | struct device *dev = &spi->dev; | |
f98677cf | 265 | const struct ti_dac_spec *spec; |
61011264 LW |
266 | struct ti_dac_chip *ti_dac; |
267 | struct iio_dev *indio_dev; | |
268 | int ret; | |
269 | ||
270 | indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac)); | |
271 | if (!indio_dev) | |
272 | return -ENOMEM; | |
273 | ||
274 | indio_dev->dev.parent = dev; | |
275 | indio_dev->info = &ti_dac_info; | |
276 | indio_dev->name = spi->modalias; | |
277 | indio_dev->modes = INDIO_DIRECT_MODE; | |
278 | indio_dev->channels = ti_dac_channels; | |
279 | spi_set_drvdata(spi, indio_dev); | |
280 | ||
281 | ti_dac = iio_priv(indio_dev); | |
282 | ti_dac->xfer.tx_buf = &ti_dac->buf; | |
283 | ti_dac->xfer.len = sizeof(ti_dac->buf); | |
284 | spi_message_init_with_transfers(&ti_dac->mesg, &ti_dac->xfer, 1); | |
285 | ti_dac->mesg.spi = spi; | |
286 | ||
f98677cf LW |
287 | spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data]; |
288 | indio_dev->num_channels = spec->num_channels; | |
289 | ti_dac->resolution = spec->resolution; | |
61011264 LW |
290 | |
291 | ti_dac->vref = devm_regulator_get(dev, "vref"); | |
292 | if (IS_ERR(ti_dac->vref)) | |
293 | return PTR_ERR(ti_dac->vref); | |
294 | ||
295 | ret = regulator_enable(ti_dac->vref); | |
296 | if (ret < 0) | |
297 | return ret; | |
298 | ||
299 | mutex_init(&ti_dac->lock); | |
300 | ||
301 | ret = ti_dac_cmd(ti_dac, WRITE_ALL_UPDATE, 0); | |
302 | if (ret) { | |
303 | dev_err(dev, "failed to initialize outputs to 0\n"); | |
304 | goto err; | |
305 | } | |
306 | ||
307 | ret = iio_device_register(indio_dev); | |
308 | if (ret) | |
309 | goto err; | |
310 | ||
311 | return 0; | |
312 | ||
313 | err: | |
314 | mutex_destroy(&ti_dac->lock); | |
315 | regulator_disable(ti_dac->vref); | |
316 | return ret; | |
317 | } | |
318 | ||
319 | static int ti_dac_remove(struct spi_device *spi) | |
320 | { | |
321 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
322 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
323 | ||
324 | iio_device_unregister(indio_dev); | |
325 | mutex_destroy(&ti_dac->lock); | |
326 | regulator_disable(ti_dac->vref); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | #ifdef CONFIG_OF | |
332 | static const struct of_device_id ti_dac_of_id[] = { | |
333 | { .compatible = "ti,dac082s085" }, | |
334 | { .compatible = "ti,dac102s085" }, | |
335 | { .compatible = "ti,dac122s085" }, | |
336 | { .compatible = "ti,dac084s085" }, | |
337 | { .compatible = "ti,dac104s085" }, | |
338 | { .compatible = "ti,dac124s085" }, | |
339 | { } | |
340 | }; | |
341 | MODULE_DEVICE_TABLE(of, ti_dac_of_id); | |
342 | #endif | |
343 | ||
344 | static const struct spi_device_id ti_dac_spi_id[] = { | |
f98677cf LW |
345 | { "dac082s085", dual_8bit }, |
346 | { "dac102s085", dual_10bit }, | |
347 | { "dac122s085", dual_12bit }, | |
348 | { "dac084s085", quad_8bit }, | |
349 | { "dac104s085", quad_10bit }, | |
350 | { "dac124s085", quad_12bit }, | |
61011264 LW |
351 | { } |
352 | }; | |
353 | MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); | |
354 | ||
355 | static struct spi_driver ti_dac_driver = { | |
356 | .driver = { | |
357 | .name = "ti-dac082s085", | |
358 | .of_match_table = of_match_ptr(ti_dac_of_id), | |
359 | }, | |
360 | .probe = ti_dac_probe, | |
361 | .remove = ti_dac_remove, | |
362 | .id_table = ti_dac_spi_id, | |
363 | }; | |
364 | module_spi_driver(ti_dac_driver); | |
365 | ||
366 | MODULE_AUTHOR("Lukas Wunner <[email protected]>"); | |
367 | MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 2/4-channel DAC driver"); | |
368 | MODULE_LICENSE("GPL v2"); |