]>
Commit | Line | Data |
---|---|---|
47298897 PR |
1 | /* |
2 | * iio/dac/max5821.c | |
3 | * Copyright (C) 2014 Philippe Reynes | |
4 | * | |
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 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/i2c.h> | |
13 | #include <linux/iio/iio.h> | |
14 | #include <linux/regulator/consumer.h> | |
15 | ||
16 | #define MAX5821_MAX_DAC_CHANNELS 2 | |
17 | ||
18 | /* command bytes */ | |
19 | #define MAX5821_LOAD_DAC_A_IN_REG_B 0x00 | |
20 | #define MAX5821_LOAD_DAC_B_IN_REG_A 0x10 | |
21 | #define MAX5821_EXTENDED_COMMAND_MODE 0xf0 | |
22 | #define MAX5821_READ_DAC_A_COMMAND 0xf1 | |
23 | #define MAX5821_READ_DAC_B_COMMAND 0xf2 | |
24 | ||
25 | #define MAX5821_EXTENDED_POWER_UP 0x00 | |
26 | #define MAX5821_EXTENDED_POWER_DOWN_MODE0 0x01 | |
27 | #define MAX5821_EXTENDED_POWER_DOWN_MODE1 0x02 | |
28 | #define MAX5821_EXTENDED_POWER_DOWN_MODE2 0x03 | |
29 | #define MAX5821_EXTENDED_DAC_A 0x04 | |
30 | #define MAX5821_EXTENDED_DAC_B 0x08 | |
31 | ||
32 | enum max5821_device_ids { | |
33 | ID_MAX5821, | |
34 | }; | |
35 | ||
36 | struct max5821_data { | |
37 | struct i2c_client *client; | |
38 | struct regulator *vref_reg; | |
39 | unsigned short vref_mv; | |
40 | bool powerdown[MAX5821_MAX_DAC_CHANNELS]; | |
41 | u8 powerdown_mode[MAX5821_MAX_DAC_CHANNELS]; | |
42 | struct mutex lock; | |
43 | }; | |
44 | ||
45 | static const char * const max5821_powerdown_modes[] = { | |
46 | "three_state", | |
47 | "1kohm_to_gnd", | |
48 | "100kohm_to_gnd", | |
49 | }; | |
50 | ||
51 | enum { | |
52 | MAX5821_THREE_STATE, | |
53 | MAX5821_1KOHM_TO_GND, | |
54 | MAX5821_100KOHM_TO_GND | |
55 | }; | |
56 | ||
57 | static int max5821_get_powerdown_mode(struct iio_dev *indio_dev, | |
58 | const struct iio_chan_spec *chan) | |
59 | { | |
60 | struct max5821_data *st = iio_priv(indio_dev); | |
61 | ||
62 | return st->powerdown_mode[chan->channel]; | |
63 | } | |
64 | ||
65 | static int max5821_set_powerdown_mode(struct iio_dev *indio_dev, | |
66 | const struct iio_chan_spec *chan, | |
67 | unsigned int mode) | |
68 | { | |
69 | struct max5821_data *st = iio_priv(indio_dev); | |
70 | ||
71 | st->powerdown_mode[chan->channel] = mode; | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static const struct iio_enum max5821_powerdown_mode_enum = { | |
77 | .items = max5821_powerdown_modes, | |
78 | .num_items = ARRAY_SIZE(max5821_powerdown_modes), | |
79 | .get = max5821_get_powerdown_mode, | |
80 | .set = max5821_set_powerdown_mode, | |
81 | }; | |
82 | ||
83 | static ssize_t max5821_read_dac_powerdown(struct iio_dev *indio_dev, | |
84 | uintptr_t private, | |
85 | const struct iio_chan_spec *chan, | |
86 | char *buf) | |
87 | { | |
88 | struct max5821_data *st = iio_priv(indio_dev); | |
89 | ||
90 | return sprintf(buf, "%d\n", st->powerdown[chan->channel]); | |
91 | } | |
92 | ||
93 | static int max5821_sync_powerdown_mode(struct max5821_data *data, | |
94 | const struct iio_chan_spec *chan) | |
95 | { | |
96 | u8 outbuf[2]; | |
97 | ||
98 | outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE; | |
99 | ||
100 | if (chan->channel == 0) | |
101 | outbuf[1] = MAX5821_EXTENDED_DAC_A; | |
102 | else | |
103 | outbuf[1] = MAX5821_EXTENDED_DAC_B; | |
104 | ||
105 | if (data->powerdown[chan->channel]) | |
106 | outbuf[1] |= data->powerdown_mode[chan->channel] + 1; | |
107 | else | |
108 | outbuf[1] |= MAX5821_EXTENDED_POWER_UP; | |
109 | ||
110 | return i2c_master_send(data->client, outbuf, 2); | |
111 | } | |
112 | ||
113 | static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev, | |
114 | uintptr_t private, | |
115 | const struct iio_chan_spec *chan, | |
116 | const char *buf, size_t len) | |
117 | { | |
118 | struct max5821_data *data = iio_priv(indio_dev); | |
119 | bool powerdown; | |
120 | int ret; | |
121 | ||
122 | ret = strtobool(buf, &powerdown); | |
123 | if (ret) | |
124 | return ret; | |
125 | ||
126 | data->powerdown[chan->channel] = powerdown; | |
127 | ||
128 | ret = max5821_sync_powerdown_mode(data, chan); | |
129 | if (ret < 0) | |
130 | return ret; | |
131 | ||
132 | return len; | |
133 | } | |
134 | ||
135 | static const struct iio_chan_spec_ext_info max5821_ext_info[] = { | |
136 | { | |
137 | .name = "powerdown", | |
138 | .read = max5821_read_dac_powerdown, | |
139 | .write = max5821_write_dac_powerdown, | |
140 | .shared = IIO_SEPARATE, | |
141 | }, | |
142 | IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum), | |
143 | IIO_ENUM_AVAILABLE("powerdown_mode", &max5821_powerdown_mode_enum), | |
144 | { }, | |
145 | }; | |
146 | ||
147 | #define MAX5821_CHANNEL(chan) { \ | |
148 | .type = IIO_VOLTAGE, \ | |
149 | .indexed = 1, \ | |
150 | .output = 1, \ | |
151 | .channel = (chan), \ | |
152 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
153 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \ | |
154 | .ext_info = max5821_ext_info, \ | |
155 | } | |
156 | ||
157 | static const struct iio_chan_spec max5821_channels[] = { | |
158 | MAX5821_CHANNEL(0), | |
159 | MAX5821_CHANNEL(1) | |
160 | }; | |
161 | ||
162 | static const u8 max5821_read_dac_command[] = { | |
163 | MAX5821_READ_DAC_A_COMMAND, | |
164 | MAX5821_READ_DAC_B_COMMAND | |
165 | }; | |
166 | ||
167 | static const u8 max5821_load_dac_command[] = { | |
168 | MAX5821_LOAD_DAC_A_IN_REG_B, | |
169 | MAX5821_LOAD_DAC_B_IN_REG_A | |
170 | }; | |
171 | ||
172 | static int max5821_get_value(struct iio_dev *indio_dev, | |
173 | int *val, int channel) | |
174 | { | |
175 | struct max5821_data *data = iio_priv(indio_dev); | |
176 | struct i2c_client *client = data->client; | |
177 | u8 outbuf[1]; | |
178 | u8 inbuf[2]; | |
179 | int ret; | |
180 | ||
181 | if ((channel != 0) && (channel != 1)) | |
182 | return -EINVAL; | |
183 | ||
184 | outbuf[0] = max5821_read_dac_command[channel]; | |
185 | ||
186 | mutex_lock(&data->lock); | |
187 | ||
188 | ret = i2c_master_send(client, outbuf, 1); | |
189 | if (ret < 0) { | |
190 | mutex_unlock(&data->lock); | |
191 | return ret; | |
192 | } else if (ret != 1) { | |
193 | mutex_unlock(&data->lock); | |
194 | return -EIO; | |
195 | } | |
196 | ||
197 | ret = i2c_master_recv(client, inbuf, 2); | |
198 | if (ret < 0) { | |
199 | mutex_unlock(&data->lock); | |
200 | return ret; | |
201 | } else if (ret != 2) { | |
202 | mutex_unlock(&data->lock); | |
203 | return -EIO; | |
204 | } | |
205 | ||
206 | mutex_unlock(&data->lock); | |
207 | ||
208 | *val = ((inbuf[0] & 0x0f) << 6) | (inbuf[1] >> 2); | |
209 | ||
210 | return IIO_VAL_INT; | |
211 | } | |
212 | ||
213 | static int max5821_set_value(struct iio_dev *indio_dev, | |
214 | int val, int channel) | |
215 | { | |
216 | struct max5821_data *data = iio_priv(indio_dev); | |
217 | struct i2c_client *client = data->client; | |
218 | u8 outbuf[2]; | |
219 | int ret; | |
220 | ||
221 | if ((val < 0) || (val > 1023)) | |
222 | return -EINVAL; | |
223 | ||
224 | if ((channel != 0) && (channel != 1)) | |
225 | return -EINVAL; | |
226 | ||
227 | outbuf[0] = max5821_load_dac_command[channel]; | |
228 | outbuf[0] |= val >> 6; | |
229 | outbuf[1] = (val & 0x3f) << 2; | |
230 | ||
231 | ret = i2c_master_send(client, outbuf, 2); | |
232 | if (ret < 0) | |
233 | return ret; | |
234 | else if (ret != 2) | |
235 | return -EIO; | |
236 | else | |
237 | return 0; | |
238 | } | |
239 | ||
240 | static int max5821_read_raw(struct iio_dev *indio_dev, | |
241 | struct iio_chan_spec const *chan, | |
242 | int *val, int *val2, long mask) | |
243 | { | |
244 | struct max5821_data *data = iio_priv(indio_dev); | |
245 | ||
246 | switch (mask) { | |
247 | case IIO_CHAN_INFO_RAW: | |
248 | return max5821_get_value(indio_dev, val, chan->channel); | |
249 | case IIO_CHAN_INFO_SCALE: | |
250 | *val = data->vref_mv; | |
251 | *val2 = 10; | |
252 | return IIO_VAL_FRACTIONAL_LOG2; | |
253 | default: | |
254 | return -EINVAL; | |
255 | } | |
256 | } | |
257 | ||
258 | static int max5821_write_raw(struct iio_dev *indio_dev, | |
259 | struct iio_chan_spec const *chan, | |
260 | int val, int val2, long mask) | |
261 | { | |
262 | if (val2 != 0) | |
263 | return -EINVAL; | |
264 | ||
265 | switch (mask) { | |
266 | case IIO_CHAN_INFO_RAW: | |
267 | return max5821_set_value(indio_dev, val, chan->channel); | |
268 | default: | |
269 | return -EINVAL; | |
270 | } | |
271 | } | |
272 | ||
273 | #ifdef CONFIG_PM_SLEEP | |
274 | static int max5821_suspend(struct device *dev) | |
275 | { | |
276 | u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE, | |
277 | MAX5821_EXTENDED_DAC_A | | |
278 | MAX5821_EXTENDED_DAC_B | | |
279 | MAX5821_EXTENDED_POWER_DOWN_MODE2 }; | |
280 | ||
281 | return i2c_master_send(to_i2c_client(dev), outbuf, 2); | |
282 | } | |
283 | ||
284 | static int max5821_resume(struct device *dev) | |
285 | { | |
286 | u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE, | |
287 | MAX5821_EXTENDED_DAC_A | | |
288 | MAX5821_EXTENDED_DAC_B | | |
289 | MAX5821_EXTENDED_POWER_UP }; | |
290 | ||
291 | return i2c_master_send(to_i2c_client(dev), outbuf, 2); | |
292 | } | |
293 | ||
294 | static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume); | |
295 | #define MAX5821_PM_OPS (&max5821_pm_ops) | |
296 | #else | |
297 | #define MAX5821_PM_OPS NULL | |
298 | #endif /* CONFIG_PM_SLEEP */ | |
299 | ||
300 | static const struct iio_info max5821_info = { | |
301 | .read_raw = max5821_read_raw, | |
302 | .write_raw = max5821_write_raw, | |
303 | .driver_module = THIS_MODULE, | |
304 | }; | |
305 | ||
306 | static int max5821_probe(struct i2c_client *client, | |
307 | const struct i2c_device_id *id) | |
308 | { | |
309 | struct max5821_data *data; | |
310 | struct iio_dev *indio_dev; | |
311 | u32 tmp; | |
312 | int ret; | |
313 | ||
314 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
315 | if (!indio_dev) | |
316 | return -ENOMEM; | |
317 | data = iio_priv(indio_dev); | |
318 | i2c_set_clientdata(client, indio_dev); | |
319 | data->client = client; | |
320 | mutex_init(&data->lock); | |
321 | ||
322 | /* max5821 start in powerdown mode 100Kohm to ground */ | |
323 | for (tmp = 0; tmp < MAX5821_MAX_DAC_CHANNELS; tmp++) { | |
324 | data->powerdown[tmp] = true; | |
325 | data->powerdown_mode[tmp] = MAX5821_100KOHM_TO_GND; | |
326 | } | |
327 | ||
328 | data->vref_reg = devm_regulator_get(&client->dev, "vref"); | |
329 | if (IS_ERR(data->vref_reg)) { | |
330 | ret = PTR_ERR(data->vref_reg); | |
331 | dev_err(&client->dev, | |
332 | "Failed to get vref regulator: %d\n", ret); | |
333 | goto error_free_reg; | |
334 | } | |
335 | ||
336 | ret = regulator_enable(data->vref_reg); | |
337 | if (ret) { | |
338 | dev_err(&client->dev, | |
339 | "Failed to enable vref regulator: %d\n", ret); | |
340 | goto error_free_reg; | |
341 | } | |
342 | ||
343 | ret = regulator_get_voltage(data->vref_reg); | |
344 | if (ret < 0) { | |
345 | dev_err(&client->dev, | |
346 | "Failed to get voltage on regulator: %d\n", ret); | |
347 | goto error_disable_reg; | |
348 | } | |
349 | ||
350 | data->vref_mv = ret / 1000; | |
351 | ||
352 | indio_dev->name = id->name; | |
353 | indio_dev->dev.parent = &client->dev; | |
354 | indio_dev->num_channels = ARRAY_SIZE(max5821_channels); | |
355 | indio_dev->channels = max5821_channels; | |
356 | indio_dev->modes = INDIO_DIRECT_MODE; | |
357 | indio_dev->info = &max5821_info; | |
358 | ||
359 | return iio_device_register(indio_dev); | |
360 | ||
361 | error_disable_reg: | |
362 | regulator_disable(data->vref_reg); | |
363 | ||
364 | error_free_reg: | |
365 | ||
366 | return ret; | |
367 | } | |
368 | ||
369 | static int max5821_remove(struct i2c_client *client) | |
370 | { | |
371 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
372 | struct max5821_data *data = iio_priv(indio_dev); | |
373 | ||
374 | iio_device_unregister(indio_dev); | |
375 | regulator_disable(data->vref_reg); | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | static const struct i2c_device_id max5821_id[] = { | |
381 | { "max5821", ID_MAX5821 }, | |
382 | { } | |
383 | }; | |
384 | MODULE_DEVICE_TABLE(i2c, max5821_id); | |
385 | ||
386 | static const struct of_device_id max5821_of_match[] = { | |
387 | { .compatible = "maxim,max5821" }, | |
388 | { } | |
389 | }; | |
390 | ||
391 | static struct i2c_driver max5821_driver = { | |
392 | .driver = { | |
393 | .name = "max5821", | |
394 | .pm = MAX5821_PM_OPS, | |
47298897 PR |
395 | }, |
396 | .probe = max5821_probe, | |
397 | .remove = max5821_remove, | |
398 | .id_table = max5821_id, | |
399 | }; | |
400 | module_i2c_driver(max5821_driver); | |
401 | ||
402 | MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>"); | |
403 | MODULE_DESCRIPTION("MAX5821 DAC"); | |
404 | MODULE_LICENSE("GPL v2"); |