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");