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 #include <linux/module.h>
9
10 #include "qcom-vadc-common.h"
11
12 /* Voltage to temperature */
13 static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
14 {1758, -40},
15 {1742, -35},
16 {1719, -30},
17 {1691, -25},
18 {1654, -20},
19 {1608, -15},
20 {1551, -10},
21 {1483, -5},
22 {1404, 0},
23 {1315, 5},
24 {1218, 10},
25 {1114, 15},
26 {1007, 20},
27 {900, 25},
28 {795, 30},
29 {696, 35},
30 {605, 40},
31 {522, 45},
32 {448, 50},
33 {383, 55},
34 {327, 60},
35 {278, 65},
36 {237, 70},
37 {202, 75},
38 {172, 80},
39 {146, 85},
40 {125, 90},
41 {107, 95},
42 {92, 100},
43 {79, 105},
44 {68, 110},
45 {59, 115},
46 {51, 120},
47 {44, 125}
48 };
49
50 /*
51 * Voltage to temperature table for 100k pull up for NTCG104EF104 with
52 * 1.875V reference.
53 */
54 static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
55 { 1831, -40000 },
56 { 1814, -35000 },
57 { 1791, -30000 },
58 { 1761, -25000 },
59 { 1723, -20000 },
60 { 1675, -15000 },
61 { 1616, -10000 },
62 { 1545, -5000 },
63 { 1463, 0 },
64 { 1370, 5000 },
65 { 1268, 10000 },
66 { 1160, 15000 },
67 { 1049, 20000 },
68 { 937, 25000 },
69 { 828, 30000 },
70 { 726, 35000 },
71 { 630, 40000 },
72 { 544, 45000 },
73 { 467, 50000 },
74 { 399, 55000 },
75 { 340, 60000 },
76 { 290, 65000 },
77 { 247, 70000 },
78 { 209, 75000 },
79 { 179, 80000 },
80 { 153, 85000 },
81 { 130, 90000 },
82 { 112, 95000 },
83 { 96, 100000 },
84 { 82, 105000 },
85 { 71, 110000 },
86 { 62, 115000 },
87 { 53, 120000 },
88 { 46, 125000 },
89 };
90
91 static int qcom_vadc_scale_hw_calib_volt(
92 const struct vadc_prescale_ratio *prescale,
93 const struct adc5_data *data,
94 u16 adc_code, int *result_uv);
95 static int qcom_vadc_scale_hw_calib_therm(
96 const struct vadc_prescale_ratio *prescale,
97 const struct adc5_data *data,
98 u16 adc_code, int *result_mdec);
99 static int qcom_vadc_scale_hw_smb_temp(
100 const struct vadc_prescale_ratio *prescale,
101 const struct adc5_data *data,
102 u16 adc_code, int *result_mdec);
103 static int qcom_vadc_scale_hw_chg5_temp(
104 const struct vadc_prescale_ratio *prescale,
105 const struct adc5_data *data,
106 u16 adc_code, int *result_mdec);
107 static int qcom_vadc_scale_hw_calib_die_temp(
108 const struct vadc_prescale_ratio *prescale,
109 const struct adc5_data *data,
110 u16 adc_code, int *result_mdec);
111
112 static struct qcom_adc5_scale_type scale_adc5_fn[] = {
113 [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
114 [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
115 [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
116 [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
117 [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
118 [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
119 };
120
qcom_vadc_map_voltage_temp(const struct vadc_map_pt * pts,u32 tablesize,s32 input,int * output)121 static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
122 u32 tablesize, s32 input, int *output)
123 {
124 bool descending = 1;
125 u32 i = 0;
126
127 if (!pts)
128 return -EINVAL;
129
130 /* Check if table is descending or ascending */
131 if (tablesize > 1) {
132 if (pts[0].x < pts[1].x)
133 descending = 0;
134 }
135
136 while (i < tablesize) {
137 if ((descending) && (pts[i].x < input)) {
138 /* table entry is less than measured*/
139 /* value and table is descending, stop */
140 break;
141 } else if ((!descending) &&
142 (pts[i].x > input)) {
143 /* table entry is greater than measured*/
144 /*value and table is ascending, stop */
145 break;
146 }
147 i++;
148 }
149
150 if (i == 0) {
151 *output = pts[0].y;
152 } else if (i == tablesize) {
153 *output = pts[tablesize - 1].y;
154 } else {
155 /* result is between search_index and search_index-1 */
156 /* interpolate linearly */
157 *output = (((s32)((pts[i].y - pts[i - 1].y) *
158 (input - pts[i - 1].x)) /
159 (pts[i].x - pts[i - 1].x)) +
160 pts[i - 1].y);
161 }
162
163 return 0;
164 }
165
qcom_vadc_scale_calib(const struct vadc_linear_graph * calib_graph,u16 adc_code,bool absolute,s64 * scale_voltage)166 static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
167 u16 adc_code,
168 bool absolute,
169 s64 *scale_voltage)
170 {
171 *scale_voltage = (adc_code - calib_graph->gnd);
172 *scale_voltage *= calib_graph->dx;
173 *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
174 if (absolute)
175 *scale_voltage += calib_graph->dx;
176
177 if (*scale_voltage < 0)
178 *scale_voltage = 0;
179 }
180
qcom_vadc_scale_volt(const struct vadc_linear_graph * calib_graph,const struct vadc_prescale_ratio * prescale,bool absolute,u16 adc_code,int * result_uv)181 static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
182 const struct vadc_prescale_ratio *prescale,
183 bool absolute, u16 adc_code,
184 int *result_uv)
185 {
186 s64 voltage = 0, result = 0;
187
188 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
189
190 voltage = voltage * prescale->den;
191 result = div64_s64(voltage, prescale->num);
192 *result_uv = result;
193
194 return 0;
195 }
196
qcom_vadc_scale_therm(const struct vadc_linear_graph * calib_graph,const struct vadc_prescale_ratio * prescale,bool absolute,u16 adc_code,int * result_mdec)197 static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
198 const struct vadc_prescale_ratio *prescale,
199 bool absolute, u16 adc_code,
200 int *result_mdec)
201 {
202 s64 voltage = 0;
203 int ret;
204
205 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
206
207 if (absolute)
208 voltage = div64_s64(voltage, 1000);
209
210 ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
211 ARRAY_SIZE(adcmap_100k_104ef_104fb),
212 voltage, result_mdec);
213 if (ret)
214 return ret;
215
216 *result_mdec *= 1000;
217
218 return 0;
219 }
220
qcom_vadc_scale_die_temp(const struct vadc_linear_graph * calib_graph,const struct vadc_prescale_ratio * prescale,bool absolute,u16 adc_code,int * result_mdec)221 static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
222 const struct vadc_prescale_ratio *prescale,
223 bool absolute,
224 u16 adc_code, int *result_mdec)
225 {
226 s64 voltage = 0;
227 u64 temp; /* Temporary variable for do_div */
228
229 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
230
231 if (voltage > 0) {
232 temp = voltage * prescale->den;
233 do_div(temp, prescale->num * 2);
234 voltage = temp;
235 } else {
236 voltage = 0;
237 }
238
239 voltage -= KELVINMIL_CELSIUSMIL;
240 *result_mdec = voltage;
241
242 return 0;
243 }
244
qcom_vadc_scale_chg_temp(const struct vadc_linear_graph * calib_graph,const struct vadc_prescale_ratio * prescale,bool absolute,u16 adc_code,int * result_mdec)245 static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
246 const struct vadc_prescale_ratio *prescale,
247 bool absolute,
248 u16 adc_code, int *result_mdec)
249 {
250 s64 voltage = 0, result = 0;
251
252 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
253
254 voltage = voltage * prescale->den;
255 voltage = div64_s64(voltage, prescale->num);
256 voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
257 voltage = (voltage + PMI_CHG_SCALE_2);
258 result = div64_s64(voltage, 1000000);
259 *result_mdec = result;
260
261 return 0;
262 }
263
qcom_vadc_scale_code_voltage_factor(u16 adc_code,const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,unsigned int factor)264 static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
265 const struct vadc_prescale_ratio *prescale,
266 const struct adc5_data *data,
267 unsigned int factor)
268 {
269 s64 voltage, temp, adc_vdd_ref_mv = 1875;
270
271 /*
272 * The normal data range is between 0V to 1.875V. On cases where
273 * we read low voltage values, the ADC code can go beyond the
274 * range and the scale result is incorrect so we clamp the values
275 * for the cases where the code represents a value below 0V
276 */
277 if (adc_code > VADC5_MAX_CODE)
278 adc_code = 0;
279
280 /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
281 voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
282 voltage = div64_s64(voltage, data->full_scale_code_volt);
283 if (voltage > 0) {
284 voltage *= prescale->den;
285 temp = prescale->num * factor;
286 voltage = div64_s64(voltage, temp);
287 } else {
288 voltage = 0;
289 }
290
291 return (int) voltage;
292 }
293
qcom_vadc_scale_hw_calib_volt(const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,u16 adc_code,int * result_uv)294 static int qcom_vadc_scale_hw_calib_volt(
295 const struct vadc_prescale_ratio *prescale,
296 const struct adc5_data *data,
297 u16 adc_code, int *result_uv)
298 {
299 *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
300 prescale, data, 1);
301
302 return 0;
303 }
304
qcom_vadc_scale_hw_calib_therm(const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,u16 adc_code,int * result_mdec)305 static int qcom_vadc_scale_hw_calib_therm(
306 const struct vadc_prescale_ratio *prescale,
307 const struct adc5_data *data,
308 u16 adc_code, int *result_mdec)
309 {
310 int voltage;
311
312 voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
313 prescale, data, 1000);
314
315 /* Map voltage to temperature from look-up table */
316 return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
317 ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
318 voltage, result_mdec);
319 }
320
qcom_vadc_scale_hw_calib_die_temp(const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,u16 adc_code,int * result_mdec)321 static int qcom_vadc_scale_hw_calib_die_temp(
322 const struct vadc_prescale_ratio *prescale,
323 const struct adc5_data *data,
324 u16 adc_code, int *result_mdec)
325 {
326 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
327 prescale, data, 2);
328 *result_mdec -= KELVINMIL_CELSIUSMIL;
329
330 return 0;
331 }
332
qcom_vadc_scale_hw_smb_temp(const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,u16 adc_code,int * result_mdec)333 static int qcom_vadc_scale_hw_smb_temp(
334 const struct vadc_prescale_ratio *prescale,
335 const struct adc5_data *data,
336 u16 adc_code, int *result_mdec)
337 {
338 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
339 prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
340 *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
341
342 return 0;
343 }
344
qcom_vadc_scale_hw_chg5_temp(const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,u16 adc_code,int * result_mdec)345 static int qcom_vadc_scale_hw_chg5_temp(
346 const struct vadc_prescale_ratio *prescale,
347 const struct adc5_data *data,
348 u16 adc_code, int *result_mdec)
349 {
350 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
351 prescale, data, 4);
352 *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
353
354 return 0;
355 }
356
qcom_vadc_scale(enum vadc_scale_fn_type scaletype,const struct vadc_linear_graph * calib_graph,const struct vadc_prescale_ratio * prescale,bool absolute,u16 adc_code,int * result)357 int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
358 const struct vadc_linear_graph *calib_graph,
359 const struct vadc_prescale_ratio *prescale,
360 bool absolute,
361 u16 adc_code, int *result)
362 {
363 switch (scaletype) {
364 case SCALE_DEFAULT:
365 return qcom_vadc_scale_volt(calib_graph, prescale,
366 absolute, adc_code,
367 result);
368 case SCALE_THERM_100K_PULLUP:
369 case SCALE_XOTHERM:
370 return qcom_vadc_scale_therm(calib_graph, prescale,
371 absolute, adc_code,
372 result);
373 case SCALE_PMIC_THERM:
374 return qcom_vadc_scale_die_temp(calib_graph, prescale,
375 absolute, adc_code,
376 result);
377 case SCALE_PMI_CHG_TEMP:
378 return qcom_vadc_scale_chg_temp(calib_graph, prescale,
379 absolute, adc_code,
380 result);
381 default:
382 return -EINVAL;
383 }
384 }
385 EXPORT_SYMBOL(qcom_vadc_scale);
386
qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,const struct vadc_prescale_ratio * prescale,const struct adc5_data * data,u16 adc_code,int * result)387 int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
388 const struct vadc_prescale_ratio *prescale,
389 const struct adc5_data *data,
390 u16 adc_code, int *result)
391 {
392 if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
393 scaletype < SCALE_HW_CALIB_INVALID)) {
394 pr_err("Invalid scale type %d\n", scaletype);
395 return -EINVAL;
396 }
397
398 return scale_adc5_fn[scaletype].scale_fn(prescale, data,
399 adc_code, result);
400 }
401 EXPORT_SYMBOL(qcom_adc5_hw_scale);
402
qcom_vadc_decimation_from_dt(u32 value)403 int qcom_vadc_decimation_from_dt(u32 value)
404 {
405 if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
406 value > VADC_DECIMATION_MAX)
407 return -EINVAL;
408
409 return __ffs64(value / VADC_DECIMATION_MIN);
410 }
411 EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
412
413 MODULE_LICENSE("GPL v2");
414 MODULE_DESCRIPTION("Qualcomm ADC common functionality");
415