GNU Linux-libre 4.14.266-gnu1
[releases.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 #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");