blob: 102fc51b10aa192a21fb77e972e5c0b4afe2e25b [file] [log] [blame]
Linus Walleije932d4f2017-04-04 14:08:18 +02001#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 */
11static 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
48static 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
93static 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
108static 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
124static 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
149static 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
173static 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
192int 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}
220EXPORT_SYMBOL(qcom_vadc_scale);
221
222int 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}
230EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);