1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/bug.h>
3 #include <linux/kernel.h>
4 #include <linux/bitops.h>
5 #include <linux/math64.h>
6 #include <linux/log2.h>
7 #include <linux/err.h>
8 
9 #include "qcom-vadc-common.h"
10 
11 /* Voltage to temperature */
12 static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
13 	{1758,	-40},
14 	{1742,	-35},
15 	{1719,	-30},
16 	{1691,	-25},
17 	{1654,	-20},
18 	{1608,	-15},
19 	{1551,	-10},
20 	{1483,	-5},
21 	{1404,	0},
22 	{1315,	5},
23 	{1218,	10},
24 	{1114,	15},
25 	{1007,	20},
26 	{900,	25},
27 	{795,	30},
28 	{696,	35},
29 	{605,	40},
30 	{522,	45},
31 	{448,	50},
32 	{383,	55},
33 	{327,	60},
34 	{278,	65},
35 	{237,	70},
36 	{202,	75},
37 	{172,	80},
38 	{146,	85},
39 	{125,	90},
40 	{107,	95},
41 	{92,	100},
42 	{79,	105},
43 	{68,	110},
44 	{59,	115},
45 	{51,	120},
46 	{44,	125}
47 };
48 
49 static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
50 				      u32 tablesize, s32 input, s64 *output)
51 {
52 	bool descending = 1;
53 	u32 i = 0;
54 
55 	if (!pts)
56 		return -EINVAL;
57 
58 	/* Check if table is descending or ascending */
59 	if (tablesize > 1) {
60 		if (pts[0].x < pts[1].x)
61 			descending = 0;
62 	}
63 
64 	while (i < tablesize) {
65 		if ((descending) && (pts[i].x < input)) {
66 			/* table entry is less than measured*/
67 			 /* value and table is descending, stop */
68 			break;
69 		} else if ((!descending) &&
70 				(pts[i].x > input)) {
71 			/* table entry is greater than measured*/
72 			/*value and table is ascending, stop */
73 			break;
74 		}
75 		i++;
76 	}
77 
78 	if (i == 0) {
79 		*output = pts[0].y;
80 	} else if (i == tablesize) {
81 		*output = pts[tablesize - 1].y;
82 	} else {
83 		/* result is between search_index and search_index-1 */
84 		/* interpolate linearly */
85 		*output = (((s32)((pts[i].y - pts[i - 1].y) *
86 			(input - pts[i - 1].x)) /
87 			(pts[i].x - pts[i - 1].x)) +
88 			pts[i - 1].y);
89 	}
90 
91 	return 0;
92 }
93 
94 static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
95 				  u16 adc_code,
96 				  bool absolute,
97 				  s64 *scale_voltage)
98 {
99 	*scale_voltage = (adc_code - calib_graph->gnd);
100 	*scale_voltage *= calib_graph->dx;
101 	*scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
102 	if (absolute)
103 		*scale_voltage += calib_graph->dx;
104 
105 	if (*scale_voltage < 0)
106 		*scale_voltage = 0;
107 }
108 
109 static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
110 				const struct vadc_prescale_ratio *prescale,
111 				bool absolute, u16 adc_code,
112 				int *result_uv)
113 {
114 	s64 voltage = 0, result = 0;
115 
116 	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
117 
118 	voltage = voltage * prescale->den;
119 	result = div64_s64(voltage, prescale->num);
120 	*result_uv = result;
121 
122 	return 0;
123 }
124 
125 static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
126 				 const struct vadc_prescale_ratio *prescale,
127 				 bool absolute, u16 adc_code,
128 				 int *result_mdec)
129 {
130 	s64 voltage = 0, result = 0;
131 	int ret;
132 
133 	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
134 
135 	if (absolute)
136 		voltage = div64_s64(voltage, 1000);
137 
138 	ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
139 					 ARRAY_SIZE(adcmap_100k_104ef_104fb),
140 					 voltage, &result);
141 	if (ret)
142 		return ret;
143 
144 	result *= 1000;
145 	*result_mdec = result;
146 
147 	return 0;
148 }
149 
150 static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
151 				    const struct vadc_prescale_ratio *prescale,
152 				    bool absolute,
153 				    u16 adc_code, int *result_mdec)
154 {
155 	s64 voltage = 0;
156 	u64 temp; /* Temporary variable for do_div */
157 
158 	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
159 
160 	if (voltage > 0) {
161 		temp = voltage * prescale->den;
162 		do_div(temp, prescale->num * 2);
163 		voltage = temp;
164 	} else {
165 		voltage = 0;
166 	}
167 
168 	voltage -= KELVINMIL_CELSIUSMIL;
169 	*result_mdec = voltage;
170 
171 	return 0;
172 }
173 
174 static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
175 				    const struct vadc_prescale_ratio *prescale,
176 				    bool absolute,
177 				    u16 adc_code, int *result_mdec)
178 {
179 	s64 voltage = 0, result = 0;
180 
181 	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
182 
183 	voltage = voltage * prescale->den;
184 	voltage = div64_s64(voltage, prescale->num);
185 	voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
186 	voltage = (voltage + PMI_CHG_SCALE_2);
187 	result =  div64_s64(voltage, 1000000);
188 	*result_mdec = result;
189 
190 	return 0;
191 }
192 
193 int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
194 		    const struct vadc_linear_graph *calib_graph,
195 		    const struct vadc_prescale_ratio *prescale,
196 		    bool absolute,
197 		    u16 adc_code, int *result)
198 {
199 	switch (scaletype) {
200 	case SCALE_DEFAULT:
201 		return qcom_vadc_scale_volt(calib_graph, prescale,
202 					    absolute, adc_code,
203 					    result);
204 	case SCALE_THERM_100K_PULLUP:
205 	case SCALE_XOTHERM:
206 		return qcom_vadc_scale_therm(calib_graph, prescale,
207 					     absolute, adc_code,
208 					     result);
209 	case SCALE_PMIC_THERM:
210 		return qcom_vadc_scale_die_temp(calib_graph, prescale,
211 						absolute, adc_code,
212 						result);
213 	case SCALE_PMI_CHG_TEMP:
214 		return qcom_vadc_scale_chg_temp(calib_graph, prescale,
215 						absolute, adc_code,
216 						result);
217 	default:
218 		return -EINVAL;
219 	}
220 }
221 EXPORT_SYMBOL(qcom_vadc_scale);
222 
223 int qcom_vadc_decimation_from_dt(u32 value)
224 {
225 	if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
226 	    value > VADC_DECIMATION_MAX)
227 		return -EINVAL;
228 
229 	return __ffs64(value / VADC_DECIMATION_MIN);
230 }
231 EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
232