1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f8347824SLars-Peter Clausen /*
3f8347824SLars-Peter Clausen * AD7303 Digital to analog converters driver
4f8347824SLars-Peter Clausen *
5f8347824SLars-Peter Clausen * Copyright 2013 Analog Devices Inc.
6f8347824SLars-Peter Clausen */
7f8347824SLars-Peter Clausen
8f8347824SLars-Peter Clausen #include <linux/err.h>
9f8347824SLars-Peter Clausen #include <linux/module.h>
1034860a19SJonathan Cameron #include <linux/mod_devicetable.h>
11f8347824SLars-Peter Clausen #include <linux/kernel.h>
12f8347824SLars-Peter Clausen #include <linux/spi/spi.h>
13f8347824SLars-Peter Clausen #include <linux/slab.h>
14f8347824SLars-Peter Clausen #include <linux/sysfs.h>
15f8347824SLars-Peter Clausen #include <linux/regulator/consumer.h>
16f8347824SLars-Peter Clausen
17f8347824SLars-Peter Clausen #include <linux/iio/iio.h>
18f8347824SLars-Peter Clausen #include <linux/iio/sysfs.h>
19f8347824SLars-Peter Clausen
20f8347824SLars-Peter Clausen #define AD7303_CFG_EXTERNAL_VREF BIT(15)
21f8347824SLars-Peter Clausen #define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
22f8347824SLars-Peter Clausen #define AD7303_CFG_ADDR_OFFSET 10
23f8347824SLars-Peter Clausen
24f8347824SLars-Peter Clausen #define AD7303_CMD_UPDATE_DAC (0x3 << 8)
25f8347824SLars-Peter Clausen
26f8347824SLars-Peter Clausen /**
27f8347824SLars-Peter Clausen * struct ad7303_state - driver instance specific data
28f8347824SLars-Peter Clausen * @spi: the device for this driver instance
29f8347824SLars-Peter Clausen * @config: cached config register value
30f8347824SLars-Peter Clausen * @dac_cache: current DAC raw value (chip does not support readback)
3160262924SLee Jones * @vdd_reg: reference to VDD regulator
3260262924SLee Jones * @vref_reg: reference to VREF regulator
3360262924SLee Jones * @lock: protect writes and cache updates
34f8347824SLars-Peter Clausen * @data: spi transfer buffer
35f8347824SLars-Peter Clausen */
36f8347824SLars-Peter Clausen
37f8347824SLars-Peter Clausen struct ad7303_state {
38f8347824SLars-Peter Clausen struct spi_device *spi;
39f8347824SLars-Peter Clausen uint16_t config;
40f8347824SLars-Peter Clausen uint8_t dac_cache[2];
41f8347824SLars-Peter Clausen
42f8347824SLars-Peter Clausen struct regulator *vdd_reg;
43f8347824SLars-Peter Clausen struct regulator *vref_reg;
44f8347824SLars-Peter Clausen
45c991bf9bSAlexandru Ardelean struct mutex lock;
46f8347824SLars-Peter Clausen /*
47*69e51448SJonathan Cameron * DMA (thus cache coherency maintenance) may require the
48f8347824SLars-Peter Clausen * transfer buffers to live in their own cache lines.
49f8347824SLars-Peter Clausen */
50*69e51448SJonathan Cameron __be16 data __aligned(IIO_DMA_MINALIGN);
51f8347824SLars-Peter Clausen };
52f8347824SLars-Peter Clausen
ad7303_write(struct ad7303_state * st,unsigned int chan,uint8_t val)53f8347824SLars-Peter Clausen static int ad7303_write(struct ad7303_state *st, unsigned int chan,
54f8347824SLars-Peter Clausen uint8_t val)
55f8347824SLars-Peter Clausen {
56f8347824SLars-Peter Clausen st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC |
57f8347824SLars-Peter Clausen (chan << AD7303_CFG_ADDR_OFFSET) |
58f8347824SLars-Peter Clausen st->config | val);
59f8347824SLars-Peter Clausen
60f8347824SLars-Peter Clausen return spi_write(st->spi, &st->data, sizeof(st->data));
61f8347824SLars-Peter Clausen }
62f8347824SLars-Peter Clausen
ad7303_read_dac_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,char * buf)63f8347824SLars-Peter Clausen static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev,
64f8347824SLars-Peter Clausen uintptr_t private, const struct iio_chan_spec *chan, char *buf)
65f8347824SLars-Peter Clausen {
66f8347824SLars-Peter Clausen struct ad7303_state *st = iio_priv(indio_dev);
67f8347824SLars-Peter Clausen
68f46ac009SLars-Peter Clausen return sysfs_emit(buf, "%d\n", (bool)(st->config &
69f8347824SLars-Peter Clausen AD7303_CFG_POWER_DOWN(chan->channel)));
70f8347824SLars-Peter Clausen }
71f8347824SLars-Peter Clausen
ad7303_write_dac_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,const char * buf,size_t len)72f8347824SLars-Peter Clausen static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
73f8347824SLars-Peter Clausen uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
74f8347824SLars-Peter Clausen size_t len)
75f8347824SLars-Peter Clausen {
76f8347824SLars-Peter Clausen struct ad7303_state *st = iio_priv(indio_dev);
77f8347824SLars-Peter Clausen bool pwr_down;
78f8347824SLars-Peter Clausen int ret;
79f8347824SLars-Peter Clausen
8074f582ecSLars-Peter Clausen ret = kstrtobool(buf, &pwr_down);
81f8347824SLars-Peter Clausen if (ret)
82f8347824SLars-Peter Clausen return ret;
83f8347824SLars-Peter Clausen
84c991bf9bSAlexandru Ardelean mutex_lock(&st->lock);
85f8347824SLars-Peter Clausen
86f8347824SLars-Peter Clausen if (pwr_down)
87f8347824SLars-Peter Clausen st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
88f8347824SLars-Peter Clausen else
89f8347824SLars-Peter Clausen st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel);
90f8347824SLars-Peter Clausen
91f8347824SLars-Peter Clausen /* There is no noop cmd which allows us to only update the powerdown
92f8347824SLars-Peter Clausen * mode, so just write one of the DAC channels again */
93f8347824SLars-Peter Clausen ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
94f8347824SLars-Peter Clausen
95c991bf9bSAlexandru Ardelean mutex_unlock(&st->lock);
96a04cf55aSDan Carpenter return len;
97f8347824SLars-Peter Clausen }
98f8347824SLars-Peter Clausen
ad7303_get_vref(struct ad7303_state * st,struct iio_chan_spec const * chan)99f8347824SLars-Peter Clausen static int ad7303_get_vref(struct ad7303_state *st,
100f8347824SLars-Peter Clausen struct iio_chan_spec const *chan)
101f8347824SLars-Peter Clausen {
102f8347824SLars-Peter Clausen int ret;
103f8347824SLars-Peter Clausen
104f8347824SLars-Peter Clausen if (st->config & AD7303_CFG_EXTERNAL_VREF)
105f8347824SLars-Peter Clausen return regulator_get_voltage(st->vref_reg);
106f8347824SLars-Peter Clausen
107f8347824SLars-Peter Clausen ret = regulator_get_voltage(st->vdd_reg);
108f8347824SLars-Peter Clausen if (ret < 0)
109f8347824SLars-Peter Clausen return ret;
110f8347824SLars-Peter Clausen return ret / 2;
111f8347824SLars-Peter Clausen }
112f8347824SLars-Peter Clausen
ad7303_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long info)113f8347824SLars-Peter Clausen static int ad7303_read_raw(struct iio_dev *indio_dev,
114f8347824SLars-Peter Clausen struct iio_chan_spec const *chan, int *val, int *val2, long info)
115f8347824SLars-Peter Clausen {
116f8347824SLars-Peter Clausen struct ad7303_state *st = iio_priv(indio_dev);
117f8347824SLars-Peter Clausen int vref_uv;
118f8347824SLars-Peter Clausen
119f8347824SLars-Peter Clausen switch (info) {
120f8347824SLars-Peter Clausen case IIO_CHAN_INFO_RAW:
121c991bf9bSAlexandru Ardelean mutex_lock(&st->lock);
122f8347824SLars-Peter Clausen *val = st->dac_cache[chan->channel];
123c991bf9bSAlexandru Ardelean mutex_unlock(&st->lock);
124f8347824SLars-Peter Clausen return IIO_VAL_INT;
125f8347824SLars-Peter Clausen case IIO_CHAN_INFO_SCALE:
126f8347824SLars-Peter Clausen vref_uv = ad7303_get_vref(st, chan);
127f8347824SLars-Peter Clausen if (vref_uv < 0)
128f8347824SLars-Peter Clausen return vref_uv;
129f8347824SLars-Peter Clausen
130f8347824SLars-Peter Clausen *val = 2 * vref_uv / 1000;
131f8347824SLars-Peter Clausen *val2 = chan->scan_type.realbits;
132f8347824SLars-Peter Clausen
133f8347824SLars-Peter Clausen return IIO_VAL_FRACTIONAL_LOG2;
134f8347824SLars-Peter Clausen default:
135f8347824SLars-Peter Clausen break;
136f8347824SLars-Peter Clausen }
137f8347824SLars-Peter Clausen return -EINVAL;
138f8347824SLars-Peter Clausen }
139f8347824SLars-Peter Clausen
ad7303_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)140f8347824SLars-Peter Clausen static int ad7303_write_raw(struct iio_dev *indio_dev,
141f8347824SLars-Peter Clausen struct iio_chan_spec const *chan, int val, int val2, long mask)
142f8347824SLars-Peter Clausen {
143f8347824SLars-Peter Clausen struct ad7303_state *st = iio_priv(indio_dev);
144f8347824SLars-Peter Clausen int ret;
145f8347824SLars-Peter Clausen
146f8347824SLars-Peter Clausen switch (mask) {
147f8347824SLars-Peter Clausen case IIO_CHAN_INFO_RAW:
148f8347824SLars-Peter Clausen if (val >= (1 << chan->scan_type.realbits) || val < 0)
149f8347824SLars-Peter Clausen return -EINVAL;
150f8347824SLars-Peter Clausen
151c991bf9bSAlexandru Ardelean mutex_lock(&st->lock);
152f8347824SLars-Peter Clausen ret = ad7303_write(st, chan->address, val);
153f8347824SLars-Peter Clausen if (ret == 0)
154f8347824SLars-Peter Clausen st->dac_cache[chan->channel] = val;
155c991bf9bSAlexandru Ardelean mutex_unlock(&st->lock);
156f8347824SLars-Peter Clausen break;
157f8347824SLars-Peter Clausen default:
158f8347824SLars-Peter Clausen ret = -EINVAL;
159f8347824SLars-Peter Clausen }
160f8347824SLars-Peter Clausen
161f8347824SLars-Peter Clausen return ret;
162f8347824SLars-Peter Clausen }
163f8347824SLars-Peter Clausen
164f8347824SLars-Peter Clausen static const struct iio_info ad7303_info = {
165f8347824SLars-Peter Clausen .read_raw = ad7303_read_raw,
166f8347824SLars-Peter Clausen .write_raw = ad7303_write_raw,
167f8347824SLars-Peter Clausen };
168f8347824SLars-Peter Clausen
169f8347824SLars-Peter Clausen static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
170f8347824SLars-Peter Clausen {
171f8347824SLars-Peter Clausen .name = "powerdown",
172f8347824SLars-Peter Clausen .read = ad7303_read_dac_powerdown,
173f8347824SLars-Peter Clausen .write = ad7303_write_dac_powerdown,
1743704432fSJonathan Cameron .shared = IIO_SEPARATE,
175f8347824SLars-Peter Clausen },
176f8347824SLars-Peter Clausen { },
177f8347824SLars-Peter Clausen };
178f8347824SLars-Peter Clausen
179f8347824SLars-Peter Clausen #define AD7303_CHANNEL(chan) { \
180f8347824SLars-Peter Clausen .type = IIO_VOLTAGE, \
181f8347824SLars-Peter Clausen .indexed = 1, \
182f8347824SLars-Peter Clausen .output = 1, \
183f8347824SLars-Peter Clausen .channel = (chan), \
184f8347824SLars-Peter Clausen .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
185f8347824SLars-Peter Clausen .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
186f8347824SLars-Peter Clausen .address = (chan), \
187f8347824SLars-Peter Clausen .scan_type = { \
188f8347824SLars-Peter Clausen .sign = 'u', \
189ce420fd4SPavel Roskin .realbits = 8, \
190ce420fd4SPavel Roskin .storagebits = 8, \
191ce420fd4SPavel Roskin .shift = 0, \
192f8347824SLars-Peter Clausen }, \
193f8347824SLars-Peter Clausen .ext_info = ad7303_ext_info, \
194f8347824SLars-Peter Clausen }
195f8347824SLars-Peter Clausen
196f8347824SLars-Peter Clausen static const struct iio_chan_spec ad7303_channels[] = {
197f8347824SLars-Peter Clausen AD7303_CHANNEL(0),
198f8347824SLars-Peter Clausen AD7303_CHANNEL(1),
199f8347824SLars-Peter Clausen };
200f8347824SLars-Peter Clausen
ad7303_reg_disable(void * reg)2018a16c76eSAlexandru Ardelean static void ad7303_reg_disable(void *reg)
2028a16c76eSAlexandru Ardelean {
2038a16c76eSAlexandru Ardelean regulator_disable(reg);
2048a16c76eSAlexandru Ardelean }
2058a16c76eSAlexandru Ardelean
ad7303_probe(struct spi_device * spi)206f8347824SLars-Peter Clausen static int ad7303_probe(struct spi_device *spi)
207f8347824SLars-Peter Clausen {
208f8347824SLars-Peter Clausen const struct spi_device_id *id = spi_get_device_id(spi);
209f8347824SLars-Peter Clausen struct iio_dev *indio_dev;
210f8347824SLars-Peter Clausen struct ad7303_state *st;
211f8347824SLars-Peter Clausen int ret;
212f8347824SLars-Peter Clausen
21366e670aaSSachin Kamat indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
214f8347824SLars-Peter Clausen if (indio_dev == NULL)
215f8347824SLars-Peter Clausen return -ENOMEM;
216f8347824SLars-Peter Clausen
217f8347824SLars-Peter Clausen st = iio_priv(indio_dev);
218f8347824SLars-Peter Clausen
219f8347824SLars-Peter Clausen st->spi = spi;
220f8347824SLars-Peter Clausen
221c991bf9bSAlexandru Ardelean mutex_init(&st->lock);
222c991bf9bSAlexandru Ardelean
22366e670aaSSachin Kamat st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd");
22466e670aaSSachin Kamat if (IS_ERR(st->vdd_reg))
22566e670aaSSachin Kamat return PTR_ERR(st->vdd_reg);
226f8347824SLars-Peter Clausen
227f8347824SLars-Peter Clausen ret = regulator_enable(st->vdd_reg);
228f8347824SLars-Peter Clausen if (ret)
22966e670aaSSachin Kamat return ret;
230f8347824SLars-Peter Clausen
2318a16c76eSAlexandru Ardelean ret = devm_add_action_or_reset(&spi->dev, ad7303_reg_disable, st->vdd_reg);
2328a16c76eSAlexandru Ardelean if (ret)
2338a16c76eSAlexandru Ardelean return ret;
2348a16c76eSAlexandru Ardelean
235a8b26c2dSAlexandru Tachici st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF");
23694fccb78SWei Yongjun if (IS_ERR(st->vref_reg)) {
23794fccb78SWei Yongjun ret = PTR_ERR(st->vref_reg);
238a8b26c2dSAlexandru Tachici if (ret != -ENODEV)
2398a16c76eSAlexandru Ardelean return ret;
240a8b26c2dSAlexandru Tachici st->vref_reg = NULL;
24194fccb78SWei Yongjun }
242f8347824SLars-Peter Clausen
243a8b26c2dSAlexandru Tachici if (st->vref_reg) {
244f8347824SLars-Peter Clausen ret = regulator_enable(st->vref_reg);
245f8347824SLars-Peter Clausen if (ret)
2468a16c76eSAlexandru Ardelean return ret;
2478a16c76eSAlexandru Ardelean
2488a16c76eSAlexandru Ardelean ret = devm_add_action_or_reset(&spi->dev, ad7303_reg_disable,
2498a16c76eSAlexandru Ardelean st->vref_reg);
2508a16c76eSAlexandru Ardelean if (ret)
2518a16c76eSAlexandru Ardelean return ret;
252f8347824SLars-Peter Clausen
253f8347824SLars-Peter Clausen st->config |= AD7303_CFG_EXTERNAL_VREF;
254f8347824SLars-Peter Clausen }
255f8347824SLars-Peter Clausen
256f8347824SLars-Peter Clausen indio_dev->name = id->name;
257f8347824SLars-Peter Clausen indio_dev->info = &ad7303_info;
258f8347824SLars-Peter Clausen indio_dev->modes = INDIO_DIRECT_MODE;
259f8347824SLars-Peter Clausen indio_dev->channels = ad7303_channels;
260f8347824SLars-Peter Clausen indio_dev->num_channels = ARRAY_SIZE(ad7303_channels);
261f8347824SLars-Peter Clausen
2628a16c76eSAlexandru Ardelean return devm_iio_device_register(&spi->dev, indio_dev);
263f8347824SLars-Peter Clausen }
264f8347824SLars-Peter Clausen
2651c00dcd3SJavier Martinez Canillas static const struct of_device_id ad7303_spi_of_match[] = {
2661c00dcd3SJavier Martinez Canillas { .compatible = "adi,ad7303", },
2671c00dcd3SJavier Martinez Canillas { /* sentinel */ },
2681c00dcd3SJavier Martinez Canillas };
2691c00dcd3SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
2701c00dcd3SJavier Martinez Canillas
271f8347824SLars-Peter Clausen static const struct spi_device_id ad7303_spi_ids[] = {
272f8347824SLars-Peter Clausen { "ad7303", 0 },
273f8347824SLars-Peter Clausen {}
274f8347824SLars-Peter Clausen };
275f8347824SLars-Peter Clausen MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
276f8347824SLars-Peter Clausen
277f8347824SLars-Peter Clausen static struct spi_driver ad7303_driver = {
278f8347824SLars-Peter Clausen .driver = {
279f8347824SLars-Peter Clausen .name = "ad7303",
28034860a19SJonathan Cameron .of_match_table = ad7303_spi_of_match,
281f8347824SLars-Peter Clausen },
282f8347824SLars-Peter Clausen .probe = ad7303_probe,
283f8347824SLars-Peter Clausen .id_table = ad7303_spi_ids,
284f8347824SLars-Peter Clausen };
285f8347824SLars-Peter Clausen module_spi_driver(ad7303_driver);
286f8347824SLars-Peter Clausen
287f8347824SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
288f8347824SLars-Peter Clausen MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
289f8347824SLars-Peter Clausen MODULE_LICENSE("GPL v2");
290