Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/hwmon/m_adcproc.c b/drivers/hwmon/m_adcproc.c
new file mode 100644
index 0000000..70e505e
--- /dev/null
+++ b/drivers/hwmon/m_adcproc.c
@@ -0,0 +1,469 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. 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;
+}