1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * AD5446 CORE DAC driver
4 *
5 * Copyright 2010 Analog Devices Inc.
6 */
7
8 #include <linux/array_size.h>
9 #include <linux/cleanup.h>
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/export.h>
13 #include <linux/iio/iio.h>
14 #include <linux/kstrtox.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/sysfs.h>
19
20 #include "ad5446.h"
21
22 #define MODE_PWRDWN_1k 0x1
23 #define MODE_PWRDWN_100k 0x2
24 #define MODE_PWRDWN_TRISTATE 0x3
25
26 static const char * const ad5446_powerdown_modes[] = {
27 "1kohm_to_gnd", "100kohm_to_gnd", "three_state"
28 };
29
ad5446_set_powerdown_mode(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,unsigned int mode)30 static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev,
31 const struct iio_chan_spec *chan,
32 unsigned int mode)
33 {
34 struct ad5446_state *st = iio_priv(indio_dev);
35
36 st->pwr_down_mode = mode + 1;
37
38 return 0;
39 }
40
ad5446_get_powerdown_mode(struct iio_dev * indio_dev,const struct iio_chan_spec * chan)41 static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev,
42 const struct iio_chan_spec *chan)
43 {
44 struct ad5446_state *st = iio_priv(indio_dev);
45
46 return st->pwr_down_mode - 1;
47 }
48
49 static const struct iio_enum ad5446_powerdown_mode_enum = {
50 .items = ad5446_powerdown_modes,
51 .num_items = ARRAY_SIZE(ad5446_powerdown_modes),
52 .get = ad5446_get_powerdown_mode,
53 .set = ad5446_set_powerdown_mode,
54 };
55
ad5446_read_dac_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,char * buf)56 static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev,
57 uintptr_t private,
58 const struct iio_chan_spec *chan,
59 char *buf)
60 {
61 struct ad5446_state *st = iio_priv(indio_dev);
62
63 return sysfs_emit(buf, "%d\n", st->pwr_down);
64 }
65
ad5446_write_dac_powerdown(struct iio_dev * indio_dev,uintptr_t private,const struct iio_chan_spec * chan,const char * buf,size_t len)66 static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
67 uintptr_t private,
68 const struct iio_chan_spec *chan,
69 const char *buf, size_t len)
70 {
71 struct ad5446_state *st = iio_priv(indio_dev);
72 unsigned int shift;
73 unsigned int val;
74 bool powerdown;
75 int ret;
76
77 ret = kstrtobool(buf, &powerdown);
78 if (ret)
79 return ret;
80
81 guard(mutex)(&st->lock);
82 st->pwr_down = powerdown;
83
84 if (st->pwr_down) {
85 shift = chan->scan_type.realbits + chan->scan_type.shift;
86 val = st->pwr_down_mode << shift;
87 } else {
88 val = st->cached_val;
89 }
90
91 ret = st->chip_info->write(st, val);
92 if (ret)
93 return ret;
94
95 return len;
96 }
97
98 const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
99 {
100 .name = "powerdown",
101 .read = ad5446_read_dac_powerdown,
102 .write = ad5446_write_dac_powerdown,
103 .shared = IIO_SEPARATE,
104 },
105 IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum),
106 IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5446_powerdown_mode_enum),
107 { }
108 };
109 EXPORT_SYMBOL_NS_GPL(ad5446_ext_info_powerdown, "IIO_AD5446");
110
ad5446_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long m)111 static int ad5446_read_raw(struct iio_dev *indio_dev,
112 struct iio_chan_spec const *chan,
113 int *val,
114 int *val2,
115 long m)
116 {
117 struct ad5446_state *st = iio_priv(indio_dev);
118
119 switch (m) {
120 case IIO_CHAN_INFO_RAW:
121 *val = st->cached_val >> chan->scan_type.shift;
122 return IIO_VAL_INT;
123 case IIO_CHAN_INFO_SCALE:
124 *val = st->vref_mv;
125 *val2 = chan->scan_type.realbits;
126 return IIO_VAL_FRACTIONAL_LOG2;
127 }
128 return -EINVAL;
129 }
130
ad5446_write_dac_raw(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,int val)131 static int ad5446_write_dac_raw(struct iio_dev *indio_dev,
132 const struct iio_chan_spec *chan,
133 int val)
134 {
135 struct ad5446_state *st = iio_priv(indio_dev);
136
137 if (val >= (1 << chan->scan_type.realbits) || val < 0)
138 return -EINVAL;
139
140 val <<= chan->scan_type.shift;
141 guard(mutex)(&st->lock);
142
143 st->cached_val = val;
144 if (st->pwr_down)
145 return 0;
146
147 return st->chip_info->write(st, val);
148 }
149
ad5446_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)150 static int ad5446_write_raw(struct iio_dev *indio_dev,
151 struct iio_chan_spec const *chan, int val,
152 int val2, long mask)
153 {
154 switch (mask) {
155 case IIO_CHAN_INFO_RAW:
156 return ad5446_write_dac_raw(indio_dev, chan, val);
157 default:
158 return -EINVAL;
159 }
160 }
161
162 static const struct iio_info ad5446_info = {
163 .read_raw = ad5446_read_raw,
164 .write_raw = ad5446_write_raw,
165 };
166
ad5446_probe(struct device * dev,const char * name,const struct ad5446_chip_info * chip_info)167 int ad5446_probe(struct device *dev, const char *name,
168 const struct ad5446_chip_info *chip_info)
169 {
170 struct ad5446_state *st;
171 struct iio_dev *indio_dev;
172 int ret;
173
174 indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
175 if (!indio_dev)
176 return -ENOMEM;
177
178 st = iio_priv(indio_dev);
179 st->chip_info = chip_info;
180
181 st->dev = dev;
182
183 indio_dev->name = name;
184 indio_dev->info = &ad5446_info;
185 indio_dev->modes = INDIO_DIRECT_MODE;
186 indio_dev->channels = &st->chip_info->channel;
187 indio_dev->num_channels = 1;
188
189 ret = devm_mutex_init(dev, &st->lock);
190 if (ret)
191 return ret;
192
193 st->pwr_down_mode = MODE_PWRDWN_1k;
194
195 ret = devm_regulator_get_enable_read_voltage(dev, "vcc");
196 if (ret < 0 && ret != -ENODEV)
197 return ret;
198 if (ret == -ENODEV) {
199 if (!chip_info->int_vref_mv)
200 return dev_err_probe(dev, ret,
201 "reference voltage unspecified\n");
202
203 st->vref_mv = chip_info->int_vref_mv;
204 } else {
205 st->vref_mv = ret / 1000;
206 }
207
208 return devm_iio_device_register(dev, indio_dev);
209 }
210 EXPORT_SYMBOL_NS_GPL(ad5446_probe, "IIO_AD5446");
211
212 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
213 MODULE_DESCRIPTION("Analog Devices CORE AD5446 DAC and similar devices");
214 MODULE_LICENSE("GPL v2");
215