]>
Commit | Line | Data |
---|---|---|
a3e2940c LPC |
1 | /* |
2 | * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373 | |
3 | * multi-channel Digital to Analog Converters driver | |
4 | * | |
5 | * Copyright 2011 Analog Devices Inc. | |
6 | * | |
7 | * Licensed under the GPL-2. | |
8 | */ | |
9 | ||
10 | #include <linux/device.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/spi/spi.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/sysfs.h> | |
17 | #include <linux/regulator/consumer.h> | |
18 | ||
06458e27 JC |
19 | #include <linux/iio/iio.h> |
20 | #include <linux/iio/sysfs.h> | |
a3e2940c LPC |
21 | |
22 | #define AD5360_CMD(x) ((x) << 22) | |
23 | #define AD5360_ADDR(x) ((x) << 16) | |
24 | ||
25 | #define AD5360_READBACK_TYPE(x) ((x) << 13) | |
26 | #define AD5360_READBACK_ADDR(x) ((x) << 7) | |
27 | ||
28 | #define AD5360_CHAN_ADDR(chan) ((chan) + 0x8) | |
29 | ||
30 | #define AD5360_CMD_WRITE_DATA 0x3 | |
31 | #define AD5360_CMD_WRITE_OFFSET 0x2 | |
32 | #define AD5360_CMD_WRITE_GAIN 0x1 | |
33 | #define AD5360_CMD_SPECIAL_FUNCTION 0x0 | |
34 | ||
35 | /* Special function register addresses */ | |
36 | #define AD5360_REG_SF_NOP 0x0 | |
37 | #define AD5360_REG_SF_CTRL 0x1 | |
38 | #define AD5360_REG_SF_OFS(x) (0x2 + (x)) | |
39 | #define AD5360_REG_SF_READBACK 0x5 | |
40 | ||
41 | #define AD5360_SF_CTRL_PWR_DOWN BIT(0) | |
42 | ||
43 | #define AD5360_READBACK_X1A 0x0 | |
44 | #define AD5360_READBACK_X1B 0x1 | |
45 | #define AD5360_READBACK_OFFSET 0x2 | |
46 | #define AD5360_READBACK_GAIN 0x3 | |
47 | #define AD5360_READBACK_SF 0x4 | |
48 | ||
49 | ||
50 | /** | |
51 | * struct ad5360_chip_info - chip specific information | |
52 | * @channel_template: channel specification template | |
53 | * @num_channels: number of channels | |
54 | * @channels_per_group: number of channels per group | |
55 | * @num_vrefs: number of vref supplies for the chip | |
56 | */ | |
57 | ||
58 | struct ad5360_chip_info { | |
59 | struct iio_chan_spec channel_template; | |
60 | unsigned int num_channels; | |
61 | unsigned int channels_per_group; | |
62 | unsigned int num_vrefs; | |
63 | }; | |
64 | ||
65 | /** | |
66 | * struct ad5360_state - driver instance specific data | |
67 | * @spi: spi_device | |
68 | * @chip_info: chip model specific constants, available modes etc | |
69 | * @vref_reg: vref supply regulators | |
70 | * @ctrl: control register cache | |
71 | * @data: spi transfer buffers | |
72 | */ | |
73 | ||
74 | struct ad5360_state { | |
75 | struct spi_device *spi; | |
76 | const struct ad5360_chip_info *chip_info; | |
77 | struct regulator_bulk_data vref_reg[3]; | |
78 | unsigned int ctrl; | |
79 | ||
80 | /* | |
81 | * DMA (thus cache coherency maintenance) requires the | |
82 | * transfer buffers to live in their own cache lines. | |
83 | */ | |
84 | union { | |
85 | __be32 d32; | |
86 | u8 d8[4]; | |
87 | } data[2] ____cacheline_aligned; | |
88 | }; | |
89 | ||
90 | enum ad5360_type { | |
91 | ID_AD5360, | |
92 | ID_AD5361, | |
93 | ID_AD5362, | |
94 | ID_AD5363, | |
95 | ID_AD5370, | |
96 | ID_AD5371, | |
97 | ID_AD5372, | |
98 | ID_AD5373, | |
99 | }; | |
100 | ||
101 | #define AD5360_CHANNEL(bits) { \ | |
102 | .type = IIO_VOLTAGE, \ | |
103 | .indexed = 1, \ | |
104 | .output = 1, \ | |
1727a301 JC |
105 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
106 | BIT(IIO_CHAN_INFO_SCALE) | \ | |
107 | BIT(IIO_CHAN_INFO_OFFSET) | \ | |
108 | BIT(IIO_CHAN_INFO_CALIBSCALE) | \ | |
109 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ | |
c865b537 JC |
110 | .scan_type = { \ |
111 | .sign = 'u', \ | |
112 | .realbits = (bits), \ | |
113 | .storagebits = 16, \ | |
114 | .shift = 16 - (bits), \ | |
115 | }, \ | |
a3e2940c LPC |
116 | } |
117 | ||
118 | static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { | |
119 | [ID_AD5360] = { | |
120 | .channel_template = AD5360_CHANNEL(16), | |
121 | .num_channels = 16, | |
122 | .channels_per_group = 8, | |
123 | .num_vrefs = 2, | |
124 | }, | |
125 | [ID_AD5361] = { | |
126 | .channel_template = AD5360_CHANNEL(14), | |
127 | .num_channels = 16, | |
128 | .channels_per_group = 8, | |
129 | .num_vrefs = 2, | |
130 | }, | |
131 | [ID_AD5362] = { | |
132 | .channel_template = AD5360_CHANNEL(16), | |
133 | .num_channels = 8, | |
134 | .channels_per_group = 4, | |
135 | .num_vrefs = 2, | |
136 | }, | |
137 | [ID_AD5363] = { | |
138 | .channel_template = AD5360_CHANNEL(14), | |
139 | .num_channels = 8, | |
140 | .channels_per_group = 4, | |
141 | .num_vrefs = 2, | |
142 | }, | |
143 | [ID_AD5370] = { | |
144 | .channel_template = AD5360_CHANNEL(16), | |
145 | .num_channels = 40, | |
146 | .channels_per_group = 8, | |
147 | .num_vrefs = 2, | |
148 | }, | |
149 | [ID_AD5371] = { | |
150 | .channel_template = AD5360_CHANNEL(14), | |
151 | .num_channels = 40, | |
152 | .channels_per_group = 8, | |
153 | .num_vrefs = 3, | |
154 | }, | |
155 | [ID_AD5372] = { | |
156 | .channel_template = AD5360_CHANNEL(16), | |
157 | .num_channels = 32, | |
158 | .channels_per_group = 8, | |
159 | .num_vrefs = 2, | |
160 | }, | |
161 | [ID_AD5373] = { | |
162 | .channel_template = AD5360_CHANNEL(14), | |
163 | .num_channels = 32, | |
164 | .channels_per_group = 8, | |
165 | .num_vrefs = 2, | |
166 | }, | |
167 | }; | |
168 | ||
169 | static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st, | |
170 | unsigned int channel) | |
171 | { | |
172 | unsigned int i; | |
173 | ||
174 | /* The first groups have their own vref, while the remaining groups | |
175 | * share the last vref */ | |
176 | i = channel / st->chip_info->channels_per_group; | |
177 | if (i >= st->chip_info->num_vrefs) | |
178 | i = st->chip_info->num_vrefs - 1; | |
179 | ||
180 | return i; | |
181 | } | |
182 | ||
183 | static int ad5360_get_channel_vref(struct ad5360_state *st, | |
184 | unsigned int channel) | |
185 | { | |
186 | unsigned int i = ad5360_get_channel_vref_index(st, channel); | |
187 | ||
188 | return regulator_get_voltage(st->vref_reg[i].consumer); | |
189 | } | |
190 | ||
191 | ||
192 | static int ad5360_write_unlocked(struct iio_dev *indio_dev, | |
193 | unsigned int cmd, unsigned int addr, unsigned int val, | |
194 | unsigned int shift) | |
195 | { | |
196 | struct ad5360_state *st = iio_priv(indio_dev); | |
197 | ||
198 | val <<= shift; | |
199 | val |= AD5360_CMD(cmd) | AD5360_ADDR(addr); | |
200 | st->data[0].d32 = cpu_to_be32(val); | |
201 | ||
202 | return spi_write(st->spi, &st->data[0].d8[1], 3); | |
203 | } | |
204 | ||
205 | static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, | |
206 | unsigned int addr, unsigned int val, unsigned int shift) | |
207 | { | |
208 | int ret; | |
209 | ||
210 | mutex_lock(&indio_dev->mlock); | |
211 | ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); | |
212 | mutex_unlock(&indio_dev->mlock); | |
213 | ||
214 | return ret; | |
215 | } | |
216 | ||
217 | static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, | |
218 | unsigned int addr) | |
219 | { | |
220 | struct ad5360_state *st = iio_priv(indio_dev); | |
a3e2940c LPC |
221 | int ret; |
222 | struct spi_transfer t[] = { | |
223 | { | |
224 | .tx_buf = &st->data[0].d8[1], | |
225 | .len = 3, | |
226 | .cs_change = 1, | |
227 | }, { | |
228 | .rx_buf = &st->data[1].d8[1], | |
229 | .len = 3, | |
230 | }, | |
231 | }; | |
232 | ||
a3e2940c LPC |
233 | mutex_lock(&indio_dev->mlock); |
234 | ||
235 | st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | | |
236 | AD5360_ADDR(AD5360_REG_SF_READBACK) | | |
237 | AD5360_READBACK_TYPE(type) | | |
238 | AD5360_READBACK_ADDR(addr)); | |
239 | ||
14543a00 | 240 | ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); |
a3e2940c LPC |
241 | if (ret >= 0) |
242 | ret = be32_to_cpu(st->data[1].d32) & 0xffff; | |
243 | ||
244 | mutex_unlock(&indio_dev->mlock); | |
245 | ||
246 | return ret; | |
247 | } | |
248 | ||
249 | static ssize_t ad5360_read_dac_powerdown(struct device *dev, | |
250 | struct device_attribute *attr, | |
251 | char *buf) | |
252 | { | |
ac3f851e | 253 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
a3e2940c LPC |
254 | struct ad5360_state *st = iio_priv(indio_dev); |
255 | ||
256 | return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); | |
257 | } | |
258 | ||
259 | static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, | |
260 | unsigned int clr) | |
261 | { | |
262 | struct ad5360_state *st = iio_priv(indio_dev); | |
263 | unsigned int ret; | |
264 | ||
265 | mutex_lock(&indio_dev->mlock); | |
266 | ||
267 | st->ctrl |= set; | |
268 | st->ctrl &= ~clr; | |
269 | ||
270 | ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, | |
271 | AD5360_REG_SF_CTRL, st->ctrl, 0); | |
272 | ||
273 | mutex_unlock(&indio_dev->mlock); | |
274 | ||
275 | return ret; | |
276 | } | |
277 | ||
278 | static ssize_t ad5360_write_dac_powerdown(struct device *dev, | |
279 | struct device_attribute *attr, const char *buf, size_t len) | |
280 | { | |
ac3f851e | 281 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
a3e2940c LPC |
282 | bool pwr_down; |
283 | int ret; | |
284 | ||
285 | ret = strtobool(buf, &pwr_down); | |
286 | if (ret) | |
287 | return ret; | |
288 | ||
289 | if (pwr_down) | |
290 | ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0); | |
291 | else | |
292 | ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN); | |
293 | ||
294 | return ret ? ret : len; | |
295 | } | |
296 | ||
297 | static IIO_DEVICE_ATTR(out_voltage_powerdown, | |
298 | S_IRUGO | S_IWUSR, | |
299 | ad5360_read_dac_powerdown, | |
300 | ad5360_write_dac_powerdown, 0); | |
301 | ||
302 | static struct attribute *ad5360_attributes[] = { | |
303 | &iio_dev_attr_out_voltage_powerdown.dev_attr.attr, | |
304 | NULL, | |
305 | }; | |
306 | ||
307 | static const struct attribute_group ad5360_attribute_group = { | |
308 | .attrs = ad5360_attributes, | |
309 | }; | |
310 | ||
311 | static int ad5360_write_raw(struct iio_dev *indio_dev, | |
312 | struct iio_chan_spec const *chan, | |
313 | int val, | |
314 | int val2, | |
315 | long mask) | |
316 | { | |
317 | struct ad5360_state *st = iio_priv(indio_dev); | |
318 | int max_val = (1 << chan->scan_type.realbits); | |
319 | unsigned int ofs_index; | |
320 | ||
321 | switch (mask) { | |
09f4eb40 | 322 | case IIO_CHAN_INFO_RAW: |
a3e2940c LPC |
323 | if (val >= max_val || val < 0) |
324 | return -EINVAL; | |
325 | ||
326 | return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, | |
327 | chan->address, val, chan->scan_type.shift); | |
328 | ||
c8a9f805 | 329 | case IIO_CHAN_INFO_CALIBBIAS: |
a3e2940c LPC |
330 | if (val >= max_val || val < 0) |
331 | return -EINVAL; | |
332 | ||
333 | return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, | |
334 | chan->address, val, chan->scan_type.shift); | |
335 | ||
c8a9f805 | 336 | case IIO_CHAN_INFO_CALIBSCALE: |
a3e2940c LPC |
337 | if (val >= max_val || val < 0) |
338 | return -EINVAL; | |
339 | ||
340 | return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, | |
341 | chan->address, val, chan->scan_type.shift); | |
342 | ||
c8a9f805 | 343 | case IIO_CHAN_INFO_OFFSET: |
a3e2940c LPC |
344 | if (val <= -max_val || val > 0) |
345 | return -EINVAL; | |
346 | ||
347 | val = -val; | |
348 | ||
349 | /* offset is supposed to have the same scale as raw, but it | |
350 | * is always 14bits wide, so on a chip where the raw value has | |
351 | * more bits, we need to shift offset. */ | |
352 | val >>= (chan->scan_type.realbits - 14); | |
353 | ||
354 | /* There is one DAC offset register per vref. Changing one | |
355 | * channels offset will also change the offset for all other | |
356 | * channels which share the same vref supply. */ | |
357 | ofs_index = ad5360_get_channel_vref_index(st, chan->channel); | |
358 | return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, | |
359 | AD5360_REG_SF_OFS(ofs_index), val, 0); | |
360 | default: | |
361 | break; | |
362 | } | |
363 | ||
364 | return -EINVAL; | |
365 | } | |
366 | ||
367 | static int ad5360_read_raw(struct iio_dev *indio_dev, | |
368 | struct iio_chan_spec const *chan, | |
369 | int *val, | |
370 | int *val2, | |
371 | long m) | |
372 | { | |
373 | struct ad5360_state *st = iio_priv(indio_dev); | |
a3e2940c | 374 | unsigned int ofs_index; |
685e0107 | 375 | int scale_uv; |
a3e2940c LPC |
376 | int ret; |
377 | ||
378 | switch (m) { | |
09f4eb40 | 379 | case IIO_CHAN_INFO_RAW: |
a3e2940c LPC |
380 | ret = ad5360_read(indio_dev, AD5360_READBACK_X1A, |
381 | chan->address); | |
382 | if (ret < 0) | |
383 | return ret; | |
384 | *val = ret >> chan->scan_type.shift; | |
385 | return IIO_VAL_INT; | |
c8a9f805 | 386 | case IIO_CHAN_INFO_SCALE: |
ca3bc8b6 | 387 | scale_uv = ad5360_get_channel_vref(st, chan->channel); |
a3e2940c LPC |
388 | if (scale_uv < 0) |
389 | return scale_uv; | |
390 | ||
ca3bc8b6 LPC |
391 | /* vout = 4 * vref * dac_code */ |
392 | *val = scale_uv * 4 / 1000; | |
393 | *val2 = chan->scan_type.realbits; | |
394 | return IIO_VAL_FRACTIONAL_LOG2; | |
c8a9f805 | 395 | case IIO_CHAN_INFO_CALIBBIAS: |
a3e2940c LPC |
396 | ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, |
397 | chan->address); | |
398 | if (ret < 0) | |
399 | return ret; | |
400 | *val = ret; | |
401 | return IIO_VAL_INT; | |
c8a9f805 | 402 | case IIO_CHAN_INFO_CALIBSCALE: |
a3e2940c LPC |
403 | ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, |
404 | chan->address); | |
405 | if (ret < 0) | |
406 | return ret; | |
407 | *val = ret; | |
408 | return IIO_VAL_INT; | |
c8a9f805 | 409 | case IIO_CHAN_INFO_OFFSET: |
a3e2940c LPC |
410 | ofs_index = ad5360_get_channel_vref_index(st, chan->channel); |
411 | ret = ad5360_read(indio_dev, AD5360_READBACK_SF, | |
412 | AD5360_REG_SF_OFS(ofs_index)); | |
413 | if (ret < 0) | |
414 | return ret; | |
415 | ||
416 | ret <<= (chan->scan_type.realbits - 14); | |
417 | *val = -ret; | |
418 | return IIO_VAL_INT; | |
419 | } | |
420 | ||
421 | return -EINVAL; | |
422 | } | |
423 | ||
424 | static const struct iio_info ad5360_info = { | |
425 | .read_raw = ad5360_read_raw, | |
426 | .write_raw = ad5360_write_raw, | |
427 | .attrs = &ad5360_attribute_group, | |
a3e2940c LPC |
428 | }; |
429 | ||
430 | static const char * const ad5360_vref_name[] = { | |
431 | "vref0", "vref1", "vref2" | |
432 | }; | |
433 | ||
fc52692c | 434 | static int ad5360_alloc_channels(struct iio_dev *indio_dev) |
a3e2940c LPC |
435 | { |
436 | struct ad5360_state *st = iio_priv(indio_dev); | |
437 | struct iio_chan_spec *channels; | |
438 | unsigned int i; | |
439 | ||
09f993e6 AL |
440 | channels = kcalloc(st->chip_info->num_channels, |
441 | sizeof(struct iio_chan_spec), GFP_KERNEL); | |
a3e2940c LPC |
442 | |
443 | if (!channels) | |
444 | return -ENOMEM; | |
445 | ||
446 | for (i = 0; i < st->chip_info->num_channels; ++i) { | |
447 | channels[i] = st->chip_info->channel_template; | |
448 | channels[i].channel = i; | |
449 | channels[i].address = AD5360_CHAN_ADDR(i); | |
450 | } | |
451 | ||
452 | indio_dev->channels = channels; | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
fc52692c | 457 | static int ad5360_probe(struct spi_device *spi) |
a3e2940c LPC |
458 | { |
459 | enum ad5360_type type = spi_get_device_id(spi)->driver_data; | |
460 | struct iio_dev *indio_dev; | |
461 | struct ad5360_state *st; | |
462 | unsigned int i; | |
463 | int ret; | |
464 | ||
400d36e6 | 465 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
a3e2940c LPC |
466 | if (indio_dev == NULL) { |
467 | dev_err(&spi->dev, "Failed to allocate iio device\n"); | |
468 | return -ENOMEM; | |
469 | } | |
470 | ||
471 | st = iio_priv(indio_dev); | |
472 | spi_set_drvdata(spi, indio_dev); | |
473 | ||
474 | st->chip_info = &ad5360_chip_info_tbl[type]; | |
475 | st->spi = spi; | |
476 | ||
477 | indio_dev->dev.parent = &spi->dev; | |
478 | indio_dev->name = spi_get_device_id(spi)->name; | |
479 | indio_dev->info = &ad5360_info; | |
480 | indio_dev->modes = INDIO_DIRECT_MODE; | |
481 | indio_dev->num_channels = st->chip_info->num_channels; | |
482 | ||
483 | ret = ad5360_alloc_channels(indio_dev); | |
484 | if (ret) { | |
485 | dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); | |
400d36e6 | 486 | return ret; |
a3e2940c LPC |
487 | } |
488 | ||
489 | for (i = 0; i < st->chip_info->num_vrefs; ++i) | |
490 | st->vref_reg[i].supply = ad5360_vref_name[i]; | |
491 | ||
400d36e6 | 492 | ret = devm_regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs, |
a3e2940c LPC |
493 | st->vref_reg); |
494 | if (ret) { | |
495 | dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret); | |
496 | goto error_free_channels; | |
497 | } | |
498 | ||
499 | ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg); | |
500 | if (ret) { | |
501 | dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret); | |
400d36e6 | 502 | goto error_free_channels; |
a3e2940c LPC |
503 | } |
504 | ||
505 | ret = iio_device_register(indio_dev); | |
506 | if (ret) { | |
507 | dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); | |
508 | goto error_disable_reg; | |
509 | } | |
510 | ||
511 | return 0; | |
512 | ||
513 | error_disable_reg: | |
514 | regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); | |
a3e2940c LPC |
515 | error_free_channels: |
516 | kfree(indio_dev->channels); | |
a3e2940c LPC |
517 | |
518 | return ret; | |
519 | } | |
520 | ||
fc52692c | 521 | static int ad5360_remove(struct spi_device *spi) |
a3e2940c LPC |
522 | { |
523 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
524 | struct ad5360_state *st = iio_priv(indio_dev); | |
525 | ||
526 | iio_device_unregister(indio_dev); | |
527 | ||
528 | kfree(indio_dev->channels); | |
529 | ||
530 | regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); | |
a3e2940c LPC |
531 | |
532 | return 0; | |
533 | } | |
534 | ||
535 | static const struct spi_device_id ad5360_ids[] = { | |
536 | { "ad5360", ID_AD5360 }, | |
537 | { "ad5361", ID_AD5361 }, | |
538 | { "ad5362", ID_AD5362 }, | |
539 | { "ad5363", ID_AD5363 }, | |
540 | { "ad5370", ID_AD5370 }, | |
541 | { "ad5371", ID_AD5371 }, | |
542 | { "ad5372", ID_AD5372 }, | |
543 | { "ad5373", ID_AD5373 }, | |
544 | {} | |
545 | }; | |
546 | MODULE_DEVICE_TABLE(spi, ad5360_ids); | |
547 | ||
548 | static struct spi_driver ad5360_driver = { | |
549 | .driver = { | |
550 | .name = "ad5360", | |
a3e2940c LPC |
551 | }, |
552 | .probe = ad5360_probe, | |
fc52692c | 553 | .remove = ad5360_remove, |
a3e2940c LPC |
554 | .id_table = ad5360_ids, |
555 | }; | |
ae6ae6fe | 556 | module_spi_driver(ad5360_driver); |
a3e2940c LPC |
557 | |
558 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | |
559 | MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); | |
560 | MODULE_LICENSE("GPL v2"); |