1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/kernel.h>
4 #include <linux/bitops.h>
5 #include <linux/math64.h>
6 #include <linux/log2.h>
8 #include <linux/module.h>
9 #include <linux/units.h>
11 #include "qcom-vadc-common.h"
13 /* Voltage to temperature */
14 static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
52 * Voltage to temperature table for 100k pull up for NTCG104EF104 with
55 static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
92 static const struct vadc_map_pt adcmap7_die_temp[] = {
108 * Resistance to temperature table for 100k pull up for NTCG104EF104.
110 static const struct vadc_map_pt adcmap7_100k[] = {
281 static int qcom_vadc_scale_hw_calib_volt(
282 const struct vadc_prescale_ratio *prescale,
283 const struct adc5_data *data,
284 u16 adc_code, int *result_uv);
285 static int qcom_vadc_scale_hw_calib_therm(
286 const struct vadc_prescale_ratio *prescale,
287 const struct adc5_data *data,
288 u16 adc_code, int *result_mdec);
289 static int qcom_vadc7_scale_hw_calib_therm(
290 const struct vadc_prescale_ratio *prescale,
291 const struct adc5_data *data,
292 u16 adc_code, int *result_mdec);
293 static int qcom_vadc_scale_hw_smb_temp(
294 const struct vadc_prescale_ratio *prescale,
295 const struct adc5_data *data,
296 u16 adc_code, int *result_mdec);
297 static int qcom_vadc_scale_hw_chg5_temp(
298 const struct vadc_prescale_ratio *prescale,
299 const struct adc5_data *data,
300 u16 adc_code, int *result_mdec);
301 static int qcom_vadc_scale_hw_calib_die_temp(
302 const struct vadc_prescale_ratio *prescale,
303 const struct adc5_data *data,
304 u16 adc_code, int *result_mdec);
305 static int qcom_vadc7_scale_hw_calib_die_temp(
306 const struct vadc_prescale_ratio *prescale,
307 const struct adc5_data *data,
308 u16 adc_code, int *result_mdec);
310 static struct qcom_adc5_scale_type scale_adc5_fn[] = {
311 [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
312 [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
313 [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
314 [SCALE_HW_CALIB_THERM_100K_PU_PM7] = {
315 qcom_vadc7_scale_hw_calib_therm},
316 [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
317 [SCALE_HW_CALIB_PMIC_THERM_PM7] = {
318 qcom_vadc7_scale_hw_calib_die_temp},
319 [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
320 [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
323 static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
324 u32 tablesize, s32 input, int *output)
332 /* Check if table is descending or ascending */
334 if (pts[0].x < pts[1].x)
338 while (i < tablesize) {
339 if ((descending) && (pts[i].x < input)) {
340 /* table entry is less than measured*/
341 /* value and table is descending, stop */
343 } else if ((!descending) &&
344 (pts[i].x > input)) {
345 /* table entry is greater than measured*/
346 /*value and table is ascending, stop */
354 } else if (i == tablesize) {
355 *output = pts[tablesize - 1].y;
357 /* result is between search_index and search_index-1 */
358 /* interpolate linearly */
359 *output = (((s32)((pts[i].y - pts[i - 1].y) *
360 (input - pts[i - 1].x)) /
361 (pts[i].x - pts[i - 1].x)) +
368 static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
373 *scale_voltage = (adc_code - calib_graph->gnd);
374 *scale_voltage *= calib_graph->dx;
375 *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
377 *scale_voltage += calib_graph->dx;
379 if (*scale_voltage < 0)
383 static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
384 const struct vadc_prescale_ratio *prescale,
385 bool absolute, u16 adc_code,
388 s64 voltage = 0, result = 0;
390 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
392 voltage = voltage * prescale->den;
393 result = div64_s64(voltage, prescale->num);
399 static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
400 const struct vadc_prescale_ratio *prescale,
401 bool absolute, u16 adc_code,
407 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
410 voltage = div64_s64(voltage, 1000);
412 ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
413 ARRAY_SIZE(adcmap_100k_104ef_104fb),
414 voltage, result_mdec);
418 *result_mdec *= 1000;
423 static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
424 const struct vadc_prescale_ratio *prescale,
426 u16 adc_code, int *result_mdec)
429 u64 temp; /* Temporary variable for do_div */
431 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
434 temp = voltage * prescale->den;
435 do_div(temp, prescale->num * 2);
441 *result_mdec = milli_kelvin_to_millicelsius(voltage);
446 static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
447 const struct vadc_prescale_ratio *prescale,
449 u16 adc_code, int *result_mdec)
451 s64 voltage = 0, result = 0;
453 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
455 voltage = voltage * prescale->den;
456 voltage = div64_s64(voltage, prescale->num);
457 voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
458 voltage = (voltage + PMI_CHG_SCALE_2);
459 result = div64_s64(voltage, 1000000);
460 *result_mdec = result;
465 static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
466 const struct vadc_prescale_ratio *prescale,
467 const struct adc5_data *data,
470 s64 voltage, temp, adc_vdd_ref_mv = 1875;
473 * The normal data range is between 0V to 1.875V. On cases where
474 * we read low voltage values, the ADC code can go beyond the
475 * range and the scale result is incorrect so we clamp the values
476 * for the cases where the code represents a value below 0V
478 if (adc_code > VADC5_MAX_CODE)
481 /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
482 voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
483 voltage = div64_s64(voltage, data->full_scale_code_volt);
485 voltage *= prescale->den;
486 temp = prescale->num * factor;
487 voltage = div64_s64(voltage, temp);
492 return (int) voltage;
495 static int qcom_vadc7_scale_hw_calib_therm(
496 const struct vadc_prescale_ratio *prescale,
497 const struct adc5_data *data,
498 u16 adc_code, int *result_mdec)
500 s64 resistance = adc_code;
503 if (adc_code >= RATIO_MAX_ADC7)
506 /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
507 resistance *= R_PU_100K;
508 resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code);
510 ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
511 ARRAY_SIZE(adcmap7_100k),
512 resistance, &result);
516 *result_mdec = result;
521 static int qcom_vadc_scale_hw_calib_volt(
522 const struct vadc_prescale_ratio *prescale,
523 const struct adc5_data *data,
524 u16 adc_code, int *result_uv)
526 *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
532 static int qcom_vadc_scale_hw_calib_therm(
533 const struct vadc_prescale_ratio *prescale,
534 const struct adc5_data *data,
535 u16 adc_code, int *result_mdec)
539 voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
540 prescale, data, 1000);
542 /* Map voltage to temperature from look-up table */
543 return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
544 ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
545 voltage, result_mdec);
548 static int qcom_vadc_scale_hw_calib_die_temp(
549 const struct vadc_prescale_ratio *prescale,
550 const struct adc5_data *data,
551 u16 adc_code, int *result_mdec)
553 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
555 *result_mdec = milli_kelvin_to_millicelsius(*result_mdec);
560 static int qcom_vadc7_scale_hw_calib_die_temp(
561 const struct vadc_prescale_ratio *prescale,
562 const struct adc5_data *data,
563 u16 adc_code, int *result_mdec)
566 int voltage, vtemp0, temp, i;
568 voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
571 if (adcmap7_die_temp[0].x > voltage) {
572 *result_mdec = DIE_TEMP_ADC7_SCALE_1;
576 if (adcmap7_die_temp[ARRAY_SIZE(adcmap7_die_temp) - 1].x <= voltage) {
577 *result_mdec = DIE_TEMP_ADC7_MAX;
581 for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
582 if (adcmap7_die_temp[i].x > voltage)
585 vtemp0 = adcmap7_die_temp[i - 1].x;
586 voltage = voltage - vtemp0;
587 temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
588 adcmap7_die_temp[i - 1].y);
589 temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
595 static int qcom_vadc_scale_hw_smb_temp(
596 const struct vadc_prescale_ratio *prescale,
597 const struct adc5_data *data,
598 u16 adc_code, int *result_mdec)
600 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
601 prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
602 *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
607 static int qcom_vadc_scale_hw_chg5_temp(
608 const struct vadc_prescale_ratio *prescale,
609 const struct adc5_data *data,
610 u16 adc_code, int *result_mdec)
612 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
614 *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
619 int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
620 const struct vadc_linear_graph *calib_graph,
621 const struct vadc_prescale_ratio *prescale,
623 u16 adc_code, int *result)
627 return qcom_vadc_scale_volt(calib_graph, prescale,
630 case SCALE_THERM_100K_PULLUP:
632 return qcom_vadc_scale_therm(calib_graph, prescale,
635 case SCALE_PMIC_THERM:
636 return qcom_vadc_scale_die_temp(calib_graph, prescale,
639 case SCALE_PMI_CHG_TEMP:
640 return qcom_vadc_scale_chg_temp(calib_graph, prescale,
647 EXPORT_SYMBOL(qcom_vadc_scale);
649 int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
650 const struct vadc_prescale_ratio *prescale,
651 const struct adc5_data *data,
652 u16 adc_code, int *result)
654 if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
655 scaletype < SCALE_HW_CALIB_INVALID)) {
656 pr_err("Invalid scale type %d\n", scaletype);
660 return scale_adc5_fn[scaletype].scale_fn(prescale, data,
663 EXPORT_SYMBOL(qcom_adc5_hw_scale);
665 int qcom_vadc_decimation_from_dt(u32 value)
667 if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
668 value > VADC_DECIMATION_MAX)
671 return __ffs64(value / VADC_DECIMATION_MIN);
673 EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
675 MODULE_LICENSE("GPL v2");
676 MODULE_DESCRIPTION("Qualcomm ADC common functionality");