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 static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, 51 u32 tablesize, s32 input, s64 *output) 52 { 53 bool descending = 1; 54 u32 i = 0; 55 56 if (!pts) 57 return -EINVAL; 58 59 /* Check if table is descending or ascending */ 60 if (tablesize > 1) { 61 if (pts[0].x < pts[1].x) 62 descending = 0; 63 } 64 65 while (i < tablesize) { 66 if ((descending) && (pts[i].x < input)) { 67 /* table entry is less than measured*/ 68 /* value and table is descending, stop */ 69 break; 70 } else if ((!descending) && 71 (pts[i].x > input)) { 72 /* table entry is greater than measured*/ 73 /*value and table is ascending, stop */ 74 break; 75 } 76 i++; 77 } 78 79 if (i == 0) { 80 *output = pts[0].y; 81 } else if (i == tablesize) { 82 *output = pts[tablesize - 1].y; 83 } else { 84 /* result is between search_index and search_index-1 */ 85 /* interpolate linearly */ 86 *output = (((s32)((pts[i].y - pts[i - 1].y) * 87 (input - pts[i - 1].x)) / 88 (pts[i].x - pts[i - 1].x)) + 89 pts[i - 1].y); 90 } 91 92 return 0; 93 } 94 95 static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph, 96 u16 adc_code, 97 bool absolute, 98 s64 *scale_voltage) 99 { 100 *scale_voltage = (adc_code - calib_graph->gnd); 101 *scale_voltage *= calib_graph->dx; 102 *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy); 103 if (absolute) 104 *scale_voltage += calib_graph->dx; 105 106 if (*scale_voltage < 0) 107 *scale_voltage = 0; 108 } 109 110 static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph, 111 const struct vadc_prescale_ratio *prescale, 112 bool absolute, u16 adc_code, 113 int *result_uv) 114 { 115 s64 voltage = 0, result = 0; 116 117 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 118 119 voltage = voltage * prescale->den; 120 result = div64_s64(voltage, prescale->num); 121 *result_uv = result; 122 123 return 0; 124 } 125 126 static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, 127 const struct vadc_prescale_ratio *prescale, 128 bool absolute, u16 adc_code, 129 int *result_mdec) 130 { 131 s64 voltage = 0, result = 0; 132 int ret; 133 134 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 135 136 if (absolute) 137 voltage = div64_s64(voltage, 1000); 138 139 ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, 140 ARRAY_SIZE(adcmap_100k_104ef_104fb), 141 voltage, &result); 142 if (ret) 143 return ret; 144 145 result *= 1000; 146 *result_mdec = result; 147 148 return 0; 149 } 150 151 static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, 152 const struct vadc_prescale_ratio *prescale, 153 bool absolute, 154 u16 adc_code, int *result_mdec) 155 { 156 s64 voltage = 0; 157 u64 temp; /* Temporary variable for do_div */ 158 159 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 160 161 if (voltage > 0) { 162 temp = voltage * prescale->den; 163 do_div(temp, prescale->num * 2); 164 voltage = temp; 165 } else { 166 voltage = 0; 167 } 168 169 voltage -= KELVINMIL_CELSIUSMIL; 170 *result_mdec = voltage; 171 172 return 0; 173 } 174 175 static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, 176 const struct vadc_prescale_ratio *prescale, 177 bool absolute, 178 u16 adc_code, int *result_mdec) 179 { 180 s64 voltage = 0, result = 0; 181 182 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 183 184 voltage = voltage * prescale->den; 185 voltage = div64_s64(voltage, prescale->num); 186 voltage = ((PMI_CHG_SCALE_1) * (voltage * 2)); 187 voltage = (voltage + PMI_CHG_SCALE_2); 188 result = div64_s64(voltage, 1000000); 189 *result_mdec = result; 190 191 return 0; 192 } 193 194 int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, 195 const struct vadc_linear_graph *calib_graph, 196 const struct vadc_prescale_ratio *prescale, 197 bool absolute, 198 u16 adc_code, int *result) 199 { 200 switch (scaletype) { 201 case SCALE_DEFAULT: 202 return qcom_vadc_scale_volt(calib_graph, prescale, 203 absolute, adc_code, 204 result); 205 case SCALE_THERM_100K_PULLUP: 206 case SCALE_XOTHERM: 207 return qcom_vadc_scale_therm(calib_graph, prescale, 208 absolute, adc_code, 209 result); 210 case SCALE_PMIC_THERM: 211 return qcom_vadc_scale_die_temp(calib_graph, prescale, 212 absolute, adc_code, 213 result); 214 case SCALE_PMI_CHG_TEMP: 215 return qcom_vadc_scale_chg_temp(calib_graph, prescale, 216 absolute, adc_code, 217 result); 218 default: 219 return -EINVAL; 220 } 221 } 222 EXPORT_SYMBOL(qcom_vadc_scale); 223 224 int qcom_vadc_decimation_from_dt(u32 value) 225 { 226 if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || 227 value > VADC_DECIMATION_MAX) 228 return -EINVAL; 229 230 return __ffs64(value / VADC_DECIMATION_MIN); 231 } 232 EXPORT_SYMBOL(qcom_vadc_decimation_from_dt); 233 234 MODULE_LICENSE("GPL v2"); 235 MODULE_DESCRIPTION("Qualcomm ADC common functionality"); 236