123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- /* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/kernel.h>
- #include <linux/msm_adc.h>
- #define KELVINMIL_DEGMIL 273160
- static const struct adc_map_pt adcmap_batttherm[] = {
- {2020, -30},
- {1923, -20},
- {1796, -10},
- {1640, 0},
- {1459, 10},
- {1260, 20},
- {1159, 25},
- {1059, 30},
- {871, 40},
- {706, 50},
- {567, 60},
- {453, 70},
- {364, 80}
- };
- static const struct adc_map_pt adcmap_msmtherm[] = {
- {2150, -30},
- {2107, -20},
- {2037, -10},
- {1929, 0},
- {1776, 10},
- {1579, 20},
- {1467, 25},
- {1349, 30},
- {1108, 40},
- {878, 50},
- {677, 60},
- {513, 70},
- {385, 80},
- {287, 90},
- {215, 100},
- {186, 110},
- {107, 120}
- };
- static const struct adc_map_pt adcmap_ntcg104ef104fb[] = {
- {696483, -40960},
- {649148, -39936},
- {605368, -38912},
- {564809, -37888},
- {527215, -36864},
- {492322, -35840},
- {460007, -34816},
- {429982, -33792},
- {402099, -32768},
- {376192, -31744},
- {352075, -30720},
- {329714, -29696},
- {308876, -28672},
- {289480, -27648},
- {271417, -26624},
- {254574, -25600},
- {238903, -24576},
- {224276, -23552},
- {210631, -22528},
- {197896, -21504},
- {186007, -20480},
- {174899, -19456},
- {164521, -18432},
- {154818, -17408},
- {145744, -16384},
- {137265, -15360},
- {129307, -14336},
- {121866, -13312},
- {114896, -12288},
- {108365, -11264},
- {102252, -10240},
- {96499, -9216},
- {91111, -8192},
- {86055, -7168},
- {81308, -6144},
- {76857, -5120},
- {72660, -4096},
- {68722, -3072},
- {65020, -2048},
- {61538, -1024},
- {58261, 0},
- {55177, 1024},
- {52274, 2048},
- {49538, 3072},
- {46962, 4096},
- {44531, 5120},
- {42243, 6144},
- {40083, 7168},
- {38045, 8192},
- {36122, 9216},
- {34308, 10240},
- {32592, 11264},
- {30972, 12288},
- {29442, 13312},
- {27995, 14336},
- {26624, 15360},
- {25333, 16384},
- {24109, 17408},
- {22951, 18432},
- {21854, 19456},
- {20807, 20480},
- {19831, 21504},
- {18899, 22528},
- {18016, 23552},
- {17178, 24576},
- {16384, 25600},
- {15631, 26624},
- {14916, 27648},
- {14237, 28672},
- {13593, 29696},
- {12976, 30720},
- {12400, 31744},
- {11848, 32768},
- {11324, 33792},
- {10825, 34816},
- {10354, 35840},
- {9900, 36864},
- {9471, 37888},
- {9062, 38912},
- {8674, 39936},
- {8306, 40960},
- {7951, 41984},
- {7616, 43008},
- {7296, 44032},
- {6991, 45056},
- {6701, 46080},
- {6424, 47104},
- {6160, 48128},
- {5908, 49152},
- {5667, 50176},
- {5439, 51200},
- {5219, 52224},
- {5010, 53248},
- {4810, 54272},
- {4619, 55296},
- {4440, 56320},
- {4263, 57344},
- {4097, 58368},
- {3938, 59392},
- {3785, 60416},
- {3637, 61440},
- {3501, 62464},
- {3368, 63488},
- {3240, 64512},
- {3118, 65536},
- {2998, 66560},
- {2889, 67584},
- {2782, 68608},
- {2680, 69632},
- {2581, 70656},
- {2490, 71680},
- {2397, 72704},
- {2310, 73728},
- {2227, 74752},
- {2147, 75776},
- {2064, 76800},
- {1998, 77824},
- {1927, 78848},
- {1860, 79872},
- {1795, 80896},
- {1736, 81920},
- {1673, 82944},
- {1615, 83968},
- {1560, 84992},
- {1507, 86016},
- {1456, 87040},
- {1407, 88064},
- {1360, 89088},
- {1314, 90112},
- {1271, 91136},
- {1228, 92160},
- {1189, 93184},
- {1150, 94208},
- {1112, 95232},
- {1076, 96256},
- {1042, 97280},
- {1008, 98304},
- {976, 99328},
- {945, 100352},
- {915, 101376},
- {886, 102400},
- {859, 103424},
- {832, 104448},
- {807, 105472},
- {782, 106496},
- {756, 107520},
- {735, 108544},
- {712, 109568},
- {691, 110592},
- {670, 111616},
- {650, 112640},
- {631, 113664},
- {612, 114688},
- {594, 115712},
- {577, 116736},
- {560, 117760},
- {544, 118784},
- {528, 119808},
- {513, 120832},
- {498, 121856},
- {483, 122880},
- {470, 123904},
- {457, 124928},
- {444, 125952},
- {431, 126976},
- {419, 128000}
- };
- static int32_t
- adc_map_linear(const struct adc_map_pt *pts,
- uint32_t tablesize, int32_t input, int64_t *output)
- {
- bool descending = 1;
- uint32_t i = 0;
- if ((pts == NULL) || (output == NULL))
- return -EINVAL;
- /* Check if table is descending or ascending */
- if (tablesize > 1) {
- if (pts[0].x < pts[1].x)
- descending = 0;
- }
- while (i < tablesize) {
- if ((descending == 1) && (pts[i].x < input)) {
- /* table entry is less than measured
- value and table is descending, stop */
- break;
- } else if ((descending == 0) &&
- (pts[i].x > input)) {
- /* table entry is greater than measured
- value and table is ascending, stop */
- break;
- } else
- i++;
- }
- if (i == 0)
- *output = pts[0].y;
- else if (i == tablesize)
- *output = pts[tablesize-1].y;
- else {
- /* result is between search_index and search_index-1 */
- /* interpolate linearly */
- *output = (((int32_t) ((pts[i].y - pts[i-1].y)*
- (input - pts[i-1].x))/
- (pts[i].x - pts[i-1].x))+
- pts[i-1].y);
- }
- return 0;
- }
- int32_t scale_default(int32_t adc_code,
- const struct adc_properties *adc_properties,
- const struct chan_properties *chan_properties,
- struct adc_chan_result *adc_chan_result)
- {
- bool negative_rawfromoffset = 0;
- int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
- if (!chan_properties->gain_numerator ||
- !chan_properties->gain_denominator)
- return -EINVAL;
- adc_chan_result->adc_code = adc_code;
- if (rawfromoffset < 0) {
- if (adc_properties->bipolar) {
- rawfromoffset = (rawfromoffset ^ -1) + 1;
- negative_rawfromoffset = 1;
- } else
- rawfromoffset = 0;
- }
- if (rawfromoffset >= 1 << adc_properties->bitresolution)
- rawfromoffset = (1 << adc_properties->bitresolution) - 1;
- adc_chan_result->measurement = (int64_t)rawfromoffset*
- chan_properties->adc_graph->dx*
- chan_properties->gain_denominator;
- /* do_div only perform positive integer division! */
- do_div(adc_chan_result->measurement, chan_properties->adc_graph->dy*
- chan_properties->gain_numerator);
- if (negative_rawfromoffset)
- adc_chan_result->measurement =
- (adc_chan_result->measurement ^ -1) + 1;
- /* Note: adc_chan_result->measurement is in the unit of
- * adc_properties.adc_reference. For generic channel processing,
- * channel measurement is a scale/ratio relative to the adc
- * reference input */
- adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
- return 0;
- }
- int32_t scale_batt_therm(int32_t adc_code,
- const struct adc_properties *adc_properties,
- const struct chan_properties *chan_properties,
- struct adc_chan_result *adc_chan_result)
- {
- scale_default(adc_code, adc_properties, chan_properties,
- adc_chan_result);
- /* convert mV ---> degC using the table */
- return adc_map_linear(
- adcmap_batttherm,
- sizeof(adcmap_batttherm)/sizeof(adcmap_batttherm[0]),
- adc_chan_result->physical,
- &adc_chan_result->physical);
- }
- int32_t scale_msm_therm(int32_t adc_code,
- const struct adc_properties *adc_properties,
- const struct chan_properties *chan_properties,
- struct adc_chan_result *adc_chan_result)
- {
- scale_default(adc_code, adc_properties, chan_properties,
- adc_chan_result);
- /* convert mV ---> degC using the table */
- return adc_map_linear(
- adcmap_msmtherm,
- sizeof(adcmap_msmtherm)/sizeof(adcmap_msmtherm[0]),
- adc_chan_result->physical,
- &adc_chan_result->physical);
- }
- int32_t scale_pmic_therm(int32_t adc_code,
- const struct adc_properties *adc_properties,
- const struct chan_properties *chan_properties,
- struct adc_chan_result *adc_chan_result)
- {
- /* 2mV/K */
- int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
- if (!chan_properties->gain_numerator ||
- !chan_properties->gain_denominator)
- return -EINVAL;
- adc_chan_result->adc_code = adc_code;
- if (rawfromoffset > 0) {
- if (rawfromoffset >= 1 << adc_properties->bitresolution)
- rawfromoffset = (1 << adc_properties->bitresolution)
- - 1;
- adc_chan_result->measurement = (int64_t)rawfromoffset*
- chan_properties->adc_graph->dx*
- chan_properties->gain_denominator*1000;
- do_div(adc_chan_result->measurement,
- chan_properties->adc_graph->dy*
- chan_properties->gain_numerator*2);
- } else {
- adc_chan_result->measurement = 0;
- }
- /* Note: adc_chan_result->measurement is in the unit of
- adc_properties.adc_reference */
- adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
- /* Change to .001 deg C */
- adc_chan_result->physical -= KELVINMIL_DEGMIL;
- adc_chan_result->measurement <<= 1;
- return 0;
- }
- /* Scales the ADC code to 0.001 degrees C using the map
- * table for the XO thermistor.
- */
- int32_t tdkntcgtherm(int32_t adc_code,
- const struct adc_properties *adc_properties,
- const struct chan_properties *chan_properties,
- struct adc_chan_result *adc_chan_result)
- {
- int32_t offset = chan_properties->adc_graph->offset,
- dy = chan_properties->adc_graph->dy,
- dx = chan_properties->adc_graph->dx,
- fullscale_calibrated_adc_code;
- uint32_t rt_r25;
- uint32_t num1, num2, denom;
- adc_chan_result->adc_code = adc_code;
- fullscale_calibrated_adc_code = dy + offset;
- /* The above is a short cut in math that would reduce a lot of
- computation whereas the below expression
- (adc_properties->adc_reference*dy+dx*offset+(dx>>1))/dx
- is a more generic formula when the 2 reference voltages are
- different than 0 and full scale voltage. */
- if ((dy == 0) || (dx == 0) ||
- (offset >= fullscale_calibrated_adc_code)) {
- return -EINVAL;
- } else {
- if (adc_code >= fullscale_calibrated_adc_code) {
- rt_r25 = (uint32_t)-1;
- } else if (adc_code <= offset) {
- rt_r25 = 0;
- } else {
- /* The formula used is (adc_code of current reading - offset)/
- * (the calibrated fullscale adc code - adc_code of current reading).
- * For this channel, at this time, chan_properties->gain_numerator =
- * chan_properties->gain_denominator = 1, so no need to incorporate
- * into the formula even though we could and multiply/divide by 1
- * which yields the same result but expensive on computation. */
- num1 = (adc_code - offset) << 14;
- num2 = (fullscale_calibrated_adc_code - adc_code) >> 1;
- denom = fullscale_calibrated_adc_code - adc_code;
- if ((int)denom <= 0)
- rt_r25 = 0x7FFFFFFF;
- else
- rt_r25 = (num1 + num2) / denom;
- }
- if (rt_r25 > 0x7FFFFFFF)
- rt_r25 = 0x7FFFFFFF;
- adc_map_linear(adcmap_ntcg104ef104fb,
- sizeof(adcmap_ntcg104ef104fb)/sizeof(adcmap_ntcg104ef104fb[0]),
- (int32_t)rt_r25, &adc_chan_result->physical);
- }
- return 0;
- }
- int32_t scale_xtern_chgr_cur(int32_t adc_code,
- const struct adc_properties *adc_properties,
- const struct chan_properties *chan_properties,
- struct adc_chan_result *adc_chan_result)
- {
- int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
- if (!chan_properties->gain_numerator ||
- !chan_properties->gain_denominator)
- return -EINVAL;
- adc_chan_result->adc_code = adc_code;
- if (rawfromoffset > 0) {
- if (rawfromoffset >= 1 << adc_properties->bitresolution)
- rawfromoffset = (1 << adc_properties->bitresolution)
- - 1;
- adc_chan_result->measurement = ((int64_t)rawfromoffset * 5)*
- chan_properties->adc_graph->dx*
- chan_properties->gain_denominator;
- do_div(adc_chan_result->measurement,
- chan_properties->adc_graph->dy*
- chan_properties->gain_numerator);
- } else {
- adc_chan_result->measurement = 0;
- }
- adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
- return 0;
- }
|