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 int qcom_vadc_scale_hw_calib_volt(
93 const struct vadc_prescale_ratio *prescale,
94 const struct adc5_data *data,
95 u16 adc_code, int *result_uv);
96 static int qcom_vadc_scale_hw_calib_therm(
97 const struct vadc_prescale_ratio *prescale,
98 const struct adc5_data *data,
99 u16 adc_code, int *result_mdec);
100 static int qcom_vadc_scale_hw_smb_temp(
101 const struct vadc_prescale_ratio *prescale,
102 const struct adc5_data *data,
103 u16 adc_code, int *result_mdec);
104 static int qcom_vadc_scale_hw_chg5_temp(
105 const struct vadc_prescale_ratio *prescale,
106 const struct adc5_data *data,
107 u16 adc_code, int *result_mdec);
108 static int qcom_vadc_scale_hw_calib_die_temp(
109 const struct vadc_prescale_ratio *prescale,
110 const struct adc5_data *data,
111 u16 adc_code, int *result_mdec);
113 static struct qcom_adc5_scale_type scale_adc5_fn[] = {
114 [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
115 [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
116 [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
117 [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
118 [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
119 [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
122 static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
123 u32 tablesize, s32 input, int *output)
131 /* Check if table is descending or ascending */
133 if (pts[0].x < pts[1].x)
137 while (i < tablesize) {
138 if ((descending) && (pts[i].x < input)) {
139 /* table entry is less than measured*/
140 /* value and table is descending, stop */
142 } else if ((!descending) &&
143 (pts[i].x > input)) {
144 /* table entry is greater than measured*/
145 /*value and table is ascending, stop */
153 } else if (i == tablesize) {
154 *output = pts[tablesize - 1].y;
156 /* result is between search_index and search_index-1 */
157 /* interpolate linearly */
158 *output = (((s32)((pts[i].y - pts[i - 1].y) *
159 (input - pts[i - 1].x)) /
160 (pts[i].x - pts[i - 1].x)) +
167 static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
172 *scale_voltage = (adc_code - calib_graph->gnd);
173 *scale_voltage *= calib_graph->dx;
174 *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
176 *scale_voltage += calib_graph->dx;
178 if (*scale_voltage < 0)
182 static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
183 const struct vadc_prescale_ratio *prescale,
184 bool absolute, u16 adc_code,
187 s64 voltage = 0, result = 0;
189 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
191 voltage = voltage * prescale->den;
192 result = div64_s64(voltage, prescale->num);
198 static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
199 const struct vadc_prescale_ratio *prescale,
200 bool absolute, u16 adc_code,
206 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
209 voltage = div64_s64(voltage, 1000);
211 ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
212 ARRAY_SIZE(adcmap_100k_104ef_104fb),
213 voltage, result_mdec);
217 *result_mdec *= 1000;
222 static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
223 const struct vadc_prescale_ratio *prescale,
225 u16 adc_code, int *result_mdec)
228 u64 temp; /* Temporary variable for do_div */
230 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
233 temp = voltage * prescale->den;
234 do_div(temp, prescale->num * 2);
240 *result_mdec = milli_kelvin_to_millicelsius(voltage);
245 static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
246 const struct vadc_prescale_ratio *prescale,
248 u16 adc_code, int *result_mdec)
250 s64 voltage = 0, result = 0;
252 qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
254 voltage = voltage * prescale->den;
255 voltage = div64_s64(voltage, prescale->num);
256 voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
257 voltage = (voltage + PMI_CHG_SCALE_2);
258 result = div64_s64(voltage, 1000000);
259 *result_mdec = result;
264 static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
265 const struct vadc_prescale_ratio *prescale,
266 const struct adc5_data *data,
269 s64 voltage, temp, adc_vdd_ref_mv = 1875;
272 * The normal data range is between 0V to 1.875V. On cases where
273 * we read low voltage values, the ADC code can go beyond the
274 * range and the scale result is incorrect so we clamp the values
275 * for the cases where the code represents a value below 0V
277 if (adc_code > VADC5_MAX_CODE)
280 /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
281 voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
282 voltage = div64_s64(voltage, data->full_scale_code_volt);
284 voltage *= prescale->den;
285 temp = prescale->num * factor;
286 voltage = div64_s64(voltage, temp);
291 return (int) voltage;
294 static int qcom_vadc_scale_hw_calib_volt(
295 const struct vadc_prescale_ratio *prescale,
296 const struct adc5_data *data,
297 u16 adc_code, int *result_uv)
299 *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
305 static int qcom_vadc_scale_hw_calib_therm(
306 const struct vadc_prescale_ratio *prescale,
307 const struct adc5_data *data,
308 u16 adc_code, int *result_mdec)
312 voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
313 prescale, data, 1000);
315 /* Map voltage to temperature from look-up table */
316 return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
317 ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
318 voltage, result_mdec);
321 static int qcom_vadc_scale_hw_calib_die_temp(
322 const struct vadc_prescale_ratio *prescale,
323 const struct adc5_data *data,
324 u16 adc_code, int *result_mdec)
326 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
328 *result_mdec = milli_kelvin_to_millicelsius(*result_mdec);
333 static int qcom_vadc_scale_hw_smb_temp(
334 const struct vadc_prescale_ratio *prescale,
335 const struct adc5_data *data,
336 u16 adc_code, int *result_mdec)
338 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
339 prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
340 *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
345 static int qcom_vadc_scale_hw_chg5_temp(
346 const struct vadc_prescale_ratio *prescale,
347 const struct adc5_data *data,
348 u16 adc_code, int *result_mdec)
350 *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
352 *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
357 int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
358 const struct vadc_linear_graph *calib_graph,
359 const struct vadc_prescale_ratio *prescale,
361 u16 adc_code, int *result)
365 return qcom_vadc_scale_volt(calib_graph, prescale,
368 case SCALE_THERM_100K_PULLUP:
370 return qcom_vadc_scale_therm(calib_graph, prescale,
373 case SCALE_PMIC_THERM:
374 return qcom_vadc_scale_die_temp(calib_graph, prescale,
377 case SCALE_PMI_CHG_TEMP:
378 return qcom_vadc_scale_chg_temp(calib_graph, prescale,
385 EXPORT_SYMBOL(qcom_vadc_scale);
387 int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
388 const struct vadc_prescale_ratio *prescale,
389 const struct adc5_data *data,
390 u16 adc_code, int *result)
392 if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
393 scaletype < SCALE_HW_CALIB_INVALID)) {
394 pr_err("Invalid scale type %d\n", scaletype);
398 return scale_adc5_fn[scaletype].scale_fn(prescale, data,
401 EXPORT_SYMBOL(qcom_adc5_hw_scale);
403 int qcom_vadc_decimation_from_dt(u32 value)
405 if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
406 value > VADC_DECIMATION_MAX)
409 return __ffs64(value / VADC_DECIMATION_MIN);
411 EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
413 MODULE_LICENSE("GPL v2");
414 MODULE_DESCRIPTION("Qualcomm ADC common functionality");