1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dbdc025bSLars-Peter Clausen /*
3dbdc025bSLars-Peter Clausen * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392
4dbdc025bSLars-Peter Clausen * multi-channel Digital to Analog Converters driver
5dbdc025bSLars-Peter Clausen *
6dbdc025bSLars-Peter Clausen * Copyright 2011 Analog Devices Inc.
7dbdc025bSLars-Peter Clausen */
8dbdc025bSLars-Peter Clausen
9dbdc025bSLars-Peter Clausen #include <linux/device.h>
10dbdc025bSLars-Peter Clausen #include <linux/err.h>
11dbdc025bSLars-Peter Clausen #include <linux/i2c.h>
12dbdc025bSLars-Peter Clausen #include <linux/kernel.h>
13dbdc025bSLars-Peter Clausen #include <linux/module.h>
14dbdc025bSLars-Peter Clausen #include <linux/spi/spi.h>
15dbdc025bSLars-Peter Clausen #include <linux/slab.h>
16dbdc025bSLars-Peter Clausen #include <linux/sysfs.h>
17dbdc025bSLars-Peter Clausen #include <linux/regmap.h>
18dbdc025bSLars-Peter Clausen #include <linux/regulator/consumer.h>
19dbdc025bSLars-Peter Clausen
20dbdc025bSLars-Peter Clausen #include <linux/iio/iio.h>
21dbdc025bSLars-Peter Clausen #include <linux/iio/sysfs.h>
22dbdc025bSLars-Peter Clausen
23dbdc025bSLars-Peter Clausen #define AD5380_REG_DATA(x) (((x) << 2) | 3)
24dbdc025bSLars-Peter Clausen #define AD5380_REG_OFFSET(x) (((x) << 2) | 2)
25dbdc025bSLars-Peter Clausen #define AD5380_REG_GAIN(x) (((x) << 2) | 1)
26dbdc025bSLars-Peter Clausen #define AD5380_REG_SF_PWR_DOWN (8 << 2)
27dbdc025bSLars-Peter Clausen #define AD5380_REG_SF_PWR_UP (9 << 2)
28dbdc025bSLars-Peter Clausen #define AD5380_REG_SF_CTRL (12 << 2)
29dbdc025bSLars-Peter Clausen
30dbdc025bSLars-Peter Clausen #define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13
31dbdc025bSLars-Peter Clausen #define AD5380_CTRL_INT_VREF_2V5 BIT(12)
32dbdc025bSLars-Peter Clausen #define AD5380_CTRL_INT_VREF_EN BIT(10)
33dbdc025bSLars-Peter Clausen
34dbdc025bSLars-Peter Clausen /**
35dbdc025bSLars-Peter Clausen * struct ad5380_chip_info - chip specific information
36dbdc025bSLars-Peter Clausen * @channel_template: channel specification template
37dbdc025bSLars-Peter Clausen * @num_channels: number of channels
38dbdc025bSLars-Peter Clausen * @int_vref: internal vref in uV
39dbdc025bSLars-Peter Clausen */
40dbdc025bSLars-Peter Clausen struct ad5380_chip_info {
41dbdc025bSLars-Peter Clausen struct iio_chan_spec channel_template;
42dbdc025bSLars-Peter Clausen unsigned int num_channels;
43dbdc025bSLars-Peter Clausen unsigned int int_vref;
44dbdc025bSLars-Peter Clausen };
45dbdc025bSLars-Peter Clausen
46dbdc025bSLars-Peter Clausen /**
47dbdc025bSLars-Peter Clausen * struct ad5380_state - driver instance specific data
48dbdc025bSLars-Peter Clausen * @regmap: regmap instance used by the device
49dbdc025bSLars-Peter Clausen * @chip_info: chip model specific constants, available modes etc
50dbdc025bSLars-Peter Clausen * @vref_reg: vref supply regulator
51dbdc025bSLars-Peter Clausen * @vref: actual reference voltage used in uA
52dbdc025bSLars-Peter Clausen * @pwr_down: whether the chip is currently in power down mode
5312630129SLee Jones * @lock: lock to protect the data buffer during regmap ops
54dbdc025bSLars-Peter Clausen */
55dbdc025bSLars-Peter Clausen struct ad5380_state {
56dbdc025bSLars-Peter Clausen struct regmap *regmap;
57dbdc025bSLars-Peter Clausen const struct ad5380_chip_info *chip_info;
58dbdc025bSLars-Peter Clausen struct regulator *vref_reg;
59dbdc025bSLars-Peter Clausen int vref;
60dbdc025bSLars-Peter Clausen bool pwr_down;
61fa444a18SSergiu Cuciurean struct mutex lock;
62dbdc025bSLars-Peter Clausen };
63dbdc025bSLars-Peter Clausen
64dbdc025bSLars-Peter Clausen enum ad5380_type {
65dbdc025bSLars-Peter Clausen ID_AD5380_3,
66dbdc025bSLars-Peter Clausen ID_AD5380_5,
67dbdc025bSLars-Peter Clausen ID_AD5381_3,
68dbdc025bSLars-Peter Clausen ID_AD5381_5,
69dbdc025bSLars-Peter Clausen ID_AD5382_3,
70dbdc025bSLars-Peter Clausen ID_AD5382_5,
71dbdc025bSLars-Peter Clausen ID_AD5383_3,
72dbdc025bSLars-Peter Clausen ID_AD5383_5,
73dbdc025bSLars-Peter Clausen ID_AD5390_3,
74dbdc025bSLars-Peter Clausen ID_AD5390_5,
75dbdc025bSLars-Peter Clausen ID_AD5391_3,
76dbdc025bSLars-Peter Clausen ID_AD5391_5,
77dbdc025bSLars-Peter Clausen ID_AD5392_3,
78dbdc025bSLars-Peter Clausen ID_AD5392_5,
79dbdc025bSLars-Peter Clausen };
80dbdc025bSLars-Peter Clausen
ad5380_read_dac_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,char * buf)81dbdc025bSLars-Peter Clausen static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev,
82dbdc025bSLars-Peter Clausen uintptr_t private, const struct iio_chan_spec *chan, char *buf)
83dbdc025bSLars-Peter Clausen {
84dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
85dbdc025bSLars-Peter Clausen
86f46ac009SLars-Peter Clausen return sysfs_emit(buf, "%d\n", st->pwr_down);
87dbdc025bSLars-Peter Clausen }
88dbdc025bSLars-Peter Clausen
ad5380_write_dac_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,const char * buf,size_t len)89dbdc025bSLars-Peter Clausen static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev,
90dbdc025bSLars-Peter Clausen uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
91dbdc025bSLars-Peter Clausen size_t len)
92dbdc025bSLars-Peter Clausen {
93dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
94dbdc025bSLars-Peter Clausen bool pwr_down;
95dbdc025bSLars-Peter Clausen int ret;
96dbdc025bSLars-Peter Clausen
9774f582ecSLars-Peter Clausen ret = kstrtobool(buf, &pwr_down);
98dbdc025bSLars-Peter Clausen if (ret)
99dbdc025bSLars-Peter Clausen return ret;
100dbdc025bSLars-Peter Clausen
101fa444a18SSergiu Cuciurean mutex_lock(&st->lock);
102dbdc025bSLars-Peter Clausen
103dbdc025bSLars-Peter Clausen if (pwr_down)
104dbdc025bSLars-Peter Clausen ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0);
105dbdc025bSLars-Peter Clausen else
106dbdc025bSLars-Peter Clausen ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0);
107dbdc025bSLars-Peter Clausen
108dbdc025bSLars-Peter Clausen st->pwr_down = pwr_down;
109dbdc025bSLars-Peter Clausen
110fa444a18SSergiu Cuciurean mutex_unlock(&st->lock);
111dbdc025bSLars-Peter Clausen
112dbdc025bSLars-Peter Clausen return ret ? ret : len;
113dbdc025bSLars-Peter Clausen }
114dbdc025bSLars-Peter Clausen
115dbdc025bSLars-Peter Clausen static const char * const ad5380_powerdown_modes[] = {
116dbdc025bSLars-Peter Clausen "100kohm_to_gnd",
117dbdc025bSLars-Peter Clausen "three_state",
118dbdc025bSLars-Peter Clausen };
119dbdc025bSLars-Peter Clausen
ad5380_get_powerdown_mode(struct iio_dev * indio_dev,const struct iio_chan_spec * chan)120dbdc025bSLars-Peter Clausen static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev,
121dbdc025bSLars-Peter Clausen const struct iio_chan_spec *chan)
122dbdc025bSLars-Peter Clausen {
123dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
124dbdc025bSLars-Peter Clausen unsigned int mode;
125dbdc025bSLars-Peter Clausen int ret;
126dbdc025bSLars-Peter Clausen
127dbdc025bSLars-Peter Clausen ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode);
128dbdc025bSLars-Peter Clausen if (ret)
129dbdc025bSLars-Peter Clausen return ret;
130dbdc025bSLars-Peter Clausen
131dbdc025bSLars-Peter Clausen mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1;
132dbdc025bSLars-Peter Clausen
133dbdc025bSLars-Peter Clausen return mode;
134dbdc025bSLars-Peter Clausen }
135dbdc025bSLars-Peter Clausen
ad5380_set_powerdown_mode(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,unsigned int mode)136dbdc025bSLars-Peter Clausen static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev,
137dbdc025bSLars-Peter Clausen const struct iio_chan_spec *chan, unsigned int mode)
138dbdc025bSLars-Peter Clausen {
139dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
140dbdc025bSLars-Peter Clausen int ret;
141dbdc025bSLars-Peter Clausen
142dbdc025bSLars-Peter Clausen ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL,
143dbdc025bSLars-Peter Clausen 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET,
144dbdc025bSLars-Peter Clausen mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET);
145dbdc025bSLars-Peter Clausen
146dbdc025bSLars-Peter Clausen return ret;
147dbdc025bSLars-Peter Clausen }
148dbdc025bSLars-Peter Clausen
149dbdc025bSLars-Peter Clausen static const struct iio_enum ad5380_powerdown_mode_enum = {
150dbdc025bSLars-Peter Clausen .items = ad5380_powerdown_modes,
151dbdc025bSLars-Peter Clausen .num_items = ARRAY_SIZE(ad5380_powerdown_modes),
152dbdc025bSLars-Peter Clausen .get = ad5380_get_powerdown_mode,
153dbdc025bSLars-Peter Clausen .set = ad5380_set_powerdown_mode,
154dbdc025bSLars-Peter Clausen };
155dbdc025bSLars-Peter Clausen
ad5380_info_to_reg(struct iio_chan_spec const * chan,long info)156dbdc025bSLars-Peter Clausen static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
157dbdc025bSLars-Peter Clausen long info)
158dbdc025bSLars-Peter Clausen {
159dbdc025bSLars-Peter Clausen switch (info) {
16067464a54SRodrigo Siqueira case IIO_CHAN_INFO_RAW:
161dbdc025bSLars-Peter Clausen return AD5380_REG_DATA(chan->address);
162dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_CALIBBIAS:
163dbdc025bSLars-Peter Clausen return AD5380_REG_OFFSET(chan->address);
164dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_CALIBSCALE:
165dbdc025bSLars-Peter Clausen return AD5380_REG_GAIN(chan->address);
166dbdc025bSLars-Peter Clausen default:
167dbdc025bSLars-Peter Clausen break;
168dbdc025bSLars-Peter Clausen }
169dbdc025bSLars-Peter Clausen
170dbdc025bSLars-Peter Clausen return 0;
171dbdc025bSLars-Peter Clausen }
172dbdc025bSLars-Peter Clausen
ad5380_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long info)173dbdc025bSLars-Peter Clausen static int ad5380_write_raw(struct iio_dev *indio_dev,
174dbdc025bSLars-Peter Clausen struct iio_chan_spec const *chan, int val, int val2, long info)
175dbdc025bSLars-Peter Clausen {
176dbdc025bSLars-Peter Clausen const unsigned int max_val = (1 << chan->scan_type.realbits);
177dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
178dbdc025bSLars-Peter Clausen
179dbdc025bSLars-Peter Clausen switch (info) {
180dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_RAW:
181dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_CALIBSCALE:
182dbdc025bSLars-Peter Clausen if (val >= max_val || val < 0)
183dbdc025bSLars-Peter Clausen return -EINVAL;
184dbdc025bSLars-Peter Clausen
185dbdc025bSLars-Peter Clausen return regmap_write(st->regmap,
186dbdc025bSLars-Peter Clausen ad5380_info_to_reg(chan, info),
187dbdc025bSLars-Peter Clausen val << chan->scan_type.shift);
188dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_CALIBBIAS:
189dbdc025bSLars-Peter Clausen val += (1 << chan->scan_type.realbits) / 2;
190dbdc025bSLars-Peter Clausen if (val >= max_val || val < 0)
191dbdc025bSLars-Peter Clausen return -EINVAL;
192dbdc025bSLars-Peter Clausen
193dbdc025bSLars-Peter Clausen return regmap_write(st->regmap,
194dbdc025bSLars-Peter Clausen AD5380_REG_OFFSET(chan->address),
195dbdc025bSLars-Peter Clausen val << chan->scan_type.shift);
196dbdc025bSLars-Peter Clausen default:
197dbdc025bSLars-Peter Clausen break;
198dbdc025bSLars-Peter Clausen }
199dbdc025bSLars-Peter Clausen return -EINVAL;
200dbdc025bSLars-Peter Clausen }
201dbdc025bSLars-Peter Clausen
ad5380_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long info)202dbdc025bSLars-Peter Clausen static int ad5380_read_raw(struct iio_dev *indio_dev,
203dbdc025bSLars-Peter Clausen struct iio_chan_spec const *chan, int *val, int *val2, long info)
204dbdc025bSLars-Peter Clausen {
205dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
206dbdc025bSLars-Peter Clausen int ret;
207dbdc025bSLars-Peter Clausen
208dbdc025bSLars-Peter Clausen switch (info) {
209dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_RAW:
210dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_CALIBSCALE:
211dbdc025bSLars-Peter Clausen ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info),
212dbdc025bSLars-Peter Clausen val);
213dbdc025bSLars-Peter Clausen if (ret)
214dbdc025bSLars-Peter Clausen return ret;
215dbdc025bSLars-Peter Clausen *val >>= chan->scan_type.shift;
216dbdc025bSLars-Peter Clausen return IIO_VAL_INT;
217dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_CALIBBIAS:
218dbdc025bSLars-Peter Clausen ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address),
219dbdc025bSLars-Peter Clausen val);
220dbdc025bSLars-Peter Clausen if (ret)
221dbdc025bSLars-Peter Clausen return ret;
222dbdc025bSLars-Peter Clausen *val >>= chan->scan_type.shift;
223b1e18768SColin Ian King *val -= (1 << chan->scan_type.realbits) / 2;
224dbdc025bSLars-Peter Clausen return IIO_VAL_INT;
225dbdc025bSLars-Peter Clausen case IIO_CHAN_INFO_SCALE:
2264f2c1885SLars-Peter Clausen *val = 2 * st->vref;
2274f2c1885SLars-Peter Clausen *val2 = chan->scan_type.realbits;
2284f2c1885SLars-Peter Clausen return IIO_VAL_FRACTIONAL_LOG2;
229dbdc025bSLars-Peter Clausen default:
230dbdc025bSLars-Peter Clausen break;
231dbdc025bSLars-Peter Clausen }
232dbdc025bSLars-Peter Clausen
233dbdc025bSLars-Peter Clausen return -EINVAL;
234dbdc025bSLars-Peter Clausen }
235dbdc025bSLars-Peter Clausen
236dbdc025bSLars-Peter Clausen static const struct iio_info ad5380_info = {
237dbdc025bSLars-Peter Clausen .read_raw = ad5380_read_raw,
238dbdc025bSLars-Peter Clausen .write_raw = ad5380_write_raw,
239dbdc025bSLars-Peter Clausen };
240dbdc025bSLars-Peter Clausen
241948e6dd1SRikard Falkeborn static const struct iio_chan_spec_ext_info ad5380_ext_info[] = {
242dbdc025bSLars-Peter Clausen {
243dbdc025bSLars-Peter Clausen .name = "powerdown",
244dbdc025bSLars-Peter Clausen .read = ad5380_read_dac_powerdown,
245dbdc025bSLars-Peter Clausen .write = ad5380_write_dac_powerdown,
2463704432fSJonathan Cameron .shared = IIO_SEPARATE,
247dbdc025bSLars-Peter Clausen },
2483704432fSJonathan Cameron IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
2493704432fSJonathan Cameron &ad5380_powerdown_mode_enum),
250ffc7c517SAntoniu Miclaus IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum),
251dbdc025bSLars-Peter Clausen { },
252dbdc025bSLars-Peter Clausen };
253dbdc025bSLars-Peter Clausen
254dbdc025bSLars-Peter Clausen #define AD5380_CHANNEL(_bits) { \
255dbdc025bSLars-Peter Clausen .type = IIO_VOLTAGE, \
256dbdc025bSLars-Peter Clausen .indexed = 1, \
257dbdc025bSLars-Peter Clausen .output = 1, \
258c69e1397SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
259c69e1397SJonathan Cameron BIT(IIO_CHAN_INFO_CALIBSCALE) | \
260c69e1397SJonathan Cameron BIT(IIO_CHAN_INFO_CALIBBIAS), \
261c69e1397SJonathan Cameron .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
262da9b1a21SJonathan Cameron .scan_type = { \
263da9b1a21SJonathan Cameron .sign = 'u', \
264da9b1a21SJonathan Cameron .realbits = (_bits), \
265da9b1a21SJonathan Cameron .storagebits = 16, \
266da9b1a21SJonathan Cameron .shift = 14 - (_bits), \
267da9b1a21SJonathan Cameron }, \
268dbdc025bSLars-Peter Clausen .ext_info = ad5380_ext_info, \
269dbdc025bSLars-Peter Clausen }
270dbdc025bSLars-Peter Clausen
271dbdc025bSLars-Peter Clausen static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {
272dbdc025bSLars-Peter Clausen [ID_AD5380_3] = {
273dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
274dbdc025bSLars-Peter Clausen .num_channels = 40,
2754f2c1885SLars-Peter Clausen .int_vref = 1250,
276dbdc025bSLars-Peter Clausen },
277dbdc025bSLars-Peter Clausen [ID_AD5380_5] = {
278dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
279dbdc025bSLars-Peter Clausen .num_channels = 40,
2804f2c1885SLars-Peter Clausen .int_vref = 2500,
281dbdc025bSLars-Peter Clausen },
282dbdc025bSLars-Peter Clausen [ID_AD5381_3] = {
283dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(12),
284dbdc025bSLars-Peter Clausen .num_channels = 16,
2854f2c1885SLars-Peter Clausen .int_vref = 1250,
286dbdc025bSLars-Peter Clausen },
287dbdc025bSLars-Peter Clausen [ID_AD5381_5] = {
288dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(12),
289dbdc025bSLars-Peter Clausen .num_channels = 16,
2904f2c1885SLars-Peter Clausen .int_vref = 2500,
291dbdc025bSLars-Peter Clausen },
292dbdc025bSLars-Peter Clausen [ID_AD5382_3] = {
293dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
294dbdc025bSLars-Peter Clausen .num_channels = 32,
2954f2c1885SLars-Peter Clausen .int_vref = 1250,
296dbdc025bSLars-Peter Clausen },
297dbdc025bSLars-Peter Clausen [ID_AD5382_5] = {
298dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
299dbdc025bSLars-Peter Clausen .num_channels = 32,
3004f2c1885SLars-Peter Clausen .int_vref = 2500,
301dbdc025bSLars-Peter Clausen },
302dbdc025bSLars-Peter Clausen [ID_AD5383_3] = {
303dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(12),
304dbdc025bSLars-Peter Clausen .num_channels = 32,
3054f2c1885SLars-Peter Clausen .int_vref = 1250,
306dbdc025bSLars-Peter Clausen },
307dbdc025bSLars-Peter Clausen [ID_AD5383_5] = {
308dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(12),
309dbdc025bSLars-Peter Clausen .num_channels = 32,
3104f2c1885SLars-Peter Clausen .int_vref = 2500,
311dbdc025bSLars-Peter Clausen },
312dbdc025bSLars-Peter Clausen [ID_AD5390_3] = {
313dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
314dbdc025bSLars-Peter Clausen .num_channels = 16,
3154f2c1885SLars-Peter Clausen .int_vref = 1250,
316dbdc025bSLars-Peter Clausen },
317dbdc025bSLars-Peter Clausen [ID_AD5390_5] = {
318dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
319dbdc025bSLars-Peter Clausen .num_channels = 16,
3204f2c1885SLars-Peter Clausen .int_vref = 2500,
321dbdc025bSLars-Peter Clausen },
322dbdc025bSLars-Peter Clausen [ID_AD5391_3] = {
323dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(12),
324dbdc025bSLars-Peter Clausen .num_channels = 16,
3254f2c1885SLars-Peter Clausen .int_vref = 1250,
326dbdc025bSLars-Peter Clausen },
327dbdc025bSLars-Peter Clausen [ID_AD5391_5] = {
328dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(12),
329dbdc025bSLars-Peter Clausen .num_channels = 16,
3304f2c1885SLars-Peter Clausen .int_vref = 2500,
331dbdc025bSLars-Peter Clausen },
332dbdc025bSLars-Peter Clausen [ID_AD5392_3] = {
333dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
334dbdc025bSLars-Peter Clausen .num_channels = 8,
3354f2c1885SLars-Peter Clausen .int_vref = 1250,
336dbdc025bSLars-Peter Clausen },
337dbdc025bSLars-Peter Clausen [ID_AD5392_5] = {
338dbdc025bSLars-Peter Clausen .channel_template = AD5380_CHANNEL(14),
339dbdc025bSLars-Peter Clausen .num_channels = 8,
3404f2c1885SLars-Peter Clausen .int_vref = 2500,
341dbdc025bSLars-Peter Clausen },
342dbdc025bSLars-Peter Clausen };
343dbdc025bSLars-Peter Clausen
ad5380_alloc_channels(struct iio_dev * indio_dev)344fc52692cSGreg Kroah-Hartman static int ad5380_alloc_channels(struct iio_dev *indio_dev)
345dbdc025bSLars-Peter Clausen {
346dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
347dbdc025bSLars-Peter Clausen struct iio_chan_spec *channels;
348dbdc025bSLars-Peter Clausen unsigned int i;
349dbdc025bSLars-Peter Clausen
350dbdc025bSLars-Peter Clausen channels = kcalloc(st->chip_info->num_channels,
351dbdc025bSLars-Peter Clausen sizeof(struct iio_chan_spec), GFP_KERNEL);
352dbdc025bSLars-Peter Clausen
353dbdc025bSLars-Peter Clausen if (!channels)
354dbdc025bSLars-Peter Clausen return -ENOMEM;
355dbdc025bSLars-Peter Clausen
356dbdc025bSLars-Peter Clausen for (i = 0; i < st->chip_info->num_channels; ++i) {
357dbdc025bSLars-Peter Clausen channels[i] = st->chip_info->channel_template;
358dbdc025bSLars-Peter Clausen channels[i].channel = i;
359dbdc025bSLars-Peter Clausen channels[i].address = i;
360dbdc025bSLars-Peter Clausen }
361dbdc025bSLars-Peter Clausen
362dbdc025bSLars-Peter Clausen indio_dev->channels = channels;
363dbdc025bSLars-Peter Clausen
364dbdc025bSLars-Peter Clausen return 0;
365dbdc025bSLars-Peter Clausen }
366dbdc025bSLars-Peter Clausen
ad5380_probe(struct device * dev,struct regmap * regmap,enum ad5380_type type,const char * name)367fc52692cSGreg Kroah-Hartman static int ad5380_probe(struct device *dev, struct regmap *regmap,
368dbdc025bSLars-Peter Clausen enum ad5380_type type, const char *name)
369dbdc025bSLars-Peter Clausen {
370dbdc025bSLars-Peter Clausen struct iio_dev *indio_dev;
371dbdc025bSLars-Peter Clausen struct ad5380_state *st;
372dbdc025bSLars-Peter Clausen unsigned int ctrl = 0;
373dbdc025bSLars-Peter Clausen int ret;
374dbdc025bSLars-Peter Clausen
375c815ad37SSachin Kamat indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
376dbdc025bSLars-Peter Clausen if (indio_dev == NULL) {
377dbdc025bSLars-Peter Clausen dev_err(dev, "Failed to allocate iio device\n");
378c815ad37SSachin Kamat return -ENOMEM;
379dbdc025bSLars-Peter Clausen }
380dbdc025bSLars-Peter Clausen
381dbdc025bSLars-Peter Clausen st = iio_priv(indio_dev);
382dbdc025bSLars-Peter Clausen dev_set_drvdata(dev, indio_dev);
383dbdc025bSLars-Peter Clausen
384dbdc025bSLars-Peter Clausen st->chip_info = &ad5380_chip_info_tbl[type];
385dbdc025bSLars-Peter Clausen st->regmap = regmap;
386dbdc025bSLars-Peter Clausen
387dbdc025bSLars-Peter Clausen indio_dev->name = name;
388dbdc025bSLars-Peter Clausen indio_dev->info = &ad5380_info;
389dbdc025bSLars-Peter Clausen indio_dev->modes = INDIO_DIRECT_MODE;
390dbdc025bSLars-Peter Clausen indio_dev->num_channels = st->chip_info->num_channels;
391dbdc025bSLars-Peter Clausen
392fa444a18SSergiu Cuciurean mutex_init(&st->lock);
393fa444a18SSergiu Cuciurean
394dbdc025bSLars-Peter Clausen ret = ad5380_alloc_channels(indio_dev);
395dbdc025bSLars-Peter Clausen if (ret) {
396dbdc025bSLars-Peter Clausen dev_err(dev, "Failed to allocate channel spec: %d\n", ret);
397c815ad37SSachin Kamat return ret;
398dbdc025bSLars-Peter Clausen }
399dbdc025bSLars-Peter Clausen
4004f2c1885SLars-Peter Clausen if (st->chip_info->int_vref == 2500)
401dbdc025bSLars-Peter Clausen ctrl |= AD5380_CTRL_INT_VREF_2V5;
402dbdc025bSLars-Peter Clausen
403c815ad37SSachin Kamat st->vref_reg = devm_regulator_get(dev, "vref");
404dbdc025bSLars-Peter Clausen if (!IS_ERR(st->vref_reg)) {
405dbdc025bSLars-Peter Clausen ret = regulator_enable(st->vref_reg);
406dbdc025bSLars-Peter Clausen if (ret) {
407dbdc025bSLars-Peter Clausen dev_err(dev, "Failed to enable vref regulators: %d\n",
408dbdc025bSLars-Peter Clausen ret);
409dbdc025bSLars-Peter Clausen goto error_free_reg;
410dbdc025bSLars-Peter Clausen }
411dbdc025bSLars-Peter Clausen
412272cc9c8SAxel Lin ret = regulator_get_voltage(st->vref_reg);
413272cc9c8SAxel Lin if (ret < 0)
414272cc9c8SAxel Lin goto error_disable_reg;
415272cc9c8SAxel Lin
4164f2c1885SLars-Peter Clausen st->vref = ret / 1000;
417dbdc025bSLars-Peter Clausen } else {
418dbdc025bSLars-Peter Clausen st->vref = st->chip_info->int_vref;
419dbdc025bSLars-Peter Clausen ctrl |= AD5380_CTRL_INT_VREF_EN;
420dbdc025bSLars-Peter Clausen }
421dbdc025bSLars-Peter Clausen
422dbdc025bSLars-Peter Clausen ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl);
423dbdc025bSLars-Peter Clausen if (ret) {
424dbdc025bSLars-Peter Clausen dev_err(dev, "Failed to write to device: %d\n", ret);
425dbdc025bSLars-Peter Clausen goto error_disable_reg;
426dbdc025bSLars-Peter Clausen }
427dbdc025bSLars-Peter Clausen
428dbdc025bSLars-Peter Clausen ret = iio_device_register(indio_dev);
429dbdc025bSLars-Peter Clausen if (ret) {
430dbdc025bSLars-Peter Clausen dev_err(dev, "Failed to register iio device: %d\n", ret);
431dbdc025bSLars-Peter Clausen goto error_disable_reg;
432dbdc025bSLars-Peter Clausen }
433dbdc025bSLars-Peter Clausen
434dbdc025bSLars-Peter Clausen return 0;
435dbdc025bSLars-Peter Clausen
436dbdc025bSLars-Peter Clausen error_disable_reg:
437dbdc025bSLars-Peter Clausen if (!IS_ERR(st->vref_reg))
438dbdc025bSLars-Peter Clausen regulator_disable(st->vref_reg);
439dbdc025bSLars-Peter Clausen error_free_reg:
440dbdc025bSLars-Peter Clausen kfree(indio_dev->channels);
441dbdc025bSLars-Peter Clausen
442dbdc025bSLars-Peter Clausen return ret;
443dbdc025bSLars-Peter Clausen }
444dbdc025bSLars-Peter Clausen
ad5380_remove(struct device * dev)445d6220554SUwe Kleine-König static void ad5380_remove(struct device *dev)
446dbdc025bSLars-Peter Clausen {
447dbdc025bSLars-Peter Clausen struct iio_dev *indio_dev = dev_get_drvdata(dev);
448dbdc025bSLars-Peter Clausen struct ad5380_state *st = iio_priv(indio_dev);
449dbdc025bSLars-Peter Clausen
450dbdc025bSLars-Peter Clausen iio_device_unregister(indio_dev);
451dbdc025bSLars-Peter Clausen
452dbdc025bSLars-Peter Clausen kfree(indio_dev->channels);
453dbdc025bSLars-Peter Clausen
454d6220554SUwe Kleine-König if (!IS_ERR(st->vref_reg))
455dbdc025bSLars-Peter Clausen regulator_disable(st->vref_reg);
456dbdc025bSLars-Peter Clausen }
457dbdc025bSLars-Peter Clausen
ad5380_reg_false(struct device * dev,unsigned int reg)458dbdc025bSLars-Peter Clausen static bool ad5380_reg_false(struct device *dev, unsigned int reg)
459dbdc025bSLars-Peter Clausen {
460dbdc025bSLars-Peter Clausen return false;
461dbdc025bSLars-Peter Clausen }
462dbdc025bSLars-Peter Clausen
463dbdc025bSLars-Peter Clausen static const struct regmap_config ad5380_regmap_config = {
464dbdc025bSLars-Peter Clausen .reg_bits = 10,
465dbdc025bSLars-Peter Clausen .val_bits = 14,
466dbdc025bSLars-Peter Clausen
467dbdc025bSLars-Peter Clausen .max_register = AD5380_REG_DATA(40),
468dbdc025bSLars-Peter Clausen .cache_type = REGCACHE_RBTREE,
469dbdc025bSLars-Peter Clausen
470dbdc025bSLars-Peter Clausen .volatile_reg = ad5380_reg_false,
471dbdc025bSLars-Peter Clausen .readable_reg = ad5380_reg_false,
472dbdc025bSLars-Peter Clausen };
473dbdc025bSLars-Peter Clausen
474dbdc025bSLars-Peter Clausen #if IS_ENABLED(CONFIG_SPI_MASTER)
475dbdc025bSLars-Peter Clausen
ad5380_spi_probe(struct spi_device * spi)476fc52692cSGreg Kroah-Hartman static int ad5380_spi_probe(struct spi_device *spi)
477dbdc025bSLars-Peter Clausen {
478dbdc025bSLars-Peter Clausen const struct spi_device_id *id = spi_get_device_id(spi);
479dbdc025bSLars-Peter Clausen struct regmap *regmap;
480dbdc025bSLars-Peter Clausen
481d03fcfe6SAxel Lin regmap = devm_regmap_init_spi(spi, &ad5380_regmap_config);
482dbdc025bSLars-Peter Clausen
483dbdc025bSLars-Peter Clausen if (IS_ERR(regmap))
484dbdc025bSLars-Peter Clausen return PTR_ERR(regmap);
485dbdc025bSLars-Peter Clausen
486dbdc025bSLars-Peter Clausen return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name);
487dbdc025bSLars-Peter Clausen }
488dbdc025bSLars-Peter Clausen
ad5380_spi_remove(struct spi_device * spi)489a0386bbaSUwe Kleine-König static void ad5380_spi_remove(struct spi_device *spi)
490dbdc025bSLars-Peter Clausen {
491d6220554SUwe Kleine-König ad5380_remove(&spi->dev);
492dbdc025bSLars-Peter Clausen }
493dbdc025bSLars-Peter Clausen
494dbdc025bSLars-Peter Clausen static const struct spi_device_id ad5380_spi_ids[] = {
495dbdc025bSLars-Peter Clausen { "ad5380-3", ID_AD5380_3 },
496dbdc025bSLars-Peter Clausen { "ad5380-5", ID_AD5380_5 },
497dbdc025bSLars-Peter Clausen { "ad5381-3", ID_AD5381_3 },
498dbdc025bSLars-Peter Clausen { "ad5381-5", ID_AD5381_5 },
499dbdc025bSLars-Peter Clausen { "ad5382-3", ID_AD5382_3 },
500dbdc025bSLars-Peter Clausen { "ad5382-5", ID_AD5382_5 },
501dbdc025bSLars-Peter Clausen { "ad5383-3", ID_AD5383_3 },
502dbdc025bSLars-Peter Clausen { "ad5383-5", ID_AD5383_5 },
503dbdc025bSLars-Peter Clausen { "ad5384-3", ID_AD5380_3 },
504dbdc025bSLars-Peter Clausen { "ad5384-5", ID_AD5380_5 },
505dbdc025bSLars-Peter Clausen { "ad5390-3", ID_AD5390_3 },
506dbdc025bSLars-Peter Clausen { "ad5390-5", ID_AD5390_5 },
507dbdc025bSLars-Peter Clausen { "ad5391-3", ID_AD5391_3 },
508dbdc025bSLars-Peter Clausen { "ad5391-5", ID_AD5391_5 },
509dbdc025bSLars-Peter Clausen { "ad5392-3", ID_AD5392_3 },
510dbdc025bSLars-Peter Clausen { "ad5392-5", ID_AD5392_5 },
511dbdc025bSLars-Peter Clausen { }
512dbdc025bSLars-Peter Clausen };
513dbdc025bSLars-Peter Clausen MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
514dbdc025bSLars-Peter Clausen
515dbdc025bSLars-Peter Clausen static struct spi_driver ad5380_spi_driver = {
516dbdc025bSLars-Peter Clausen .driver = {
517dbdc025bSLars-Peter Clausen .name = "ad5380",
518dbdc025bSLars-Peter Clausen },
519dbdc025bSLars-Peter Clausen .probe = ad5380_spi_probe,
520fc52692cSGreg Kroah-Hartman .remove = ad5380_spi_remove,
521dbdc025bSLars-Peter Clausen .id_table = ad5380_spi_ids,
522dbdc025bSLars-Peter Clausen };
523dbdc025bSLars-Peter Clausen
ad5380_spi_register_driver(void)524dbdc025bSLars-Peter Clausen static inline int ad5380_spi_register_driver(void)
525dbdc025bSLars-Peter Clausen {
526dbdc025bSLars-Peter Clausen return spi_register_driver(&ad5380_spi_driver);
527dbdc025bSLars-Peter Clausen }
528dbdc025bSLars-Peter Clausen
ad5380_spi_unregister_driver(void)529dbdc025bSLars-Peter Clausen static inline void ad5380_spi_unregister_driver(void)
530dbdc025bSLars-Peter Clausen {
531dbdc025bSLars-Peter Clausen spi_unregister_driver(&ad5380_spi_driver);
532dbdc025bSLars-Peter Clausen }
533dbdc025bSLars-Peter Clausen
534dbdc025bSLars-Peter Clausen #else
535dbdc025bSLars-Peter Clausen
ad5380_spi_register_driver(void)536dbdc025bSLars-Peter Clausen static inline int ad5380_spi_register_driver(void)
537dbdc025bSLars-Peter Clausen {
538dbdc025bSLars-Peter Clausen return 0;
539dbdc025bSLars-Peter Clausen }
540dbdc025bSLars-Peter Clausen
ad5380_spi_unregister_driver(void)541dbdc025bSLars-Peter Clausen static inline void ad5380_spi_unregister_driver(void)
542dbdc025bSLars-Peter Clausen {
543dbdc025bSLars-Peter Clausen }
544dbdc025bSLars-Peter Clausen
545dbdc025bSLars-Peter Clausen #endif
546dbdc025bSLars-Peter Clausen
547dbdc025bSLars-Peter Clausen #if IS_ENABLED(CONFIG_I2C)
548dbdc025bSLars-Peter Clausen
ad5380_i2c_probe(struct i2c_client * i2c)5494b2b4370SUwe Kleine-König static int ad5380_i2c_probe(struct i2c_client *i2c)
550dbdc025bSLars-Peter Clausen {
5514b2b4370SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
552dbdc025bSLars-Peter Clausen struct regmap *regmap;
553dbdc025bSLars-Peter Clausen
554d03fcfe6SAxel Lin regmap = devm_regmap_init_i2c(i2c, &ad5380_regmap_config);
555dbdc025bSLars-Peter Clausen
556dbdc025bSLars-Peter Clausen if (IS_ERR(regmap))
557dbdc025bSLars-Peter Clausen return PTR_ERR(regmap);
558dbdc025bSLars-Peter Clausen
559dbdc025bSLars-Peter Clausen return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name);
560dbdc025bSLars-Peter Clausen }
561dbdc025bSLars-Peter Clausen
ad5380_i2c_remove(struct i2c_client * i2c)562ed5c2f5fSUwe Kleine-König static void ad5380_i2c_remove(struct i2c_client *i2c)
563dbdc025bSLars-Peter Clausen {
564d6220554SUwe Kleine-König ad5380_remove(&i2c->dev);
565dbdc025bSLars-Peter Clausen }
566dbdc025bSLars-Peter Clausen
567dbdc025bSLars-Peter Clausen static const struct i2c_device_id ad5380_i2c_ids[] = {
568dbdc025bSLars-Peter Clausen { "ad5380-3", ID_AD5380_3 },
569dbdc025bSLars-Peter Clausen { "ad5380-5", ID_AD5380_5 },
570dbdc025bSLars-Peter Clausen { "ad5381-3", ID_AD5381_3 },
571dbdc025bSLars-Peter Clausen { "ad5381-5", ID_AD5381_5 },
572dbdc025bSLars-Peter Clausen { "ad5382-3", ID_AD5382_3 },
573dbdc025bSLars-Peter Clausen { "ad5382-5", ID_AD5382_5 },
574dbdc025bSLars-Peter Clausen { "ad5383-3", ID_AD5383_3 },
575dbdc025bSLars-Peter Clausen { "ad5383-5", ID_AD5383_5 },
576dbdc025bSLars-Peter Clausen { "ad5384-3", ID_AD5380_3 },
577dbdc025bSLars-Peter Clausen { "ad5384-5", ID_AD5380_5 },
578dbdc025bSLars-Peter Clausen { "ad5390-3", ID_AD5390_3 },
579dbdc025bSLars-Peter Clausen { "ad5390-5", ID_AD5390_5 },
580dbdc025bSLars-Peter Clausen { "ad5391-3", ID_AD5391_3 },
581dbdc025bSLars-Peter Clausen { "ad5391-5", ID_AD5391_5 },
582dbdc025bSLars-Peter Clausen { "ad5392-3", ID_AD5392_3 },
583dbdc025bSLars-Peter Clausen { "ad5392-5", ID_AD5392_5 },
584dbdc025bSLars-Peter Clausen { }
585dbdc025bSLars-Peter Clausen };
586dbdc025bSLars-Peter Clausen MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids);
587dbdc025bSLars-Peter Clausen
588dbdc025bSLars-Peter Clausen static struct i2c_driver ad5380_i2c_driver = {
589dbdc025bSLars-Peter Clausen .driver = {
590dbdc025bSLars-Peter Clausen .name = "ad5380",
591dbdc025bSLars-Peter Clausen },
592*7cf15f42SUwe Kleine-König .probe = ad5380_i2c_probe,
593fc52692cSGreg Kroah-Hartman .remove = ad5380_i2c_remove,
594dbdc025bSLars-Peter Clausen .id_table = ad5380_i2c_ids,
595dbdc025bSLars-Peter Clausen };
596dbdc025bSLars-Peter Clausen
ad5380_i2c_register_driver(void)597dbdc025bSLars-Peter Clausen static inline int ad5380_i2c_register_driver(void)
598dbdc025bSLars-Peter Clausen {
599dbdc025bSLars-Peter Clausen return i2c_add_driver(&ad5380_i2c_driver);
600dbdc025bSLars-Peter Clausen }
601dbdc025bSLars-Peter Clausen
ad5380_i2c_unregister_driver(void)602dbdc025bSLars-Peter Clausen static inline void ad5380_i2c_unregister_driver(void)
603dbdc025bSLars-Peter Clausen {
604dbdc025bSLars-Peter Clausen i2c_del_driver(&ad5380_i2c_driver);
605dbdc025bSLars-Peter Clausen }
606dbdc025bSLars-Peter Clausen
607dbdc025bSLars-Peter Clausen #else
608dbdc025bSLars-Peter Clausen
ad5380_i2c_register_driver(void)609dbdc025bSLars-Peter Clausen static inline int ad5380_i2c_register_driver(void)
610dbdc025bSLars-Peter Clausen {
611dbdc025bSLars-Peter Clausen return 0;
612dbdc025bSLars-Peter Clausen }
613dbdc025bSLars-Peter Clausen
ad5380_i2c_unregister_driver(void)614dbdc025bSLars-Peter Clausen static inline void ad5380_i2c_unregister_driver(void)
615dbdc025bSLars-Peter Clausen {
616dbdc025bSLars-Peter Clausen }
617dbdc025bSLars-Peter Clausen
618dbdc025bSLars-Peter Clausen #endif
619dbdc025bSLars-Peter Clausen
ad5380_spi_init(void)620dbdc025bSLars-Peter Clausen static int __init ad5380_spi_init(void)
621dbdc025bSLars-Peter Clausen {
622dbdc025bSLars-Peter Clausen int ret;
623dbdc025bSLars-Peter Clausen
624dbdc025bSLars-Peter Clausen ret = ad5380_spi_register_driver();
625dbdc025bSLars-Peter Clausen if (ret)
626dbdc025bSLars-Peter Clausen return ret;
627dbdc025bSLars-Peter Clausen
628dbdc025bSLars-Peter Clausen ret = ad5380_i2c_register_driver();
629dbdc025bSLars-Peter Clausen if (ret) {
630dbdc025bSLars-Peter Clausen ad5380_spi_unregister_driver();
631dbdc025bSLars-Peter Clausen return ret;
632dbdc025bSLars-Peter Clausen }
633dbdc025bSLars-Peter Clausen
634dbdc025bSLars-Peter Clausen return 0;
635dbdc025bSLars-Peter Clausen }
636dbdc025bSLars-Peter Clausen module_init(ad5380_spi_init);
637dbdc025bSLars-Peter Clausen
ad5380_spi_exit(void)638dbdc025bSLars-Peter Clausen static void __exit ad5380_spi_exit(void)
639dbdc025bSLars-Peter Clausen {
640dbdc025bSLars-Peter Clausen ad5380_i2c_unregister_driver();
641dbdc025bSLars-Peter Clausen ad5380_spi_unregister_driver();
642dbdc025bSLars-Peter Clausen
643dbdc025bSLars-Peter Clausen }
644dbdc025bSLars-Peter Clausen module_exit(ad5380_spi_exit);
645dbdc025bSLars-Peter Clausen
646dbdc025bSLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
647dbdc025bSLars-Peter Clausen MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC");
648dbdc025bSLars-Peter Clausen MODULE_LICENSE("GPL v2");
649