]>
Commit | Line | Data |
---|---|---|
7a02ef79 CAC |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* ti-dac7311.c - Texas Instruments 8/10/12-bit 1-channel DAC driver | |
3 | * | |
4 | * Copyright (C) 2018 CMC NV | |
5 | * | |
6 | * http://www.ti.com/lit/ds/symlink/dac7311.pdf | |
7 | */ | |
8 | ||
9 | #include <linux/iio/iio.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/regulator/consumer.h> | |
12 | #include <linux/spi/spi.h> | |
13 | ||
14 | enum { | |
15 | ID_DAC5311 = 0, | |
16 | ID_DAC6311, | |
17 | ID_DAC7311, | |
18 | }; | |
19 | ||
20 | enum { | |
21 | POWER_1KOHM_TO_GND = 0, | |
22 | POWER_100KOHM_TO_GND, | |
23 | POWER_TRI_STATE, | |
24 | }; | |
25 | ||
26 | struct ti_dac_spec { | |
27 | u8 resolution; | |
28 | }; | |
29 | ||
30 | static const struct ti_dac_spec ti_dac_spec[] = { | |
31 | [ID_DAC5311] = { .resolution = 8 }, | |
32 | [ID_DAC6311] = { .resolution = 10 }, | |
33 | [ID_DAC7311] = { .resolution = 12 }, | |
34 | }; | |
35 | ||
36 | /** | |
37 | * struct ti_dac_chip - TI DAC chip | |
38 | * @lock: protects write sequences | |
39 | * @vref: regulator generating Vref | |
40 | * @spi: SPI device to send data to the device | |
41 | * @val: cached value | |
42 | * @powerdown: whether the chip is powered down | |
43 | * @powerdown_mode: selected by the user | |
44 | * @resolution: resolution of the chip | |
45 | * @buf: buffer for transfer data | |
46 | */ | |
47 | struct ti_dac_chip { | |
48 | struct mutex lock; | |
49 | struct regulator *vref; | |
50 | struct spi_device *spi; | |
51 | u16 val; | |
52 | bool powerdown; | |
53 | u8 powerdown_mode; | |
54 | u8 resolution; | |
55 | u8 buf[2] ____cacheline_aligned; | |
56 | }; | |
57 | ||
58 | static u8 ti_dac_get_power(struct ti_dac_chip *ti_dac, bool powerdown) | |
59 | { | |
60 | if (powerdown) | |
61 | return ti_dac->powerdown_mode + 1; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 power, u16 val) | |
67 | { | |
68 | u8 shift = 14 - ti_dac->resolution; | |
69 | ||
70 | ti_dac->buf[0] = (val << shift) & 0xFF; | |
71 | ti_dac->buf[1] = (power << 6) | (val >> (8 - shift)); | |
72 | return spi_write(ti_dac->spi, ti_dac->buf, 2); | |
73 | } | |
74 | ||
75 | static const char * const ti_dac_powerdown_modes[] = { | |
76 | "1kohm_to_gnd", | |
77 | "100kohm_to_gnd", | |
78 | "three_state", | |
79 | }; | |
80 | ||
81 | static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev, | |
82 | const struct iio_chan_spec *chan) | |
83 | { | |
84 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
85 | ||
86 | return ti_dac->powerdown_mode; | |
87 | } | |
88 | ||
89 | static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev, | |
90 | const struct iio_chan_spec *chan, | |
91 | unsigned int mode) | |
92 | { | |
93 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
94 | ||
95 | ti_dac->powerdown_mode = mode; | |
96 | return 0; | |
97 | } | |
98 | ||
99 | static const struct iio_enum ti_dac_powerdown_mode = { | |
100 | .items = ti_dac_powerdown_modes, | |
101 | .num_items = ARRAY_SIZE(ti_dac_powerdown_modes), | |
102 | .get = ti_dac_get_powerdown_mode, | |
103 | .set = ti_dac_set_powerdown_mode, | |
104 | }; | |
105 | ||
106 | static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev, | |
107 | uintptr_t private, | |
108 | const struct iio_chan_spec *chan, | |
109 | char *buf) | |
110 | { | |
111 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
112 | ||
113 | return sprintf(buf, "%d\n", ti_dac->powerdown); | |
114 | } | |
115 | ||
116 | static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev, | |
117 | uintptr_t private, | |
118 | const struct iio_chan_spec *chan, | |
119 | const char *buf, size_t len) | |
120 | { | |
121 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
122 | bool powerdown; | |
123 | u8 power; | |
124 | int ret; | |
125 | ||
126 | ret = strtobool(buf, &powerdown); | |
127 | if (ret) | |
128 | return ret; | |
129 | ||
130 | power = ti_dac_get_power(ti_dac, powerdown); | |
131 | ||
132 | mutex_lock(&ti_dac->lock); | |
133 | ret = ti_dac_cmd(ti_dac, power, 0); | |
134 | if (!ret) | |
135 | ti_dac->powerdown = powerdown; | |
136 | mutex_unlock(&ti_dac->lock); | |
137 | ||
138 | return ret ? ret : len; | |
139 | } | |
140 | ||
141 | static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { | |
142 | { | |
143 | .name = "powerdown", | |
144 | .read = ti_dac_read_powerdown, | |
145 | .write = ti_dac_write_powerdown, | |
146 | .shared = IIO_SHARED_BY_TYPE, | |
147 | }, | |
148 | IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), | |
149 | IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), | |
150 | { }, | |
151 | }; | |
152 | ||
153 | #define TI_DAC_CHANNEL(chan) { \ | |
154 | .type = IIO_VOLTAGE, \ | |
155 | .channel = (chan), \ | |
156 | .output = true, \ | |
157 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
158 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
159 | .ext_info = ti_dac_ext_info, \ | |
160 | } | |
161 | ||
162 | static const struct iio_chan_spec ti_dac_channels[] = { | |
163 | TI_DAC_CHANNEL(0), | |
164 | }; | |
165 | ||
166 | static int ti_dac_read_raw(struct iio_dev *indio_dev, | |
167 | struct iio_chan_spec const *chan, | |
168 | int *val, int *val2, long mask) | |
169 | { | |
170 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
171 | int ret; | |
172 | ||
173 | switch (mask) { | |
174 | case IIO_CHAN_INFO_RAW: | |
175 | *val = ti_dac->val; | |
176 | return IIO_VAL_INT; | |
177 | ||
178 | case IIO_CHAN_INFO_SCALE: | |
179 | ret = regulator_get_voltage(ti_dac->vref); | |
180 | if (ret < 0) | |
181 | return ret; | |
182 | ||
183 | *val = ret / 1000; | |
184 | *val2 = ti_dac->resolution; | |
185 | return IIO_VAL_FRACTIONAL_LOG2; | |
186 | } | |
187 | ||
188 | return -EINVAL; | |
189 | } | |
190 | ||
191 | static int ti_dac_write_raw(struct iio_dev *indio_dev, | |
192 | struct iio_chan_spec const *chan, | |
193 | int val, int val2, long mask) | |
194 | { | |
195 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
196 | u8 power = ti_dac_get_power(ti_dac, ti_dac->powerdown); | |
197 | int ret; | |
198 | ||
199 | switch (mask) { | |
200 | case IIO_CHAN_INFO_RAW: | |
201 | if (ti_dac->val == val) | |
202 | return 0; | |
203 | ||
204 | if (val >= (1 << ti_dac->resolution) || val < 0) | |
205 | return -EINVAL; | |
206 | ||
207 | if (ti_dac->powerdown) | |
208 | return -EBUSY; | |
209 | ||
210 | mutex_lock(&ti_dac->lock); | |
211 | ret = ti_dac_cmd(ti_dac, power, val); | |
212 | if (!ret) | |
213 | ti_dac->val = val; | |
214 | mutex_unlock(&ti_dac->lock); | |
215 | break; | |
216 | ||
217 | default: | |
218 | ret = -EINVAL; | |
219 | } | |
220 | ||
221 | return ret; | |
222 | } | |
223 | ||
224 | static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev, | |
225 | struct iio_chan_spec const *chan, long mask) | |
226 | { | |
227 | return IIO_VAL_INT; | |
228 | } | |
229 | ||
230 | static const struct iio_info ti_dac_info = { | |
231 | .read_raw = ti_dac_read_raw, | |
232 | .write_raw = ti_dac_write_raw, | |
233 | .write_raw_get_fmt = ti_dac_write_raw_get_fmt, | |
234 | }; | |
235 | ||
236 | static int ti_dac_probe(struct spi_device *spi) | |
237 | { | |
238 | struct device *dev = &spi->dev; | |
239 | const struct ti_dac_spec *spec; | |
240 | struct ti_dac_chip *ti_dac; | |
241 | struct iio_dev *indio_dev; | |
242 | int ret; | |
243 | ||
244 | indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac)); | |
245 | if (!indio_dev) { | |
246 | dev_err(dev, "can not allocate iio device\n"); | |
247 | return -ENOMEM; | |
248 | } | |
249 | ||
250 | spi->mode = SPI_MODE_1; | |
251 | spi->bits_per_word = 16; | |
252 | spi_setup(spi); | |
253 | ||
254 | indio_dev->dev.parent = dev; | |
255 | indio_dev->dev.of_node = spi->dev.of_node; | |
256 | indio_dev->info = &ti_dac_info; | |
257 | indio_dev->name = spi_get_device_id(spi)->name; | |
258 | indio_dev->modes = INDIO_DIRECT_MODE; | |
259 | indio_dev->channels = ti_dac_channels; | |
260 | spi_set_drvdata(spi, indio_dev); | |
261 | ||
262 | ti_dac = iio_priv(indio_dev); | |
263 | ti_dac->powerdown = false; | |
264 | ti_dac->spi = spi; | |
265 | ||
266 | spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data]; | |
267 | indio_dev->num_channels = 1; | |
268 | ti_dac->resolution = spec->resolution; | |
269 | ||
270 | ti_dac->vref = devm_regulator_get(dev, "vref"); | |
271 | if (IS_ERR(ti_dac->vref)) { | |
272 | dev_err(dev, "error to get regulator\n"); | |
273 | return PTR_ERR(ti_dac->vref); | |
274 | } | |
275 | ||
276 | ret = regulator_enable(ti_dac->vref); | |
277 | if (ret < 0) { | |
278 | dev_err(dev, "can not enable regulator\n"); | |
279 | return ret; | |
280 | } | |
281 | ||
282 | mutex_init(&ti_dac->lock); | |
283 | ||
284 | ret = iio_device_register(indio_dev); | |
285 | if (ret) { | |
286 | dev_err(dev, "fail to register iio device: %d\n", ret); | |
287 | goto err; | |
288 | } | |
289 | ||
290 | return 0; | |
291 | ||
292 | err: | |
293 | mutex_destroy(&ti_dac->lock); | |
294 | regulator_disable(ti_dac->vref); | |
295 | return ret; | |
296 | } | |
297 | ||
298 | static int ti_dac_remove(struct spi_device *spi) | |
299 | { | |
300 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | |
301 | struct ti_dac_chip *ti_dac = iio_priv(indio_dev); | |
302 | ||
303 | iio_device_unregister(indio_dev); | |
304 | mutex_destroy(&ti_dac->lock); | |
305 | regulator_disable(ti_dac->vref); | |
306 | return 0; | |
307 | } | |
308 | ||
309 | static const struct of_device_id ti_dac_of_id[] = { | |
310 | { .compatible = "ti,dac5311" }, | |
311 | { .compatible = "ti,dac6311" }, | |
312 | { .compatible = "ti,dac7311" }, | |
313 | { } | |
314 | }; | |
315 | MODULE_DEVICE_TABLE(of, ti_dac_of_id); | |
316 | ||
317 | static const struct spi_device_id ti_dac_spi_id[] = { | |
318 | { "dac5311", ID_DAC5311 }, | |
319 | { "dac6311", ID_DAC6311 }, | |
320 | { "dac7311", ID_DAC7311 }, | |
321 | { } | |
322 | }; | |
323 | MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); | |
324 | ||
325 | static struct spi_driver ti_dac_driver = { | |
326 | .driver = { | |
327 | .name = "ti-dac7311", | |
328 | .of_match_table = ti_dac_of_id, | |
329 | }, | |
330 | .probe = ti_dac_probe, | |
331 | .remove = ti_dac_remove, | |
332 | .id_table = ti_dac_spi_id, | |
333 | }; | |
334 | module_spi_driver(ti_dac_driver); | |
335 | ||
336 | MODULE_AUTHOR("Charles-Antoine Couret <[email protected]>"); | |
337 | MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1-channel DAC driver"); | |
338 | MODULE_LICENSE("GPL v2"); |