2 * AD7303 Digital to analog converters driver
4 * Copyright 2013 Analog Devices Inc.
6 * Licensed under the GPL-2.
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/spi/spi.h>
13 #include <linux/slab.h>
14 #include <linux/sysfs.h>
15 #include <linux/regulator/consumer.h>
18 #include <linux/iio/iio.h>
19 #include <linux/iio/sysfs.h>
21 #include <linux/platform_data/ad7303.h>
23 #define AD7303_CFG_EXTERNAL_VREF BIT(15)
24 #define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
25 #define AD7303_CFG_ADDR_OFFSET 10
27 #define AD7303_CMD_UPDATE_DAC (0x3 << 8)
30 * struct ad7303_state - driver instance specific data
31 * @spi: the device for this driver instance
32 * @config: cached config register value
33 * @dac_cache: current DAC raw value (chip does not support readback)
34 * @data: spi transfer buffer
38 struct spi_device
*spi
;
42 struct regulator
*vdd_reg
;
43 struct regulator
*vref_reg
;
46 * DMA (thus cache coherency maintenance) requires the
47 * transfer buffers to live in their own cache lines.
49 __be16 data ____cacheline_aligned
;
52 static int ad7303_write(struct ad7303_state
*st
, unsigned int chan
,
55 st
->data
= cpu_to_be16(AD7303_CMD_UPDATE_DAC
|
56 (chan
<< AD7303_CFG_ADDR_OFFSET
) |
59 return spi_write(st
->spi
, &st
->data
, sizeof(st
->data
));
62 static ssize_t
ad7303_read_dac_powerdown(struct iio_dev
*indio_dev
,
63 uintptr_t private, const struct iio_chan_spec
*chan
, char *buf
)
65 struct ad7303_state
*st
= iio_priv(indio_dev
);
67 return sprintf(buf
, "%d\n", (bool)(st
->config
&
68 AD7303_CFG_POWER_DOWN(chan
->channel
)));
71 static ssize_t
ad7303_write_dac_powerdown(struct iio_dev
*indio_dev
,
72 uintptr_t private, const struct iio_chan_spec
*chan
, const char *buf
,
75 struct ad7303_state
*st
= iio_priv(indio_dev
);
79 ret
= strtobool(buf
, &pwr_down
);
83 mutex_lock(&indio_dev
->mlock
);
86 st
->config
|= AD7303_CFG_POWER_DOWN(chan
->channel
);
88 st
->config
&= ~AD7303_CFG_POWER_DOWN(chan
->channel
);
90 /* There is no noop cmd which allows us to only update the powerdown
91 * mode, so just write one of the DAC channels again */
92 ad7303_write(st
, chan
->channel
, st
->dac_cache
[chan
->channel
]);
94 mutex_unlock(&indio_dev
->mlock
);
98 static int ad7303_get_vref(struct ad7303_state
*st
,
99 struct iio_chan_spec
const *chan
)
103 if (st
->config
& AD7303_CFG_EXTERNAL_VREF
)
104 return regulator_get_voltage(st
->vref_reg
);
106 ret
= regulator_get_voltage(st
->vdd_reg
);
112 static int ad7303_read_raw(struct iio_dev
*indio_dev
,
113 struct iio_chan_spec
const *chan
, int *val
, int *val2
, long info
)
115 struct ad7303_state
*st
= iio_priv(indio_dev
);
119 case IIO_CHAN_INFO_RAW
:
120 *val
= st
->dac_cache
[chan
->channel
];
122 case IIO_CHAN_INFO_SCALE
:
123 vref_uv
= ad7303_get_vref(st
, chan
);
127 *val
= 2 * vref_uv
/ 1000;
128 *val2
= chan
->scan_type
.realbits
;
130 return IIO_VAL_FRACTIONAL_LOG2
;
137 static int ad7303_write_raw(struct iio_dev
*indio_dev
,
138 struct iio_chan_spec
const *chan
, int val
, int val2
, long mask
)
140 struct ad7303_state
*st
= iio_priv(indio_dev
);
144 case IIO_CHAN_INFO_RAW
:
145 if (val
>= (1 << chan
->scan_type
.realbits
) || val
< 0)
148 mutex_lock(&indio_dev
->mlock
);
149 ret
= ad7303_write(st
, chan
->address
, val
);
151 st
->dac_cache
[chan
->channel
] = val
;
152 mutex_unlock(&indio_dev
->mlock
);
161 static const struct iio_info ad7303_info
= {
162 .read_raw
= ad7303_read_raw
,
163 .write_raw
= ad7303_write_raw
,
164 .driver_module
= THIS_MODULE
,
167 static const struct iio_chan_spec_ext_info ad7303_ext_info
[] = {
170 .read
= ad7303_read_dac_powerdown
,
171 .write
= ad7303_write_dac_powerdown
,
172 .shared
= IIO_SEPARATE
,
177 #define AD7303_CHANNEL(chan) { \
178 .type = IIO_VOLTAGE, \
182 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
183 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
188 .storagebits = '8', \
191 .ext_info = ad7303_ext_info, \
194 static const struct iio_chan_spec ad7303_channels
[] = {
199 static int ad7303_probe(struct spi_device
*spi
)
201 const struct spi_device_id
*id
= spi_get_device_id(spi
);
202 struct iio_dev
*indio_dev
;
203 struct ad7303_state
*st
;
207 indio_dev
= devm_iio_device_alloc(&spi
->dev
, sizeof(*st
));
208 if (indio_dev
== NULL
)
211 st
= iio_priv(indio_dev
);
212 spi_set_drvdata(spi
, indio_dev
);
216 st
->vdd_reg
= devm_regulator_get(&spi
->dev
, "Vdd");
217 if (IS_ERR(st
->vdd_reg
))
218 return PTR_ERR(st
->vdd_reg
);
220 ret
= regulator_enable(st
->vdd_reg
);
224 if (spi
->dev
.of_node
) {
225 ext_ref
= of_property_read_bool(spi
->dev
.of_node
,
228 struct ad7303_platform_data
*pdata
= spi
->dev
.platform_data
;
229 if (pdata
&& pdata
->use_external_ref
)
236 st
->vref_reg
= devm_regulator_get(&spi
->dev
, "REF");
237 if (IS_ERR(st
->vref_reg
)) {
238 ret
= PTR_ERR(st
->vref_reg
);
239 goto err_disable_vdd_reg
;
242 ret
= regulator_enable(st
->vref_reg
);
244 goto err_disable_vdd_reg
;
246 st
->config
|= AD7303_CFG_EXTERNAL_VREF
;
249 indio_dev
->dev
.parent
= &spi
->dev
;
250 indio_dev
->name
= id
->name
;
251 indio_dev
->info
= &ad7303_info
;
252 indio_dev
->modes
= INDIO_DIRECT_MODE
;
253 indio_dev
->channels
= ad7303_channels
;
254 indio_dev
->num_channels
= ARRAY_SIZE(ad7303_channels
);
256 ret
= iio_device_register(indio_dev
);
258 goto err_disable_vref_reg
;
262 err_disable_vref_reg
:
264 regulator_disable(st
->vref_reg
);
266 regulator_disable(st
->vdd_reg
);
270 static int ad7303_remove(struct spi_device
*spi
)
272 struct iio_dev
*indio_dev
= spi_get_drvdata(spi
);
273 struct ad7303_state
*st
= iio_priv(indio_dev
);
275 iio_device_unregister(indio_dev
);
278 regulator_disable(st
->vref_reg
);
279 regulator_disable(st
->vdd_reg
);
284 static const struct spi_device_id ad7303_spi_ids
[] = {
288 MODULE_DEVICE_TABLE(spi
, ad7303_spi_ids
);
290 static struct spi_driver ad7303_driver
= {
293 .owner
= THIS_MODULE
,
295 .probe
= ad7303_probe
,
296 .remove
= ad7303_remove
,
297 .id_table
= ad7303_spi_ids
,
299 module_spi_driver(ad7303_driver
);
301 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
302 MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
303 MODULE_LICENSE("GPL v2");