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 9 #include "qcom-vadc-common.h" 10 11 /* Voltage to temperature */ 12 static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { 13 {1758, -40}, 14 {1742, -35}, 15 {1719, -30}, 16 {1691, -25}, 17 {1654, -20}, 18 {1608, -15}, 19 {1551, -10}, 20 {1483, -5}, 21 {1404, 0}, 22 {1315, 5}, 23 {1218, 10}, 24 {1114, 15}, 25 {1007, 20}, 26 {900, 25}, 27 {795, 30}, 28 {696, 35}, 29 {605, 40}, 30 {522, 45}, 31 {448, 50}, 32 {383, 55}, 33 {327, 60}, 34 {278, 65}, 35 {237, 70}, 36 {202, 75}, 37 {172, 80}, 38 {146, 85}, 39 {125, 90}, 40 {107, 95}, 41 {92, 100}, 42 {79, 105}, 43 {68, 110}, 44 {59, 115}, 45 {51, 120}, 46 {44, 125} 47 }; 48 49 static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, 50 u32 tablesize, s32 input, s64 *output) 51 { 52 bool descending = 1; 53 u32 i = 0; 54 55 if (!pts) 56 return -EINVAL; 57 58 /* Check if table is descending or ascending */ 59 if (tablesize > 1) { 60 if (pts[0].x < pts[1].x) 61 descending = 0; 62 } 63 64 while (i < tablesize) { 65 if ((descending) && (pts[i].x < input)) { 66 /* table entry is less than measured*/ 67 /* value and table is descending, stop */ 68 break; 69 } else if ((!descending) && 70 (pts[i].x > input)) { 71 /* table entry is greater than measured*/ 72 /*value and table is ascending, stop */ 73 break; 74 } 75 i++; 76 } 77 78 if (i == 0) { 79 *output = pts[0].y; 80 } else if (i == tablesize) { 81 *output = pts[tablesize - 1].y; 82 } else { 83 /* result is between search_index and search_index-1 */ 84 /* interpolate linearly */ 85 *output = (((s32)((pts[i].y - pts[i - 1].y) * 86 (input - pts[i - 1].x)) / 87 (pts[i].x - pts[i - 1].x)) + 88 pts[i - 1].y); 89 } 90 91 return 0; 92 } 93 94 static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph, 95 u16 adc_code, 96 bool absolute, 97 s64 *scale_voltage) 98 { 99 *scale_voltage = (adc_code - calib_graph->gnd); 100 *scale_voltage *= calib_graph->dx; 101 *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy); 102 if (absolute) 103 *scale_voltage += calib_graph->dx; 104 105 if (*scale_voltage < 0) 106 *scale_voltage = 0; 107 } 108 109 static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph, 110 const struct vadc_prescale_ratio *prescale, 111 bool absolute, u16 adc_code, 112 int *result_uv) 113 { 114 s64 voltage = 0, result = 0; 115 116 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 117 118 voltage = voltage * prescale->den; 119 result = div64_s64(voltage, prescale->num); 120 *result_uv = result; 121 122 return 0; 123 } 124 125 static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, 126 const struct vadc_prescale_ratio *prescale, 127 bool absolute, u16 adc_code, 128 int *result_mdec) 129 { 130 s64 voltage = 0, result = 0; 131 int ret; 132 133 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 134 135 if (absolute) 136 voltage = div64_s64(voltage, 1000); 137 138 ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, 139 ARRAY_SIZE(adcmap_100k_104ef_104fb), 140 voltage, &result); 141 if (ret) 142 return ret; 143 144 result *= 1000; 145 *result_mdec = result; 146 147 return 0; 148 } 149 150 static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, 151 const struct vadc_prescale_ratio *prescale, 152 bool absolute, 153 u16 adc_code, int *result_mdec) 154 { 155 s64 voltage = 0; 156 u64 temp; /* Temporary variable for do_div */ 157 158 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 159 160 if (voltage > 0) { 161 temp = voltage * prescale->den; 162 do_div(temp, prescale->num * 2); 163 voltage = temp; 164 } else { 165 voltage = 0; 166 } 167 168 voltage -= KELVINMIL_CELSIUSMIL; 169 *result_mdec = voltage; 170 171 return 0; 172 } 173 174 static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, 175 const struct vadc_prescale_ratio *prescale, 176 bool absolute, 177 u16 adc_code, int *result_mdec) 178 { 179 s64 voltage = 0, result = 0; 180 181 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); 182 183 voltage = voltage * prescale->den; 184 voltage = div64_s64(voltage, prescale->num); 185 voltage = ((PMI_CHG_SCALE_1) * (voltage * 2)); 186 voltage = (voltage + PMI_CHG_SCALE_2); 187 result = div64_s64(voltage, 1000000); 188 *result_mdec = result; 189 190 return 0; 191 } 192 193 int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, 194 const struct vadc_linear_graph *calib_graph, 195 const struct vadc_prescale_ratio *prescale, 196 bool absolute, 197 u16 adc_code, int *result) 198 { 199 switch (scaletype) { 200 case SCALE_DEFAULT: 201 return qcom_vadc_scale_volt(calib_graph, prescale, 202 absolute, adc_code, 203 result); 204 case SCALE_THERM_100K_PULLUP: 205 case SCALE_XOTHERM: 206 return qcom_vadc_scale_therm(calib_graph, prescale, 207 absolute, adc_code, 208 result); 209 case SCALE_PMIC_THERM: 210 return qcom_vadc_scale_die_temp(calib_graph, prescale, 211 absolute, adc_code, 212 result); 213 case SCALE_PMI_CHG_TEMP: 214 return qcom_vadc_scale_chg_temp(calib_graph, prescale, 215 absolute, adc_code, 216 result); 217 default: 218 return -EINVAL; 219 } 220 } 221 EXPORT_SYMBOL(qcom_vadc_scale); 222 223 int qcom_vadc_decimation_from_dt(u32 value) 224 { 225 if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || 226 value > VADC_DECIMATION_MAX) 227 return -EINVAL; 228 229 return __ffs64(value / VADC_DECIMATION_MIN); 230 } 231 EXPORT_SYMBOL(qcom_vadc_decimation_from_dt); 232