xref: /linux/drivers/iio/dac/ad5446.c (revision 83bd89291f5cc866f60d32c34e268896c7ba8a3d)
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