Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
26static const char * const ad5446_powerdown_modes[] = {
27 "1kohm_to_gnd", "100kohm_to_gnd", "three_state"
28};
29
30static 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
41static 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
49static 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
56static 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
66static 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
98const 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};
109EXPORT_SYMBOL_NS_GPL(ad5446_ext_info_powerdown, "IIO_AD5446");
110
111static 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
131static 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
150static 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
162static const struct iio_info ad5446_info = {
163 .read_raw = ad5446_read_raw,
164 .write_raw = ad5446_write_raw,
165};
166
167int 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}
210EXPORT_SYMBOL_NS_GPL(ad5446_probe, "IIO_AD5446");
211
212MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
213MODULE_DESCRIPTION("Analog Devices CORE AD5446 DAC and similar devices");
214MODULE_LICENSE("GPL v2");