diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2013-06-11 12:56:00 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2013-06-11 15:25:58 -0400 |
commit | f83478240e742efe1103110c28d48cc2b4dcee5c (patch) | |
tree | 0a44961fa6e7c54e2d08610a2137d2d04e0ba21d /drivers/iio | |
parent | e764df67963940b4123325710536a9471d1e24ae (diff) |
iio:dac: Add support for the AD7303
This patch adds support for the AD7303. The AD7303 is a simple 2 channel 8 bit
DAC with an SPI interface.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/dac/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/dac/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/dac/ad7303.c | 315 |
3 files changed, 326 insertions, 0 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b61160bd935e..c9c33ce32d3a 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig | |||
@@ -130,6 +130,16 @@ config AD5686 | |||
130 | To compile this driver as a module, choose M here: the | 130 | To compile this driver as a module, choose M here: the |
131 | module will be called ad5686. | 131 | module will be called ad5686. |
132 | 132 | ||
133 | config AD7303 | ||
134 | tristate "Analog Devices Analog Devices AD7303 DAC driver" | ||
135 | depends on SPI | ||
136 | help | ||
137 | Say yes here to build support for Analog Devices AD7303 Digital to Analog | ||
138 | Converters (DAC). | ||
139 | |||
140 | To compile this driver as module choose M here: the module will be called | ||
141 | ad7303. | ||
142 | |||
133 | config MAX517 | 143 | config MAX517 |
134 | tristate "Maxim MAX517/518/519 DAC driver" | 144 | tristate "Maxim MAX517/518/519 DAC driver" |
135 | depends on I2C | 145 | depends on I2C |
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 5b528ebb3343..c8d7ab6bff01 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile | |||
@@ -14,5 +14,6 @@ obj-$(CONFIG_AD5755) += ad5755.o | |||
14 | obj-$(CONFIG_AD5764) += ad5764.o | 14 | obj-$(CONFIG_AD5764) += ad5764.o |
15 | obj-$(CONFIG_AD5791) += ad5791.o | 15 | obj-$(CONFIG_AD5791) += ad5791.o |
16 | obj-$(CONFIG_AD5686) += ad5686.o | 16 | obj-$(CONFIG_AD5686) += ad5686.o |
17 | obj-$(CONFIG_AD7303) += ad7303.o | ||
17 | obj-$(CONFIG_MAX517) += max517.o | 18 | obj-$(CONFIG_MAX517) += max517.o |
18 | obj-$(CONFIG_MCP4725) += mcp4725.o | 19 | obj-$(CONFIG_MCP4725) += mcp4725.o |
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c new file mode 100644 index 000000000000..85aeef60dc5f --- /dev/null +++ b/drivers/iio/dac/ad7303.c | |||
@@ -0,0 +1,315 @@ | |||
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"); | ||
238 | if (IS_ERR(st->vref_reg)) | ||
239 | goto err_disable_vdd_reg; | ||
240 | |||
241 | ret = regulator_enable(st->vref_reg); | ||
242 | if (ret) | ||
243 | goto err_put_vref_reg; | ||
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); | ||
264 | err_put_vref_reg: | ||
265 | if (st->vref_reg) | ||
266 | regulator_put(st->vref_reg); | ||
267 | err_disable_vdd_reg: | ||
268 | regulator_disable(st->vdd_reg); | ||
269 | err_put_vdd_reg: | ||
270 | regulator_put(st->vdd_reg); | ||
271 | err_free: | ||
272 | iio_device_free(indio_dev); | ||
273 | |||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | static int ad7303_remove(struct spi_device *spi) | ||
278 | { | ||
279 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
280 | struct ad7303_state *st = iio_priv(indio_dev); | ||
281 | |||
282 | iio_device_unregister(indio_dev); | ||
283 | |||
284 | if (st->vref_reg) { | ||
285 | regulator_disable(st->vref_reg); | ||
286 | regulator_put(st->vref_reg); | ||
287 | } | ||
288 | regulator_disable(st->vdd_reg); | ||
289 | regulator_put(st->vdd_reg); | ||
290 | |||
291 | iio_device_free(indio_dev); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static const struct spi_device_id ad7303_spi_ids[] = { | ||
297 | { "ad7303", 0 }, | ||
298 | {} | ||
299 | }; | ||
300 | MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); | ||
301 | |||
302 | static struct spi_driver ad7303_driver = { | ||
303 | .driver = { | ||
304 | .name = "ad7303", | ||
305 | .owner = THIS_MODULE, | ||
306 | }, | ||
307 | .probe = ad7303_probe, | ||
308 | .remove = ad7303_remove, | ||
309 | .id_table = ad7303_spi_ids, | ||
310 | }; | ||
311 | module_spi_driver(ad7303_driver); | ||
312 | |||
313 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
314 | MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver"); | ||
315 | MODULE_LICENSE("GPL v2"); | ||