2 * AD5446 SPI DAC driver
4 * Copyright 2010 Analog Devices Inc.
6 * Licensed under the GPL-2 or later.
9 #include <linux/interrupt.h>
10 #include <linux/workqueue.h>
11 #include <linux/device.h>
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
14 #include <linux/sysfs.h>
15 #include <linux/list.h>
16 #include <linux/spi/spi.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/err.h>
26 static void ad5446_store_sample(struct ad5446_state
*st
, unsigned val
)
28 st
->data
.d16
= cpu_to_be16(AD5446_LOAD
|
29 (val
<< st
->chip_info
->left_shift
));
32 static void ad5542_store_sample(struct ad5446_state
*st
, unsigned val
)
34 st
->data
.d16
= cpu_to_be16(val
<< st
->chip_info
->left_shift
);
37 static void ad5620_store_sample(struct ad5446_state
*st
, unsigned val
)
39 st
->data
.d16
= cpu_to_be16(AD5620_LOAD
|
40 (val
<< st
->chip_info
->left_shift
));
43 static void ad5660_store_sample(struct ad5446_state
*st
, unsigned val
)
46 st
->data
.d24
[0] = (val
>> 16) & 0xFF;
47 st
->data
.d24
[1] = (val
>> 8) & 0xFF;
48 st
->data
.d24
[2] = val
& 0xFF;
51 static ssize_t
ad5446_write(struct device
*dev
,
52 struct device_attribute
*attr
,
56 struct iio_dev
*dev_info
= dev_get_drvdata(dev
);
57 struct ad5446_state
*st
= dev_info
->dev_data
;
61 ret
= strict_strtol(buf
, 10, &val
);
65 if (val
> RES_MASK(st
->chip_info
->bits
)) {
70 mutex_lock(&dev_info
->mlock
);
71 st
->chip_info
->store_sample(st
, val
);
72 ret
= spi_sync(st
->spi
, &st
->msg
);
73 mutex_unlock(&dev_info
->mlock
);
76 return ret
? ret
: len
;
79 static IIO_DEV_ATTR_OUT_RAW(0, ad5446_write
, 0);
81 static ssize_t
ad5446_show_scale(struct device
*dev
,
82 struct device_attribute
*attr
,
85 struct iio_dev
*dev_info
= dev_get_drvdata(dev
);
86 struct ad5446_state
*st
= iio_dev_get_devdata(dev_info
);
87 /* Corresponds to Vref / 2^(bits) */
88 unsigned int scale_uv
= (st
->vref_mv
* 1000) >> st
->chip_info
->bits
;
90 return sprintf(buf
, "%d.%03d\n", scale_uv
/ 1000, scale_uv
% 1000);
92 static IIO_DEVICE_ATTR(out_scale
, S_IRUGO
, ad5446_show_scale
, NULL
, 0);
94 static ssize_t
ad5446_show_name(struct device
*dev
,
95 struct device_attribute
*attr
,
98 struct iio_dev
*dev_info
= dev_get_drvdata(dev
);
99 struct ad5446_state
*st
= iio_dev_get_devdata(dev_info
);
101 return sprintf(buf
, "%s\n", spi_get_device_id(st
->spi
)->name
);
103 static IIO_DEVICE_ATTR(name
, S_IRUGO
, ad5446_show_name
, NULL
, 0);
105 static struct attribute
*ad5446_attributes
[] = {
106 &iio_dev_attr_out0_raw
.dev_attr
.attr
,
107 &iio_dev_attr_out_scale
.dev_attr
.attr
,
108 &iio_dev_attr_name
.dev_attr
.attr
,
112 static const struct attribute_group ad5446_attribute_group
= {
113 .attrs
= ad5446_attributes
,
116 static const struct ad5446_chip_info ad5446_chip_info_tbl
[] = {
121 .store_sample
= ad5446_store_sample
,
127 .store_sample
= ad5446_store_sample
,
133 .store_sample
= ad5542_store_sample
,
139 .store_sample
= ad5542_store_sample
,
145 .store_sample
= ad5542_store_sample
,
151 .store_sample
= ad5542_store_sample
,
158 .store_sample
= ad5620_store_sample
,
165 .store_sample
= ad5620_store_sample
,
172 .store_sample
= ad5620_store_sample
,
179 .store_sample
= ad5620_store_sample
,
186 .store_sample
= ad5660_store_sample
,
193 .store_sample
= ad5660_store_sample
,
197 static int __devinit
ad5446_probe(struct spi_device
*spi
)
199 struct ad5446_state
*st
;
200 int ret
, voltage_uv
= 0;
202 st
= kzalloc(sizeof(*st
), GFP_KERNEL
);
208 st
->reg
= regulator_get(&spi
->dev
, "vcc");
209 if (!IS_ERR(st
->reg
)) {
210 ret
= regulator_enable(st
->reg
);
214 voltage_uv
= regulator_get_voltage(st
->reg
);
218 &ad5446_chip_info_tbl
[spi_get_device_id(spi
)->driver_data
];
220 spi_set_drvdata(spi
, st
);
224 st
->indio_dev
= iio_allocate_device();
225 if (st
->indio_dev
== NULL
) {
227 goto error_disable_reg
;
230 /* Estabilish that the iio_dev is a child of the spi device */
231 st
->indio_dev
->dev
.parent
= &spi
->dev
;
232 st
->indio_dev
->attrs
= &ad5446_attribute_group
;
233 st
->indio_dev
->dev_data
= (void *)(st
);
234 st
->indio_dev
->driver_module
= THIS_MODULE
;
235 st
->indio_dev
->modes
= INDIO_DIRECT_MODE
;
237 /* Setup default message */
239 st
->xfer
.tx_buf
= &st
->data
;
240 st
->xfer
.len
= st
->chip_info
->storagebits
/ 8;
242 spi_message_init(&st
->msg
);
243 spi_message_add_tail(&st
->xfer
, &st
->msg
);
245 switch (spi_get_device_id(spi
)->driver_data
) {
252 st
->vref_mv
= st
->chip_info
->int_vref_mv
;
256 st
->vref_mv
= voltage_uv
/ 1000;
259 "reference voltage unspecified\n");
262 ret
= iio_device_register(st
->indio_dev
);
264 goto error_free_device
;
269 iio_free_device(st
->indio_dev
);
271 if (!IS_ERR(st
->reg
))
272 regulator_disable(st
->reg
);
274 if (!IS_ERR(st
->reg
))
275 regulator_put(st
->reg
);
281 static int ad5446_remove(struct spi_device
*spi
)
283 struct ad5446_state
*st
= spi_get_drvdata(spi
);
284 struct iio_dev
*indio_dev
= st
->indio_dev
;
286 iio_device_unregister(indio_dev
);
287 if (!IS_ERR(st
->reg
)) {
288 regulator_disable(st
->reg
);
289 regulator_put(st
->reg
);
295 static const struct spi_device_id ad5446_id
[] = {
296 {"ad5444", ID_AD5444
},
297 {"ad5446", ID_AD5446
},
298 {"ad5542a", ID_AD5542A
},
299 {"ad5512a", ID_AD5512A
},
300 {"ad5620-2500", ID_AD5620_2500
}, /* AD5620/40/60: */
301 {"ad5620-1250", ID_AD5620_1250
}, /* part numbers may look differently */
302 {"ad5640-2500", ID_AD5640_2500
},
303 {"ad5640-1250", ID_AD5640_1250
},
304 {"ad5660-2500", ID_AD5660_2500
},
305 {"ad5660-1250", ID_AD5660_1250
},
309 static struct spi_driver ad5446_driver
= {
312 .bus
= &spi_bus_type
,
313 .owner
= THIS_MODULE
,
315 .probe
= ad5446_probe
,
316 .remove
= __devexit_p(ad5446_remove
),
317 .id_table
= ad5446_id
,
320 static int __init
ad5446_init(void)
322 return spi_register_driver(&ad5446_driver
);
324 module_init(ad5446_init
);
326 static void __exit
ad5446_exit(void)
328 spi_unregister_driver(&ad5446_driver
);
330 module_exit(ad5446_exit
);
332 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
333 MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
334 MODULE_LICENSE("GPL v2");
335 MODULE_ALIAS("spi:ad5446");