diff options
Diffstat (limited to 'drivers/iio/dac/ad5449.c')
-rw-r--r-- | drivers/iio/dac/ad5449.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c new file mode 100644 index 000000000000..5b43030fe6e3 --- /dev/null +++ b/drivers/iio/dac/ad5449.c | |||
@@ -0,0 +1,375 @@ | |||
1 | /* | ||
2 | * AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog | ||
3 | * Converter driver. | ||
4 | * | ||
5 | * Copyright 2012 Analog Devices Inc. | ||
6 | * Author: Lars-Peter Clausen <lars@metafoo.de> | ||
7 | * | ||
8 | * Licensed under the GPL-2. | ||
9 | */ | ||
10 | |||
11 | #include <linux/device.h> | ||
12 | #include <linux/err.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/sysfs.h> | ||
18 | #include <linux/regulator/consumer.h> | ||
19 | #include <asm/unaligned.h> | ||
20 | |||
21 | #include <linux/iio/iio.h> | ||
22 | #include <linux/iio/sysfs.h> | ||
23 | |||
24 | #include <linux/platform_data/ad5449.h> | ||
25 | |||
26 | #define AD5449_MAX_CHANNELS 2 | ||
27 | #define AD5449_MAX_VREFS 2 | ||
28 | |||
29 | #define AD5449_CMD_NOOP 0x0 | ||
30 | #define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3) | ||
31 | #define AD5449_CMD_READ(x) (0x2 + (x) * 3) | ||
32 | #define AD5449_CMD_LOAD(x) (0x3 + (x) * 3) | ||
33 | #define AD5449_CMD_CTRL 13 | ||
34 | |||
35 | #define AD5449_CTRL_SDO_OFFSET 10 | ||
36 | #define AD5449_CTRL_DAISY_CHAIN BIT(9) | ||
37 | #define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8) | ||
38 | #define AD5449_CTRL_SAMPLE_RISING BIT(7) | ||
39 | |||
40 | /** | ||
41 | * struct ad5449_chip_info - chip specific information | ||
42 | * @channels: Channel specification | ||
43 | * @num_channels: Number of channels | ||
44 | * @has_ctrl: Chip has a control register | ||
45 | */ | ||
46 | struct ad5449_chip_info { | ||
47 | const struct iio_chan_spec *channels; | ||
48 | unsigned int num_channels; | ||
49 | bool has_ctrl; | ||
50 | }; | ||
51 | |||
52 | /** | ||
53 | * struct ad5449 - driver instance specific data | ||
54 | * @spi: the SPI device for this driver instance | ||
55 | * @chip_info: chip model specific constants, available modes etc | ||
56 | * @vref_reg: vref supply regulators | ||
57 | * @has_sdo: whether the SDO line is connected | ||
58 | * @dac_cache: Cache for the DAC values | ||
59 | * @data: spi transfer buffers | ||
60 | */ | ||
61 | struct ad5449 { | ||
62 | struct spi_device *spi; | ||
63 | const struct ad5449_chip_info *chip_info; | ||
64 | struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS]; | ||
65 | |||
66 | bool has_sdo; | ||
67 | uint16_t dac_cache[AD5449_MAX_CHANNELS]; | ||
68 | |||
69 | /* | ||
70 | * DMA (thus cache coherency maintenance) requires the | ||
71 | * transfer buffers to live in their own cache lines. | ||
72 | */ | ||
73 | __be16 data[2] ____cacheline_aligned; | ||
74 | }; | ||
75 | |||
76 | enum ad5449_type { | ||
77 | ID_AD5426, | ||
78 | ID_AD5429, | ||
79 | ID_AD5432, | ||
80 | ID_AD5439, | ||
81 | ID_AD5443, | ||
82 | ID_AD5449, | ||
83 | }; | ||
84 | |||
85 | static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr, | ||
86 | unsigned int val) | ||
87 | { | ||
88 | struct ad5449 *st = iio_priv(indio_dev); | ||
89 | int ret; | ||
90 | |||
91 | mutex_lock(&indio_dev->mlock); | ||
92 | st->data[0] = cpu_to_be16((addr << 12) | val); | ||
93 | ret = spi_write(st->spi, st->data, 2); | ||
94 | mutex_unlock(&indio_dev->mlock); | ||
95 | |||
96 | return ret; | ||
97 | } | ||
98 | |||
99 | static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr, | ||
100 | unsigned int *val) | ||
101 | { | ||
102 | struct ad5449 *st = iio_priv(indio_dev); | ||
103 | int ret; | ||
104 | struct spi_message msg; | ||
105 | struct spi_transfer t[] = { | ||
106 | { | ||
107 | .tx_buf = &st->data[0], | ||
108 | .len = 2, | ||
109 | .cs_change = 1, | ||
110 | }, { | ||
111 | .tx_buf = &st->data[1], | ||
112 | .rx_buf = &st->data[1], | ||
113 | .len = 2, | ||
114 | }, | ||
115 | }; | ||
116 | |||
117 | spi_message_init(&msg); | ||
118 | spi_message_add_tail(&t[0], &msg); | ||
119 | spi_message_add_tail(&t[1], &msg); | ||
120 | |||
121 | mutex_lock(&indio_dev->mlock); | ||
122 | st->data[0] = cpu_to_be16(addr << 12); | ||
123 | st->data[1] = cpu_to_be16(AD5449_CMD_NOOP); | ||
124 | |||
125 | ret = spi_sync(st->spi, &msg); | ||
126 | if (ret < 0) | ||
127 | return ret; | ||
128 | |||
129 | *val = be16_to_cpu(st->data[1]); | ||
130 | mutex_unlock(&indio_dev->mlock); | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int ad5449_read_raw(struct iio_dev *indio_dev, | ||
136 | struct iio_chan_spec const *chan, int *val, int *val2, long info) | ||
137 | { | ||
138 | struct ad5449 *st = iio_priv(indio_dev); | ||
139 | struct regulator_bulk_data *reg; | ||
140 | int scale_uv; | ||
141 | int ret; | ||
142 | |||
143 | switch (info) { | ||
144 | case IIO_CHAN_INFO_RAW: | ||
145 | if (st->has_sdo) { | ||
146 | ret = ad5449_read(indio_dev, | ||
147 | AD5449_CMD_READ(chan->address), val); | ||
148 | if (ret) | ||
149 | return ret; | ||
150 | *val &= 0xfff; | ||
151 | } else { | ||
152 | *val = st->dac_cache[chan->address]; | ||
153 | } | ||
154 | |||
155 | return IIO_VAL_INT; | ||
156 | case IIO_CHAN_INFO_SCALE: | ||
157 | reg = &st->vref_reg[chan->channel]; | ||
158 | scale_uv = regulator_get_voltage(reg->consumer); | ||
159 | if (scale_uv < 0) | ||
160 | return scale_uv; | ||
161 | |||
162 | *val = scale_uv / 1000; | ||
163 | *val2 = chan->scan_type.realbits; | ||
164 | |||
165 | return IIO_VAL_FRACTIONAL_LOG2; | ||
166 | default: | ||
167 | break; | ||
168 | } | ||
169 | |||
170 | return -EINVAL; | ||
171 | } | ||
172 | |||
173 | static int ad5449_write_raw(struct iio_dev *indio_dev, | ||
174 | struct iio_chan_spec const *chan, int val, int val2, long info) | ||
175 | { | ||
176 | struct ad5449 *st = iio_priv(indio_dev); | ||
177 | int ret; | ||
178 | |||
179 | switch (info) { | ||
180 | case IIO_CHAN_INFO_RAW: | ||
181 | if (val < 0 || val >= (1 << chan->scan_type.realbits)) | ||
182 | return -EINVAL; | ||
183 | |||
184 | ret = ad5449_write(indio_dev, | ||
185 | AD5449_CMD_LOAD_AND_UPDATE(chan->address), | ||
186 | val << chan->scan_type.shift); | ||
187 | if (ret == 0) | ||
188 | st->dac_cache[chan->address] = val; | ||
189 | break; | ||
190 | default: | ||
191 | ret = -EINVAL; | ||
192 | } | ||
193 | |||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | static const struct iio_info ad5449_info = { | ||
198 | .read_raw = ad5449_read_raw, | ||
199 | .write_raw = ad5449_write_raw, | ||
200 | .driver_module = THIS_MODULE, | ||
201 | }; | ||
202 | |||
203 | #define AD5449_CHANNEL(chan, bits) { \ | ||
204 | .type = IIO_VOLTAGE, \ | ||
205 | .indexed = 1, \ | ||
206 | .output = 1, \ | ||
207 | .channel = (chan), \ | ||
208 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ | ||
209 | IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ | ||
210 | .address = (chan), \ | ||
211 | .scan_type = IIO_ST('u', (bits), 16, 12 - (bits)), \ | ||
212 | } | ||
213 | |||
214 | #define DECLARE_AD5449_CHANNELS(name, bits) \ | ||
215 | const struct iio_chan_spec name[] = { \ | ||
216 | AD5449_CHANNEL(0, bits), \ | ||
217 | AD5449_CHANNEL(1, bits), \ | ||
218 | } | ||
219 | |||
220 | static DECLARE_AD5449_CHANNELS(ad5429_channels, 8); | ||
221 | static DECLARE_AD5449_CHANNELS(ad5439_channels, 10); | ||
222 | static DECLARE_AD5449_CHANNELS(ad5449_channels, 12); | ||
223 | |||
224 | static const struct ad5449_chip_info ad5449_chip_info[] = { | ||
225 | [ID_AD5426] = { | ||
226 | .channels = ad5429_channels, | ||
227 | .num_channels = 1, | ||
228 | .has_ctrl = false, | ||
229 | }, | ||
230 | [ID_AD5429] = { | ||
231 | .channels = ad5429_channels, | ||
232 | .num_channels = 2, | ||
233 | .has_ctrl = true, | ||
234 | }, | ||
235 | [ID_AD5432] = { | ||
236 | .channels = ad5439_channels, | ||
237 | .num_channels = 1, | ||
238 | .has_ctrl = false, | ||
239 | }, | ||
240 | [ID_AD5439] = { | ||
241 | .channels = ad5439_channels, | ||
242 | .num_channels = 2, | ||
243 | .has_ctrl = true, | ||
244 | }, | ||
245 | [ID_AD5443] = { | ||
246 | .channels = ad5449_channels, | ||
247 | .num_channels = 1, | ||
248 | .has_ctrl = false, | ||
249 | }, | ||
250 | [ID_AD5449] = { | ||
251 | .channels = ad5449_channels, | ||
252 | .num_channels = 2, | ||
253 | .has_ctrl = true, | ||
254 | }, | ||
255 | }; | ||
256 | |||
257 | static const char *ad5449_vref_name(struct ad5449 *st, int n) | ||
258 | { | ||
259 | if (st->chip_info->num_channels == 1) | ||
260 | return "VREF"; | ||
261 | |||
262 | if (n == 0) | ||
263 | return "VREFA"; | ||
264 | else | ||
265 | return "VREFB"; | ||
266 | } | ||
267 | |||
268 | static int __devinit ad5449_spi_probe(struct spi_device *spi) | ||
269 | { | ||
270 | struct ad5449_platform_data *pdata = spi->dev.platform_data; | ||
271 | const struct spi_device_id *id = spi_get_device_id(spi); | ||
272 | struct iio_dev *indio_dev; | ||
273 | struct ad5449 *st; | ||
274 | unsigned int i; | ||
275 | int ret; | ||
276 | |||
277 | indio_dev = iio_device_alloc(sizeof(*st)); | ||
278 | if (indio_dev == NULL) | ||
279 | return -ENOMEM; | ||
280 | |||
281 | st = iio_priv(indio_dev); | ||
282 | spi_set_drvdata(spi, indio_dev); | ||
283 | |||
284 | st->chip_info = &ad5449_chip_info[id->driver_data]; | ||
285 | st->spi = spi; | ||
286 | |||
287 | for (i = 0; i < st->chip_info->num_channels; ++i) | ||
288 | st->vref_reg[i].supply = ad5449_vref_name(st, i); | ||
289 | |||
290 | ret = regulator_bulk_get(&spi->dev, st->chip_info->num_channels, | ||
291 | st->vref_reg); | ||
292 | if (ret) | ||
293 | goto error_free; | ||
294 | |||
295 | ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg); | ||
296 | if (ret) | ||
297 | goto error_free_reg; | ||
298 | |||
299 | indio_dev->dev.parent = &spi->dev; | ||
300 | indio_dev->name = id->name; | ||
301 | indio_dev->info = &ad5449_info; | ||
302 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
303 | indio_dev->channels = st->chip_info->channels; | ||
304 | indio_dev->num_channels = st->chip_info->num_channels; | ||
305 | |||
306 | if (st->chip_info->has_ctrl) { | ||
307 | unsigned int ctrl = 0x00; | ||
308 | if (pdata) { | ||
309 | if (pdata->hardware_clear_to_midscale) | ||
310 | ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE; | ||
311 | ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET; | ||
312 | st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED; | ||
313 | } else { | ||
314 | st->has_sdo = true; | ||
315 | } | ||
316 | ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl); | ||
317 | } | ||
318 | |||
319 | ret = iio_device_register(indio_dev); | ||
320 | if (ret) | ||
321 | goto error_disable_reg; | ||
322 | |||
323 | return 0; | ||
324 | |||
325 | error_disable_reg: | ||
326 | regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg); | ||
327 | error_free_reg: | ||
328 | regulator_bulk_free(st->chip_info->num_channels, st->vref_reg); | ||
329 | error_free: | ||
330 | iio_device_free(indio_dev); | ||
331 | |||
332 | return ret; | ||
333 | } | ||
334 | |||
335 | static int __devexit ad5449_spi_remove(struct spi_device *spi) | ||
336 | { | ||
337 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
338 | struct ad5449 *st = iio_priv(indio_dev); | ||
339 | |||
340 | iio_device_unregister(indio_dev); | ||
341 | |||
342 | regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg); | ||
343 | regulator_bulk_free(st->chip_info->num_channels, st->vref_reg); | ||
344 | |||
345 | iio_device_free(indio_dev); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static const struct spi_device_id ad5449_spi_ids[] = { | ||
351 | { "ad5415", ID_AD5449 }, | ||
352 | { "ad5426", ID_AD5426 }, | ||
353 | { "ad5429", ID_AD5429 }, | ||
354 | { "ad5432", ID_AD5432 }, | ||
355 | { "ad5439", ID_AD5439 }, | ||
356 | { "ad5443", ID_AD5443 }, | ||
357 | { "ad5449", ID_AD5449 }, | ||
358 | {} | ||
359 | }; | ||
360 | MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); | ||
361 | |||
362 | static struct spi_driver ad5449_spi_driver = { | ||
363 | .driver = { | ||
364 | .name = "ad5449", | ||
365 | .owner = THIS_MODULE, | ||
366 | }, | ||
367 | .probe = ad5449_spi_probe, | ||
368 | .remove = __devexit_p(ad5449_spi_remove), | ||
369 | .id_table = ad5449_spi_ids, | ||
370 | }; | ||
371 | module_spi_driver(ad5449_spi_driver); | ||
372 | |||
373 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
374 | MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs"); | ||
375 | MODULE_LICENSE("GPL v2"); | ||