]>
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, | |
f8347824 LPC |
164 | }; |
165 | ||
166 | static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { | |
167 | { | |
168 | .name = "powerdown", | |
169 | .read = ad7303_read_dac_powerdown, | |
170 | .write = ad7303_write_dac_powerdown, | |
3704432f | 171 | .shared = IIO_SEPARATE, |
f8347824 LPC |
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', \ | |
ce420fd4 PR |
186 | .realbits = 8, \ |
187 | .storagebits = 8, \ | |
188 | .shift = 0, \ | |
f8347824 LPC |
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 | ||
66e670aa | 206 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
f8347824 LPC |
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 | ||
66e670aa SK |
215 | st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd"); |
216 | if (IS_ERR(st->vdd_reg)) | |
217 | return PTR_ERR(st->vdd_reg); | |
f8347824 LPC |
218 | |
219 | ret = regulator_enable(st->vdd_reg); | |
220 | if (ret) | |
66e670aa | 221 | return ret; |
f8347824 LPC |
222 | |
223 | if (spi->dev.of_node) { | |
224 | ext_ref = of_property_read_bool(spi->dev.of_node, | |
225 | "REF-supply"); | |
226 | } else { | |
227 | struct ad7303_platform_data *pdata = spi->dev.platform_data; | |
228 | if (pdata && pdata->use_external_ref) | |
229 | ext_ref = true; | |
230 | else | |
231 | ext_ref = false; | |
232 | } | |
233 | ||
234 | if (ext_ref) { | |
66e670aa | 235 | st->vref_reg = devm_regulator_get(&spi->dev, "REF"); |
94fccb78 WY |
236 | if (IS_ERR(st->vref_reg)) { |
237 | ret = PTR_ERR(st->vref_reg); | |
f8347824 | 238 | goto err_disable_vdd_reg; |
94fccb78 | 239 | } |
f8347824 LPC |
240 | |
241 | ret = regulator_enable(st->vref_reg); | |
242 | if (ret) | |
66e670aa | 243 | goto err_disable_vdd_reg; |
f8347824 LPC |
244 | |
245 | st->config |= AD7303_CFG_EXTERNAL_VREF; | |
246 | } | |
247 | ||
248 | indio_dev->dev.parent = &spi->dev; | |
249 | indio_dev->name = id->name; | |
250 | indio_dev->info = &ad7303_info; | |
251 | indio_dev->modes = INDIO_DIRECT_MODE; | |
252 | indio_dev->channels = ad7303_channels; | |
253 | indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); | |
254 | ||
255 | ret = iio_device_register(indio_dev); | |
256 | if (ret) | |
257 | goto err_disable_vref_reg; | |
258 | ||
259 | return 0; | |
260 | ||
261 | err_disable_vref_reg: | |
262 | if (st->vref_reg) | |
263 | regulator_disable(st->vref_reg); | |
f8347824 LPC |
264 | err_disable_vdd_reg: |
265 | regulator_disable(st->vdd_reg); | |
f8347824 LPC |
266 | return ret; |
267 | } | |
268 | ||
269 | static int ad7303_remove(struct spi_device *spi) | |
270 | { | |
271 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
272 | struct ad7303_state *st = iio_priv(indio_dev); | |
273 | ||
274 | iio_device_unregister(indio_dev); | |
275 | ||
66e670aa | 276 | if (st->vref_reg) |
f8347824 | 277 | regulator_disable(st->vref_reg); |
f8347824 | 278 | regulator_disable(st->vdd_reg); |
f8347824 LPC |
279 | |
280 | return 0; | |
281 | } | |
282 | ||
1c00dcd3 JMC |
283 | static const struct of_device_id ad7303_spi_of_match[] = { |
284 | { .compatible = "adi,ad7303", }, | |
285 | { /* sentinel */ }, | |
286 | }; | |
287 | MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); | |
288 | ||
f8347824 LPC |
289 | static const struct spi_device_id ad7303_spi_ids[] = { |
290 | { "ad7303", 0 }, | |
291 | {} | |
292 | }; | |
293 | MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); | |
294 | ||
295 | static struct spi_driver ad7303_driver = { | |
296 | .driver = { | |
297 | .name = "ad7303", | |
1c00dcd3 | 298 | .of_match_table = of_match_ptr(ad7303_spi_of_match), |
f8347824 LPC |
299 | }, |
300 | .probe = ad7303_probe, | |
301 | .remove = ad7303_remove, | |
302 | .id_table = ad7303_spi_ids, | |
303 | }; | |
304 | module_spi_driver(ad7303_driver); | |
305 | ||
306 | MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>"); | |
307 | MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver"); | |
308 | MODULE_LICENSE("GPL v2"); |