tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / arch / arm / mach-sc / board-coreprimeve3g-thermistor.c
1 /* board-coreprimeve3g-thermistor.c
2  *
3  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9 */
10
11 #include <linux/gfp.h>
12 #include <linux/of.h>
13 #include <linux/err.h>
14 #include <linux/sec_thermistor.h>
15 #include <soc/sprd/adc.h>
16
17 static struct sec_therm_adc_table temp_table_ap[] = {
18         /* adc, temperature */
19         {286,   900},
20         {407,   800},
21         {569,   700},
22         {666,   650},
23         {762,   600},
24         {790,   580},
25         {861,   550},
26         {977,   500},
27         {1079,  460},
28         {1106,  450},
29         {1237,  400},
30         {1369,  350},
31         {1497,  300},
32         {1630,  250},
33         {1764,  200},
34         {1895,  150},
35         {1996,  100},
36         {2132,  50},
37         {2149,  25},
38         {2204,  20},
39         {2237,  15},
40         {2285,  10},
41         {2315,  -70},
42         {2408,  -150},
43         {2452,  -200},
44         {2470,  -250},
45         {2495,  -300},
46         {2515,  -350},
47 };
48
49 static struct sec_therm_adc_table temp_table_battery[] = {
50         /* adc, temperature */
51         {306,   900},
52         {417,   800},
53         {571,   700},
54         {746,   600},
55         {787,   580},
56         {853,   550},
57         {870,   530},
58         {896,   520},
59         {922,   510},
60         {966,   500},
61         {1039,  470},
62         {1094,  450},
63         {1152,  430},
64         {1230,  400},
65         {1371,  350},
66         {1509,  300},
67         {1764,  200},
68         {1890,  150},
69         {1958,  120},
70         {2004,  100},
71         {2066,   70},
72         {2109,   50},
73         {2167,   20},
74         {2197,    0},
75         {2244,  -30},
76         {2274,  -50},
77         {2300,  -70},
78         {2338, -100},
79         {2425, -200},
80 };
81
82 static struct sec_therm_adc_table temp_table_xo[] = {
83         /* adc, temperature */
84         {678,   700},
85         {796,   650},
86         {923,   600},
87         {966,   580},
88         {1054,  550},
89         {1208,  500},
90         {1345,  460},
91         {1386,  450},
92         {1568,  400},
93         {1759,  350},
94         {1952,  300},
95         {2152,  250},
96         {2364,  200},
97         {2569,  150},
98         {2832,  100},
99         {2967,  50},
100         {3005,  20},
101         {3093,  0},
102         {3156,  -20},
103         {3238,  -50},
104         {3291,  -70},
105         {3458,  -150},
106         {3539,  -200},
107         {3615,  -250},
108         {3661,  -300},
109         {3703,  -350},
110 };
111
112 static struct sec_therm_adc_table temp_table_default[] = {
113         /* adc, temperature */
114         {501,   700},
115         {615,   650},
116         {738,   600},
117         {795,   580},
118         {846,   550},
119         {956,   500},
120         {1065,  460},
121         {1088,  450},
122         {1180,  400},
123         {1307,  350},
124         {1392,  300},
125         {1477,  250},
126         {1627,  200},
127         {1777,  150},
128         {1922,  100},
129         {2098,  50},
130         {2144,  20},
131         {2182,  0},
132         {2212,  -20},
133         {2260,  -50},
134         {2302,  -70},
135         {2452,  -150},
136         {2555,  -200},
137 };
138
139 struct sec_therm_adc_info kiran_adc_list[] = {
140         {
141                 .therm_id = SEC_THERM_BATTERY,
142                 .name = "batt_therm",
143                 .adc_name = "ADCI1",
144                 .adc_ch = ADC_CHANNEL_1,
145         },
146         {
147                 .therm_id = SEC_THERM_XO,
148                 .name = "xo_therm",
149                 .adc_name = "ADCI2",
150                 .adc_ch = ADC_CHANNEL_2,
151         },
152         {
153                 .therm_id = SEC_THERM_AP,
154                 .name = "ap_therm",
155                 .adc_name = "ADCI3",
156                 .adc_ch = ADC_CHANNEL_3,
157         },
158 };
159
160 static int sec_therm_temp_table_init(struct sec_therm_adc_info *adc_info)
161 {
162         if (unlikely(!adc_info))
163                 return -EINVAL;
164
165         switch (adc_info->therm_id) {
166                 case SEC_THERM_AP:
167                         adc_info->temp_table = temp_table_ap;
168                         adc_info->temp_table_size = ARRAY_SIZE(temp_table_ap);
169                         break;
170                 case SEC_THERM_BATTERY:
171                         adc_info->temp_table = temp_table_battery;
172                         adc_info->temp_table_size = ARRAY_SIZE(temp_table_battery);
173                         break;
174                 case SEC_THERM_XO:
175                         adc_info->temp_table = temp_table_xo;
176                         adc_info->temp_table_size = ARRAY_SIZE(temp_table_xo);
177                         break;
178                 case SEC_THERM_PAM0:
179                 case SEC_THERM_PAM1:
180                         case SEC_THERM_FLASH:
181                         adc_info->temp_table = temp_table_default;
182                         adc_info->temp_table_size = ARRAY_SIZE(temp_table_default);
183                         break;
184                 default:
185                         return -EINVAL;
186         }
187
188         return 0;
189 }
190
191 static int sec_therm_parse_dt(struct device_node *node,
192                         struct sec_therm_adc_info *adc_list)
193 {
194         struct device_node *child = NULL;
195         int i = 0, ret;
196
197         for_each_child_of_node(node, child) {
198                 int therm_id, therm_adc_ch;
199                 const char *therm_name, *therm_adc_name;
200
201                 therm_name = child->name;
202                 if (!therm_name) {
203                         pr_err("%s: Failed to get thermistor name\n", __func__);
204                         return -EINVAL;
205                 }
206
207                 ret = of_property_read_u32(child, "sec,therm-id", &therm_id);
208                 if (ret) {
209                         pr_err("%s: Failed to get thermistor id\n", __func__);
210                         return ret;
211                 }
212
213                 therm_adc_name = of_get_property(child, "sec,therm-adc-name", NULL);
214                 if (!therm_adc_name) {
215                         pr_err("%s: Failed to get adc name\n", __func__);
216                         return -EINVAL;
217                 }
218
219                 ret = of_property_read_u32(child, "sec,therm-adc-ch", &therm_adc_ch);
220                 if (ret) {
221                         pr_err("%s: Failed to get thermistor adc channel\n", __func__);
222                         return ret;
223                 }
224
225                 pr_info("%s: name:%s, therm_id:%d, adc_name:%s, adc_ch:0x%x\n",
226                                 __func__, therm_name, therm_id, therm_adc_name, therm_adc_ch);
227
228                 adc_list[i].name = therm_name;
229                 adc_list[i].therm_id = therm_id;
230                 adc_list[i].adc_name = therm_adc_name;
231                 adc_list[i].adc_ch = therm_adc_ch;
232                 i++;
233         }
234
235         return 0;
236 }
237
238 int sec_therm_adc_read(struct sec_therm_info *info, int therm_id, int *val)
239 {
240         struct sec_therm_adc_info *adc_info = NULL;
241         int adc_data;
242         int i, ret = 0;
243
244         if (unlikely(!info || !val))
245                 return -EINVAL;
246
247         for (i = 0; i < info->adc_list_size; i++) {
248                 if (therm_id == info->adc_list[i].therm_id) {
249                         adc_info = &info->adc_list[i];
250                         break;
251                 }
252         }
253
254         if (!adc_info) {
255                 pr_err("%s: Failed to found therm_id %d\n", __func__, therm_id);
256                 return -EINVAL;
257         }
258
259         adc_data = sci_adc_get_value(adc_info->adc_ch, false);
260         if (ret < 0) {
261                 pr_err("%s: Failed to read adc channel: %d (%d)\n",
262                                         __func__, adc_info->adc_ch, ret);
263                 return -EINVAL;
264         }
265
266         *val = adc_data;
267         return 0;
268 }
269
270 int sec_therm_adc_init(struct platform_device *pdev)
271 {
272         struct sec_therm_info *info = platform_get_drvdata(pdev);
273         struct sec_therm_adc_info *adc_list = NULL;
274         int adc_list_size = 0;
275         int i, ret = 0;
276
277         /* device tree support */
278         if (pdev->dev.of_node) {
279                 struct device_node *node = pdev->dev.of_node;
280                 struct device_node *child;
281
282                 for_each_child_of_node(node, child)
283                         adc_list_size++;
284
285                 if (adc_list_size <= 0) {
286                         pr_err("%s: No adc channel info\n", __func__);
287                         return -ENODEV;
288                 }
289
290                 adc_list = devm_kzalloc(&pdev->dev,
291                                 sizeof(struct sec_therm_adc_info) * adc_list_size, GFP_KERNEL);
292                 if (!adc_list) {
293                         pr_err("%s: Failed to allocate memory\n", __func__);
294                         return -ENOMEM;
295                 }
296
297                 ret = sec_therm_parse_dt(node, adc_list);
298                 if (ret) {
299                         pr_err("%s: Failed to parse dt (%d)\n", __func__, ret);
300                         goto err;
301                 }
302         } else {
303                 adc_list = kiran_adc_list;
304                 adc_list_size = ARRAY_SIZE(kiran_adc_list);
305
306                 for (i = 0; i < adc_list_size; i++) {
307                         pr_info("%s: name:%s, therm_id:%d, adc_name:%s, adc_ch:0x%x\n",
308                                         __func__,
309                                         adc_list[i].name, adc_list[i].therm_id,
310                                         adc_list[i].adc_name, adc_list[i].adc_ch);
311                 }
312         }
313
314         for (i = 0; i < adc_list_size; i++) {
315                 ret = sec_therm_temp_table_init(&adc_list[i]);
316                 if (ret) {
317                         pr_err("%s: Failed to init %d adc_temp_table\n",
318                                         __func__, adc_list[i].therm_id);
319                         goto err;
320                 }
321         }
322
323         info->adc_list = adc_list;
324         info->adc_list_size = adc_list_size;
325
326         return 0;
327
328 err:
329         devm_kfree(&pdev->dev, adc_list);
330         return ret;
331 }
332
333 void sec_therm_adc_exit(struct platform_device *pdev)
334 {
335         struct sec_therm_info *info = platform_get_drvdata(pdev);
336
337         if (!info)
338                 return;
339
340         if (pdev->dev.of_node && info->adc_list)
341                 devm_kfree(&pdev->dev, info->adc_list);
342 }