blob: b2e9491de4b9dbe27a5323718c6d183890d8e0d2 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14#include <linux/kernel.h>
15#include <linux/err.h>
16#include <linux/module.h>
17#include <linux/mfd/pm8921-adc.h>
18#define KELVINMIL_DEGMIL 273160
19#define PM8921_ADC_SLOPE 10
20#define PM8921_ADC_CODE_SCALE 24576
21
22static const struct pm8921_adc_map_pt adcmap_batttherm[] = {
Siddartha Mohanadoss93249a92011-08-09 14:28:00 -070023 {41001, -30},
24 {40017, -20},
25 {38721, -10},
26 {37186, 0},
27 {35554, 10},
28 {33980, 20},
29 {33253, 25},
30 {32580, 30},
31 {31412, 40},
32 {30481, 50},
33 {29759, 60},
34 {29209, 70},
35 {28794, 80}
36};
37
38static const struct pm8921_adc_map_pt adcmap_btm_threshold[] = {
39 {-30, 41001},
40 {-20, 40017},
41 {-10, 38721},
42 {0, 37186},
43 {10, 35554},
44 {11, 35392},
45 {12, 35230},
46 {13, 35070},
47 {14, 34910},
48 {15, 34751},
49 {16, 34594},
50 {17, 34438},
51 {18, 34284},
52 {19, 34131},
53 {20, 33980},
54 {21, 33830},
55 {22, 33683},
56 {23, 33538},
57 {24, 33394},
58 {25, 33253},
59 {26, 33114},
60 {27, 32977},
61 {28, 32842},
62 {29, 32710},
63 {30, 32580},
64 {31, 32452},
65 {32, 32327},
66 {33, 32204},
67 {34, 32084},
68 {35, 31966},
69 {36, 31850},
70 {37, 31737},
71 {38, 31627},
72 {39, 31518},
73 {40, 31412},
74 {41, 31309},
75 {42, 31208},
76 {43, 31109},
77 {44, 31013},
78 {45, 30918},
79 {46, 30827},
80 {47, 30737},
81 {48, 30649},
82 {49, 30564},
83 {50, 30481},
84 {51, 30400},
85 {52, 30321},
86 {53, 30244},
87 {54, 30169},
88 {55, 30096},
89 {56, 30025},
90 {57, 29956},
91 {58, 29889},
92 {59, 29823},
93 {60, 29759},
94 {61, 29697},
95 {62, 29637},
96 {63, 29578},
97 {64, 29521},
98 {65, 29465},
99 {66, 29411},
100 {67, 29359},
101 {68, 29308},
102 {69, 29258},
103 {70, 29209},
104 {71, 29162},
105 {72, 29117},
106 {73, 29072},
107 {74, 29029},
108 {75, 28987},
109 {76, 28946},
110 {77, 28906},
111 {78, 28868},
112 {79, 28830},
113 {80, 28794}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114};
115
116static const struct pm8921_adc_map_pt adcmap_ntcg_104ef_104fb[] = {
117 {696483, -40960},
118 {649148, -39936},
119 {605368, -38912},
120 {564809, -37888},
121 {527215, -36864},
122 {492322, -35840},
123 {460007, -34816},
124 {429982, -33792},
125 {402099, -32768},
126 {376192, -31744},
127 {352075, -30720},
128 {329714, -29696},
129 {308876, -28672},
130 {289480, -27648},
131 {271417, -26624},
132 {254574, -25600},
133 {238903, -24576},
134 {224276, -23552},
135 {210631, -22528},
136 {197896, -21504},
137 {186007, -20480},
138 {174899, -19456},
139 {164521, -18432},
140 {154818, -17408},
141 {145744, -16384},
142 {137265, -15360},
143 {129307, -14336},
144 {121866, -13312},
145 {114896, -12288},
146 {108365, -11264},
147 {102252, -10240},
148 {96499, -9216},
149 {91111, -8192},
150 {86055, -7168},
151 {81308, -6144},
152 {76857, -5120},
153 {72660, -4096},
154 {68722, -3072},
155 {65020, -2048},
156 {61538, -1024},
157 {58261, 0},
158 {55177, 1024},
159 {52274, 2048},
160 {49538, 3072},
161 {46962, 4096},
162 {44531, 5120},
163 {42243, 6144},
164 {40083, 7168},
165 {38045, 8192},
166 {36122, 9216},
167 {34308, 10240},
168 {32592, 11264},
169 {30972, 12288},
170 {29442, 13312},
171 {27995, 14336},
172 {26624, 15360},
173 {25333, 16384},
174 {24109, 17408},
175 {22951, 18432},
176 {21854, 19456},
177 {20807, 20480},
178 {19831, 21504},
179 {18899, 22528},
180 {18016, 23552},
181 {17178, 24576},
182 {16384, 25600},
183 {15631, 26624},
184 {14916, 27648},
185 {14237, 28672},
186 {13593, 29696},
187 {12976, 30720},
188 {12400, 31744},
189 {11848, 32768},
190 {11324, 33792},
191 {10825, 34816},
192 {10354, 35840},
193 {9900, 36864},
194 {9471, 37888},
195 {9062, 38912},
196 {8674, 39936},
197 {8306, 40960},
198 {7951, 41984},
199 {7616, 43008},
200 {7296, 44032},
201 {6991, 45056},
202 {6701, 46080},
203 {6424, 47104},
204 {6160, 48128},
205 {5908, 49152},
206 {5667, 50176},
207 {5439, 51200},
208 {5219, 52224},
209 {5010, 53248},
210 {4810, 54272},
211 {4619, 55296},
212 {4440, 56320},
213 {4263, 57344},
214 {4097, 58368},
215 {3938, 59392},
216 {3785, 60416},
217 {3637, 61440},
218 {3501, 62464},
219 {3368, 63488},
220 {3240, 64512},
221 {3118, 65536},
222 {2998, 66560},
223 {2889, 67584},
224 {2782, 68608},
225 {2680, 69632},
226 {2581, 70656},
227 {2490, 71680},
228 {2397, 72704},
229 {2310, 73728},
230 {2227, 74752},
231 {2147, 75776},
232 {2064, 76800},
233 {1998, 77824},
234 {1927, 78848},
235 {1860, 79872},
236 {1795, 80896},
237 {1736, 81920},
238 {1673, 82944},
239 {1615, 83968},
240 {1560, 84992},
241 {1507, 86016},
242 {1456, 87040},
243 {1407, 88064},
244 {1360, 89088},
245 {1314, 90112},
246 {1271, 91136},
247 {1228, 92160},
248 {1189, 93184},
249 {1150, 94208},
250 {1112, 95232},
251 {1076, 96256},
252 {1042, 97280},
253 {1008, 98304},
254 {976, 99328},
255 {945, 100352},
256 {915, 101376},
257 {886, 102400},
258 {859, 103424},
259 {832, 104448},
260 {807, 105472},
261 {782, 106496},
262 {756, 107520},
263 {735, 108544},
264 {712, 109568},
265 {691, 110592},
266 {670, 111616},
267 {650, 112640},
268 {631, 113664},
269 {612, 114688},
270 {594, 115712},
271 {577, 116736},
272 {560, 117760},
273 {544, 118784},
274 {528, 119808},
275 {513, 120832},
276 {498, 121856},
277 {483, 122880},
278 {470, 123904},
279 {457, 124928},
280 {444, 125952},
281 {431, 126976},
282 {419, 128000}
283};
284
285static int32_t pm8921_adc_map_linear(const struct pm8921_adc_map_pt *pts,
286 uint32_t tablesize, int32_t input, int64_t *output)
287{
288 bool descending = 1;
289 uint32_t i = 0;
290
291 if ((pts == NULL) || (output == NULL))
292 return -EINVAL;
293
294 /* Check if table is descending or ascending */
295 if (tablesize > 1) {
296 if (pts[0].x < pts[1].x)
297 descending = 0;
298 }
299
300 while (i < tablesize) {
301 if ((descending == 1) && (pts[i].x < input)) {
302 /* table entry is less than measured
303 value and table is descending, stop */
304 break;
305 } else if ((descending == 0) &&
306 (pts[i].x > input)) {
307 /* table entry is greater than measured
308 value and table is ascending, stop */
309 break;
310 } else {
311 i++;
312 }
313 }
314
315 if (i == 0)
316 *output = pts[0].y;
317 else if (i == tablesize)
318 *output = pts[tablesize-1].y;
319 else {
320 /* result is between search_index and search_index-1 */
321 /* interpolate linearly */
322 *output = (((int32_t) ((pts[i].y - pts[i-1].y)*
323 (input - pts[i-1].x))/
324 (pts[i].x - pts[i-1].x))+
325 pts[i-1].y);
326 }
327
328 return 0;
329}
330
331int32_t pm8921_adc_scale_default(int32_t adc_code,
332 const struct pm8921_adc_properties *adc_properties,
333 const struct pm8921_adc_chan_properties *chan_properties,
334 struct pm8921_adc_chan_result *adc_chan_result)
335{
336 bool negative_rawfromoffset = 0;
337 int32_t rawfromoffset = 0;
338
339 if (!chan_properties || !chan_properties->offset_gain_numerator ||
340 !chan_properties->offset_gain_denominator || !adc_properties
341 || !adc_chan_result)
342 return -EINVAL;
343
344 rawfromoffset = adc_code -
345 chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
346
347 adc_chan_result->adc_code = adc_code;
348 if (rawfromoffset < 0) {
349 if (adc_properties->bipolar) {
350 rawfromoffset = -rawfromoffset;
351 negative_rawfromoffset = 1;
352 } else {
353 rawfromoffset = 0;
354 }
355 }
356
357 if (rawfromoffset >= 1 << adc_properties->bitresolution)
358 rawfromoffset = (1 << adc_properties->bitresolution) - 1;
359
360 adc_chan_result->measurement = (int64_t)rawfromoffset *
361 chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
362 chan_properties->offset_gain_denominator;
363
364 /* do_div only perform positive integer division! */
365 do_div(adc_chan_result->measurement,
366 chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
367 chan_properties->offset_gain_numerator);
368
369 if (negative_rawfromoffset)
370 adc_chan_result->measurement = -adc_chan_result->measurement;
371
372 /* Note: adc_chan_result->measurement is in the unit of
373 * adc_properties.adc_reference. For generic channel processing,
374 * channel measurement is a scale/ratio relative to the adc
375 * reference input */
376 adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
377
378 return 0;
379}
380EXPORT_SYMBOL_GPL(pm8921_adc_scale_default);
381
382int32_t pm8921_adc_scale_batt_therm(int32_t adc_code,
383 const struct pm8921_adc_properties *adc_properties,
384 const struct pm8921_adc_chan_properties *chan_properties,
385 struct pm8921_adc_chan_result *adc_chan_result)
386{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 /* convert mV ---> degC using the table */
388 return pm8921_adc_map_linear(
389 adcmap_batttherm,
Siddartha Mohanadoss93249a92011-08-09 14:28:00 -0700390 ARRAY_SIZE(adcmap_batttherm),
391 adc_code,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 &adc_chan_result->physical);
393}
394EXPORT_SYMBOL_GPL(pm8921_adc_scale_batt_therm);
395
396int32_t pm8921_adc_scale_pmic_therm(int32_t adc_code,
397 const struct pm8921_adc_properties *adc_properties,
398 const struct pm8921_adc_chan_properties *chan_properties,
399 struct pm8921_adc_chan_result *adc_chan_result)
400{
401 int32_t rawfromoffset;
402
403 if (!chan_properties || !chan_properties->offset_gain_numerator ||
404 !chan_properties->offset_gain_denominator || !adc_properties
405 || !adc_chan_result)
406 return -EINVAL;
407
408 adc_chan_result->adc_code = adc_code;
409 rawfromoffset = adc_code -
410 chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
411 if (rawfromoffset > 0) {
412 if (rawfromoffset >= 1 << adc_properties->bitresolution)
413 rawfromoffset = (1 << adc_properties->bitresolution)
414 - 1;
415 /* 2mV/K */
416 adc_chan_result->measurement = (int64_t)rawfromoffset*
417 chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
418 chan_properties->offset_gain_denominator * 1000;
419
420 do_div(adc_chan_result->measurement,
421 chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
422 chan_properties->offset_gain_numerator*2);
423 } else {
424 adc_chan_result->measurement = 0;
425 }
426 /* Note: adc_chan_result->measurement is in the unit of
427 adc_properties.adc_reference */
428 adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
429 /* Change to .001 deg C */
430 adc_chan_result->physical -= KELVINMIL_DEGMIL;
431 adc_chan_result->measurement <<= 1;
432
433 return 0;
434}
435EXPORT_SYMBOL_GPL(pm8921_adc_scale_pmic_therm);
436
437/* Scales the ADC code to 0.001 degrees C using the map
438 * table for the XO thermistor.
439 */
440int32_t pm8921_adc_tdkntcg_therm(int32_t adc_code,
441 const struct pm8921_adc_properties *adc_properties,
442 const struct pm8921_adc_chan_properties *chan_properties,
443 struct pm8921_adc_chan_result *adc_chan_result)
444{
445 uint32_t num1, num2, denom, rt_r25;
446 int32_t offset = chan_properties->adc_graph->offset,
447 dy = chan_properties->adc_graph->dy,
448 dx = chan_properties->adc_graph->dx,
449 fullscale_calibrated_adc_code;
450
451 adc_chan_result->adc_code = adc_code;
452 fullscale_calibrated_adc_code = dy + offset;
453 /* The above is a short cut in math that would reduce a lot of
454 computation whereas the below expression
455 (adc_properties->adc_reference*dy+dx*offset+(dx>>1))/dx
456 is a more generic formula when the 2 reference voltages are
457 different than 0 and full scale voltage. */
458
459 if ((dy == 0) || (dx == 0) ||
460 (offset >= fullscale_calibrated_adc_code)) {
461 return -EINVAL;
462 } else {
463 if (adc_code >= fullscale_calibrated_adc_code) {
464 rt_r25 = (uint32_t)-1;
465 } else if (adc_code <= offset) {
466 rt_r25 = 0;
467 } else {
468 /* The formula used is (adc_code of current reading - offset)/
469 * (the calibrated fullscale adc code - adc_code of current
470 * reading). For this channel, at this time, chan_properties->
471 * offset_gain_numerator = chan_properties->
472 * offset_gain_denominator = 1, so no need to incorporate into
473 * the formula even though it could be multiplied/divided by 1
474 * which yields the same result but
475 * expensive on computation. */
476 num1 = (adc_code - offset) << 14;
477 num2 = (fullscale_calibrated_adc_code - adc_code) >> 1;
478 denom = fullscale_calibrated_adc_code - adc_code;
479
480 if ((int)denom <= 0)
481 rt_r25 = 0x7FFFFFFF;
482 else
483 rt_r25 = (num1 + num2) / denom;
484 }
485
486 if (rt_r25 > 0x7FFFFFFF)
487 rt_r25 = 0x7FFFFFFF;
488
489 pm8921_adc_map_linear(adcmap_ntcg_104ef_104fb,
490 sizeof(adcmap_ntcg_104ef_104fb)/
491 sizeof(adcmap_ntcg_104ef_104fb[0]),
492 (int32_t)rt_r25, &adc_chan_result->physical);
493 }
494
495 return 0;
496}
497EXPORT_SYMBOL_GPL(pm8921_adc_tdkntcg_therm);
498
499int32_t pm8921_adc_scale_xtern_chgr_cur(int32_t adc_code,
500 const struct pm8921_adc_properties *adc_properties,
501 const struct pm8921_adc_chan_properties *chan_properties,
502 struct pm8921_adc_chan_result *adc_chan_result)
503{
504 int32_t rawfromoffset = (adc_code - PM8921_ADC_CODE_SCALE)
505 /PM8921_ADC_SLOPE;
506
507 if (!chan_properties || !chan_properties->offset_gain_numerator ||
508 !chan_properties->offset_gain_denominator || !adc_properties
509 || !adc_chan_result)
510 return -EINVAL;
511
512 adc_chan_result->adc_code = adc_code;
513 if (rawfromoffset > 0) {
514 if (rawfromoffset >= 1 << adc_properties->bitresolution)
515 rawfromoffset = (1 << adc_properties->bitresolution)
516 - 1;
517 adc_chan_result->measurement = ((int64_t)rawfromoffset * 5)*
518 chan_properties->offset_gain_denominator;
519 do_div(adc_chan_result->measurement,
520 chan_properties->offset_gain_numerator);
521 } else {
522 adc_chan_result->measurement = 0;
523 }
524 adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
525
526 return 0;
527}
528EXPORT_SYMBOL_GPL(pm8921_adc_scale_xtern_chgr_cur);
529
530int32_t pm8921_adc_batt_scaler(struct pm8921_adc_arb_btm_param *btm_param)
531{
Siddartha Mohanadoss93249a92011-08-09 14:28:00 -0700532 int rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533
Siddartha Mohanadoss93249a92011-08-09 14:28:00 -0700534 rc = pm8921_adc_map_linear(
535 adcmap_btm_threshold,
536 ARRAY_SIZE(adcmap_btm_threshold),
537 btm_param->low_thr_temp,
538 &btm_param->low_thr_voltage);
539
540 if (!rc) {
541 rc = pm8921_adc_map_linear(
542 adcmap_btm_threshold,
543 ARRAY_SIZE(adcmap_btm_threshold),
544 btm_param->high_thr_temp,
545 &btm_param->high_thr_voltage);
546 }
547
548 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549}
550EXPORT_SYMBOL_GPL(pm8921_adc_batt_scaler);