1 // SPDX-License-Identifier: GPL-2.0-only
3 * AD7303 Digital to analog converters driver
5 * Copyright 2013 Analog Devices Inc.
9 #include <linux/module.h>
10 #include <linux/kernel.h>
11 #include <linux/spi/spi.h>
12 #include <linux/slab.h>
13 #include <linux/sysfs.h>
14 #include <linux/regulator/consumer.h>
16 #include <linux/iio/iio.h>
17 #include <linux/iio/sysfs.h>
19 #include <linux/platform_data/ad7303.h>
21 #define AD7303_CFG_EXTERNAL_VREF BIT(15)
22 #define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
23 #define AD7303_CFG_ADDR_OFFSET 10
25 #define AD7303_CMD_UPDATE_DAC (0x3 << 8)
28 * struct ad7303_state - driver instance specific data
29 * @spi: the device for this driver instance
30 * @config: cached config register value
31 * @dac_cache: current DAC raw value (chip does not support readback)
32 * @data: spi transfer buffer
36 struct spi_device
*spi
;
40 struct regulator
*vdd_reg
;
41 struct regulator
*vref_reg
;
45 * DMA (thus cache coherency maintenance) requires the
46 * transfer buffers to live in their own cache lines.
48 __be16 data ____cacheline_aligned
;
51 static int ad7303_write(struct ad7303_state
*st
, unsigned int chan
,
54 st
->data
= cpu_to_be16(AD7303_CMD_UPDATE_DAC
|
55 (chan
<< AD7303_CFG_ADDR_OFFSET
) |
58 return spi_write(st
->spi
, &st
->data
, sizeof(st
->data
));
61 static ssize_t
ad7303_read_dac_powerdown(struct iio_dev
*indio_dev
,
62 uintptr_t private, const struct iio_chan_spec
*chan
, char *buf
)
64 struct ad7303_state
*st
= iio_priv(indio_dev
);
66 return sprintf(buf
, "%d\n", (bool)(st
->config
&
67 AD7303_CFG_POWER_DOWN(chan
->channel
)));
70 static ssize_t
ad7303_write_dac_powerdown(struct iio_dev
*indio_dev
,
71 uintptr_t private, const struct iio_chan_spec
*chan
, const char *buf
,
74 struct ad7303_state
*st
= iio_priv(indio_dev
);
78 ret
= strtobool(buf
, &pwr_down
);
82 mutex_lock(&st
->lock
);
85 st
->config
|= AD7303_CFG_POWER_DOWN(chan
->channel
);
87 st
->config
&= ~AD7303_CFG_POWER_DOWN(chan
->channel
);
89 /* There is no noop cmd which allows us to only update the powerdown
90 * mode, so just write one of the DAC channels again */
91 ad7303_write(st
, chan
->channel
, st
->dac_cache
[chan
->channel
]);
93 mutex_unlock(&st
->lock
);
97 static int ad7303_get_vref(struct ad7303_state
*st
,
98 struct iio_chan_spec
const *chan
)
102 if (st
->config
& AD7303_CFG_EXTERNAL_VREF
)
103 return regulator_get_voltage(st
->vref_reg
);
105 ret
= regulator_get_voltage(st
->vdd_reg
);
111 static int ad7303_read_raw(struct iio_dev
*indio_dev
,
112 struct iio_chan_spec
const *chan
, int *val
, int *val2
, long info
)
114 struct ad7303_state
*st
= iio_priv(indio_dev
);
118 case IIO_CHAN_INFO_RAW
:
119 mutex_lock(&st
->lock
);
120 *val
= st
->dac_cache
[chan
->channel
];
121 mutex_unlock(&st
->lock
);
123 case IIO_CHAN_INFO_SCALE
:
124 vref_uv
= ad7303_get_vref(st
, chan
);
128 *val
= 2 * vref_uv
/ 1000;
129 *val2
= chan
->scan_type
.realbits
;
131 return IIO_VAL_FRACTIONAL_LOG2
;
138 static int ad7303_write_raw(struct iio_dev
*indio_dev
,
139 struct iio_chan_spec
const *chan
, int val
, int val2
, long mask
)
141 struct ad7303_state
*st
= iio_priv(indio_dev
);
145 case IIO_CHAN_INFO_RAW
:
146 if (val
>= (1 << chan
->scan_type
.realbits
) || val
< 0)
149 mutex_lock(&st
->lock
);
150 ret
= ad7303_write(st
, chan
->address
, val
);
152 st
->dac_cache
[chan
->channel
] = val
;
153 mutex_unlock(&st
->lock
);
162 static const struct iio_info ad7303_info
= {
163 .read_raw
= ad7303_read_raw
,
164 .write_raw
= ad7303_write_raw
,
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), \
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
;
206 indio_dev
= devm_iio_device_alloc(&spi
->dev
, sizeof(*st
));
207 if (indio_dev
== NULL
)
210 st
= iio_priv(indio_dev
);
211 spi_set_drvdata(spi
, indio_dev
);
215 mutex_init(&st
->lock
);
217 st
->vdd_reg
= devm_regulator_get(&spi
->dev
, "Vdd");
218 if (IS_ERR(st
->vdd_reg
))
219 return PTR_ERR(st
->vdd_reg
);
221 ret
= regulator_enable(st
->vdd_reg
);
225 st
->vref_reg
= devm_regulator_get_optional(&spi
->dev
, "REF");
226 if (IS_ERR(st
->vref_reg
)) {
227 ret
= PTR_ERR(st
->vref_reg
);
229 goto err_disable_vdd_reg
;
234 ret
= regulator_enable(st
->vref_reg
);
236 goto err_disable_vdd_reg
;
238 st
->config
|= AD7303_CFG_EXTERNAL_VREF
;
241 indio_dev
->dev
.parent
= &spi
->dev
;
242 indio_dev
->name
= id
->name
;
243 indio_dev
->info
= &ad7303_info
;
244 indio_dev
->modes
= INDIO_DIRECT_MODE
;
245 indio_dev
->channels
= ad7303_channels
;
246 indio_dev
->num_channels
= ARRAY_SIZE(ad7303_channels
);
248 ret
= iio_device_register(indio_dev
);
250 goto err_disable_vref_reg
;
254 err_disable_vref_reg
:
256 regulator_disable(st
->vref_reg
);
258 regulator_disable(st
->vdd_reg
);
262 static int ad7303_remove(struct spi_device
*spi
)
264 struct iio_dev
*indio_dev
= spi_get_drvdata(spi
);
265 struct ad7303_state
*st
= iio_priv(indio_dev
);
267 iio_device_unregister(indio_dev
);
270 regulator_disable(st
->vref_reg
);
271 regulator_disable(st
->vdd_reg
);
276 static const struct of_device_id ad7303_spi_of_match
[] = {
277 { .compatible
= "adi,ad7303", },
280 MODULE_DEVICE_TABLE(of
, ad7303_spi_of_match
);
282 static const struct spi_device_id ad7303_spi_ids
[] = {
286 MODULE_DEVICE_TABLE(spi
, ad7303_spi_ids
);
288 static struct spi_driver ad7303_driver
= {
291 .of_match_table
= of_match_ptr(ad7303_spi_of_match
),
293 .probe
= ad7303_probe
,
294 .remove
= ad7303_remove
,
295 .id_table
= ad7303_spi_ids
,
297 module_spi_driver(ad7303_driver
);
299 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
300 MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
301 MODULE_LICENSE("GPL v2");