1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392 |
4 | * multi-channel Digital to Analog Converters driver |
5 | * |
6 | * Copyright 2011 Analog Devices Inc. |
7 | */ |
8 | |
9 | #include <linux/device.h> |
10 | #include <linux/err.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/spi/spi.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/sysfs.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/regulator/consumer.h> |
19 | |
20 | #include <linux/iio/iio.h> |
21 | #include <linux/iio/sysfs.h> |
22 | |
23 | #define AD5380_REG_DATA(x) (((x) << 2) | 3) |
24 | #define AD5380_REG_OFFSET(x) (((x) << 2) | 2) |
25 | #define AD5380_REG_GAIN(x) (((x) << 2) | 1) |
26 | #define AD5380_REG_SF_PWR_DOWN (8 << 2) |
27 | #define AD5380_REG_SF_PWR_UP (9 << 2) |
28 | #define AD5380_REG_SF_CTRL (12 << 2) |
29 | |
30 | #define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13 |
31 | #define AD5380_CTRL_INT_VREF_2V5 BIT(12) |
32 | #define AD5380_CTRL_INT_VREF_EN BIT(10) |
33 | |
34 | /** |
35 | * struct ad5380_chip_info - chip specific information |
36 | * @channel_template: channel specification template |
37 | * @num_channels: number of channels |
38 | * @int_vref: internal vref in uV |
39 | */ |
40 | struct ad5380_chip_info { |
41 | struct iio_chan_spec channel_template; |
42 | unsigned int num_channels; |
43 | unsigned int int_vref; |
44 | }; |
45 | |
46 | /** |
47 | * struct ad5380_state - driver instance specific data |
48 | * @regmap: regmap instance used by the device |
49 | * @chip_info: chip model specific constants, available modes etc |
50 | * @vref_reg: vref supply regulator |
51 | * @vref: actual reference voltage used in uA |
52 | * @pwr_down: whether the chip is currently in power down mode |
53 | * @lock: lock to protect the data buffer during regmap ops |
54 | */ |
55 | struct ad5380_state { |
56 | struct regmap *regmap; |
57 | const struct ad5380_chip_info *chip_info; |
58 | struct regulator *vref_reg; |
59 | int vref; |
60 | bool pwr_down; |
61 | struct mutex lock; |
62 | }; |
63 | |
64 | enum ad5380_type { |
65 | ID_AD5380_3, |
66 | ID_AD5380_5, |
67 | ID_AD5381_3, |
68 | ID_AD5381_5, |
69 | ID_AD5382_3, |
70 | ID_AD5382_5, |
71 | ID_AD5383_3, |
72 | ID_AD5383_5, |
73 | ID_AD5390_3, |
74 | ID_AD5390_5, |
75 | ID_AD5391_3, |
76 | ID_AD5391_5, |
77 | ID_AD5392_3, |
78 | ID_AD5392_5, |
79 | }; |
80 | |
81 | static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev, |
82 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) |
83 | { |
84 | struct ad5380_state *st = iio_priv(indio_dev); |
85 | |
86 | return sysfs_emit(buf, fmt: "%d\n" , st->pwr_down); |
87 | } |
88 | |
89 | static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, |
90 | uintptr_t private, const struct iio_chan_spec *chan, const char *buf, |
91 | size_t len) |
92 | { |
93 | struct ad5380_state *st = iio_priv(indio_dev); |
94 | bool pwr_down; |
95 | int ret; |
96 | |
97 | ret = kstrtobool(s: buf, res: &pwr_down); |
98 | if (ret) |
99 | return ret; |
100 | |
101 | mutex_lock(&st->lock); |
102 | |
103 | if (pwr_down) |
104 | ret = regmap_write(map: st->regmap, AD5380_REG_SF_PWR_DOWN, val: 0); |
105 | else |
106 | ret = regmap_write(map: st->regmap, AD5380_REG_SF_PWR_UP, val: 0); |
107 | |
108 | st->pwr_down = pwr_down; |
109 | |
110 | mutex_unlock(lock: &st->lock); |
111 | |
112 | return ret ? ret : len; |
113 | } |
114 | |
115 | static const char * const ad5380_powerdown_modes[] = { |
116 | "100kohm_to_gnd" , |
117 | "three_state" , |
118 | }; |
119 | |
120 | static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev, |
121 | const struct iio_chan_spec *chan) |
122 | { |
123 | struct ad5380_state *st = iio_priv(indio_dev); |
124 | unsigned int mode; |
125 | int ret; |
126 | |
127 | ret = regmap_read(map: st->regmap, AD5380_REG_SF_CTRL, val: &mode); |
128 | if (ret) |
129 | return ret; |
130 | |
131 | mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1; |
132 | |
133 | return mode; |
134 | } |
135 | |
136 | static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev, |
137 | const struct iio_chan_spec *chan, unsigned int mode) |
138 | { |
139 | struct ad5380_state *st = iio_priv(indio_dev); |
140 | int ret; |
141 | |
142 | ret = regmap_update_bits(map: st->regmap, AD5380_REG_SF_CTRL, |
143 | mask: 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET, |
144 | val: mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET); |
145 | |
146 | return ret; |
147 | } |
148 | |
149 | static const struct iio_enum ad5380_powerdown_mode_enum = { |
150 | .items = ad5380_powerdown_modes, |
151 | .num_items = ARRAY_SIZE(ad5380_powerdown_modes), |
152 | .get = ad5380_get_powerdown_mode, |
153 | .set = ad5380_set_powerdown_mode, |
154 | }; |
155 | |
156 | static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan, |
157 | long info) |
158 | { |
159 | switch (info) { |
160 | case IIO_CHAN_INFO_RAW: |
161 | return AD5380_REG_DATA(chan->address); |
162 | case IIO_CHAN_INFO_CALIBBIAS: |
163 | return AD5380_REG_OFFSET(chan->address); |
164 | case IIO_CHAN_INFO_CALIBSCALE: |
165 | return AD5380_REG_GAIN(chan->address); |
166 | default: |
167 | break; |
168 | } |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static int ad5380_write_raw(struct iio_dev *indio_dev, |
174 | struct iio_chan_spec const *chan, int val, int val2, long info) |
175 | { |
176 | const unsigned int max_val = (1 << chan->scan_type.realbits); |
177 | struct ad5380_state *st = iio_priv(indio_dev); |
178 | |
179 | switch (info) { |
180 | case IIO_CHAN_INFO_RAW: |
181 | case IIO_CHAN_INFO_CALIBSCALE: |
182 | if (val >= max_val || val < 0) |
183 | return -EINVAL; |
184 | |
185 | return regmap_write(map: st->regmap, |
186 | reg: ad5380_info_to_reg(chan, info), |
187 | val: val << chan->scan_type.shift); |
188 | case IIO_CHAN_INFO_CALIBBIAS: |
189 | val += (1 << chan->scan_type.realbits) / 2; |
190 | if (val >= max_val || val < 0) |
191 | return -EINVAL; |
192 | |
193 | return regmap_write(map: st->regmap, |
194 | AD5380_REG_OFFSET(chan->address), |
195 | val: val << chan->scan_type.shift); |
196 | default: |
197 | break; |
198 | } |
199 | return -EINVAL; |
200 | } |
201 | |
202 | static int ad5380_read_raw(struct iio_dev *indio_dev, |
203 | struct iio_chan_spec const *chan, int *val, int *val2, long info) |
204 | { |
205 | struct ad5380_state *st = iio_priv(indio_dev); |
206 | int ret; |
207 | |
208 | switch (info) { |
209 | case IIO_CHAN_INFO_RAW: |
210 | case IIO_CHAN_INFO_CALIBSCALE: |
211 | ret = regmap_read(map: st->regmap, reg: ad5380_info_to_reg(chan, info), |
212 | val); |
213 | if (ret) |
214 | return ret; |
215 | *val >>= chan->scan_type.shift; |
216 | return IIO_VAL_INT; |
217 | case IIO_CHAN_INFO_CALIBBIAS: |
218 | ret = regmap_read(map: st->regmap, AD5380_REG_OFFSET(chan->address), |
219 | val); |
220 | if (ret) |
221 | return ret; |
222 | *val >>= chan->scan_type.shift; |
223 | *val -= (1 << chan->scan_type.realbits) / 2; |
224 | return IIO_VAL_INT; |
225 | case IIO_CHAN_INFO_SCALE: |
226 | *val = 2 * st->vref; |
227 | *val2 = chan->scan_type.realbits; |
228 | return IIO_VAL_FRACTIONAL_LOG2; |
229 | default: |
230 | break; |
231 | } |
232 | |
233 | return -EINVAL; |
234 | } |
235 | |
236 | static const struct iio_info ad5380_info = { |
237 | .read_raw = ad5380_read_raw, |
238 | .write_raw = ad5380_write_raw, |
239 | }; |
240 | |
241 | static const struct iio_chan_spec_ext_info ad5380_ext_info[] = { |
242 | { |
243 | .name = "powerdown" , |
244 | .read = ad5380_read_dac_powerdown, |
245 | .write = ad5380_write_dac_powerdown, |
246 | .shared = IIO_SEPARATE, |
247 | }, |
248 | IIO_ENUM("powerdown_mode" , IIO_SHARED_BY_TYPE, |
249 | &ad5380_powerdown_mode_enum), |
250 | IIO_ENUM_AVAILABLE("powerdown_mode" , IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), |
251 | { }, |
252 | }; |
253 | |
254 | #define AD5380_CHANNEL(_bits) { \ |
255 | .type = IIO_VOLTAGE, \ |
256 | .indexed = 1, \ |
257 | .output = 1, \ |
258 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
259 | BIT(IIO_CHAN_INFO_CALIBSCALE) | \ |
260 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ |
261 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
262 | .scan_type = { \ |
263 | .sign = 'u', \ |
264 | .realbits = (_bits), \ |
265 | .storagebits = 16, \ |
266 | .shift = 14 - (_bits), \ |
267 | }, \ |
268 | .ext_info = ad5380_ext_info, \ |
269 | } |
270 | |
271 | static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { |
272 | [ID_AD5380_3] = { |
273 | .channel_template = AD5380_CHANNEL(14), |
274 | .num_channels = 40, |
275 | .int_vref = 1250, |
276 | }, |
277 | [ID_AD5380_5] = { |
278 | .channel_template = AD5380_CHANNEL(14), |
279 | .num_channels = 40, |
280 | .int_vref = 2500, |
281 | }, |
282 | [ID_AD5381_3] = { |
283 | .channel_template = AD5380_CHANNEL(12), |
284 | .num_channels = 16, |
285 | .int_vref = 1250, |
286 | }, |
287 | [ID_AD5381_5] = { |
288 | .channel_template = AD5380_CHANNEL(12), |
289 | .num_channels = 16, |
290 | .int_vref = 2500, |
291 | }, |
292 | [ID_AD5382_3] = { |
293 | .channel_template = AD5380_CHANNEL(14), |
294 | .num_channels = 32, |
295 | .int_vref = 1250, |
296 | }, |
297 | [ID_AD5382_5] = { |
298 | .channel_template = AD5380_CHANNEL(14), |
299 | .num_channels = 32, |
300 | .int_vref = 2500, |
301 | }, |
302 | [ID_AD5383_3] = { |
303 | .channel_template = AD5380_CHANNEL(12), |
304 | .num_channels = 32, |
305 | .int_vref = 1250, |
306 | }, |
307 | [ID_AD5383_5] = { |
308 | .channel_template = AD5380_CHANNEL(12), |
309 | .num_channels = 32, |
310 | .int_vref = 2500, |
311 | }, |
312 | [ID_AD5390_3] = { |
313 | .channel_template = AD5380_CHANNEL(14), |
314 | .num_channels = 16, |
315 | .int_vref = 1250, |
316 | }, |
317 | [ID_AD5390_5] = { |
318 | .channel_template = AD5380_CHANNEL(14), |
319 | .num_channels = 16, |
320 | .int_vref = 2500, |
321 | }, |
322 | [ID_AD5391_3] = { |
323 | .channel_template = AD5380_CHANNEL(12), |
324 | .num_channels = 16, |
325 | .int_vref = 1250, |
326 | }, |
327 | [ID_AD5391_5] = { |
328 | .channel_template = AD5380_CHANNEL(12), |
329 | .num_channels = 16, |
330 | .int_vref = 2500, |
331 | }, |
332 | [ID_AD5392_3] = { |
333 | .channel_template = AD5380_CHANNEL(14), |
334 | .num_channels = 8, |
335 | .int_vref = 1250, |
336 | }, |
337 | [ID_AD5392_5] = { |
338 | .channel_template = AD5380_CHANNEL(14), |
339 | .num_channels = 8, |
340 | .int_vref = 2500, |
341 | }, |
342 | }; |
343 | |
344 | static int ad5380_alloc_channels(struct iio_dev *indio_dev) |
345 | { |
346 | struct ad5380_state *st = iio_priv(indio_dev); |
347 | struct iio_chan_spec *channels; |
348 | unsigned int i; |
349 | |
350 | channels = kcalloc(n: st->chip_info->num_channels, |
351 | size: sizeof(struct iio_chan_spec), GFP_KERNEL); |
352 | |
353 | if (!channels) |
354 | return -ENOMEM; |
355 | |
356 | for (i = 0; i < st->chip_info->num_channels; ++i) { |
357 | channels[i] = st->chip_info->channel_template; |
358 | channels[i].channel = i; |
359 | channels[i].address = i; |
360 | } |
361 | |
362 | indio_dev->channels = channels; |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static int ad5380_probe(struct device *dev, struct regmap *regmap, |
368 | enum ad5380_type type, const char *name) |
369 | { |
370 | struct iio_dev *indio_dev; |
371 | struct ad5380_state *st; |
372 | unsigned int ctrl = 0; |
373 | int ret; |
374 | |
375 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*st)); |
376 | if (indio_dev == NULL) { |
377 | dev_err(dev, "Failed to allocate iio device\n" ); |
378 | return -ENOMEM; |
379 | } |
380 | |
381 | st = iio_priv(indio_dev); |
382 | dev_set_drvdata(dev, data: indio_dev); |
383 | |
384 | st->chip_info = &ad5380_chip_info_tbl[type]; |
385 | st->regmap = regmap; |
386 | |
387 | indio_dev->name = name; |
388 | indio_dev->info = &ad5380_info; |
389 | indio_dev->modes = INDIO_DIRECT_MODE; |
390 | indio_dev->num_channels = st->chip_info->num_channels; |
391 | |
392 | mutex_init(&st->lock); |
393 | |
394 | ret = ad5380_alloc_channels(indio_dev); |
395 | if (ret) { |
396 | dev_err(dev, "Failed to allocate channel spec: %d\n" , ret); |
397 | return ret; |
398 | } |
399 | |
400 | if (st->chip_info->int_vref == 2500) |
401 | ctrl |= AD5380_CTRL_INT_VREF_2V5; |
402 | |
403 | st->vref_reg = devm_regulator_get(dev, id: "vref" ); |
404 | if (!IS_ERR(ptr: st->vref_reg)) { |
405 | ret = regulator_enable(regulator: st->vref_reg); |
406 | if (ret) { |
407 | dev_err(dev, "Failed to enable vref regulators: %d\n" , |
408 | ret); |
409 | goto error_free_reg; |
410 | } |
411 | |
412 | ret = regulator_get_voltage(regulator: st->vref_reg); |
413 | if (ret < 0) |
414 | goto error_disable_reg; |
415 | |
416 | st->vref = ret / 1000; |
417 | } else { |
418 | st->vref = st->chip_info->int_vref; |
419 | ctrl |= AD5380_CTRL_INT_VREF_EN; |
420 | } |
421 | |
422 | ret = regmap_write(map: st->regmap, AD5380_REG_SF_CTRL, val: ctrl); |
423 | if (ret) { |
424 | dev_err(dev, "Failed to write to device: %d\n" , ret); |
425 | goto error_disable_reg; |
426 | } |
427 | |
428 | ret = iio_device_register(indio_dev); |
429 | if (ret) { |
430 | dev_err(dev, "Failed to register iio device: %d\n" , ret); |
431 | goto error_disable_reg; |
432 | } |
433 | |
434 | return 0; |
435 | |
436 | error_disable_reg: |
437 | if (!IS_ERR(ptr: st->vref_reg)) |
438 | regulator_disable(regulator: st->vref_reg); |
439 | error_free_reg: |
440 | kfree(objp: indio_dev->channels); |
441 | |
442 | return ret; |
443 | } |
444 | |
445 | static void ad5380_remove(struct device *dev) |
446 | { |
447 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
448 | struct ad5380_state *st = iio_priv(indio_dev); |
449 | |
450 | iio_device_unregister(indio_dev); |
451 | |
452 | kfree(objp: indio_dev->channels); |
453 | |
454 | if (!IS_ERR(ptr: st->vref_reg)) |
455 | regulator_disable(regulator: st->vref_reg); |
456 | } |
457 | |
458 | static bool ad5380_reg_false(struct device *dev, unsigned int reg) |
459 | { |
460 | return false; |
461 | } |
462 | |
463 | static const struct regmap_config ad5380_regmap_config = { |
464 | .reg_bits = 10, |
465 | .val_bits = 14, |
466 | |
467 | .max_register = AD5380_REG_DATA(40), |
468 | .cache_type = REGCACHE_RBTREE, |
469 | |
470 | .volatile_reg = ad5380_reg_false, |
471 | .readable_reg = ad5380_reg_false, |
472 | }; |
473 | |
474 | #if IS_ENABLED(CONFIG_SPI_MASTER) |
475 | |
476 | static int ad5380_spi_probe(struct spi_device *spi) |
477 | { |
478 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
479 | struct regmap *regmap; |
480 | |
481 | regmap = devm_regmap_init_spi(spi, &ad5380_regmap_config); |
482 | |
483 | if (IS_ERR(ptr: regmap)) |
484 | return PTR_ERR(ptr: regmap); |
485 | |
486 | return ad5380_probe(dev: &spi->dev, regmap, type: id->driver_data, name: id->name); |
487 | } |
488 | |
489 | static void ad5380_spi_remove(struct spi_device *spi) |
490 | { |
491 | ad5380_remove(dev: &spi->dev); |
492 | } |
493 | |
494 | static const struct spi_device_id ad5380_spi_ids[] = { |
495 | { "ad5380-3" , ID_AD5380_3 }, |
496 | { "ad5380-5" , ID_AD5380_5 }, |
497 | { "ad5381-3" , ID_AD5381_3 }, |
498 | { "ad5381-5" , ID_AD5381_5 }, |
499 | { "ad5382-3" , ID_AD5382_3 }, |
500 | { "ad5382-5" , ID_AD5382_5 }, |
501 | { "ad5383-3" , ID_AD5383_3 }, |
502 | { "ad5383-5" , ID_AD5383_5 }, |
503 | { "ad5384-3" , ID_AD5380_3 }, |
504 | { "ad5384-5" , ID_AD5380_5 }, |
505 | { "ad5390-3" , ID_AD5390_3 }, |
506 | { "ad5390-5" , ID_AD5390_5 }, |
507 | { "ad5391-3" , ID_AD5391_3 }, |
508 | { "ad5391-5" , ID_AD5391_5 }, |
509 | { "ad5392-3" , ID_AD5392_3 }, |
510 | { "ad5392-5" , ID_AD5392_5 }, |
511 | { } |
512 | }; |
513 | MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); |
514 | |
515 | static struct spi_driver ad5380_spi_driver = { |
516 | .driver = { |
517 | .name = "ad5380" , |
518 | }, |
519 | .probe = ad5380_spi_probe, |
520 | .remove = ad5380_spi_remove, |
521 | .id_table = ad5380_spi_ids, |
522 | }; |
523 | |
524 | static inline int ad5380_spi_register_driver(void) |
525 | { |
526 | return spi_register_driver(&ad5380_spi_driver); |
527 | } |
528 | |
529 | static inline void ad5380_spi_unregister_driver(void) |
530 | { |
531 | spi_unregister_driver(sdrv: &ad5380_spi_driver); |
532 | } |
533 | |
534 | #else |
535 | |
536 | static inline int ad5380_spi_register_driver(void) |
537 | { |
538 | return 0; |
539 | } |
540 | |
541 | static inline void ad5380_spi_unregister_driver(void) |
542 | { |
543 | } |
544 | |
545 | #endif |
546 | |
547 | #if IS_ENABLED(CONFIG_I2C) |
548 | |
549 | static int ad5380_i2c_probe(struct i2c_client *i2c) |
550 | { |
551 | const struct i2c_device_id *id = i2c_client_get_device_id(client: i2c); |
552 | struct regmap *regmap; |
553 | |
554 | regmap = devm_regmap_init_i2c(i2c, &ad5380_regmap_config); |
555 | |
556 | if (IS_ERR(ptr: regmap)) |
557 | return PTR_ERR(ptr: regmap); |
558 | |
559 | return ad5380_probe(dev: &i2c->dev, regmap, type: id->driver_data, name: id->name); |
560 | } |
561 | |
562 | static void ad5380_i2c_remove(struct i2c_client *i2c) |
563 | { |
564 | ad5380_remove(dev: &i2c->dev); |
565 | } |
566 | |
567 | static const struct i2c_device_id ad5380_i2c_ids[] = { |
568 | { "ad5380-3" , ID_AD5380_3 }, |
569 | { "ad5380-5" , ID_AD5380_5 }, |
570 | { "ad5381-3" , ID_AD5381_3 }, |
571 | { "ad5381-5" , ID_AD5381_5 }, |
572 | { "ad5382-3" , ID_AD5382_3 }, |
573 | { "ad5382-5" , ID_AD5382_5 }, |
574 | { "ad5383-3" , ID_AD5383_3 }, |
575 | { "ad5383-5" , ID_AD5383_5 }, |
576 | { "ad5384-3" , ID_AD5380_3 }, |
577 | { "ad5384-5" , ID_AD5380_5 }, |
578 | { "ad5390-3" , ID_AD5390_3 }, |
579 | { "ad5390-5" , ID_AD5390_5 }, |
580 | { "ad5391-3" , ID_AD5391_3 }, |
581 | { "ad5391-5" , ID_AD5391_5 }, |
582 | { "ad5392-3" , ID_AD5392_3 }, |
583 | { "ad5392-5" , ID_AD5392_5 }, |
584 | { } |
585 | }; |
586 | MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids); |
587 | |
588 | static struct i2c_driver ad5380_i2c_driver = { |
589 | .driver = { |
590 | .name = "ad5380" , |
591 | }, |
592 | .probe = ad5380_i2c_probe, |
593 | .remove = ad5380_i2c_remove, |
594 | .id_table = ad5380_i2c_ids, |
595 | }; |
596 | |
597 | static inline int ad5380_i2c_register_driver(void) |
598 | { |
599 | return i2c_add_driver(&ad5380_i2c_driver); |
600 | } |
601 | |
602 | static inline void ad5380_i2c_unregister_driver(void) |
603 | { |
604 | i2c_del_driver(driver: &ad5380_i2c_driver); |
605 | } |
606 | |
607 | #else |
608 | |
609 | static inline int ad5380_i2c_register_driver(void) |
610 | { |
611 | return 0; |
612 | } |
613 | |
614 | static inline void ad5380_i2c_unregister_driver(void) |
615 | { |
616 | } |
617 | |
618 | #endif |
619 | |
620 | static int __init ad5380_spi_init(void) |
621 | { |
622 | int ret; |
623 | |
624 | ret = ad5380_spi_register_driver(); |
625 | if (ret) |
626 | return ret; |
627 | |
628 | ret = ad5380_i2c_register_driver(); |
629 | if (ret) { |
630 | ad5380_spi_unregister_driver(); |
631 | return ret; |
632 | } |
633 | |
634 | return 0; |
635 | } |
636 | module_init(ad5380_spi_init); |
637 | |
638 | static void __exit ad5380_spi_exit(void) |
639 | { |
640 | ad5380_i2c_unregister_driver(); |
641 | ad5380_spi_unregister_driver(); |
642 | |
643 | } |
644 | module_exit(ad5380_spi_exit); |
645 | |
646 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>" ); |
647 | MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC" ); |
648 | MODULE_LICENSE("GPL v2" ); |
649 | |