License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[sfrench/cifs-2.6.git] / drivers / iio / adc / qcom-vadc-common.c
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);