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