]>
Commit | Line | Data |
---|---|---|
7f270bc9 GGM |
1 | /* |
2 | * IIO DAC driver for Analog Devices AD8801 DAC | |
3 | * | |
4 | * Copyright (C) 2016 Gwenhael Goavec-Merou | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License, version 2, as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * General Public License for more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/iio/iio.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/regulator/consumer.h> | |
19 | #include <linux/spi/spi.h> | |
20 | #include <linux/sysfs.h> | |
21 | ||
22 | #define AD8801_CFG_ADDR_OFFSET 8 | |
23 | ||
24 | enum ad8801_device_ids { | |
25 | ID_AD8801, | |
26 | ID_AD8803, | |
27 | }; | |
28 | ||
29 | struct ad8801_state { | |
30 | struct spi_device *spi; | |
31 | unsigned char dac_cache[8]; /* Value write on each channel */ | |
32 | unsigned int vrefh_mv; | |
33 | unsigned int vrefl_mv; | |
34 | struct regulator *vrefh_reg; | |
35 | struct regulator *vrefl_reg; | |
36 | ||
37 | __be16 data ____cacheline_aligned; | |
38 | }; | |
39 | ||
40 | static int ad8801_spi_write(struct ad8801_state *state, | |
41 | u8 channel, unsigned char value) | |
42 | { | |
43 | state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value); | |
44 | return spi_write(state->spi, &state->data, sizeof(state->data)); | |
45 | } | |
46 | ||
47 | static int ad8801_write_raw(struct iio_dev *indio_dev, | |
48 | struct iio_chan_spec const *chan, int val, int val2, long mask) | |
49 | { | |
50 | struct ad8801_state *state = iio_priv(indio_dev); | |
51 | int ret; | |
52 | ||
53 | switch (mask) { | |
54 | case IIO_CHAN_INFO_RAW: | |
55 | if (val >= 256 || val < 0) | |
56 | return -EINVAL; | |
57 | ||
58 | ret = ad8801_spi_write(state, chan->channel, val); | |
59 | if (ret == 0) | |
60 | state->dac_cache[chan->channel] = val; | |
61 | break; | |
62 | default: | |
63 | ret = -EINVAL; | |
64 | } | |
65 | ||
66 | return ret; | |
67 | } | |
68 | ||
69 | static int ad8801_read_raw(struct iio_dev *indio_dev, | |
70 | struct iio_chan_spec const *chan, int *val, int *val2, long info) | |
71 | { | |
72 | struct ad8801_state *state = iio_priv(indio_dev); | |
73 | ||
74 | switch (info) { | |
75 | case IIO_CHAN_INFO_RAW: | |
76 | *val = state->dac_cache[chan->channel]; | |
77 | return IIO_VAL_INT; | |
78 | case IIO_CHAN_INFO_SCALE: | |
79 | *val = state->vrefh_mv - state->vrefl_mv; | |
80 | *val2 = 8; | |
81 | return IIO_VAL_FRACTIONAL_LOG2; | |
82 | case IIO_CHAN_INFO_OFFSET: | |
83 | *val = state->vrefl_mv; | |
84 | return IIO_VAL_INT; | |
85 | default: | |
86 | return -EINVAL; | |
87 | } | |
88 | ||
89 | return -EINVAL; | |
90 | } | |
91 | ||
92 | static const struct iio_info ad8801_info = { | |
93 | .read_raw = ad8801_read_raw, | |
94 | .write_raw = ad8801_write_raw, | |
7f270bc9 GGM |
95 | }; |
96 | ||
97 | #define AD8801_CHANNEL(chan) { \ | |
98 | .type = IIO_VOLTAGE, \ | |
99 | .indexed = 1, \ | |
100 | .output = 1, \ | |
101 | .channel = chan, \ | |
102 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
103 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ | |
104 | BIT(IIO_CHAN_INFO_OFFSET), \ | |
105 | } | |
106 | ||
107 | static const struct iio_chan_spec ad8801_channels[] = { | |
108 | AD8801_CHANNEL(0), | |
109 | AD8801_CHANNEL(1), | |
110 | AD8801_CHANNEL(2), | |
111 | AD8801_CHANNEL(3), | |
112 | AD8801_CHANNEL(4), | |
113 | AD8801_CHANNEL(5), | |
114 | AD8801_CHANNEL(6), | |
115 | AD8801_CHANNEL(7), | |
116 | }; | |
117 | ||
118 | static int ad8801_probe(struct spi_device *spi) | |
119 | { | |
120 | struct iio_dev *indio_dev; | |
121 | struct ad8801_state *state; | |
122 | const struct spi_device_id *id; | |
123 | int ret; | |
124 | ||
125 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); | |
126 | if (indio_dev == NULL) | |
127 | return -ENOMEM; | |
128 | ||
129 | state = iio_priv(indio_dev); | |
130 | state->spi = spi; | |
131 | id = spi_get_device_id(spi); | |
132 | ||
133 | state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); | |
134 | if (IS_ERR(state->vrefh_reg)) { | |
135 | dev_err(&spi->dev, "Vrefh regulator not specified\n"); | |
136 | return PTR_ERR(state->vrefh_reg); | |
137 | } | |
138 | ||
139 | ret = regulator_enable(state->vrefh_reg); | |
140 | if (ret) { | |
141 | dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n", | |
142 | ret); | |
143 | return ret; | |
144 | } | |
145 | ||
146 | ret = regulator_get_voltage(state->vrefh_reg); | |
147 | if (ret < 0) { | |
148 | dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n", | |
149 | ret); | |
150 | goto error_disable_vrefh_reg; | |
151 | } | |
152 | state->vrefh_mv = ret / 1000; | |
153 | ||
154 | if (id->driver_data == ID_AD8803) { | |
155 | state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); | |
156 | if (IS_ERR(state->vrefl_reg)) { | |
157 | dev_err(&spi->dev, "Vrefl regulator not specified\n"); | |
158 | ret = PTR_ERR(state->vrefl_reg); | |
159 | goto error_disable_vrefh_reg; | |
160 | } | |
161 | ||
162 | ret = regulator_enable(state->vrefl_reg); | |
163 | if (ret) { | |
164 | dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n", | |
165 | ret); | |
166 | goto error_disable_vrefh_reg; | |
167 | } | |
168 | ||
169 | ret = regulator_get_voltage(state->vrefl_reg); | |
170 | if (ret < 0) { | |
171 | dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n", | |
172 | ret); | |
173 | goto error_disable_vrefl_reg; | |
174 | } | |
175 | state->vrefl_mv = ret / 1000; | |
176 | } else { | |
177 | state->vrefl_mv = 0; | |
178 | state->vrefl_reg = NULL; | |
179 | } | |
180 | ||
181 | spi_set_drvdata(spi, indio_dev); | |
182 | indio_dev->dev.parent = &spi->dev; | |
183 | indio_dev->info = &ad8801_info; | |
184 | indio_dev->modes = INDIO_DIRECT_MODE; | |
185 | indio_dev->channels = ad8801_channels; | |
186 | indio_dev->num_channels = ARRAY_SIZE(ad8801_channels); | |
187 | indio_dev->name = id->name; | |
188 | ||
189 | ret = iio_device_register(indio_dev); | |
190 | if (ret) { | |
191 | dev_err(&spi->dev, "Failed to register iio device: %d\n", | |
192 | ret); | |
193 | goto error_disable_vrefl_reg; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | ||
198 | error_disable_vrefl_reg: | |
199 | if (state->vrefl_reg) | |
200 | regulator_disable(state->vrefl_reg); | |
201 | error_disable_vrefh_reg: | |
202 | regulator_disable(state->vrefh_reg); | |
203 | return ret; | |
204 | } | |
205 | ||
206 | static int ad8801_remove(struct spi_device *spi) | |
207 | { | |
208 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
209 | struct ad8801_state *state = iio_priv(indio_dev); | |
210 | ||
211 | iio_device_unregister(indio_dev); | |
212 | if (state->vrefl_reg) | |
213 | regulator_disable(state->vrefl_reg); | |
214 | regulator_disable(state->vrefh_reg); | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
219 | static const struct spi_device_id ad8801_ids[] = { | |
220 | {"ad8801", ID_AD8801}, | |
221 | {"ad8803", ID_AD8803}, | |
222 | {} | |
223 | }; | |
224 | MODULE_DEVICE_TABLE(spi, ad8801_ids); | |
225 | ||
226 | static struct spi_driver ad8801_driver = { | |
227 | .driver = { | |
228 | .name = "ad8801", | |
229 | }, | |
230 | .probe = ad8801_probe, | |
231 | .remove = ad8801_remove, | |
232 | .id_table = ad8801_ids, | |
233 | }; | |
234 | module_spi_driver(ad8801_driver); | |
235 | ||
236 | MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>"); | |
237 | MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC"); | |
238 | MODULE_LICENSE("GPL v2"); |