9cac0def4751e04bf384663a2816ad9e90dbe77c
[platform/adaptation/tm2/pass-hal-tm2.git] / src / cpu / cpu.c
1 /*
2  * PASS (Power Aware System Service)
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 #include <errno.h>
19 #include <limits.h>
20 #include <pass/hal.h>
21 #include <pass/hal-log.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "../shared/sysfs.h"
27
28 /* TODO: Version! */
29 #define HAL_VERSION     MAKE_2B_CODE_4(VER_MAJOR,VER_MINOR,VER_REVISION,VER_RELEASE)
30 #define DEV_VERSION_CPU MAKE_2B_CODE_2(1,0)
31
32 #define CPUFREQ_PATH_PREFIX                     "/sys/devices/system/cpu/"
33 #define CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX       "/cpufreq/scaling_governor"
34 #define CPUFREQ_AVAIL_GOVERNOR_PATH_SUFFIX      "/cpufreq/scaling_available_governors"
35
36 /*
37  * The cpuinfo_cur_freq indicates the actual operating CPU freqeuncy
38  * and scaling_cur_freq is the CPU frequency set by the CPUFREQ policy.
39  */
40 #define CPUFREQ_CURR_FREQ_PATH_SUFFIX           "/cpufreq/cpuinfo_cur_freq"
41 #define CPUFREQ_MIN_FREQ_PATH_SUFFIX            "/cpufreq/scaling_min_freq"
42 #define CPUFREQ_MAX_FREQ_PATH_SUFFIX            "/cpufreq/scaling_max_freq"
43 #define CPUFREQ_UP_THRESHOLD_PATH_SUFFIX        "/cpufreq/ondemand/up_threshold"
44
45 #define LOADTABLE_PATH_PREFIX                   "/sys/kernel/debug/cpufreq/"
46 #define LOADTABLE_PATH_SUFFIX                   "/load_table"
47
48 #define CPU_ONLINE_PATH_PREFIX                  "/sys/devices/system/cpu/cpu"
49 #define CPU_ONLINE_PATH_SUFFIX                  "/online"
50 #define CPU_ONLINE_STATE_ON                     1
51 #define CPU_ONLINE_STATE_OFF                    0
52
53 #define TMU_PATH_PREFIX                         "/sys/class/thermal/"
54 #define TMU_TEMP_PATH_SUFFIX                    "/temp"
55 #define TMU_POLICY_PATH_SUFFIX                  "/policy"
56
57 #define TM2_CPU_MIN_NUM                         0
58 #define TM2_CPU_MAX_NUM                         7
59
60 static int tm2_dvfs_get_curr_governor(char *res_name, char *governor)
61 {
62         char path[PATH_MAX];
63         int ret;
64
65         if ((!res_name) || (!governor))
66                 return -EINVAL;
67
68         snprintf(path, PATH_MAX, "%s%s%s",
69                 CPUFREQ_PATH_PREFIX,
70                 res_name,
71                 CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX);
72
73         ret = sysfs_read_str(path, governor, BUFF_MAX);
74         if (ret < 0)
75                 return ret;
76
77         return 0;
78 }
79
80 static int tm2_dvfs_set_curr_governor(char *res_name, char *governor)
81 {
82         char path[PATH_MAX];
83         int ret;
84
85         if ((!res_name) || (!governor))
86                 return -EINVAL;
87
88         snprintf(path, PATH_MAX, "%s%s%s",
89                 CPUFREQ_PATH_PREFIX,
90                 res_name,
91                 CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX);
92
93         ret = sysfs_write_str(path, governor);
94         if (ret < 0)
95                 return ret;
96
97         return 0;
98 }
99
100 static int tm2_dvfs_get_curr_freq(char *res_name)
101 {
102         char path[PATH_MAX];
103         int freq, ret;
104
105         if (!res_name)
106                 return -EINVAL;
107
108         snprintf(path, PATH_MAX, "%s%s%s",
109                 CPUFREQ_PATH_PREFIX,
110                 res_name,
111                 CPUFREQ_CURR_FREQ_PATH_SUFFIX);
112
113         ret = sysfs_read_int(path, &freq);
114         if (ret < 0)
115                 return ret;
116
117         return freq;
118 }
119
120 static int tm2_dvfs_get_min_freq(char *res_name)
121 {
122         char path[PATH_MAX];
123         int freq, ret;
124
125         if (!res_name)
126                 return -EINVAL;
127
128         snprintf(path, PATH_MAX, "%s%s%s",
129                 CPUFREQ_PATH_PREFIX,
130                 res_name,
131                 CPUFREQ_MIN_FREQ_PATH_SUFFIX);
132
133         ret = sysfs_read_int(path, &freq);
134         if (ret < 0)
135                 return ret;
136
137         return freq;
138 }
139
140 static int tm2_dvfs_set_min_freq(char *res_name, int freq)
141 {
142         char path[PATH_MAX];
143         int ret;
144
145         if ((!res_name) || (freq < 0))
146                 return -EINVAL;
147
148         snprintf(path, PATH_MAX, "%s%s%s",
149                 CPUFREQ_PATH_PREFIX,
150                 res_name,
151                 CPUFREQ_MIN_FREQ_PATH_SUFFIX);
152
153         ret = sysfs_write_int(path, freq);
154         if (ret < 0)
155                 return ret;
156
157         return 0;
158 }
159
160 static int tm2_dvfs_get_max_freq(char *res_name)
161 {
162         char path[PATH_MAX];
163         int freq, ret;
164
165         if (!res_name)
166                 return -EINVAL;
167
168         snprintf(path, PATH_MAX, "%s%s%s",
169                 CPUFREQ_PATH_PREFIX,
170                 res_name,
171                 CPUFREQ_MAX_FREQ_PATH_SUFFIX);
172
173         ret = sysfs_read_int(path, &freq);
174         if (ret < 0)
175                 return ret;
176
177         return freq;
178 }
179
180 static int tm2_dvfs_set_max_freq(char *res_name, int freq)
181 {
182         char path[PATH_MAX];
183         int ret;
184
185         if ((!res_name) || (freq < 0))
186                 return -EINVAL;
187
188         snprintf(path, PATH_MAX, "%s%s%s",
189                 CPUFREQ_PATH_PREFIX,
190                 res_name,
191                 CPUFREQ_MAX_FREQ_PATH_SUFFIX);
192
193         ret = sysfs_write_int(path, freq);
194         if (ret < 0)
195                 return ret;
196         return 0;
197 }
198
199 static int tm2_dvfs_get_up_threshold(char *res_name)
200 {
201         char path[PATH_MAX];
202         int val, ret;
203
204         if (!res_name)
205                 return -EINVAL;
206
207         snprintf(path, PATH_MAX, "%s%s%s",
208                 CPUFREQ_PATH_PREFIX,
209                 res_name,
210                 CPUFREQ_UP_THRESHOLD_PATH_SUFFIX);
211
212         ret = sysfs_read_int(path, &val);
213         if (ret < 0)
214                 return ret;
215
216         return val;
217 }
218
219 static int tm2_dvfs_set_up_threshold(char *res_name, int up_threshold)
220 {
221         char path[PATH_MAX];
222         int ret;
223
224         if ((!res_name) || (up_threshold < 0))
225                 return -EINVAL;
226
227         snprintf(path, PATH_MAX, "%s%s%s",
228                 CPUFREQ_PATH_PREFIX,
229                 res_name,
230                 CPUFREQ_UP_THRESHOLD_PATH_SUFFIX);
231
232         ret = sysfs_write_int(path, up_threshold);
233         if (ret < 0)
234                 return ret;
235
236         return 0;
237 }
238
239 static int tm2_dvfs_get_load_table(char *res_name, void *pass_cpu_load_table)
240 {
241         return 0;
242 }
243
244 static struct pass_resource_dvfs_ops tm2_cpu_dvfs_ops =  {
245         .get_curr_governor = tm2_dvfs_get_curr_governor,
246         .set_curr_governor = tm2_dvfs_set_curr_governor,
247         .get_avail_governor = NULL,
248         .get_curr_freq = tm2_dvfs_get_curr_freq,
249         .get_min_freq = tm2_dvfs_get_min_freq,
250         .set_min_freq = tm2_dvfs_set_min_freq,
251         .get_max_freq = tm2_dvfs_get_max_freq,
252         .set_max_freq = tm2_dvfs_set_max_freq,
253         .get_up_threshold = tm2_dvfs_get_up_threshold,
254         .set_up_threshold = tm2_dvfs_set_up_threshold,
255         .get_load_table = tm2_dvfs_get_load_table,
256 };
257
258 static int tm2_hotplug_get_online_state(char *res_name, int cpu)
259 {
260         char path[PATH_MAX];
261         int ret, online;
262
263         if ((!res_name))
264                 return -EINVAL;
265         if ((cpu < TM2_CPU_MIN_NUM) || (cpu > TM2_CPU_MAX_NUM))
266                 return -EINVAL;
267
268         snprintf(path, PATH_MAX, "%s%d%s",
269                 CPU_ONLINE_PATH_PREFIX,
270                 cpu,
271                 CPU_ONLINE_PATH_SUFFIX);
272
273         ret = sysfs_read_int(path, &online);
274         if (ret < 0)
275                 return ret;
276
277         return online;
278 }
279
280 static int tm2_hotplug_set_online_state(char *res_name, int cpu, int on)
281 {
282         char path[PATH_MAX];
283         int ret;
284
285         if ((!res_name))
286                 return -EINVAL;
287         if ((cpu < TM2_CPU_MIN_NUM) || (cpu > TM2_CPU_MAX_NUM))
288                 return -EINVAL;
289         if ((on != CPU_ONLINE_STATE_ON) && (on != CPU_ONLINE_STATE_OFF))
290                 return -EINVAL;
291
292         /*
293          * NOTE: Exynos SoC series cannot turn off the CPU0
294          * because of h/w design. To prevent the critical problem,
295          * if someone try to turn off the CPU0, just return without any
296          * opertaion.
297          */
298         if (on == 0 && cpu == 0) {
299                 _E("cannot turn off the CPU0");
300                 return 0;
301         }
302
303         snprintf(path, PATH_MAX, "%s%d%s",
304                 CPU_ONLINE_PATH_PREFIX,
305                 cpu,
306                 CPU_ONLINE_PATH_SUFFIX);
307
308         ret = sysfs_write_int(path, on);
309         if (ret < 0)
310                 return ret;
311
312         return 0;
313 }
314
315 static struct pass_resource_hotplug_ops tm2_cpu_hotplus_ops = {
316         .get_online_state = tm2_hotplug_get_online_state,
317         .set_online_state = tm2_hotplug_set_online_state,
318         .get_online_min_num = NULL,
319         .set_online_min_num = NULL,
320         .get_online_max_num = NULL,
321         .set_online_max_num = NULL,
322 };
323
324 static int tm2_tmu_get_temp(char *res_thermal_name)
325 {
326         char path[PATH_MAX];
327         int temp, ret;
328
329         if (!res_thermal_name)
330                 return -EINVAL;
331
332         snprintf(path, PATH_MAX, "%s%s%s",
333                 TMU_PATH_PREFIX,
334                 res_thermal_name,
335                 TMU_TEMP_PATH_SUFFIX);
336
337         ret = sysfs_read_int(path, &temp);
338         if (ret < 0)
339                 return ret;
340
341         return temp;
342 }
343
344 static int tm2_tmu_get_policy(char *res_thermal_name, char *policy)
345 {
346         char path[PATH_MAX];
347         int ret;
348
349         if ((!res_thermal_name) || (!policy))
350                 return -EINVAL;
351
352         snprintf(path, PATH_MAX, "%s%s%s",
353                 TMU_PATH_PREFIX,
354                 res_thermal_name,
355                 TMU_POLICY_PATH_SUFFIX);
356
357         ret = sysfs_read_str(path, policy, BUFF_MAX);
358         if (ret < 0)
359                 return ret;
360
361         return 0;
362 }
363
364 static struct pass_resource_tmu_ops tm2_cpu_tmu_ops = {
365         .get_temp = tm2_tmu_get_temp,
366         .get_policy = tm2_tmu_get_policy,
367 };
368
369 static int tm2_cpu_open(char *res_name, struct pass_resource_info *info,
370                 struct pass_resource_common **common)
371 {
372         struct pass_resource_cpu *cpu_res;
373
374         if (!info)
375                 return -EINVAL;
376
377         /* TODO: Possibility of a memory leak */
378         cpu_res = calloc(1, sizeof(struct pass_resource_cpu));
379         if (!cpu_res)
380                 return -ENOMEM;
381
382         cpu_res->common.info = info;
383         cpu_res->dvfs = tm2_cpu_dvfs_ops;
384         cpu_res->hotplug = tm2_cpu_hotplus_ops;
385         cpu_res->tmu = tm2_cpu_tmu_ops;
386
387         *common = (struct pass_resource_common *) cpu_res;
388
389         return 0;
390 }
391
392 static int tm2_cpu_close(char *res_name, struct pass_resource_common *common)
393 {
394         if (!common)
395                 return -EINVAL;
396
397         free(common);
398
399         return 0;
400 }
401
402 HAL_MODULE_STRUCTURE = {
403         .magic = HAL_INFO_TAG,
404         .hal_version = HAL_VERSION,
405         .device_version = DEV_VERSION_CPU,
406         .id = PASS_RESOURCE_CPU_ID,
407         .name = PASS_RESOURCE_CPU_NAME,
408         .open = tm2_cpu_open,
409         .close = tm2_cpu_close,
410 };