blob: fe12492499efbc39ae2f3fb0917a2ee24eb8c18d [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14
15#include <linux/msm_adc.h>
16
17#define KELVINMIL_DEGMIL 273160
18
19static const struct adc_map_pt adcmap_batttherm[] = {
20 {2020, -30},
21 {1923, -20},
22 {1796, -10},
23 {1640, 0},
24 {1459, 10},
25 {1260, 20},
26 {1159, 25},
27 {1059, 30},
28 {871, 40},
29 {706, 50},
30 {567, 60},
31 {453, 70},
32 {364, 80}
33};
34
35static const struct adc_map_pt adcmap_msmtherm[] = {
36 {2150, -30},
37 {2107, -20},
38 {2037, -10},
39 {1929, 0},
40 {1776, 10},
41 {1579, 20},
42 {1467, 25},
43 {1349, 30},
44 {1108, 40},
45 {878, 50},
46 {677, 60},
47 {513, 70},
48 {385, 80},
49 {287, 90},
50 {215, 100},
51 {186, 110},
52 {107, 120}
53};
54
55static const struct adc_map_pt adcmap_ntcg104ef104fb[] = {
56 {696483, -40960},
57 {649148, -39936},
58 {605368, -38912},
59 {564809, -37888},
60 {527215, -36864},
61 {492322, -35840},
62 {460007, -34816},
63 {429982, -33792},
64 {402099, -32768},
65 {376192, -31744},
66 {352075, -30720},
67 {329714, -29696},
68 {308876, -28672},
69 {289480, -27648},
70 {271417, -26624},
71 {254574, -25600},
72 {238903, -24576},
73 {224276, -23552},
74 {210631, -22528},
75 {197896, -21504},
76 {186007, -20480},
77 {174899, -19456},
78 {164521, -18432},
79 {154818, -17408},
80 {145744, -16384},
81 {137265, -15360},
82 {129307, -14336},
83 {121866, -13312},
84 {114896, -12288},
85 {108365, -11264},
86 {102252, -10240},
87 {96499, -9216},
88 {91111, -8192},
89 {86055, -7168},
90 {81308, -6144},
91 {76857, -5120},
92 {72660, -4096},
93 {68722, -3072},
94 {65020, -2048},
95 {61538, -1024},
96 {58261, 0},
97 {55177, 1024},
98 {52274, 2048},
99 {49538, 3072},
100 {46962, 4096},
101 {44531, 5120},
102 {42243, 6144},
103 {40083, 7168},
104 {38045, 8192},
105 {36122, 9216},
106 {34308, 10240},
107 {32592, 11264},
108 {30972, 12288},
109 {29442, 13312},
110 {27995, 14336},
111 {26624, 15360},
112 {25333, 16384},
113 {24109, 17408},
114 {22951, 18432},
115 {21854, 19456},
116 {20807, 20480},
117 {19831, 21504},
118 {18899, 22528},
119 {18016, 23552},
120 {17178, 24576},
121 {16384, 25600},
122 {15631, 26624},
123 {14916, 27648},
124 {14237, 28672},
125 {13593, 29696},
126 {12976, 30720},
127 {12400, 31744},
128 {11848, 32768},
129 {11324, 33792},
130 {10825, 34816},
131 {10354, 35840},
132 {9900, 36864},
133 {9471, 37888},
134 {9062, 38912},
135 {8674, 39936},
136 {8306, 40960},
137 {7951, 41984},
138 {7616, 43008},
139 {7296, 44032},
140 {6991, 45056},
141 {6701, 46080},
142 {6424, 47104},
143 {6160, 48128},
144 {5908, 49152},
145 {5667, 50176},
146 {5439, 51200},
147 {5219, 52224},
148 {5010, 53248},
149 {4810, 54272},
150 {4619, 55296},
151 {4440, 56320},
152 {4263, 57344},
153 {4097, 58368},
154 {3938, 59392},
155 {3785, 60416},
156 {3637, 61440},
157 {3501, 62464},
158 {3368, 63488},
159 {3240, 64512},
160 {3118, 65536},
161 {2998, 66560},
162 {2889, 67584},
163 {2782, 68608},
164 {2680, 69632},
165 {2581, 70656},
166 {2490, 71680},
167 {2397, 72704},
168 {2310, 73728},
169 {2227, 74752},
170 {2147, 75776},
171 {2064, 76800},
172 {1998, 77824},
173 {1927, 78848},
174 {1860, 79872},
175 {1795, 80896},
176 {1736, 81920},
177 {1673, 82944},
178 {1615, 83968},
179 {1560, 84992},
180 {1507, 86016},
181 {1456, 87040},
182 {1407, 88064},
183 {1360, 89088},
184 {1314, 90112},
185 {1271, 91136},
186 {1228, 92160},
187 {1189, 93184},
188 {1150, 94208},
189 {1112, 95232},
190 {1076, 96256},
191 {1042, 97280},
192 {1008, 98304},
193 {976, 99328},
194 {945, 100352},
195 {915, 101376},
196 {886, 102400},
197 {859, 103424},
198 {832, 104448},
199 {807, 105472},
200 {782, 106496},
201 {756, 107520},
202 {735, 108544},
203 {712, 109568},
204 {691, 110592},
205 {670, 111616},
206 {650, 112640},
207 {631, 113664},
208 {612, 114688},
209 {594, 115712},
210 {577, 116736},
211 {560, 117760},
212 {544, 118784},
213 {528, 119808},
214 {513, 120832},
215 {498, 121856},
216 {483, 122880},
217 {470, 123904},
218 {457, 124928},
219 {444, 125952},
220 {431, 126976},
221 {419, 128000}
222};
223
224static int32_t
225 adc_map_linear(const struct adc_map_pt *pts,
226 uint32_t tablesize, int32_t input, int64_t *output)
227{
228 bool descending = 1;
229 uint32_t i = 0;
230
231 if ((pts == NULL) || (output == NULL))
232 return -EINVAL;
233
234 /* Check if table is descending or ascending */
235 if (tablesize > 1) {
236 if (pts[0].x < pts[1].x)
237 descending = 0;
238 }
239
240 while (i < tablesize) {
241 if ((descending == 1) && (pts[i].x < input)) {
242 /* table entry is less than measured
243 value and table is descending, stop */
244 break;
245 } else if ((descending == 0) &&
246 (pts[i].x > input)) {
247 /* table entry is greater than measured
248 value and table is ascending, stop */
249 break;
250 } else
251 i++;
252 }
253
254 if (i == 0)
255 *output = pts[0].y;
256 else if (i == tablesize)
257 *output = pts[tablesize-1].y;
258 else {
259 /* result is between search_index and search_index-1 */
260 /* interpolate linearly */
261 *output = (((int32_t) ((pts[i].y - pts[i-1].y)*
262 (input - pts[i-1].x))/
263 (pts[i].x - pts[i-1].x))+
264 pts[i-1].y);
265 }
266
267 return 0;
268}
269
270int32_t scale_default(int32_t adc_code,
271 const struct adc_properties *adc_properties,
272 const struct chan_properties *chan_properties,
273 struct adc_chan_result *adc_chan_result)
274{
275 bool negative_rawfromoffset = 0;
276 int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
277
278 if (!chan_properties->gain_numerator ||
279 !chan_properties->gain_denominator)
280 return -EINVAL;
281
282 adc_chan_result->adc_code = adc_code;
283 if (rawfromoffset < 0) {
284 if (adc_properties->bipolar) {
285 rawfromoffset = (rawfromoffset ^ -1) + 1;
286 negative_rawfromoffset = 1;
287 } else
288 rawfromoffset = 0;
289 }
290
291 if (rawfromoffset >= 1 << adc_properties->bitresolution)
292 rawfromoffset = (1 << adc_properties->bitresolution) - 1;
293
294 adc_chan_result->measurement = (int64_t)rawfromoffset*
295 chan_properties->adc_graph->dx*
296 chan_properties->gain_denominator;
297
298 /* do_div only perform positive integer division! */
299 do_div(adc_chan_result->measurement, chan_properties->adc_graph->dy*
300 chan_properties->gain_numerator);
301
302 if (negative_rawfromoffset)
303 adc_chan_result->measurement =
304 (adc_chan_result->measurement ^ -1) + 1;
305
306 /* Note: adc_chan_result->measurement is in the unit of
307 * adc_properties.adc_reference. For generic channel processing,
308 * channel measurement is a scale/ratio relative to the adc
309 * reference input */
310 adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
311
312 return 0;
313}
314
315int32_t scale_batt_therm(int32_t adc_code,
316 const struct adc_properties *adc_properties,
317 const struct chan_properties *chan_properties,
318 struct adc_chan_result *adc_chan_result)
319{
320 scale_default(adc_code, adc_properties, chan_properties,
321 adc_chan_result);
322 /* convert mV ---> degC using the table */
323 return adc_map_linear(
324 adcmap_batttherm,
325 sizeof(adcmap_batttherm)/sizeof(adcmap_batttherm[0]),
326 adc_chan_result->physical,
327 &adc_chan_result->physical);
328}
329
330int32_t scale_msm_therm(int32_t adc_code,
331 const struct adc_properties *adc_properties,
332 const struct chan_properties *chan_properties,
333 struct adc_chan_result *adc_chan_result)
334{
335 scale_default(adc_code, adc_properties, chan_properties,
336 adc_chan_result);
337 /* convert mV ---> degC using the table */
338 return adc_map_linear(
339 adcmap_msmtherm,
340 sizeof(adcmap_msmtherm)/sizeof(adcmap_msmtherm[0]),
341 adc_chan_result->physical,
342 &adc_chan_result->physical);
343}
344
345int32_t scale_pmic_therm(int32_t adc_code,
346 const struct adc_properties *adc_properties,
347 const struct chan_properties *chan_properties,
348 struct adc_chan_result *adc_chan_result)
349{
350 /* 2mV/K */
351 int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
352
353 if (!chan_properties->gain_numerator ||
354 !chan_properties->gain_denominator)
355 return -EINVAL;
356
357 adc_chan_result->adc_code = adc_code;
358 if (rawfromoffset > 0) {
359 if (rawfromoffset >= 1 << adc_properties->bitresolution)
360 rawfromoffset = (1 << adc_properties->bitresolution)
361 - 1;
362 adc_chan_result->measurement = (int64_t)rawfromoffset*
363 chan_properties->adc_graph->dx*
364 chan_properties->gain_denominator*1000;
365 do_div(adc_chan_result->measurement,
366 chan_properties->adc_graph->dy*
367 chan_properties->gain_numerator*2);
368 } else {
369 adc_chan_result->measurement = 0;
370 }
371 /* Note: adc_chan_result->measurement is in the unit of
372 adc_properties.adc_reference */
373 adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
374 /* Change to .001 deg C */
375 adc_chan_result->physical -= KELVINMIL_DEGMIL;
376 adc_chan_result->measurement <<= 1;
377
378 return 0;
379}
380
381/* Scales the ADC code to 0.001 degrees C using the map
382 * table for the XO thermistor.
383 */
384int32_t tdkntcgtherm(int32_t adc_code,
385 const struct adc_properties *adc_properties,
386 const struct chan_properties *chan_properties,
387 struct adc_chan_result *adc_chan_result)
388{
389 int32_t offset = chan_properties->adc_graph->offset,
390 dy = chan_properties->adc_graph->dy,
391 dx = chan_properties->adc_graph->dx,
392 fullscale_calibrated_adc_code;
393
394 uint32_t rt_r25;
395 uint32_t num1, num2, denom;
396
397 adc_chan_result->adc_code = adc_code;
398 fullscale_calibrated_adc_code = dy + offset;
399 /* The above is a short cut in math that would reduce a lot of
400 computation whereas the below expression
401 (adc_properties->adc_reference*dy+dx*offset+(dx>>1))/dx
402 is a more generic formula when the 2 reference voltages are
403 different than 0 and full scale voltage. */
404
405 if ((dy == 0) || (dx == 0) ||
406 (offset >= fullscale_calibrated_adc_code)) {
407 return -EINVAL;
408 } else {
409 if (adc_code >= fullscale_calibrated_adc_code) {
410 rt_r25 = (uint32_t)-1;
411 } else if (adc_code <= offset) {
412 rt_r25 = 0;
413 } else {
414 /* The formula used is (adc_code of current reading - offset)/
415 * (the calibrated fullscale adc code - adc_code of current reading).
416 * For this channel, at this time, chan_properties->gain_numerator =
417 * chan_properties->gain_denominator = 1, so no need to incorporate
418 * into the formula even though we could and multiply/divide by 1
419 * which yields the same result but expensive on computation. */
420 num1 = (adc_code - offset) << 14;
421 num2 = (fullscale_calibrated_adc_code - adc_code) >> 1;
422 denom = fullscale_calibrated_adc_code - adc_code;
423
424 if ((int)denom <= 0)
425 rt_r25 = 0x7FFFFFFF;
426 else
427 rt_r25 = (num1 + num2) / denom;
428 }
429
430 if (rt_r25 > 0x7FFFFFFF)
431 rt_r25 = 0x7FFFFFFF;
432
433 adc_map_linear(adcmap_ntcg104ef104fb,
434 sizeof(adcmap_ntcg104ef104fb)/sizeof(adcmap_ntcg104ef104fb[0]),
435 (int32_t)rt_r25, &adc_chan_result->physical);
436 }
437
438 return 0;
439}
440
441int32_t scale_xtern_chgr_cur(int32_t adc_code,
442 const struct adc_properties *adc_properties,
443 const struct chan_properties *chan_properties,
444 struct adc_chan_result *adc_chan_result)
445{
446 int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
447
448 if (!chan_properties->gain_numerator ||
449 !chan_properties->gain_denominator)
450 return -EINVAL;
451
452 adc_chan_result->adc_code = adc_code;
453 if (rawfromoffset > 0) {
454 if (rawfromoffset >= 1 << adc_properties->bitresolution)
455 rawfromoffset = (1 << adc_properties->bitresolution)
456 - 1;
457 adc_chan_result->measurement = ((int64_t)rawfromoffset * 5)*
458 chan_properties->adc_graph->dx*
459 chan_properties->gain_denominator;
460 do_div(adc_chan_result->measurement,
461 chan_properties->adc_graph->dy*
462 chan_properties->gain_numerator);
463 } else {
464 adc_chan_result->measurement = 0;
465 }
466 adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
467
468 return 0;
469}