123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 |
- /*
- * Copyright (c) 2011-2013, 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/err.h>
- #include <linux/module.h>
- #include <linux/mfd/pm8xxx/pm8xxx-adc.h>
- #define KELVINMIL_DEGMIL 273160
- /* Units for temperature below (on x axis) is in 0.1DegC as
- required by the battery driver. Note the resolution used
- here to compute the table was done for DegC to milli-volts.
- In consideration to limit the size of the table for the given
- temperature range below, the result is linearly interpolated
- and provided to the battery driver in the units desired for
- their framework which is 0.1DegC. True resolution of 0.1DegC
- will result in the below table size to increase by 10 times */
- static const struct pm8xxx_adc_map_pt adcmap_btm_threshold[] = {
- {-300, 1642},
- {-200, 1544},
- {-100, 1414},
- {0, 1260},
- {10, 1244},
- {20, 1228},
- {30, 1212},
- {40, 1195},
- {50, 1179},
- {60, 1162},
- {70, 1146},
- {80, 1129},
- {90, 1113},
- {100, 1097},
- {110, 1080},
- {120, 1064},
- {130, 1048},
- {140, 1032},
- {150, 1016},
- {160, 1000},
- {170, 985},
- {180, 969},
- {190, 954},
- {200, 939},
- {210, 924},
- {220, 909},
- {230, 894},
- {240, 880},
- {250, 866},
- {260, 852},
- {270, 838},
- {280, 824},
- {290, 811},
- {300, 798},
- {310, 785},
- {320, 773},
- {330, 760},
- {340, 748},
- {350, 736},
- {360, 725},
- {370, 713},
- {380, 702},
- {390, 691},
- {400, 681},
- {410, 670},
- {420, 660},
- {430, 650},
- {440, 640},
- {450, 631},
- {460, 622},
- {470, 613},
- {480, 604},
- {490, 595},
- {500, 587},
- {510, 579},
- {520, 571},
- {530, 563},
- {540, 556},
- {550, 548},
- {560, 541},
- {570, 534},
- {580, 527},
- {590, 521},
- {600, 514},
- {610, 508},
- {620, 502},
- {630, 496},
- {640, 490},
- {650, 485},
- {660, 281},
- {670, 274},
- {680, 267},
- {690, 260},
- {700, 254},
- {710, 247},
- {720, 241},
- {730, 235},
- {740, 229},
- {750, 224},
- {760, 218},
- {770, 213},
- {780, 208},
- {790, 203}
- };
- static const struct pm8xxx_adc_map_pt adcmap_pa_therm[] = {
- {1731, -30},
- {1726, -29},
- {1721, -28},
- {1715, -27},
- {1710, -26},
- {1703, -25},
- {1697, -24},
- {1690, -23},
- {1683, -22},
- {1675, -21},
- {1667, -20},
- {1659, -19},
- {1650, -18},
- {1641, -17},
- {1632, -16},
- {1622, -15},
- {1611, -14},
- {1600, -13},
- {1589, -12},
- {1577, -11},
- {1565, -10},
- {1552, -9},
- {1539, -8},
- {1525, -7},
- {1511, -6},
- {1496, -5},
- {1481, -4},
- {1465, -3},
- {1449, -2},
- {1432, -1},
- {1415, 0},
- {1398, 1},
- {1380, 2},
- {1362, 3},
- {1343, 4},
- {1324, 5},
- {1305, 6},
- {1285, 7},
- {1265, 8},
- {1245, 9},
- {1224, 10},
- {1203, 11},
- {1182, 12},
- {1161, 13},
- {1139, 14},
- {1118, 15},
- {1096, 16},
- {1074, 17},
- {1052, 18},
- {1030, 19},
- {1008, 20},
- {986, 21},
- {964, 22},
- {943, 23},
- {921, 24},
- {899, 25},
- {878, 26},
- {857, 27},
- {836, 28},
- {815, 29},
- {794, 30},
- {774, 31},
- {754, 32},
- {734, 33},
- {714, 34},
- {695, 35},
- {676, 36},
- {657, 37},
- {639, 38},
- {621, 39},
- {604, 40},
- {586, 41},
- {570, 42},
- {553, 43},
- {537, 44},
- {521, 45},
- {506, 46},
- {491, 47},
- {476, 48},
- {462, 49},
- {448, 50},
- {435, 51},
- {421, 52},
- {409, 53},
- {396, 54},
- {384, 55},
- {372, 56},
- {361, 57},
- {350, 58},
- {339, 59},
- {329, 60},
- {318, 61},
- {309, 62},
- {299, 63},
- {290, 64},
- {281, 65},
- {272, 66},
- {264, 67},
- {256, 68},
- {248, 69},
- {240, 70},
- {233, 71},
- {226, 72},
- {219, 73},
- {212, 74},
- {206, 75},
- {199, 76},
- {193, 77},
- {187, 78},
- {182, 79},
- {176, 80},
- {171, 81},
- {166, 82},
- {161, 83},
- {156, 84},
- {151, 85},
- {147, 86},
- {142, 87},
- {138, 88},
- {134, 89},
- {130, 90},
- {126, 91},
- {122, 92},
- {119, 93},
- {115, 94},
- {112, 95},
- {109, 96},
- {106, 97},
- {103, 98},
- {100, 99},
- {97, 100},
- {94, 101},
- {91, 102},
- {89, 103},
- {86, 104},
- {84, 105},
- {82, 106},
- {79, 107},
- {77, 108},
- {75, 109},
- {73, 110},
- {71, 111},
- {69, 112},
- {67, 113},
- {65, 114},
- {64, 115},
- {62, 116},
- {60, 117},
- {59, 118},
- {57, 119},
- {56, 120},
- {54, 121},
- {53, 122},
- {51, 123},
- {50, 124},
- {49, 125}
- };
- static const struct pm8xxx_adc_map_pt adcmap_ntcg_104ef_104fb[] = {
- {374682, -40960},
- {360553, -39936},
- {346630, -38912},
- {332940, -37888},
- {319510, -36864},
- {306363, -35840},
- {293521, -34816},
- {281001, -33792},
- {268818, -32768},
- {256987, -31744},
- {245516, -30720},
- {234413, -29696},
- {223685, -28672},
- {213333, -27648},
- {203360, -26624},
- {193763, -25600},
- {184541, -24576},
- {175691, -23552},
- {167205, -22528},
- {159079, -21504},
- {151304, -20480},
- {143872, -19456},
- {136775, -18432},
- {130001, -17408},
- {123542, -16384},
- {117387, -15360},
- {111526, -14336},
- {105946, -13312},
- {100639, -12288},
- {95592, -11264},
- {90795, -10240},
- {86238, -9216},
- {81909, -8192},
- {77800, -7168},
- {73899, -6144},
- {70197, -5120},
- {66685, -4096},
- {63354, -3072},
- {60194, -2048},
- {57198, -1024},
- {54356, 0},
- {51662, 1024},
- {49108, 2048},
- {46687, 3072},
- {44391, 4096},
- {42215, 5120},
- {40151, 6144},
- {38195, 7168},
- {36340, 8192},
- {34582, 9216},
- {32914, 10240},
- {31333, 11264},
- {29833, 12288},
- {28410, 13312},
- {27061, 14336},
- {25781, 15360},
- {24566, 16384},
- {23413, 17408},
- {22319, 18432},
- {21280, 19456},
- {20294, 20480},
- {19358, 21504},
- {18469, 22528},
- {17624, 23552},
- {16822, 24576},
- {16060, 25600},
- {15335, 26624},
- {14646, 27648},
- {13992, 28672},
- {13369, 29696},
- {12777, 30720},
- {12214, 31744},
- {11678, 32768},
- {11168, 33792},
- {10682, 34816},
- {10220, 35840},
- {9780, 36864},
- {9361, 37888},
- {8962, 38912},
- {8582, 39936},
- {8219, 40960},
- {7874, 41984},
- {7545, 43008},
- {7231, 44032},
- {6931, 45056},
- {6646, 46080},
- {6373, 47104},
- {6113, 48128},
- {5865, 49152},
- {5628, 50176},
- {5402, 51200},
- {5185, 52224},
- {4979, 53248},
- {4782, 54272},
- {4593, 55296},
- {4413, 56320},
- {4241, 57344},
- {4076, 58368},
- {3919, 59392},
- {3768, 60416},
- {3624, 61440},
- {3486, 62464},
- {3354, 63488},
- {3227, 64512},
- {3106, 65536},
- {2990, 66560},
- {2879, 67584},
- {2773, 68608},
- {2671, 69632},
- {2573, 70656},
- {2479, 71680},
- {2390, 72704},
- {2303, 73728},
- {2221, 74752},
- {2142, 75776},
- {2066, 76800},
- {1993, 77824},
- {1923, 78848},
- {1855, 79872},
- {1791, 80896},
- {1729, 81920},
- {1669, 82944},
- {1612, 83968},
- {1557, 84992},
- {1504, 86016},
- {1453, 87040},
- {1404, 88064},
- {1357, 89088},
- {1312, 90112},
- {1269, 91136},
- {1227, 92160},
- {1187, 93184},
- {1148, 94208},
- {1111, 95232},
- {1075, 96256},
- {1040, 97280},
- {1007, 98304},
- {975, 99328},
- {944, 100352},
- {914, 101376},
- {886, 102400},
- {858, 103424},
- {831, 104448},
- {806, 105472},
- {781, 106496},
- {757, 107520},
- {734, 108544},
- {712, 109568},
- {690, 110592},
- {670, 111616},
- {650, 112640},
- {630, 113664},
- {612, 114688},
- {594, 115712},
- {576, 116736},
- {559, 117760},
- {543, 118784},
- {527, 119808},
- {512, 120832},
- {498, 121856},
- {483, 122880},
- {470, 123904},
- {456, 124928},
- {444, 125952},
- {431, 126976},
- {419, 128000},
- {408, 129024},
- {396, 130048}
- };
- static int32_t pm8xxx_adc_map_linear(const struct pm8xxx_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;
- }
- static int32_t pm8xxx_adc_map_batt_therm(const struct pm8xxx_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].y < pts[1].y)
- descending = 0;
- }
- while (i < tablesize) {
- if ((descending == 1) && (pts[i].y < input)) {
- /* table entry is less than measured
- value and table is descending, stop */
- break;
- } else if ((descending == 0) && (pts[i].y > input)) {
- /* table entry is greater than measured
- value and table is ascending, stop */
- break;
- } else {
- i++;
- }
- }
- if (i == 0) {
- *output = pts[0].x;
- } else if (i == tablesize) {
- *output = pts[tablesize-1].x;
- } else {
- /* result is between search_index and search_index-1 */
- /* interpolate linearly */
- *output = (((int32_t) ((pts[i].x - pts[i-1].x)*
- (input - pts[i-1].y))/
- (pts[i].y - pts[i-1].y))+
- pts[i-1].x);
- }
- return 0;
- }
- int32_t pm8xxx_adc_scale_default(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties,
- struct pm8xxx_adc_chan_result *adc_chan_result)
- {
- bool negative_rawfromoffset = 0, negative_offset = 0;
- int64_t scale_voltage = 0;
- if (!chan_properties || !chan_properties->offset_gain_numerator ||
- !chan_properties->offset_gain_denominator || !adc_properties
- || !adc_chan_result)
- return -EINVAL;
- scale_voltage = (adc_code -
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd)
- * chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
- if (scale_voltage < 0) {
- negative_offset = 1;
- scale_voltage = -scale_voltage;
- }
- do_div(scale_voltage,
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy);
- if (negative_offset)
- scale_voltage = -scale_voltage;
- scale_voltage += chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
- if (scale_voltage < 0) {
- if (adc_properties->bipolar) {
- scale_voltage = -scale_voltage;
- negative_rawfromoffset = 1;
- } else {
- scale_voltage = 0;
- }
- }
- adc_chan_result->measurement = scale_voltage *
- chan_properties->offset_gain_denominator;
- /* do_div only perform positive integer division! */
- do_div(adc_chan_result->measurement,
- chan_properties->offset_gain_numerator);
- if (negative_rawfromoffset)
- adc_chan_result->measurement = -adc_chan_result->measurement;
- /* 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 = adc_chan_result->measurement;
- return 0;
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_default);
- static int64_t pm8xxx_adc_scale_ratiometric_calib(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties)
- {
- int64_t adc_voltage = 0;
- bool negative_offset = 0;
- if (!chan_properties || !chan_properties->offset_gain_numerator ||
- !chan_properties->offset_gain_denominator || !adc_properties)
- return -EINVAL;
- adc_voltage = (adc_code -
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd)
- * adc_properties->adc_vdd_reference;
- if (adc_voltage < 0) {
- negative_offset = 1;
- adc_voltage = -adc_voltage;
- }
- do_div(adc_voltage,
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy);
- if (negative_offset)
- adc_voltage = -adc_voltage;
- return adc_voltage;
- }
- int32_t pm8xxx_adc_scale_batt_therm(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties,
- struct pm8xxx_adc_chan_result *adc_chan_result)
- {
- int64_t bat_voltage = 0;
- bat_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
- adc_properties, chan_properties);
- return pm8xxx_adc_map_batt_therm(
- adcmap_btm_threshold,
- ARRAY_SIZE(adcmap_btm_threshold),
- bat_voltage,
- &adc_chan_result->physical);
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_batt_therm);
- int32_t pm8xxx_adc_scale_pa_therm(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties,
- struct pm8xxx_adc_chan_result *adc_chan_result)
- {
- int64_t pa_voltage = 0;
- pa_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
- adc_properties, chan_properties);
- return pm8xxx_adc_map_linear(
- adcmap_pa_therm,
- ARRAY_SIZE(adcmap_pa_therm),
- pa_voltage,
- &adc_chan_result->physical);
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_pa_therm);
- int32_t pm8xxx_adc_scale_batt_id(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties,
- struct pm8xxx_adc_chan_result *adc_chan_result)
- {
- int64_t batt_id_voltage = 0;
- batt_id_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
- adc_properties, chan_properties);
- adc_chan_result->physical = batt_id_voltage;
- adc_chan_result->physical = adc_chan_result->measurement;
- return 0;
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_batt_id);
- int32_t pm8xxx_adc_scale_pmic_therm(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties,
- struct pm8xxx_adc_chan_result *adc_chan_result)
- {
- int64_t pmic_voltage = 0;
- bool negative_offset = 0;
- if (!chan_properties || !chan_properties->offset_gain_numerator ||
- !chan_properties->offset_gain_denominator || !adc_properties
- || !adc_chan_result)
- return -EINVAL;
- pmic_voltage = (adc_code -
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd)
- * chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
- if (pmic_voltage < 0) {
- negative_offset = 1;
- pmic_voltage = -pmic_voltage;
- }
- do_div(pmic_voltage,
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy);
- if (negative_offset)
- pmic_voltage = -pmic_voltage;
- pmic_voltage += chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
- if (pmic_voltage > 0) {
- /* 2mV/K */
- adc_chan_result->measurement = pmic_voltage*
- chan_properties->offset_gain_denominator;
- do_div(adc_chan_result->measurement,
- chan_properties->offset_gain_numerator * 2);
- } else {
- adc_chan_result->measurement = 0;
- }
- /* Change to .001 deg C */
- adc_chan_result->measurement -= KELVINMIL_DEGMIL;
- adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
- return 0;
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_pmic_therm);
- /* Scales the ADC code to 0.001 degrees C using the map
- * table for the XO thermistor.
- */
- int32_t pm8xxx_adc_tdkntcg_therm(int32_t adc_code,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties,
- struct pm8xxx_adc_chan_result *adc_chan_result)
- {
- int64_t xo_thm = 0;
- uint32_t num1 = 0;
- uint32_t num2 = 0;
- uint32_t dnum = 0;
- uint32_t rt_r25 = 0;
- if (!chan_properties || !chan_properties->offset_gain_numerator ||
- !chan_properties->offset_gain_denominator || !adc_properties
- || !adc_chan_result)
- return -EINVAL;
- xo_thm = pm8xxx_adc_scale_ratiometric_calib(adc_code,
- adc_properties, chan_properties);
- if (xo_thm < 0)
- xo_thm = -xo_thm;
- num1 = xo_thm << 14;
- num2 = (adc_properties->adc_vdd_reference - xo_thm) >> 1;
- dnum = (adc_properties->adc_vdd_reference - xo_thm);
- if (dnum == 0)
- rt_r25 = 0x7FFFFFFF ;
- else
- rt_r25 = (num1 + num2)/dnum ;
- pm8xxx_adc_map_linear(adcmap_ntcg_104ef_104fb,
- ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
- rt_r25, &adc_chan_result->physical);
- return 0;
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_tdkntcg_therm);
- int32_t pm8xxx_adc_batt_scaler(struct pm8xxx_adc_arb_btm_param *btm_param,
- const struct pm8xxx_adc_properties *adc_properties,
- const struct pm8xxx_adc_chan_properties *chan_properties)
- {
- int rc;
- rc = pm8xxx_adc_map_linear(
- adcmap_btm_threshold,
- ARRAY_SIZE(adcmap_btm_threshold),
- (btm_param->low_thr_temp),
- &btm_param->low_thr_voltage);
- if (rc)
- return rc;
- btm_param->low_thr_voltage *=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy;
- do_div(btm_param->low_thr_voltage, adc_properties->adc_vdd_reference);
- btm_param->low_thr_voltage +=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd;
- rc = pm8xxx_adc_map_linear(
- adcmap_btm_threshold,
- ARRAY_SIZE(adcmap_btm_threshold),
- (btm_param->high_thr_temp),
- &btm_param->high_thr_voltage);
- if (rc)
- return rc;
- btm_param->high_thr_voltage *=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy;
- do_div(btm_param->high_thr_voltage, adc_properties->adc_vdd_reference);
- btm_param->high_thr_voltage +=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd;
- return rc;
- }
- EXPORT_SYMBOL_GPL(pm8xxx_adc_batt_scaler);
|