470 lines
11 KiB
C
470 lines
11 KiB
C
|
/* 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;
|
||
|
}
|