Commit | Line | Data |
---|---|---|
f8347824 LPC |
1 | /* |
2 | * AD7303 Digital to analog converters driver | |
3 | * | |
4 | * Copyright 2013 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2. | |
7 | */ | |
8 | ||
9 | #include <linux/err.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/spi/spi.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/sysfs.h> | |
15 | #include <linux/regulator/consumer.h> | |
16 | #include <linux/of.h> | |
17 | ||
18 | #include <linux/iio/iio.h> | |
19 | #include <linux/iio/sysfs.h> | |
20 | ||
21 | #include <linux/platform_data/ad7303.h> | |
22 | ||
23 | #define AD7303_CFG_EXTERNAL_VREF BIT(15) | |
24 | #define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch)) | |
25 | #define AD7303_CFG_ADDR_OFFSET 10 | |
26 | ||
27 | #define AD7303_CMD_UPDATE_DAC (0x3 << 8) | |
28 | ||
29 | /** | |
30 | * struct ad7303_state - driver instance specific data | |
31 | * @spi: the device for this driver instance | |
32 | * @config: cached config register value | |
33 | * @dac_cache: current DAC raw value (chip does not support readback) | |
34 | * @data: spi transfer buffer | |
35 | */ | |
36 | ||
37 | struct ad7303_state { | |
38 | struct spi_device *spi; | |
39 | uint16_t config; | |
40 | uint8_t dac_cache[2]; | |
41 | ||
42 | struct regulator *vdd_reg; | |
43 | struct regulator *vref_reg; | |
44 | ||
45 | /* | |
46 | * DMA (thus cache coherency maintenance) requires the | |
47 | * transfer buffers to live in their own cache lines. | |
48 | */ | |
49 | __be16 data ____cacheline_aligned; | |
50 | }; | |
51 | ||
52 | static int ad7303_write(struct ad7303_state *st, unsigned int chan, | |
53 | uint8_t val) | |
54 | { | |
55 | st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC | | |
56 | (chan << AD7303_CFG_ADDR_OFFSET) | | |
57 | st->config | val); | |
58 | ||
59 | return spi_write(st->spi, &st->data, sizeof(st->data)); | |
60 | } | |
61 | ||
62 | static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev, | |
63 | uintptr_t private, const struct iio_chan_spec *chan, char *buf) | |
64 | { | |
65 | struct ad7303_state *st = iio_priv(indio_dev); | |
66 | ||
67 | return sprintf(buf, "%d\n", (bool)(st->config & | |
68 | AD7303_CFG_POWER_DOWN(chan->channel))); | |
69 | } | |
70 | ||
71 | static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, | |
72 | uintptr_t private, const struct iio_chan_spec *chan, const char *buf, | |
73 | size_t len) | |
74 | { | |
75 | struct ad7303_state *st = iio_priv(indio_dev); | |
76 | bool pwr_down; | |
77 | int ret; | |
78 | ||
79 | ret = strtobool(buf, &pwr_down); | |
80 | if (ret) | |
81 | return ret; | |
82 | ||
83 | mutex_lock(&indio_dev->mlock); | |
84 | ||
85 | if (pwr_down) | |
86 | st->config |= AD7303_CFG_POWER_DOWN(chan->channel); | |
87 | else | |
88 | st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel); | |
89 | ||
90 | /* There is no noop cmd which allows us to only update the powerdown | |
91 | * mode, so just write one of the DAC channels again */ | |
92 | ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); | |
93 | ||
94 | mutex_unlock(&indio_dev->mlock); | |
95 | return ret ? ret : len; | |
96 | } | |
97 | ||
98 | static int ad7303_get_vref(struct ad7303_state *st, | |
99 | struct iio_chan_spec const *chan) | |
100 | { | |
101 | int ret; | |
102 | ||
103 | if (st->config & AD7303_CFG_EXTERNAL_VREF) | |
104 | return regulator_get_voltage(st->vref_reg); | |
105 | ||
106 | ret = regulator_get_voltage(st->vdd_reg); | |
107 | if (ret < 0) | |
108 | return ret; | |
109 | return ret / 2; | |
110 | } | |
111 | ||
112 | static int ad7303_read_raw(struct iio_dev *indio_dev, | |
113 | struct iio_chan_spec const *chan, int *val, int *val2, long info) | |
114 | { | |
115 | struct ad7303_state *st = iio_priv(indio_dev); | |
116 | int vref_uv; | |
117 | ||
118 | switch (info) { | |
119 | case IIO_CHAN_INFO_RAW: | |
120 | *val = st->dac_cache[chan->channel]; | |
121 | return IIO_VAL_INT; | |
122 | case IIO_CHAN_INFO_SCALE: | |
123 | vref_uv = ad7303_get_vref(st, chan); | |
124 | if (vref_uv < 0) | |
125 | return vref_uv; | |
126 | ||
127 | *val = 2 * vref_uv / 1000; | |
128 | *val2 = chan->scan_type.realbits; | |
129 | ||
130 | return IIO_VAL_FRACTIONAL_LOG2; | |
131 | default: | |
132 | break; | |
133 | } | |
134 | return -EINVAL; | |
135 | } | |
136 | ||
137 | static int ad7303_write_raw(struct iio_dev *indio_dev, | |
138 | struct iio_chan_spec const *chan, int val, int val2, long mask) | |
139 | { | |
140 | struct ad7303_state *st = iio_priv(indio_dev); | |
141 | int ret; | |
142 | ||
143 | switch (mask) { | |
144 | case IIO_CHAN_INFO_RAW: | |
145 | if (val >= (1 << chan->scan_type.realbits) || val < 0) | |
146 | return -EINVAL; | |
147 | ||
148 | mutex_lock(&indio_dev->mlock); | |
149 | ret = ad7303_write(st, chan->address, val); | |
150 | if (ret == 0) | |
151 | st->dac_cache[chan->channel] = val; | |
152 | mutex_unlock(&indio_dev->mlock); | |
153 | break; | |
154 | default: | |
155 | ret = -EINVAL; | |
156 | } | |
157 | ||
158 | return ret; | |
159 | } | |
160 | ||
161 | static const struct iio_info ad7303_info = { | |
162 | .read_raw = ad7303_read_raw, | |
163 | .write_raw = ad7303_write_raw, | |
164 | .driver_module = THIS_MODULE, | |
165 | }; | |
166 | ||
167 | static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { | |
168 | { | |
169 | .name = "powerdown", | |
170 | .read = ad7303_read_dac_powerdown, | |
171 | .write = ad7303_write_dac_powerdown, | |
172 | }, | |
173 | { }, | |
174 | }; | |
175 | ||
176 | #define AD7303_CHANNEL(chan) { \ | |
177 | .type = IIO_VOLTAGE, \ | |
178 | .indexed = 1, \ | |
179 | .output = 1, \ | |
180 | .channel = (chan), \ | |
181 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
182 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
183 | .address = (chan), \ | |
184 | .scan_type = { \ | |
185 | .sign = 'u', \ | |
186 | .realbits = '8', \ | |
187 | .storagebits = '8', \ | |
188 | .shift = '0', \ | |
189 | }, \ | |
190 | .ext_info = ad7303_ext_info, \ | |
191 | } | |
192 | ||
193 | static const struct iio_chan_spec ad7303_channels[] = { | |
194 | AD7303_CHANNEL(0), | |
195 | AD7303_CHANNEL(1), | |
196 | }; | |
197 | ||
198 | static int ad7303_probe(struct spi_device *spi) | |
199 | { | |
200 | const struct spi_device_id *id = spi_get_device_id(spi); | |
201 | struct iio_dev *indio_dev; | |
202 | struct ad7303_state *st; | |
203 | bool ext_ref; | |
204 | int ret; | |
205 | ||
206 | indio_dev = iio_device_alloc(sizeof(*st)); | |
207 | if (indio_dev == NULL) | |
208 | return -ENOMEM; | |
209 | ||
210 | st = iio_priv(indio_dev); | |
211 | spi_set_drvdata(spi, indio_dev); | |
212 | ||
213 | st->spi = spi; | |
214 | ||
215 | st->vdd_reg = regulator_get(&spi->dev, "Vdd"); | |
216 | if (IS_ERR(st->vdd_reg)) { | |
217 | ret = PTR_ERR(st->vdd_reg); | |
218 | goto err_free; | |
219 | } | |
220 | ||
221 | ret = regulator_enable(st->vdd_reg); | |
222 | if (ret) | |
223 | goto err_put_vdd_reg; | |
224 | ||
225 | if (spi->dev.of_node) { | |
226 | ext_ref = of_property_read_bool(spi->dev.of_node, | |
227 | "REF-supply"); | |
228 | } else { | |
229 | struct ad7303_platform_data *pdata = spi->dev.platform_data; | |
230 | if (pdata && pdata->use_external_ref) | |
231 | ext_ref = true; | |
232 | else | |
233 | ext_ref = false; | |
234 | } | |
235 | ||
236 | if (ext_ref) { | |
237 | st->vref_reg = regulator_get(&spi->dev, "REF"); | |
94fccb78 WY |
238 | if (IS_ERR(st->vref_reg)) { |
239 | ret = PTR_ERR(st->vref_reg); | |
f8347824 | 240 | goto err_disable_vdd_reg; |
94fccb78 | 241 | } |
f8347824 LPC |
242 | |
243 | ret = regulator_enable(st->vref_reg); | |
244 | if (ret) | |
245 | goto err_put_vref_reg; | |
246 | ||
247 | st->config |= AD7303_CFG_EXTERNAL_VREF; | |
248 | } | |
249 | ||
250 | indio_dev->dev.parent = &spi->dev; | |
251 | indio_dev->name = id->name; | |
252 | indio_dev->info = &ad7303_info; | |
253 | indio_dev->modes = INDIO_DIRECT_MODE; | |
254 | indio_dev->channels = ad7303_channels; | |
255 | indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); | |
256 | ||
257 | ret = iio_device_register(indio_dev); | |
258 | if (ret) | |
259 | goto err_disable_vref_reg; | |
260 | ||
261 | return 0; | |
262 | ||
263 | err_disable_vref_reg: | |
264 | if (st->vref_reg) | |
265 | regulator_disable(st->vref_reg); | |
266 | err_put_vref_reg: | |
267 | if (st->vref_reg) | |
268 | regulator_put(st->vref_reg); | |
269 | err_disable_vdd_reg: | |
270 | regulator_disable(st->vdd_reg); | |
271 | err_put_vdd_reg: | |
272 | regulator_put(st->vdd_reg); | |
273 | err_free: | |
274 | iio_device_free(indio_dev); | |
275 | ||
276 | return ret; | |
277 | } | |
278 | ||
279 | static int ad7303_remove(struct spi_device *spi) | |
280 | { | |
281 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
282 | struct ad7303_state *st = iio_priv(indio_dev); | |
283 | ||
284 | iio_device_unregister(indio_dev); | |
285 | ||
286 | if (st->vref_reg) { | |
287 | regulator_disable(st->vref_reg); | |
288 | regulator_put(st->vref_reg); | |
289 | } | |
290 | regulator_disable(st->vdd_reg); | |
291 | regulator_put(st->vdd_reg); | |
292 | ||
293 | iio_device_free(indio_dev); | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static const struct spi_device_id ad7303_spi_ids[] = { | |
299 | { "ad7303", 0 }, | |
300 | {} | |
301 | }; | |
302 | MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); | |
303 | ||
304 | static struct spi_driver ad7303_driver = { | |
305 | .driver = { | |
306 | .name = "ad7303", | |
307 | .owner = THIS_MODULE, | |
308 | }, | |
309 | .probe = ad7303_probe, | |
310 | .remove = ad7303_remove, | |
311 | .id_table = ad7303_spi_ids, | |
312 | }; | |
313 | module_spi_driver(ad7303_driver); | |
314 | ||
315 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | |
316 | MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver"); | |
317 | MODULE_LICENSE("GPL v2"); |