]>
Commit | Line | Data |
---|---|---|
d632a2bd IK |
1 | /* |
2 | * Maxim Integrated | |
3 | * 7-bit, Multi-Channel Sink/Source Current DAC Driver | |
4 | * Copyright (C) 2017 Maxim Integrated | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/i2c.h> | |
14 | #include <linux/regulator/consumer.h> | |
15 | #include <linux/err.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/iio/iio.h> | |
18 | #include <linux/iio/driver.h> | |
19 | #include <linux/iio/machine.h> | |
20 | #include <linux/iio/consumer.h> | |
21 | ||
22 | #define DS4422_MAX_DAC_CHANNELS 2 | |
23 | #define DS4424_MAX_DAC_CHANNELS 4 | |
24 | ||
25 | #define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) | |
26 | #define DS4424_SOURCE_I 1 | |
27 | #define DS4424_SINK_I 0 | |
28 | ||
29 | #define DS4424_CHANNEL(chan) { \ | |
30 | .type = IIO_CURRENT, \ | |
31 | .indexed = 1, \ | |
32 | .output = 1, \ | |
33 | .channel = chan, \ | |
34 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
35 | } | |
36 | ||
37 | /* | |
38 | * DS4424 DAC control register 8 bits | |
39 | * [7] 0: to sink; 1: to source | |
40 | * [6:0] steps to sink/source | |
41 | * bit[7] looks like a sign bit, but the value of the register is | |
42 | * not a two's complement code considering the bit[6:0] is a absolute | |
43 | * distance from the zero point. | |
44 | */ | |
45 | union ds4424_raw_data { | |
46 | struct { | |
47 | u8 dx:7; | |
48 | u8 source_bit:1; | |
49 | }; | |
50 | u8 bits; | |
51 | }; | |
52 | ||
53 | enum ds4424_device_ids { | |
54 | ID_DS4422, | |
55 | ID_DS4424, | |
56 | }; | |
57 | ||
58 | struct ds4424_data { | |
59 | struct i2c_client *client; | |
60 | struct mutex lock; | |
61 | uint8_t save[DS4424_MAX_DAC_CHANNELS]; | |
62 | struct regulator *vcc_reg; | |
63 | uint8_t raw[DS4424_MAX_DAC_CHANNELS]; | |
64 | }; | |
65 | ||
66 | static const struct iio_chan_spec ds4424_channels[] = { | |
67 | DS4424_CHANNEL(0), | |
68 | DS4424_CHANNEL(1), | |
69 | DS4424_CHANNEL(2), | |
70 | DS4424_CHANNEL(3), | |
71 | }; | |
72 | ||
73 | static int ds4424_get_value(struct iio_dev *indio_dev, | |
74 | int *val, int channel) | |
75 | { | |
76 | struct ds4424_data *data = iio_priv(indio_dev); | |
77 | int ret; | |
78 | ||
79 | mutex_lock(&data->lock); | |
80 | ret = i2c_smbus_read_byte_data(data->client, DS4424_DAC_ADDR(channel)); | |
81 | if (ret < 0) | |
82 | goto fail; | |
83 | ||
84 | *val = ret; | |
85 | ||
86 | fail: | |
87 | mutex_unlock(&data->lock); | |
88 | return ret; | |
89 | } | |
90 | ||
91 | static int ds4424_set_value(struct iio_dev *indio_dev, | |
92 | int val, struct iio_chan_spec const *chan) | |
93 | { | |
94 | struct ds4424_data *data = iio_priv(indio_dev); | |
95 | int ret; | |
96 | ||
97 | mutex_lock(&data->lock); | |
98 | ret = i2c_smbus_write_byte_data(data->client, | |
99 | DS4424_DAC_ADDR(chan->channel), val); | |
100 | if (ret < 0) | |
101 | goto fail; | |
102 | ||
103 | data->raw[chan->channel] = val; | |
104 | ||
105 | fail: | |
106 | mutex_unlock(&data->lock); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | static int ds4424_read_raw(struct iio_dev *indio_dev, | |
111 | struct iio_chan_spec const *chan, | |
112 | int *val, int *val2, long mask) | |
113 | { | |
114 | union ds4424_raw_data raw; | |
115 | int ret; | |
116 | ||
117 | switch (mask) { | |
118 | case IIO_CHAN_INFO_RAW: | |
119 | ret = ds4424_get_value(indio_dev, val, chan->channel); | |
120 | if (ret < 0) { | |
121 | pr_err("%s : ds4424_get_value returned %d\n", | |
122 | __func__, ret); | |
123 | return ret; | |
124 | } | |
125 | raw.bits = *val; | |
126 | *val = raw.dx; | |
127 | if (raw.source_bit == DS4424_SINK_I) | |
128 | *val = -*val; | |
129 | return IIO_VAL_INT; | |
130 | ||
131 | default: | |
132 | return -EINVAL; | |
133 | } | |
134 | } | |
135 | ||
136 | static int ds4424_write_raw(struct iio_dev *indio_dev, | |
137 | struct iio_chan_spec const *chan, | |
138 | int val, int val2, long mask) | |
139 | { | |
140 | union ds4424_raw_data raw; | |
141 | ||
142 | if (val2 != 0) | |
143 | return -EINVAL; | |
144 | ||
145 | switch (mask) { | |
146 | case IIO_CHAN_INFO_RAW: | |
147 | if (val < S8_MIN || val > S8_MAX) | |
148 | return -EINVAL; | |
149 | ||
150 | if (val > 0) { | |
151 | raw.source_bit = DS4424_SOURCE_I; | |
152 | raw.dx = val; | |
153 | } else { | |
154 | raw.source_bit = DS4424_SINK_I; | |
155 | raw.dx = -val; | |
156 | } | |
157 | ||
158 | return ds4424_set_value(indio_dev, raw.bits, chan); | |
159 | ||
160 | default: | |
161 | return -EINVAL; | |
162 | } | |
163 | } | |
164 | ||
165 | static int ds4424_verify_chip(struct iio_dev *indio_dev) | |
166 | { | |
167 | int ret, val; | |
168 | ||
169 | ret = ds4424_get_value(indio_dev, &val, DS4424_DAC_ADDR(0)); | |
170 | if (ret < 0) | |
171 | dev_err(&indio_dev->dev, | |
172 | "%s failed. ret: %d\n", __func__, ret); | |
173 | ||
174 | return ret; | |
175 | } | |
176 | ||
177 | static int __maybe_unused ds4424_suspend(struct device *dev) | |
178 | { | |
179 | struct i2c_client *client = to_i2c_client(dev); | |
180 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
181 | struct ds4424_data *data = iio_priv(indio_dev); | |
182 | int ret = 0; | |
183 | int i; | |
184 | ||
185 | for (i = 0; i < indio_dev->num_channels; i++) { | |
186 | data->save[i] = data->raw[i]; | |
187 | ret = ds4424_set_value(indio_dev, 0, | |
188 | &indio_dev->channels[i]); | |
189 | if (ret < 0) | |
190 | return ret; | |
191 | } | |
192 | return ret; | |
193 | } | |
194 | ||
195 | static int __maybe_unused ds4424_resume(struct device *dev) | |
196 | { | |
197 | struct i2c_client *client = to_i2c_client(dev); | |
198 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
199 | struct ds4424_data *data = iio_priv(indio_dev); | |
200 | int ret = 0; | |
201 | int i; | |
202 | ||
203 | for (i = 0; i < indio_dev->num_channels; i++) { | |
204 | ret = ds4424_set_value(indio_dev, data->save[i], | |
205 | &indio_dev->channels[i]); | |
206 | if (ret < 0) | |
207 | return ret; | |
208 | } | |
209 | return ret; | |
210 | } | |
211 | ||
212 | static SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); | |
213 | ||
214 | static const struct iio_info ds4424_info = { | |
215 | .read_raw = ds4424_read_raw, | |
216 | .write_raw = ds4424_write_raw, | |
217 | }; | |
218 | ||
219 | static int ds4424_probe(struct i2c_client *client, | |
220 | const struct i2c_device_id *id) | |
221 | { | |
222 | struct ds4424_data *data; | |
223 | struct iio_dev *indio_dev; | |
224 | int ret; | |
225 | ||
226 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
227 | if (!indio_dev) { | |
228 | dev_err(&client->dev, "iio dev alloc failed.\n"); | |
229 | return -ENOMEM; | |
230 | } | |
231 | ||
232 | data = iio_priv(indio_dev); | |
233 | i2c_set_clientdata(client, indio_dev); | |
234 | data->client = client; | |
235 | indio_dev->name = id->name; | |
236 | indio_dev->dev.of_node = client->dev.of_node; | |
237 | indio_dev->dev.parent = &client->dev; | |
238 | ||
239 | if (!client->dev.of_node) { | |
240 | dev_err(&client->dev, | |
241 | "Not found DT.\n"); | |
242 | return -ENODEV; | |
243 | } | |
244 | ||
245 | data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); | |
246 | if (IS_ERR(data->vcc_reg)) { | |
247 | dev_err(&client->dev, | |
248 | "Failed to get vcc-supply regulator. err: %ld\n", | |
249 | PTR_ERR(data->vcc_reg)); | |
250 | return PTR_ERR(data->vcc_reg); | |
251 | } | |
252 | ||
253 | mutex_init(&data->lock); | |
254 | ret = regulator_enable(data->vcc_reg); | |
255 | if (ret < 0) { | |
256 | dev_err(&client->dev, | |
257 | "Unable to enable the regulator.\n"); | |
258 | return ret; | |
259 | } | |
260 | ||
261 | usleep_range(1000, 1200); | |
262 | ret = ds4424_verify_chip(indio_dev); | |
263 | if (ret < 0) | |
264 | goto fail; | |
265 | ||
266 | switch (id->driver_data) { | |
267 | case ID_DS4422: | |
268 | indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS; | |
269 | break; | |
270 | case ID_DS4424: | |
271 | indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; | |
272 | break; | |
273 | default: | |
274 | dev_err(&client->dev, | |
275 | "ds4424: Invalid chip id.\n"); | |
276 | ret = -ENXIO; | |
277 | goto fail; | |
278 | } | |
279 | ||
280 | indio_dev->channels = ds4424_channels; | |
281 | indio_dev->modes = INDIO_DIRECT_MODE; | |
282 | indio_dev->info = &ds4424_info; | |
283 | ||
284 | ret = iio_device_register(indio_dev); | |
285 | if (ret < 0) { | |
286 | dev_err(&client->dev, | |
287 | "iio_device_register failed. ret: %d\n", ret); | |
288 | goto fail; | |
289 | } | |
290 | ||
291 | return ret; | |
292 | ||
293 | fail: | |
294 | regulator_disable(data->vcc_reg); | |
295 | return ret; | |
296 | } | |
297 | ||
298 | static int ds4424_remove(struct i2c_client *client) | |
299 | { | |
300 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
301 | struct ds4424_data *data = iio_priv(indio_dev); | |
302 | ||
303 | iio_device_unregister(indio_dev); | |
304 | regulator_disable(data->vcc_reg); | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | static const struct i2c_device_id ds4424_id[] = { | |
310 | { "ds4422", ID_DS4422 }, | |
311 | { "ds4424", ID_DS4424 }, | |
312 | { } | |
313 | }; | |
314 | ||
315 | MODULE_DEVICE_TABLE(i2c, ds4424_id); | |
316 | ||
317 | static const struct of_device_id ds4424_of_match[] = { | |
318 | { .compatible = "maxim,ds4422" }, | |
319 | { .compatible = "maxim,ds4424" }, | |
320 | { }, | |
321 | }; | |
322 | ||
323 | MODULE_DEVICE_TABLE(of, ds4424_of_match); | |
324 | ||
325 | static struct i2c_driver ds4424_driver = { | |
326 | .driver = { | |
327 | .name = "ds4424", | |
328 | .of_match_table = ds4424_of_match, | |
329 | .pm = &ds4424_pm_ops, | |
330 | }, | |
331 | .probe = ds4424_probe, | |
332 | .remove = ds4424_remove, | |
333 | .id_table = ds4424_id, | |
334 | }; | |
335 | module_i2c_driver(ds4424_driver); | |
336 | ||
337 | MODULE_DESCRIPTION("Maxim DS4424 DAC Driver"); | |
338 | MODULE_AUTHOR("Ismail H. Kose <[email protected]>"); | |
339 | MODULE_AUTHOR("Vishal Sood <[email protected]>"); | |
340 | MODULE_AUTHOR("David Jung <[email protected]>"); | |
341 | MODULE_LICENSE("GPL v2"); |