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