2 * AD7887 SPI ADC driver
4 * Copyright 2010-2011 Analog Devices Inc.
6 * Licensed under the GPL-2.
9 #include <linux/device.h>
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/sysfs.h>
13 #include <linux/spi/spi.h>
14 #include <linux/regulator/consumer.h>
15 #include <linux/err.h>
19 #include "../ring_generic.h"
24 static int ad7887_scan_direct(struct ad7887_state
*st
, unsigned ch
)
26 int ret
= spi_sync(st
->spi
, &st
->msg
[ch
]);
30 return (st
->data
[(ch
* 2)] << 8) | st
->data
[(ch
* 2) + 1];
33 static int ad7887_read_raw(struct iio_dev
*dev_info
,
34 struct iio_chan_spec
const *chan
,
40 struct ad7887_state
*st
= iio_priv(dev_info
);
41 unsigned int scale_uv
;
45 mutex_lock(&dev_info
->mlock
);
46 if (iio_ring_enabled(dev_info
))
47 ret
= ad7887_scan_from_ring(st
, 1 << chan
->address
);
49 ret
= ad7887_scan_direct(st
, chan
->address
);
50 mutex_unlock(&dev_info
->mlock
);
54 *val
= (ret
>> st
->chip_info
->channel
[0].scan_type
.shift
) &
55 RES_MASK(st
->chip_info
->channel
[0].scan_type
.realbits
);
57 case (1 << IIO_CHAN_INFO_SCALE_SHARED
):
58 scale_uv
= (st
->int_vref_mv
* 1000)
59 >> st
->chip_info
->channel
[0].scan_type
.realbits
;
61 *val2
= (scale_uv
%1000)*1000;
62 return IIO_VAL_INT_PLUS_MICRO
;
68 static const struct ad7887_chip_info ad7887_chip_info_tbl
[] = {
70 * More devices added in future
73 .channel
[0] = IIO_CHAN(IIO_IN
, 0, 1, 0, NULL
, 1, 0,
74 (1 << IIO_CHAN_INFO_SCALE_SHARED
),
75 1, 1, IIO_ST('u', 12, 16, 0), 0),
77 .channel
[1] = IIO_CHAN(IIO_IN
, 0, 1, 0, NULL
, 0, 0,
78 (1 << IIO_CHAN_INFO_SCALE_SHARED
),
79 0, 0, IIO_ST('u', 12, 16, 0), 0),
81 .channel
[2] = IIO_CHAN_SOFT_TIMESTAMP(2),
86 static const struct iio_info ad7887_info
= {
87 .read_raw
= &ad7887_read_raw
,
88 .driver_module
= THIS_MODULE
,
91 static int __devinit
ad7887_probe(struct spi_device
*spi
)
93 struct ad7887_platform_data
*pdata
= spi
->dev
.platform_data
;
94 struct ad7887_state
*st
;
95 int ret
, voltage_uv
= 0, regdone
= 0;
96 struct iio_dev
*indio_dev
= iio_allocate_device(sizeof(*st
));
98 if (indio_dev
== NULL
)
101 st
= iio_priv(indio_dev
);
103 st
->reg
= regulator_get(&spi
->dev
, "vcc");
104 if (!IS_ERR(st
->reg
)) {
105 ret
= regulator_enable(st
->reg
);
109 voltage_uv
= regulator_get_voltage(st
->reg
);
113 &ad7887_chip_info_tbl
[spi_get_device_id(spi
)->driver_data
];
115 spi_set_drvdata(spi
, indio_dev
);
118 /* Estabilish that the iio_dev is a child of the spi device */
119 indio_dev
->dev
.parent
= &spi
->dev
;
120 indio_dev
->name
= spi_get_device_id(spi
)->name
;
121 indio_dev
->info
= &ad7887_info
;
122 indio_dev
->modes
= INDIO_DIRECT_MODE
;
124 /* Setup default message */
126 st
->tx_cmd_buf
[0] = AD7887_CH_AIN0
| AD7887_PM_MODE4
|
127 ((pdata
&& pdata
->use_onchip_ref
) ?
130 st
->xfer
[0].rx_buf
= &st
->data
[0];
131 st
->xfer
[0].tx_buf
= &st
->tx_cmd_buf
[0];
134 spi_message_init(&st
->msg
[AD7887_CH0
]);
135 spi_message_add_tail(&st
->xfer
[0], &st
->msg
[AD7887_CH0
]);
137 if (pdata
&& pdata
->en_dual
) {
138 st
->tx_cmd_buf
[0] |= AD7887_DUAL
| AD7887_REF_DIS
;
140 st
->tx_cmd_buf
[2] = AD7887_CH_AIN1
| AD7887_DUAL
|
141 AD7887_REF_DIS
| AD7887_PM_MODE4
;
142 st
->tx_cmd_buf
[4] = AD7887_CH_AIN0
| AD7887_DUAL
|
143 AD7887_REF_DIS
| AD7887_PM_MODE4
;
144 st
->tx_cmd_buf
[6] = AD7887_CH_AIN1
| AD7887_DUAL
|
145 AD7887_REF_DIS
| AD7887_PM_MODE4
;
147 st
->xfer
[1].rx_buf
= &st
->data
[0];
148 st
->xfer
[1].tx_buf
= &st
->tx_cmd_buf
[2];
151 st
->xfer
[2].rx_buf
= &st
->data
[2];
152 st
->xfer
[2].tx_buf
= &st
->tx_cmd_buf
[4];
155 spi_message_init(&st
->msg
[AD7887_CH0_CH1
]);
156 spi_message_add_tail(&st
->xfer
[1], &st
->msg
[AD7887_CH0_CH1
]);
157 spi_message_add_tail(&st
->xfer
[2], &st
->msg
[AD7887_CH0_CH1
]);
159 st
->xfer
[3].rx_buf
= &st
->data
[0];
160 st
->xfer
[3].tx_buf
= &st
->tx_cmd_buf
[6];
163 spi_message_init(&st
->msg
[AD7887_CH1
]);
164 spi_message_add_tail(&st
->xfer
[3], &st
->msg
[AD7887_CH1
]);
166 if (pdata
&& pdata
->vref_mv
)
167 st
->int_vref_mv
= pdata
->vref_mv
;
169 st
->int_vref_mv
= voltage_uv
/ 1000;
171 dev_warn(&spi
->dev
, "reference voltage unspecified\n");
173 indio_dev
->channels
= st
->chip_info
->channel
;
174 indio_dev
->num_channels
= 3;
176 if (pdata
&& pdata
->vref_mv
)
177 st
->int_vref_mv
= pdata
->vref_mv
;
178 else if (pdata
&& pdata
->use_onchip_ref
)
179 st
->int_vref_mv
= st
->chip_info
->int_vref_mv
;
181 dev_warn(&spi
->dev
, "reference voltage unspecified\n");
183 indio_dev
->channels
= &st
->chip_info
->channel
[1];
184 indio_dev
->num_channels
= 2;
187 ret
= ad7887_register_ring_funcs_and_init(indio_dev
);
189 goto error_disable_reg
;
191 ret
= iio_device_register(indio_dev
);
193 goto error_disable_reg
;
196 ret
= iio_ring_buffer_register_ex(indio_dev
->ring
, 0,
198 indio_dev
->num_channels
);
200 goto error_cleanup_ring
;
204 ad7887_ring_cleanup(indio_dev
);
206 if (!IS_ERR(st
->reg
))
207 regulator_disable(st
->reg
);
209 if (!IS_ERR(st
->reg
))
210 regulator_put(st
->reg
);
212 iio_device_unregister(indio_dev
);
214 iio_free_device(indio_dev
);
219 static int ad7887_remove(struct spi_device
*spi
)
221 struct iio_dev
*indio_dev
= spi_get_drvdata(spi
);
222 struct ad7887_state
*st
= iio_priv(indio_dev
);
224 iio_ring_buffer_unregister(indio_dev
->ring
);
225 ad7887_ring_cleanup(indio_dev
);
226 if (!IS_ERR(st
->reg
)) {
227 regulator_disable(st
->reg
);
228 regulator_put(st
->reg
);
230 iio_device_unregister(indio_dev
);
235 static const struct spi_device_id ad7887_id
[] = {
236 {"ad7887", ID_AD7887
},
240 static struct spi_driver ad7887_driver
= {
243 .bus
= &spi_bus_type
,
244 .owner
= THIS_MODULE
,
246 .probe
= ad7887_probe
,
247 .remove
= __devexit_p(ad7887_remove
),
248 .id_table
= ad7887_id
,
251 static int __init
ad7887_init(void)
253 return spi_register_driver(&ad7887_driver
);
255 module_init(ad7887_init
);
257 static void __exit
ad7887_exit(void)
259 spi_unregister_driver(&ad7887_driver
);
261 module_exit(ad7887_exit
);
263 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
264 MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
265 MODULE_LICENSE("GPL v2");
266 MODULE_ALIAS("spi:ad7887");