]>
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); | |
a04cf55a | 95 | return len; |
f8347824 LPC |
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, | |
3704432f | 172 | .shared = IIO_SEPARATE, |
f8347824 LPC |
173 | }, |
174 | { }, | |
175 | }; | |
176 | ||
177 | #define AD7303_CHANNEL(chan) { \ | |
178 | .type = IIO_VOLTAGE, \ | |
179 | .indexed = 1, \ | |
180 | .output = 1, \ | |
181 | .channel = (chan), \ | |
182 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
183 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
184 | .address = (chan), \ | |
185 | .scan_type = { \ | |
186 | .sign = 'u', \ | |
ce420fd4 PR |
187 | .realbits = 8, \ |
188 | .storagebits = 8, \ | |
189 | .shift = 0, \ | |
f8347824 LPC |
190 | }, \ |
191 | .ext_info = ad7303_ext_info, \ | |
192 | } | |
193 | ||
194 | static const struct iio_chan_spec ad7303_channels[] = { | |
195 | AD7303_CHANNEL(0), | |
196 | AD7303_CHANNEL(1), | |
197 | }; | |
198 | ||
199 | static int ad7303_probe(struct spi_device *spi) | |
200 | { | |
201 | const struct spi_device_id *id = spi_get_device_id(spi); | |
202 | struct iio_dev *indio_dev; | |
203 | struct ad7303_state *st; | |
204 | bool ext_ref; | |
205 | int ret; | |
206 | ||
66e670aa | 207 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
f8347824 LPC |
208 | if (indio_dev == NULL) |
209 | return -ENOMEM; | |
210 | ||
211 | st = iio_priv(indio_dev); | |
212 | spi_set_drvdata(spi, indio_dev); | |
213 | ||
214 | st->spi = spi; | |
215 | ||
66e670aa SK |
216 | st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd"); |
217 | if (IS_ERR(st->vdd_reg)) | |
218 | return PTR_ERR(st->vdd_reg); | |
f8347824 LPC |
219 | |
220 | ret = regulator_enable(st->vdd_reg); | |
221 | if (ret) | |
66e670aa | 222 | return ret; |
f8347824 LPC |
223 | |
224 | if (spi->dev.of_node) { | |
225 | ext_ref = of_property_read_bool(spi->dev.of_node, | |
226 | "REF-supply"); | |
227 | } else { | |
228 | struct ad7303_platform_data *pdata = spi->dev.platform_data; | |
229 | if (pdata && pdata->use_external_ref) | |
230 | ext_ref = true; | |
231 | else | |
232 | ext_ref = false; | |
233 | } | |
234 | ||
235 | if (ext_ref) { | |
66e670aa | 236 | st->vref_reg = devm_regulator_get(&spi->dev, "REF"); |
94fccb78 WY |
237 | if (IS_ERR(st->vref_reg)) { |
238 | ret = PTR_ERR(st->vref_reg); | |
f8347824 | 239 | goto err_disable_vdd_reg; |
94fccb78 | 240 | } |
f8347824 LPC |
241 | |
242 | ret = regulator_enable(st->vref_reg); | |
243 | if (ret) | |
66e670aa | 244 | goto err_disable_vdd_reg; |
f8347824 LPC |
245 | |
246 | st->config |= AD7303_CFG_EXTERNAL_VREF; | |
247 | } | |
248 | ||
249 | indio_dev->dev.parent = &spi->dev; | |
250 | indio_dev->name = id->name; | |
251 | indio_dev->info = &ad7303_info; | |
252 | indio_dev->modes = INDIO_DIRECT_MODE; | |
253 | indio_dev->channels = ad7303_channels; | |
254 | indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); | |
255 | ||
256 | ret = iio_device_register(indio_dev); | |
257 | if (ret) | |
258 | goto err_disable_vref_reg; | |
259 | ||
260 | return 0; | |
261 | ||
262 | err_disable_vref_reg: | |
263 | if (st->vref_reg) | |
264 | regulator_disable(st->vref_reg); | |
f8347824 LPC |
265 | err_disable_vdd_reg: |
266 | regulator_disable(st->vdd_reg); | |
f8347824 LPC |
267 | return ret; |
268 | } | |
269 | ||
270 | static int ad7303_remove(struct spi_device *spi) | |
271 | { | |
272 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
273 | struct ad7303_state *st = iio_priv(indio_dev); | |
274 | ||
275 | iio_device_unregister(indio_dev); | |
276 | ||
66e670aa | 277 | if (st->vref_reg) |
f8347824 | 278 | regulator_disable(st->vref_reg); |
f8347824 | 279 | regulator_disable(st->vdd_reg); |
f8347824 LPC |
280 | |
281 | return 0; | |
282 | } | |
283 | ||
1c00dcd3 JMC |
284 | static const struct of_device_id ad7303_spi_of_match[] = { |
285 | { .compatible = "adi,ad7303", }, | |
286 | { /* sentinel */ }, | |
287 | }; | |
288 | MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); | |
289 | ||
f8347824 LPC |
290 | static const struct spi_device_id ad7303_spi_ids[] = { |
291 | { "ad7303", 0 }, | |
292 | {} | |
293 | }; | |
294 | MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); | |
295 | ||
296 | static struct spi_driver ad7303_driver = { | |
297 | .driver = { | |
298 | .name = "ad7303", | |
1c00dcd3 | 299 | .of_match_table = of_match_ptr(ad7303_spi_of_match), |
f8347824 LPC |
300 | }, |
301 | .probe = ad7303_probe, | |
302 | .remove = ad7303_remove, | |
303 | .id_table = ad7303_spi_ids, | |
304 | }; | |
305 | module_spi_driver(ad7303_driver); | |
306 | ||
307 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | |
308 | MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver"); | |
309 | MODULE_LICENSE("GPL v2"); |