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