| /* |
| * Copyright (c) 2012-2019, The Linux Foundation. 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/module.h> |
| #include "adc-tm.h" |
| |
| /* |
| * Voltage to temperature table for NTCG104EF104 thermistor with |
| * 1.875V reference and 100k pull-up. |
| */ |
| static const struct adc_tm_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { |
| { 1831000, -40000 }, |
| { 1814000, -35000 }, |
| { 1791000, -30000 }, |
| { 1761000, -25000 }, |
| { 1723000, -20000 }, |
| { 1675000, -15000 }, |
| { 1616000, -10000 }, |
| { 1545000, -5000 }, |
| { 1463000, 0 }, |
| { 1370000, 5000 }, |
| { 1268000, 10000 }, |
| { 1160000, 15000 }, |
| { 1049000, 20000 }, |
| { 937000, 25000 }, |
| { 828000, 30000 }, |
| { 726000, 35000 }, |
| { 630000, 40000 }, |
| { 544000, 45000 }, |
| { 467000, 50000 }, |
| { 399000, 55000 }, |
| { 340000, 60000 }, |
| { 290000, 65000 }, |
| { 247000, 70000 }, |
| { 209000, 75000 }, |
| { 179000, 80000 }, |
| { 153000, 85000 }, |
| { 130000, 90000 }, |
| { 112000, 95000 }, |
| { 96000, 100000 }, |
| { 82000, 105000 }, |
| { 71000, 110000 }, |
| { 62000, 115000 }, |
| { 53000, 120000 }, |
| { 46000, 125000 }, |
| }; |
| |
| static void adc_tm_map_voltage_temp(const struct adc_tm_map_pt *pts, |
| size_t tablesize, int input, int *output) |
| { |
| bool descending = true; |
| u32 i = 0; |
| |
| /* Check if table is descending or ascending */ |
| if (tablesize > 1) { |
| if (pts[0].x < pts[1].x) |
| descending = 0; |
| } |
| |
| while (i < tablesize) { |
| if ((descending) && (pts[i].x < input)) { |
| /* table entry is less than measured*/ |
| /* value and table is descending, stop */ |
| break; |
| } else if ((!descending) && |
| (pts[i].x > input)) { |
| /* table entry is greater than measured*/ |
| /*value and table is ascending, stop */ |
| break; |
| } |
| 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); |
| } |
| } |
| |
| static void adc_tm_map_temp_voltage(const struct adc_tm_map_pt *pts, |
| size_t tablesize, int input, int64_t *output) |
| { |
| bool descending = true; |
| unsigned int i = 0; |
| |
| /* Check if table is descending or ascending */ |
| if (tablesize > 1) { |
| if (pts[0].y < pts[1].y) |
| descending = 0; |
| } |
| |
| while (i < tablesize) { |
| if (descending && (pts[i].y < input)) { |
| /* |
| * Table entry is less than measured value. |
| * Table is descending, stop. |
| */ |
| break; |
| } else if (!descending && (pts[i].y > input)) { |
| /* |
| * Table entry is greater than measured value. |
| * Table is ascending, stop. |
| */ |
| break; |
| } |
| i++; |
| } |
| |
| if (i == 0) { |
| *output = pts[0].x; |
| } else if (i == tablesize) { |
| *output = pts[tablesize-1].x; |
| } else { |
| /* |
| * Result is between search_index and search_index-1. |
| * Interpolate linearly. |
| */ |
| *output = (((int32_t) ((pts[i].x - pts[i-1].x) * |
| (input - pts[i-1].y)) / |
| (pts[i].y - pts[i-1].y)) + |
| pts[i-1].x); |
| } |
| } |
| |
| int therm_fwd_scale(int64_t code, uint32_t adc_hc_vdd_ref_mv, |
| const struct adc_tm_data *data) |
| { |
| int64_t volt = 0; |
| int result = 0; |
| |
| volt = (s64) code * adc_hc_vdd_ref_mv; |
| volt = div64_s64(volt, (data->full_scale_code_volt)); |
| |
| adc_tm_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, |
| ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), |
| (int) volt, &result); |
| |
| return result; |
| } |
| EXPORT_SYMBOL(therm_fwd_scale); |
| |
| void adc_tm_scale_therm_voltage_100k(struct adc_tm_config *param, |
| const struct adc_tm_data *data) |
| { |
| int temp; |
| |
| /* High temperature maps to lower threshold voltage */ |
| adc_tm_map_temp_voltage( |
| adcmap_100k_104ef_104fb_1875_vref, |
| ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), |
| param->high_thr_temp, ¶m->low_thr_voltage); |
| |
| param->low_thr_voltage *= data->full_scale_code_volt; |
| param->low_thr_voltage = div64_s64(param->low_thr_voltage, |
| ADC_HC_VDD_REF); |
| |
| temp = therm_fwd_scale(param->low_thr_voltage, |
| ADC_HC_VDD_REF, data); |
| |
| if (temp < param->high_thr_temp) |
| param->low_thr_voltage--; |
| |
| /* Low temperature maps to higher threshold voltage */ |
| adc_tm_map_temp_voltage( |
| adcmap_100k_104ef_104fb_1875_vref, |
| ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), |
| param->low_thr_temp, ¶m->high_thr_voltage); |
| |
| param->high_thr_voltage *= data->full_scale_code_volt; |
| param->high_thr_voltage = div64_s64(param->high_thr_voltage, |
| ADC_HC_VDD_REF); |
| |
| temp = therm_fwd_scale(param->high_thr_voltage, |
| ADC_HC_VDD_REF, data); |
| |
| if (temp > param->low_thr_temp) |
| param->high_thr_voltage++; |
| |
| } |
| EXPORT_SYMBOL(adc_tm_scale_therm_voltage_100k); |
| |
| int32_t adc_tm_absolute_rthr(const struct adc_tm_data *data, |
| struct adc_tm_config *tm_config) |
| { |
| int64_t low_thr = 0, high_thr = 0; |
| |
| low_thr = div_s64(tm_config->low_thr_voltage, tm_config->prescal); |
| low_thr *= data->full_scale_code_volt; |
| low_thr = div64_s64(low_thr, ADC_HC_VDD_REF); |
| tm_config->low_thr_voltage = low_thr; |
| |
| high_thr = div_s64(tm_config->high_thr_voltage, tm_config->prescal); |
| high_thr *= data->full_scale_code_volt; |
| high_thr = div64_s64(high_thr, ADC_HC_VDD_REF); |
| tm_config->high_thr_voltage = high_thr; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(adc_tm_absolute_rthr); |
| |
| MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC ADC_TM common driver"); |
| MODULE_LICENSE("GPL v2"); |