diff options
-rw-r--r-- | drivers/iio/adc/Kconfig | 14 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/twl6030-gpadc.c | 1029 |
3 files changed, 1044 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f725b4581f04..09371cbc9dc1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig | |||
@@ -171,6 +171,20 @@ config TI_AM335X_ADC | |||
171 | Say yes here to build support for Texas Instruments ADC | 171 | Say yes here to build support for Texas Instruments ADC |
172 | driver which is also a MFD client. | 172 | driver which is also a MFD client. |
173 | 173 | ||
174 | config TWL6030_GPADC | ||
175 | tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" | ||
176 | depends on TWL4030_CORE | ||
177 | default n | ||
178 | help | ||
179 | Say yes here if you want support for the TWL6030/TWL6032 General | ||
180 | Purpose A/D Converter. This will add support for battery type | ||
181 | detection, battery voltage and temperature measurement, die | ||
182 | temperature measurement, system supply voltage, audio accessory, | ||
183 | USB ID detection. | ||
184 | |||
185 | This driver can also be built as a module. If so, the module will be | ||
186 | called twl6030-gpadc. | ||
187 | |||
174 | config VIPERBOARD_ADC | 188 | config VIPERBOARD_ADC |
175 | tristate "Viperboard ADC support" | 189 | tristate "Viperboard ADC support" |
176 | depends on MFD_VIPERBOARD && USB | 190 | depends on MFD_VIPERBOARD && USB |
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 2a4324ec7f14..33656ef7d1f6 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile | |||
@@ -19,4 +19,5 @@ obj-$(CONFIG_MCP320X) += mcp320x.o | |||
19 | obj-$(CONFIG_NAU7802) += nau7802.o | 19 | obj-$(CONFIG_NAU7802) += nau7802.o |
20 | obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o | 20 | obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o |
21 | obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o | 21 | obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o |
22 | obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o | ||
22 | obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o | 23 | obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o |
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c new file mode 100644 index 000000000000..a80a049c9a55 --- /dev/null +++ b/drivers/iio/adc/twl6030-gpadc.c | |||
@@ -0,0 +1,1029 @@ | |||
1 | /* | ||
2 | * TWL6030 GPADC module driver | ||
3 | * | ||
4 | * Copyright (C) 2009-2013 Texas Instruments Inc. | ||
5 | * Nishant Kamat <nskamat@ti.com> | ||
6 | * Balaji T K <balajitk@ti.com> | ||
7 | * Graeme Gregory <gg@slimlogic.co.uk> | ||
8 | * Girish S Ghongdemath <girishsg@ti.com> | ||
9 | * Ambresh K <ambresh@ti.com> | ||
10 | * Oleksandr Kozaruk <oleksandr.kozaruk@ti.com | ||
11 | * | ||
12 | * Based on twl4030-madc.c | ||
13 | * Copyright (C) 2008 Nokia Corporation | ||
14 | * Mikko Ylinen <mikko.k.ylinen@nokia.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License | ||
18 | * version 2 as published by the Free Software Foundation. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, but | ||
21 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
23 | * General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
28 | * 02110-1301 USA | ||
29 | * | ||
30 | */ | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/interrupt.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/module.h> | ||
35 | #include <linux/platform_device.h> | ||
36 | #include <linux/of_platform.h> | ||
37 | #include <linux/i2c/twl.h> | ||
38 | #include <linux/iio/iio.h> | ||
39 | #include <linux/iio/sysfs.h> | ||
40 | |||
41 | #define DRIVER_NAME "twl6030_gpadc" | ||
42 | |||
43 | /* | ||
44 | * twl6030 per TRM has 17 channels, and twl6032 has 19 channels | ||
45 | * 2 test network channels are not used, | ||
46 | * 2 die temperature channels are not used either, as it is not | ||
47 | * defined how to convert ADC value to temperature | ||
48 | */ | ||
49 | #define TWL6030_GPADC_USED_CHANNELS 13 | ||
50 | #define TWL6030_GPADC_MAX_CHANNELS 15 | ||
51 | #define TWL6032_GPADC_USED_CHANNELS 15 | ||
52 | #define TWL6032_GPADC_MAX_CHANNELS 19 | ||
53 | #define TWL6030_GPADC_NUM_TRIM_REGS 16 | ||
54 | |||
55 | #define TWL6030_GPADC_CTRL_P1 0x05 | ||
56 | |||
57 | #define TWL6032_GPADC_GPSELECT_ISB 0x07 | ||
58 | #define TWL6032_GPADC_CTRL_P1 0x08 | ||
59 | |||
60 | #define TWL6032_GPADC_GPCH0_LSB 0x0d | ||
61 | #define TWL6032_GPADC_GPCH0_MSB 0x0e | ||
62 | |||
63 | #define TWL6030_GPADC_CTRL_P1_SP1 BIT(3) | ||
64 | |||
65 | #define TWL6030_GPADC_GPCH0_LSB (0x29) | ||
66 | |||
67 | #define TWL6030_GPADC_RT_SW1_EOC_MASK BIT(5) | ||
68 | |||
69 | #define TWL6030_GPADC_TRIM1 0xCD | ||
70 | |||
71 | #define TWL6030_REG_TOGGLE1 0x90 | ||
72 | #define TWL6030_GPADCS BIT(1) | ||
73 | #define TWL6030_GPADCR BIT(0) | ||
74 | |||
75 | /** | ||
76 | * struct twl6030_chnl_calib - channel calibration | ||
77 | * @gain: slope coefficient for ideal curve | ||
78 | * @gain_error: gain error | ||
79 | * @offset_error: offset of the real curve | ||
80 | */ | ||
81 | struct twl6030_chnl_calib { | ||
82 | s32 gain; | ||
83 | s32 gain_error; | ||
84 | s32 offset_error; | ||
85 | }; | ||
86 | |||
87 | /** | ||
88 | * struct twl6030_ideal_code - GPADC calibration parameters | ||
89 | * GPADC is calibrated in two points: close to the beginning and | ||
90 | * to the and of the measurable input range | ||
91 | * | ||
92 | * @channel: channel number | ||
93 | * @code1: ideal code for the input at the beginning | ||
94 | * @code2: ideal code for at the end of the range | ||
95 | * @volt1: voltage input at the beginning(low voltage) | ||
96 | * @volt2: voltage input at the end(high voltage) | ||
97 | */ | ||
98 | struct twl6030_ideal_code { | ||
99 | int channel; | ||
100 | u16 code1; | ||
101 | u16 code2; | ||
102 | u16 volt1; | ||
103 | u16 volt2; | ||
104 | }; | ||
105 | |||
106 | struct twl6030_gpadc_data; | ||
107 | |||
108 | /** | ||
109 | * struct twl6030_gpadc_platform_data - platform specific data | ||
110 | * @nchannels: number of GPADC channels | ||
111 | * @iio_channels: iio channels | ||
112 | * @twl6030_ideal: pointer to calibration parameters | ||
113 | * @start_conversion: pointer to ADC start conversion function | ||
114 | * @channel_to_reg pointer to ADC function to convert channel to | ||
115 | * register address for reading conversion result | ||
116 | * @calibrate: pointer to calibration function | ||
117 | */ | ||
118 | struct twl6030_gpadc_platform_data { | ||
119 | const int nchannels; | ||
120 | const struct iio_chan_spec *iio_channels; | ||
121 | const struct twl6030_ideal_code *ideal; | ||
122 | int (*start_conversion)(int channel); | ||
123 | u8 (*channel_to_reg)(int channel); | ||
124 | int (*calibrate)(struct twl6030_gpadc_data *gpadc); | ||
125 | }; | ||
126 | |||
127 | /** | ||
128 | * struct twl6030_gpadc_data - GPADC data | ||
129 | * @dev: device pointer | ||
130 | * @lock: mutual exclusion lock for the structure | ||
131 | * @irq_complete: completion to signal end of conversion | ||
132 | * @twl6030_cal_tbl: pointer to calibration data for each | ||
133 | * channel with gain error and offset | ||
134 | * @pdata: pointer to device specific data | ||
135 | */ | ||
136 | struct twl6030_gpadc_data { | ||
137 | struct device *dev; | ||
138 | struct mutex lock; | ||
139 | struct completion irq_complete; | ||
140 | struct twl6030_chnl_calib *twl6030_cal_tbl; | ||
141 | const struct twl6030_gpadc_platform_data *pdata; | ||
142 | }; | ||
143 | |||
144 | /* | ||
145 | * channels 11, 12, 13, 15 and 16 have no calibration data | ||
146 | * calibration offset is same for channels 1, 3, 4, 5 | ||
147 | * | ||
148 | * The data is taken from GPADC_TRIM registers description. | ||
149 | * GPADC_TRIM registers keep difference between the code measured | ||
150 | * at volt1 and volt2 input voltages and corresponding code1 and code2 | ||
151 | */ | ||
152 | static const struct twl6030_ideal_code | ||
153 | twl6030_ideal[TWL6030_GPADC_USED_CHANNELS] = { | ||
154 | [0] = { /* ch 0, external, battery type, resistor value */ | ||
155 | .channel = 0, | ||
156 | .code1 = 116, | ||
157 | .code2 = 745, | ||
158 | .volt1 = 141, | ||
159 | .volt2 = 910, | ||
160 | }, | ||
161 | [1] = { /* ch 1, external, battery temperature, NTC resistor value */ | ||
162 | .channel = 1, | ||
163 | .code1 = 82, | ||
164 | .code2 = 900, | ||
165 | .volt1 = 100, | ||
166 | .volt2 = 1100, | ||
167 | }, | ||
168 | [2] = { /* ch 2, external, audio accessory/general purpose */ | ||
169 | .channel = 2, | ||
170 | .code1 = 55, | ||
171 | .code2 = 818, | ||
172 | .volt1 = 101, | ||
173 | .volt2 = 1499, | ||
174 | }, | ||
175 | [3] = { /* ch 3, external, general purpose */ | ||
176 | .channel = 3, | ||
177 | .code1 = 82, | ||
178 | .code2 = 900, | ||
179 | .volt1 = 100, | ||
180 | .volt2 = 1100, | ||
181 | }, | ||
182 | [4] = { /* ch 4, external, temperature measurement/general purpose */ | ||
183 | .channel = 4, | ||
184 | .code1 = 82, | ||
185 | .code2 = 900, | ||
186 | .volt1 = 100, | ||
187 | .volt2 = 1100, | ||
188 | }, | ||
189 | [5] = { /* ch 5, external, general purpose */ | ||
190 | .channel = 5, | ||
191 | .code1 = 82, | ||
192 | .code2 = 900, | ||
193 | .volt1 = 100, | ||
194 | .volt2 = 1100, | ||
195 | }, | ||
196 | [6] = { /* ch 6, external, general purpose */ | ||
197 | .channel = 6, | ||
198 | .code1 = 82, | ||
199 | .code2 = 900, | ||
200 | .volt1 = 100, | ||
201 | .volt2 = 1100, | ||
202 | }, | ||
203 | [7] = { /* ch 7, internal, main battery */ | ||
204 | .channel = 7, | ||
205 | .code1 = 614, | ||
206 | .code2 = 941, | ||
207 | .volt1 = 3001, | ||
208 | .volt2 = 4599, | ||
209 | }, | ||
210 | [8] = { /* ch 8, internal, backup battery */ | ||
211 | .channel = 8, | ||
212 | .code1 = 82, | ||
213 | .code2 = 688, | ||
214 | .volt1 = 501, | ||
215 | .volt2 = 4203, | ||
216 | }, | ||
217 | [9] = { /* ch 9, internal, external charger input */ | ||
218 | .channel = 9, | ||
219 | .code1 = 182, | ||
220 | .code2 = 818, | ||
221 | .volt1 = 2001, | ||
222 | .volt2 = 8996, | ||
223 | }, | ||
224 | [10] = { /* ch 10, internal, VBUS */ | ||
225 | .channel = 10, | ||
226 | .code1 = 149, | ||
227 | .code2 = 818, | ||
228 | .volt1 = 1001, | ||
229 | .volt2 = 5497, | ||
230 | }, | ||
231 | [11] = { /* ch 11, internal, VBUS charging current */ | ||
232 | .channel = 11, | ||
233 | }, | ||
234 | /* ch 12, internal, Die temperature */ | ||
235 | /* ch 13, internal, Die temperature */ | ||
236 | [12] = { /* ch 14, internal, USB ID line */ | ||
237 | .channel = 14, | ||
238 | .code1 = 48, | ||
239 | .code2 = 714, | ||
240 | .volt1 = 323, | ||
241 | .volt2 = 4800, | ||
242 | }, | ||
243 | }; | ||
244 | |||
245 | static const struct twl6030_ideal_code | ||
246 | twl6032_ideal[TWL6032_GPADC_USED_CHANNELS] = { | ||
247 | [0] = { /* ch 0, external, battery type, resistor value */ | ||
248 | .channel = 0, | ||
249 | .code1 = 1441, | ||
250 | .code2 = 3276, | ||
251 | .volt1 = 440, | ||
252 | .volt2 = 1000, | ||
253 | }, | ||
254 | [1] = { /* ch 1, external, battery temperature, NTC resistor value */ | ||
255 | .channel = 1, | ||
256 | .code1 = 1441, | ||
257 | .code2 = 3276, | ||
258 | .volt1 = 440, | ||
259 | .volt2 = 1000, | ||
260 | }, | ||
261 | [2] = { /* ch 2, external, audio accessory/general purpose */ | ||
262 | .channel = 2, | ||
263 | .code1 = 1441, | ||
264 | .code2 = 3276, | ||
265 | .volt1 = 660, | ||
266 | .volt2 = 1500, | ||
267 | }, | ||
268 | [3] = { /* ch 3, external, temperature with external diode/general | ||
269 | purpose */ | ||
270 | .channel = 3, | ||
271 | .code1 = 1441, | ||
272 | .code2 = 3276, | ||
273 | .volt1 = 440, | ||
274 | .volt2 = 1000, | ||
275 | }, | ||
276 | [4] = { /* ch 4, external, temperature measurement/general purpose */ | ||
277 | .channel = 4, | ||
278 | .code1 = 1441, | ||
279 | .code2 = 3276, | ||
280 | .volt1 = 440, | ||
281 | .volt2 = 1000, | ||
282 | }, | ||
283 | [5] = { /* ch 5, external, general purpose */ | ||
284 | .channel = 5, | ||
285 | .code1 = 1441, | ||
286 | .code2 = 3276, | ||
287 | .volt1 = 440, | ||
288 | .volt2 = 1000, | ||
289 | }, | ||
290 | [6] = { /* ch 6, external, general purpose */ | ||
291 | .channel = 6, | ||
292 | .code1 = 1441, | ||
293 | .code2 = 3276, | ||
294 | .volt1 = 440, | ||
295 | .volt2 = 1000, | ||
296 | }, | ||
297 | [7] = { /* ch7, internal, system supply */ | ||
298 | .channel = 7, | ||
299 | .code1 = 1441, | ||
300 | .code2 = 3276, | ||
301 | .volt1 = 2200, | ||
302 | .volt2 = 5000, | ||
303 | }, | ||
304 | [8] = { /* ch8, internal, backup battery */ | ||
305 | .channel = 8, | ||
306 | .code1 = 1441, | ||
307 | .code2 = 3276, | ||
308 | .volt1 = 2200, | ||
309 | .volt2 = 5000, | ||
310 | }, | ||
311 | [9] = { /* ch 9, internal, external charger input */ | ||
312 | .channel = 9, | ||
313 | .code1 = 1441, | ||
314 | .code2 = 3276, | ||
315 | .volt1 = 3960, | ||
316 | .volt2 = 9000, | ||
317 | }, | ||
318 | [10] = { /* ch10, internal, VBUS */ | ||
319 | .channel = 10, | ||
320 | .code1 = 150, | ||
321 | .code2 = 751, | ||
322 | .volt1 = 1000, | ||
323 | .volt2 = 5000, | ||
324 | }, | ||
325 | [11] = { /* ch 11, internal, VBUS DC-DC output current */ | ||
326 | .channel = 11, | ||
327 | .code1 = 1441, | ||
328 | .code2 = 3276, | ||
329 | .volt1 = 660, | ||
330 | .volt2 = 1500, | ||
331 | }, | ||
332 | /* ch 12, internal, Die temperature */ | ||
333 | /* ch 13, internal, Die temperature */ | ||
334 | [12] = { /* ch 14, internal, USB ID line */ | ||
335 | .channel = 14, | ||
336 | .code1 = 1441, | ||
337 | .code2 = 3276, | ||
338 | .volt1 = 2420, | ||
339 | .volt2 = 5500, | ||
340 | }, | ||
341 | /* ch 15, internal, test network */ | ||
342 | /* ch 16, internal, test network */ | ||
343 | [13] = { /* ch 17, internal, battery charging current */ | ||
344 | .channel = 17, | ||
345 | }, | ||
346 | [14] = { /* ch 18, internal, battery voltage */ | ||
347 | .channel = 18, | ||
348 | .code1 = 1441, | ||
349 | .code2 = 3276, | ||
350 | .volt1 = 2200, | ||
351 | .volt2 = 5000, | ||
352 | }, | ||
353 | }; | ||
354 | |||
355 | static inline int twl6030_gpadc_write(u8 reg, u8 val) | ||
356 | { | ||
357 | return twl_i2c_write_u8(TWL6030_MODULE_GPADC, val, reg); | ||
358 | } | ||
359 | |||
360 | static inline int twl6030_gpadc_read(u8 reg, u8 *val) | ||
361 | { | ||
362 | |||
363 | return twl_i2c_read(TWL6030_MODULE_GPADC, val, reg, 2); | ||
364 | } | ||
365 | |||
366 | static int twl6030_gpadc_enable_irq(u8 mask) | ||
367 | { | ||
368 | int ret; | ||
369 | |||
370 | ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_LINE_B); | ||
371 | if (ret < 0) | ||
372 | return ret; | ||
373 | |||
374 | ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_STS_B); | ||
375 | |||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | static void twl6030_gpadc_disable_irq(u8 mask) | ||
380 | { | ||
381 | twl6030_interrupt_mask(mask, REG_INT_MSK_LINE_B); | ||
382 | twl6030_interrupt_mask(mask, REG_INT_MSK_STS_B); | ||
383 | } | ||
384 | |||
385 | static irqreturn_t twl6030_gpadc_irq_handler(int irq, void *indio_dev) | ||
386 | { | ||
387 | struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); | ||
388 | |||
389 | complete(&gpadc->irq_complete); | ||
390 | |||
391 | return IRQ_HANDLED; | ||
392 | } | ||
393 | |||
394 | static int twl6030_start_conversion(int channel) | ||
395 | { | ||
396 | return twl6030_gpadc_write(TWL6030_GPADC_CTRL_P1, | ||
397 | TWL6030_GPADC_CTRL_P1_SP1); | ||
398 | } | ||
399 | |||
400 | static int twl6032_start_conversion(int channel) | ||
401 | { | ||
402 | int ret; | ||
403 | |||
404 | ret = twl6030_gpadc_write(TWL6032_GPADC_GPSELECT_ISB, channel); | ||
405 | if (ret) | ||
406 | return ret; | ||
407 | |||
408 | return twl6030_gpadc_write(TWL6032_GPADC_CTRL_P1, | ||
409 | TWL6030_GPADC_CTRL_P1_SP1); | ||
410 | } | ||
411 | |||
412 | static u8 twl6030_channel_to_reg(int channel) | ||
413 | { | ||
414 | return TWL6030_GPADC_GPCH0_LSB + 2 * channel; | ||
415 | } | ||
416 | |||
417 | static u8 twl6032_channel_to_reg(int channel) | ||
418 | { | ||
419 | /* | ||
420 | * for any prior chosen channel, when the conversion is ready | ||
421 | * the result is avalable in GPCH0_LSB, GPCH0_MSB. | ||
422 | */ | ||
423 | |||
424 | return TWL6032_GPADC_GPCH0_LSB; | ||
425 | } | ||
426 | |||
427 | static int twl6030_gpadc_lookup(const struct twl6030_ideal_code *ideal, | ||
428 | int channel, int size) | ||
429 | { | ||
430 | int i; | ||
431 | |||
432 | for (i = 0; i < size; i++) | ||
433 | if (ideal[i].channel == channel) | ||
434 | break; | ||
435 | |||
436 | return i; | ||
437 | } | ||
438 | |||
439 | static int twl6030_channel_calibrated(const struct twl6030_gpadc_platform_data | ||
440 | *pdata, int channel) | ||
441 | { | ||
442 | const struct twl6030_ideal_code *ideal = pdata->ideal; | ||
443 | int i; | ||
444 | |||
445 | i = twl6030_gpadc_lookup(ideal, channel, pdata->nchannels); | ||
446 | /* not calibrated channels have 0 in all structure members */ | ||
447 | return pdata->ideal[i].code2; | ||
448 | } | ||
449 | |||
450 | static int twl6030_gpadc_make_correction(struct twl6030_gpadc_data *gpadc, | ||
451 | int channel, int raw_code) | ||
452 | { | ||
453 | const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; | ||
454 | int corrected_code; | ||
455 | int i; | ||
456 | |||
457 | i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); | ||
458 | corrected_code = ((raw_code * 1000) - | ||
459 | gpadc->twl6030_cal_tbl[i].offset_error) / | ||
460 | gpadc->twl6030_cal_tbl[i].gain_error; | ||
461 | |||
462 | return corrected_code; | ||
463 | } | ||
464 | |||
465 | static int twl6030_gpadc_get_raw(struct twl6030_gpadc_data *gpadc, | ||
466 | int channel, int *res) | ||
467 | { | ||
468 | u8 reg = gpadc->pdata->channel_to_reg(channel); | ||
469 | __le16 val; | ||
470 | int raw_code; | ||
471 | int ret; | ||
472 | |||
473 | ret = twl6030_gpadc_read(reg, (u8 *)&val); | ||
474 | if (ret) { | ||
475 | dev_dbg(gpadc->dev, "unable to read register 0x%X\n", reg); | ||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | raw_code = le16_to_cpu(val); | ||
480 | dev_dbg(gpadc->dev, "GPADC raw code: %d", raw_code); | ||
481 | |||
482 | if (twl6030_channel_calibrated(gpadc->pdata, channel)) | ||
483 | *res = twl6030_gpadc_make_correction(gpadc, channel, raw_code); | ||
484 | else | ||
485 | *res = raw_code; | ||
486 | |||
487 | return ret; | ||
488 | } | ||
489 | |||
490 | static int twl6030_gpadc_get_processed(struct twl6030_gpadc_data *gpadc, | ||
491 | int channel, int *val) | ||
492 | { | ||
493 | const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; | ||
494 | int corrected_code; | ||
495 | int channel_value; | ||
496 | int i; | ||
497 | int ret; | ||
498 | |||
499 | ret = twl6030_gpadc_get_raw(gpadc, channel, &corrected_code); | ||
500 | if (ret) | ||
501 | return ret; | ||
502 | |||
503 | i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); | ||
504 | channel_value = corrected_code * | ||
505 | gpadc->twl6030_cal_tbl[i].gain; | ||
506 | |||
507 | /* Shift back into mV range */ | ||
508 | channel_value /= 1000; | ||
509 | |||
510 | dev_dbg(gpadc->dev, "GPADC corrected code: %d", corrected_code); | ||
511 | dev_dbg(gpadc->dev, "GPADC value: %d", channel_value); | ||
512 | |||
513 | *val = channel_value; | ||
514 | |||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev, | ||
519 | const struct iio_chan_spec *chan, | ||
520 | int *val, int *val2, long mask) | ||
521 | { | ||
522 | struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); | ||
523 | int ret; | ||
524 | long timeout; | ||
525 | |||
526 | mutex_lock(&gpadc->lock); | ||
527 | |||
528 | ret = gpadc->pdata->start_conversion(chan->channel); | ||
529 | if (ret) { | ||
530 | dev_err(gpadc->dev, "failed to start conversion\n"); | ||
531 | goto err; | ||
532 | } | ||
533 | /* wait for conversion to complete */ | ||
534 | timeout = wait_for_completion_interruptible_timeout( | ||
535 | &gpadc->irq_complete, msecs_to_jiffies(5000)); | ||
536 | if (timeout == 0) { | ||
537 | ret = -ETIMEDOUT; | ||
538 | goto err; | ||
539 | } else if (timeout < 0) { | ||
540 | goto err; | ||
541 | ret = -EINTR; | ||
542 | } | ||
543 | |||
544 | switch (mask) { | ||
545 | case IIO_CHAN_INFO_RAW: | ||
546 | ret = twl6030_gpadc_get_raw(gpadc, chan->channel, val); | ||
547 | ret = ret ? -EIO : IIO_VAL_INT; | ||
548 | break; | ||
549 | |||
550 | case IIO_CHAN_INFO_PROCESSED: | ||
551 | ret = twl6030_gpadc_get_processed(gpadc, chan->channel, val); | ||
552 | ret = ret ? -EIO : IIO_VAL_INT; | ||
553 | break; | ||
554 | |||
555 | default: | ||
556 | break; | ||
557 | } | ||
558 | err: | ||
559 | mutex_unlock(&gpadc->lock); | ||
560 | |||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | /* | ||
565 | * The GPADC channels are calibrated using a two point calibration method. | ||
566 | * The channels measured with two known values: volt1 and volt2, and | ||
567 | * ideal corresponding output codes are known: code1, code2. | ||
568 | * The difference(d1, d2) between ideal and measured codes stored in trim | ||
569 | * registers. | ||
570 | * The goal is to find offset and gain of the real curve for each calibrated | ||
571 | * channel. | ||
572 | * gain: k = 1 + ((d2 - d1) / (x2 - x1)) | ||
573 | * offset: b = d1 + (k - 1) * x1 | ||
574 | */ | ||
575 | static void twl6030_calibrate_channel(struct twl6030_gpadc_data *gpadc, | ||
576 | int channel, int d1, int d2) | ||
577 | { | ||
578 | int b, k, gain, x1, x2, i; | ||
579 | const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; | ||
580 | |||
581 | i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); | ||
582 | |||
583 | /* Gain */ | ||
584 | gain = ((ideal[i].volt2 - ideal[i].volt1) * 1000) / | ||
585 | (ideal[i].code2 - ideal[i].code1); | ||
586 | |||
587 | x1 = ideal[i].code1; | ||
588 | x2 = ideal[i].code2; | ||
589 | |||
590 | /* k - real curve gain */ | ||
591 | k = 1000 + (((d2 - d1) * 1000) / (x2 - x1)); | ||
592 | |||
593 | /* b - offset of the real curve gain */ | ||
594 | b = (d1 * 1000) - (k - 1000) * x1; | ||
595 | |||
596 | gpadc->twl6030_cal_tbl[i].gain = gain; | ||
597 | gpadc->twl6030_cal_tbl[i].gain_error = k; | ||
598 | gpadc->twl6030_cal_tbl[i].offset_error = b; | ||
599 | |||
600 | dev_dbg(gpadc->dev, "GPADC d1 for Chn: %d = %d\n", channel, d1); | ||
601 | dev_dbg(gpadc->dev, "GPADC d2 for Chn: %d = %d\n", channel, d2); | ||
602 | dev_dbg(gpadc->dev, "GPADC x1 for Chn: %d = %d\n", channel, x1); | ||
603 | dev_dbg(gpadc->dev, "GPADC x2 for Chn: %d = %d\n", channel, x2); | ||
604 | dev_dbg(gpadc->dev, "GPADC Gain for Chn: %d = %d\n", channel, gain); | ||
605 | dev_dbg(gpadc->dev, "GPADC k for Chn: %d = %d\n", channel, k); | ||
606 | dev_dbg(gpadc->dev, "GPADC b for Chn: %d = %d\n", channel, b); | ||
607 | } | ||
608 | |||
609 | static inline int twl6030_gpadc_get_trim_offset(s8 d) | ||
610 | { | ||
611 | /* | ||
612 | * XXX NOTE! | ||
613 | * bit 0 - sign, bit 7 - reserved, 6..1 - trim value | ||
614 | * though, the documentation states that trim value | ||
615 | * is absolute value, the correct conversion results are | ||
616 | * obtained if the value is interpreted as 2's complement. | ||
617 | */ | ||
618 | __u32 temp = ((d & 0x7f) >> 1) | ((d & 1) << 6); | ||
619 | |||
620 | return sign_extend32(temp, 6); | ||
621 | } | ||
622 | |||
623 | static int twl6030_calibration(struct twl6030_gpadc_data *gpadc) | ||
624 | { | ||
625 | int ret; | ||
626 | int chn; | ||
627 | u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS]; | ||
628 | s8 d1, d2; | ||
629 | |||
630 | /* | ||
631 | * for calibration two measurements have been performed at | ||
632 | * factory, for some channels, during the production test and | ||
633 | * have been stored in registers. This two stored values are | ||
634 | * used to correct the measurements. The values represent | ||
635 | * offsets for the given input from the output on ideal curve. | ||
636 | */ | ||
637 | ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs, | ||
638 | TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS); | ||
639 | if (ret < 0) { | ||
640 | dev_err(gpadc->dev, "calibration failed\n"); | ||
641 | return ret; | ||
642 | } | ||
643 | |||
644 | for (chn = 0; chn < TWL6030_GPADC_MAX_CHANNELS; chn++) { | ||
645 | |||
646 | switch (chn) { | ||
647 | case 0: | ||
648 | d1 = trim_regs[0]; | ||
649 | d2 = trim_regs[1]; | ||
650 | break; | ||
651 | case 1: | ||
652 | case 3: | ||
653 | case 4: | ||
654 | case 5: | ||
655 | case 6: | ||
656 | d1 = trim_regs[4]; | ||
657 | d2 = trim_regs[5]; | ||
658 | break; | ||
659 | case 2: | ||
660 | d1 = trim_regs[12]; | ||
661 | d2 = trim_regs[13]; | ||
662 | break; | ||
663 | case 7: | ||
664 | d1 = trim_regs[6]; | ||
665 | d2 = trim_regs[7]; | ||
666 | break; | ||
667 | case 8: | ||
668 | d1 = trim_regs[2]; | ||
669 | d2 = trim_regs[3]; | ||
670 | break; | ||
671 | case 9: | ||
672 | d1 = trim_regs[8]; | ||
673 | d2 = trim_regs[9]; | ||
674 | break; | ||
675 | case 10: | ||
676 | d1 = trim_regs[10]; | ||
677 | d2 = trim_regs[11]; | ||
678 | break; | ||
679 | case 14: | ||
680 | d1 = trim_regs[14]; | ||
681 | d2 = trim_regs[15]; | ||
682 | break; | ||
683 | default: | ||
684 | continue; | ||
685 | } | ||
686 | |||
687 | d1 = twl6030_gpadc_get_trim_offset(d1); | ||
688 | d2 = twl6030_gpadc_get_trim_offset(d2); | ||
689 | |||
690 | twl6030_calibrate_channel(gpadc, chn, d1, d2); | ||
691 | } | ||
692 | |||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static int twl6032_get_trim_value(u8 *trim_regs, unsigned int reg0, | ||
697 | unsigned int reg1, unsigned int mask0, unsigned int mask1, | ||
698 | unsigned int shift0) | ||
699 | { | ||
700 | int val; | ||
701 | |||
702 | val = (trim_regs[reg0] & mask0) << shift0; | ||
703 | val |= (trim_regs[reg1] & mask1) >> 1; | ||
704 | if (trim_regs[reg1] & 0x01) | ||
705 | val = -val; | ||
706 | |||
707 | return val; | ||
708 | } | ||
709 | |||
710 | static int twl6032_calibration(struct twl6030_gpadc_data *gpadc) | ||
711 | { | ||
712 | int chn, d1 = 0, d2 = 0, temp; | ||
713 | u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS]; | ||
714 | int ret; | ||
715 | |||
716 | ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs, | ||
717 | TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS); | ||
718 | if (ret < 0) { | ||
719 | dev_err(gpadc->dev, "calibration failed\n"); | ||
720 | return ret; | ||
721 | } | ||
722 | |||
723 | /* | ||
724 | * Loop to calculate the value needed for returning voltages from | ||
725 | * GPADC not values. | ||
726 | * | ||
727 | * gain is calculated to 3 decimal places fixed point. | ||
728 | */ | ||
729 | for (chn = 0; chn < TWL6032_GPADC_MAX_CHANNELS; chn++) { | ||
730 | |||
731 | switch (chn) { | ||
732 | case 0: | ||
733 | case 1: | ||
734 | case 2: | ||
735 | case 3: | ||
736 | case 4: | ||
737 | case 5: | ||
738 | case 6: | ||
739 | case 11: | ||
740 | case 14: | ||
741 | d1 = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, | ||
742 | 0x06, 2); | ||
743 | d2 = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, | ||
744 | 0x06, 2); | ||
745 | break; | ||
746 | case 8: | ||
747 | temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, | ||
748 | 0x06, 2); | ||
749 | d1 = temp + twl6032_get_trim_value(trim_regs, 7, 6, | ||
750 | 0x18, 0x1E, 1); | ||
751 | |||
752 | temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3F, | ||
753 | 0x06, 2); | ||
754 | d2 = temp + twl6032_get_trim_value(trim_regs, 9, 7, | ||
755 | 0x1F, 0x06, 2); | ||
756 | break; | ||
757 | case 9: | ||
758 | temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, | ||
759 | 0x06, 2); | ||
760 | d1 = temp + twl6032_get_trim_value(trim_regs, 13, 11, | ||
761 | 0x18, 0x1E, 1); | ||
762 | |||
763 | temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, | ||
764 | 0x06, 2); | ||
765 | d2 = temp + twl6032_get_trim_value(trim_regs, 15, 13, | ||
766 | 0x1F, 0x06, 1); | ||
767 | break; | ||
768 | case 10: | ||
769 | d1 = twl6032_get_trim_value(trim_regs, 10, 8, 0x0f, | ||
770 | 0x0E, 3); | ||
771 | d2 = twl6032_get_trim_value(trim_regs, 14, 12, 0x0f, | ||
772 | 0x0E, 3); | ||
773 | break; | ||
774 | case 7: | ||
775 | case 18: | ||
776 | temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, | ||
777 | 0x06, 2); | ||
778 | |||
779 | d1 = (trim_regs[4] & 0x7E) >> 1; | ||
780 | if (trim_regs[4] & 0x01) | ||
781 | d1 = -d1; | ||
782 | d1 += temp; | ||
783 | |||
784 | temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, | ||
785 | 0x06, 2); | ||
786 | |||
787 | d2 = (trim_regs[5] & 0xFE) >> 1; | ||
788 | if (trim_regs[5] & 0x01) | ||
789 | d2 = -d2; | ||
790 | |||
791 | d2 += temp; | ||
792 | break; | ||
793 | default: | ||
794 | /* No data for other channels */ | ||
795 | continue; | ||
796 | } | ||
797 | |||
798 | twl6030_calibrate_channel(gpadc, chn, d1, d2); | ||
799 | } | ||
800 | |||
801 | return 0; | ||
802 | } | ||
803 | |||
804 | #define TWL6030_GPADC_CHAN(chn, _type, chan_info) { \ | ||
805 | .type = _type, \ | ||
806 | .channel = chn, \ | ||
807 | .info_mask_separate = BIT(chan_info), \ | ||
808 | .indexed = 1, \ | ||
809 | } | ||
810 | |||
811 | static const struct iio_chan_spec twl6030_gpadc_iio_channels[] = { | ||
812 | TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
813 | TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW), | ||
814 | TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
815 | TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
816 | TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW), | ||
817 | TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
818 | TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
819 | TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
820 | TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
821 | TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
822 | TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
823 | TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_RAW), | ||
824 | TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
825 | }; | ||
826 | |||
827 | static const struct iio_chan_spec twl6032_gpadc_iio_channels[] = { | ||
828 | TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
829 | TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW), | ||
830 | TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
831 | TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
832 | TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW), | ||
833 | TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
834 | TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
835 | TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
836 | TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
837 | TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
838 | TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
839 | TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
840 | TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
841 | TWL6030_GPADC_CHAN(17, IIO_VOLTAGE, IIO_CHAN_INFO_RAW), | ||
842 | TWL6030_GPADC_CHAN(18, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), | ||
843 | }; | ||
844 | |||
845 | static const struct iio_info twl6030_gpadc_iio_info = { | ||
846 | .read_raw = &twl6030_gpadc_read_raw, | ||
847 | .driver_module = THIS_MODULE, | ||
848 | }; | ||
849 | |||
850 | static const struct twl6030_gpadc_platform_data twl6030_pdata = { | ||
851 | .iio_channels = twl6030_gpadc_iio_channels, | ||
852 | .nchannels = TWL6030_GPADC_USED_CHANNELS, | ||
853 | .ideal = twl6030_ideal, | ||
854 | .start_conversion = twl6030_start_conversion, | ||
855 | .channel_to_reg = twl6030_channel_to_reg, | ||
856 | .calibrate = twl6030_calibration, | ||
857 | }; | ||
858 | |||
859 | static const struct twl6030_gpadc_platform_data twl6032_pdata = { | ||
860 | .iio_channels = twl6032_gpadc_iio_channels, | ||
861 | .nchannels = TWL6032_GPADC_USED_CHANNELS, | ||
862 | .ideal = twl6032_ideal, | ||
863 | .start_conversion = twl6032_start_conversion, | ||
864 | .channel_to_reg = twl6032_channel_to_reg, | ||
865 | .calibrate = twl6032_calibration, | ||
866 | }; | ||
867 | |||
868 | static const struct of_device_id of_twl6030_match_tbl[] = { | ||
869 | { | ||
870 | .compatible = "ti,twl6030-gpadc", | ||
871 | .data = &twl6030_pdata, | ||
872 | }, | ||
873 | { | ||
874 | .compatible = "ti,twl6032-gpadc", | ||
875 | .data = &twl6032_pdata, | ||
876 | }, | ||
877 | { /* end */ } | ||
878 | }; | ||
879 | |||
880 | static int twl6030_gpadc_probe(struct platform_device *pdev) | ||
881 | { | ||
882 | struct device *dev = &pdev->dev; | ||
883 | struct twl6030_gpadc_data *gpadc; | ||
884 | const struct twl6030_gpadc_platform_data *pdata; | ||
885 | const struct of_device_id *match; | ||
886 | struct iio_dev *indio_dev; | ||
887 | int irq; | ||
888 | int ret; | ||
889 | |||
890 | match = of_match_device(of_match_ptr(of_twl6030_match_tbl), dev); | ||
891 | if (!match) | ||
892 | return -EINVAL; | ||
893 | |||
894 | pdata = match->data; | ||
895 | |||
896 | indio_dev = iio_device_alloc(sizeof(*gpadc)); | ||
897 | if (!indio_dev) { | ||
898 | dev_err(dev, "failed allocating iio device\n"); | ||
899 | ret = -ENOMEM; | ||
900 | } | ||
901 | |||
902 | gpadc = iio_priv(indio_dev); | ||
903 | |||
904 | gpadc->twl6030_cal_tbl = devm_kzalloc(dev, | ||
905 | sizeof(*gpadc->twl6030_cal_tbl) * | ||
906 | pdata->nchannels, GFP_KERNEL); | ||
907 | if (!gpadc->twl6030_cal_tbl) | ||
908 | goto err_free_device; | ||
909 | |||
910 | gpadc->dev = dev; | ||
911 | gpadc->pdata = pdata; | ||
912 | |||
913 | platform_set_drvdata(pdev, indio_dev); | ||
914 | mutex_init(&gpadc->lock); | ||
915 | init_completion(&gpadc->irq_complete); | ||
916 | |||
917 | ret = pdata->calibrate(gpadc); | ||
918 | if (ret < 0) { | ||
919 | dev_err(&pdev->dev, "failed to read calibration registers\n"); | ||
920 | goto err_free_device; | ||
921 | } | ||
922 | |||
923 | irq = platform_get_irq(pdev, 0); | ||
924 | if (irq < 0) { | ||
925 | dev_err(&pdev->dev, "failed to get irq\n"); | ||
926 | goto err_free_device; | ||
927 | } | ||
928 | |||
929 | ret = request_threaded_irq(irq, NULL, twl6030_gpadc_irq_handler, | ||
930 | IRQF_ONESHOT, "twl6030_gpadc", indio_dev); | ||
931 | if (ret) { | ||
932 | dev_dbg(&pdev->dev, "could not request irq\n"); | ||
933 | goto err_free_device; | ||
934 | } | ||
935 | |||
936 | ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); | ||
937 | if (ret < 0) { | ||
938 | dev_err(&pdev->dev, "failed to enable GPADC interrupt\n"); | ||
939 | goto err_free_irq; | ||
940 | } | ||
941 | |||
942 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, | ||
943 | TWL6030_REG_TOGGLE1); | ||
944 | if (ret < 0) { | ||
945 | dev_err(&pdev->dev, "failed to enable GPADC module\n"); | ||
946 | goto err_free_irq; | ||
947 | } | ||
948 | |||
949 | indio_dev->name = DRIVER_NAME; | ||
950 | indio_dev->dev.parent = dev; | ||
951 | indio_dev->info = &twl6030_gpadc_iio_info; | ||
952 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
953 | indio_dev->channels = pdata->iio_channels; | ||
954 | indio_dev->num_channels = pdata->nchannels; | ||
955 | |||
956 | ret = iio_device_register(indio_dev); | ||
957 | if (ret) | ||
958 | goto err_free_irq; | ||
959 | |||
960 | return ret; | ||
961 | |||
962 | err_free_irq: | ||
963 | free_irq(irq, indio_dev); | ||
964 | err_free_device: | ||
965 | iio_device_free(indio_dev); | ||
966 | |||
967 | return ret; | ||
968 | } | ||
969 | |||
970 | static int twl6030_gpadc_remove(struct platform_device *pdev) | ||
971 | { | ||
972 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
973 | |||
974 | twl6030_gpadc_disable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); | ||
975 | free_irq(platform_get_irq(pdev, 0), indio_dev); | ||
976 | iio_device_unregister(indio_dev); | ||
977 | iio_device_free(indio_dev); | ||
978 | |||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | #ifdef CONFIG_PM_SLEEP | ||
983 | static int twl6030_gpadc_suspend(struct device *pdev) | ||
984 | { | ||
985 | int ret; | ||
986 | |||
987 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCR, | ||
988 | TWL6030_REG_TOGGLE1); | ||
989 | if (ret) | ||
990 | dev_err(pdev, "error reseting GPADC (%d)!\n", ret); | ||
991 | |||
992 | return 0; | ||
993 | }; | ||
994 | |||
995 | static int twl6030_gpadc_resume(struct device *pdev) | ||
996 | { | ||
997 | int ret; | ||
998 | |||
999 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, | ||
1000 | TWL6030_REG_TOGGLE1); | ||
1001 | if (ret) | ||
1002 | dev_err(pdev, "error setting GPADC (%d)!\n", ret); | ||
1003 | |||
1004 | return 0; | ||
1005 | }; | ||
1006 | #endif | ||
1007 | |||
1008 | static SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, | ||
1009 | twl6030_gpadc_resume); | ||
1010 | |||
1011 | static struct platform_driver twl6030_gpadc_driver = { | ||
1012 | .probe = twl6030_gpadc_probe, | ||
1013 | .remove = twl6030_gpadc_remove, | ||
1014 | .driver = { | ||
1015 | .name = DRIVER_NAME, | ||
1016 | .owner = THIS_MODULE, | ||
1017 | .pm = &twl6030_gpadc_pm_ops, | ||
1018 | .of_match_table = of_twl6030_match_tbl, | ||
1019 | }, | ||
1020 | }; | ||
1021 | |||
1022 | module_platform_driver(twl6030_gpadc_driver); | ||
1023 | |||
1024 | MODULE_ALIAS("platform: " DRIVER_NAME); | ||
1025 | MODULE_AUTHOR("Balaji T K <balajitk@ti.com>"); | ||
1026 | MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); | ||
1027 | MODULE_AUTHOR("Oleksandr Kozaruk <oleksandr.kozaruk@ti.com"); | ||
1028 | MODULE_DESCRIPTION("twl6030 ADC driver"); | ||
1029 | MODULE_LICENSE("GPL"); | ||