2a9bd7fde6dcd72cc297ee7f46bb0684a6d3ebe4
[platform/hal/backend/vim3/power-vim3.git] / src / hal-backend-power.c
1 /*
2  * HAL backend for hal-api-power which is used for PASS daemon
3  *
4  * Copyright (c) 2021 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
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <hal/hal-power-interface.h>
26
27 #include "sysfs.h"
28
29 #define BUFF_MAX        255
30
31 /*************************
32  * H/W Resource Defintions
33  */
34 #define CPUFREQ_PATH_PREFIX                     "/sys/devices/system/cpu/"
35 #define CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX       "/cpufreq/scaling_governor"
36
37 /*
38  * The cpuinfo_cur_freq indicates the actual operating CPU freqeuncy
39  * and scaling_cur_freq is the CPU frequency set by the CPUFREQ policy.
40  */
41 #define CPUFREQ_CURR_FREQ_PATH_SUFFIX           "/cpufreq/cpuinfo_cur_freq"
42 #define CPUFREQ_AVAILABLE_MIN_FREQ_PATH_SUFFIX  "/cpufreq/cpuinfo_min_freq"
43 #define CPUFREQ_AVAILABLE_MAX_FREQ_PATH_SUFFIX  "/cpufreq/cpuinfo_max_freq"
44 #define CPUFREQ_MIN_FREQ_PATH_SUFFIX            "/cpufreq/scaling_min_freq"
45 #define CPUFREQ_MAX_FREQ_PATH_SUFFIX            "/cpufreq/scaling_max_freq"
46 #define CPUFREQ_UP_THRESHOLD_PATH_SUFFIX        "/cpufreq/ondemand/up_threshold"
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 #define TMU_MAX_TEMP_MASK                       0xFFFFF
57
58 #define FAULT_AROUND_BYTES_PATH                 "/sys/kernel/debug/fault_around_bytes"
59
60 static int get_path(char *path, char *prefix, char *res_name, char *suffix)
61 {
62         int ret;
63
64         if (!path || !prefix || !res_name || !suffix)
65                 return -EINVAL;
66
67         ret = snprintf(path, PATH_MAX, "%s%s%s", prefix, res_name, suffix);
68         if (ret <= 0)
69                 return -EINVAL;
70
71         path[PATH_MAX - 1] = '\0';
72
73         return 0;
74 }
75
76 static int __handle_value_string(int is_write, char *prefix, char *res_name, char *suffix, char *buf)
77 {
78         char path[PATH_MAX];
79         int ret;
80
81         if (!buf)
82                 return -EINVAL;
83
84         ret = get_path(path, prefix, res_name, suffix);
85         if (ret < 0)
86                 return ret;
87
88         if (!!is_write)
89                 ret = sysfs_write_str(path, buf);
90         else
91                 ret = sysfs_read_str(path, buf, BUFF_MAX);
92
93         return (ret < 0) ? ret : 0;
94 }
95
96 static int get_value_string(char *prefix, char *res_name, char *suffix, char *buf)
97 {
98         return __handle_value_string(0, prefix, res_name, suffix, buf);
99 }
100
101 static int set_value_string(char *prefix, char *res_name, char *suffix, char *buf)
102 {
103         return __handle_value_string(1, prefix, res_name, suffix, buf);
104 }
105
106 static int get_value_integer(char *prefix, char *res_name, char *suffix, int *value)
107 {
108         char path[PATH_MAX];
109         int ret;
110
111         if (!value)
112                 return -EINVAL;
113
114         ret = get_path(path, prefix, res_name, suffix);
115         if (ret < 0)
116                 return ret;
117
118         ret = sysfs_read_int(path, value);
119         if (ret < 0)
120                 return ret;
121
122         return 0;
123 }
124
125 static int set_value_integer(char *prefix, char *res_name, char *suffix, int value)
126 {
127         char path[PATH_MAX];
128         int ret;
129
130         ret = get_path(path, prefix, res_name, suffix);
131         if (ret < 0)
132                 return ret;
133
134         ret = sysfs_write_int(path, value);
135         if (ret < 0)
136                 return ret;
137
138         return 0;
139 }
140
141 /*************************************************
142  * HAL backend implementation for CPU H/W Resource
143  */
144 static int cpufreq_dvfs_get_curr_governor(char *res_name, char *governor)
145 {
146         int ret;
147
148         ret = get_value_string(CPUFREQ_PATH_PREFIX, res_name,
149                                 CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX, governor);
150         return (ret < 0) ? ret : 0;
151 }
152
153 static int cpufreq_dvfs_set_curr_governor(char *res_name, char *governor)
154 {
155         int ret;
156
157         ret = set_value_string(CPUFREQ_PATH_PREFIX, res_name,
158                                 CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX, governor);
159         return (ret < 0) ? ret : 0;
160 }
161
162 static int cpufreq_dvfs_get_curr_freq(char *res_name)
163 {
164         int freq, ret;
165
166         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
167                                 CPUFREQ_CURR_FREQ_PATH_SUFFIX, &freq);
168         return (ret < 0) ? ret : freq;
169 }
170
171 static int cpufreq_dvfs_get_min_freq(char *res_name)
172 {
173         int freq, ret;
174
175         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
176                                 CPUFREQ_MIN_FREQ_PATH_SUFFIX, &freq);
177         return (ret < 0) ? ret : freq;
178 }
179
180 static int cpufreq_dvfs_set_min_freq(char *res_name, int freq)
181 {
182         int ret;
183
184         if (freq < 0)
185                 return -EINVAL;
186
187         ret = set_value_integer(CPUFREQ_PATH_PREFIX, res_name,
188                                 CPUFREQ_MIN_FREQ_PATH_SUFFIX, freq);
189         return (ret < 0) ? ret : 0;
190 }
191
192 static int cpufreq_dvfs_get_max_freq(char *res_name)
193 {
194         int freq, ret;
195
196         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
197                                 CPUFREQ_MAX_FREQ_PATH_SUFFIX, &freq);
198         return (ret < 0) ? ret : freq;
199 }
200
201 static int cpufreq_dvfs_set_max_freq(char *res_name, int freq)
202 {
203         int ret;
204
205         if (freq < 0)
206                 return -EINVAL;
207
208         ret = set_value_integer(CPUFREQ_PATH_PREFIX, res_name,
209                                 CPUFREQ_MAX_FREQ_PATH_SUFFIX, freq);
210         return (ret < 0) ? ret : 0;
211 }
212
213 static int cpufreq_dvfs_get_available_min_freq(char *res_name)
214 {
215         int val, ret;
216
217         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
218                                 CPUFREQ_AVAILABLE_MIN_FREQ_PATH_SUFFIX, &val);
219         return (ret < 0) ? ret : val;
220 }
221
222 static int cpufreq_dvfs_get_available_max_freq(char *res_name)
223 {
224         int val, ret;
225
226         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
227                                 CPUFREQ_AVAILABLE_MAX_FREQ_PATH_SUFFIX, &val);
228         return (ret < 0) ? ret : val;
229 }
230
231 static int cpufreq_dvfs_get_up_threshold(char *res_name)
232 {
233         int val, ret;
234
235         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
236                                 CPUFREQ_UP_THRESHOLD_PATH_SUFFIX, &val);
237         return (ret < 0) ? ret : val;
238 }
239
240 static int cpufreq_dvfs_set_up_threshold(char *res_name, int up_threshold)
241 {
242         int ret;
243
244         if (up_threshold < 0)
245                 return -EINVAL;
246
247         ret = set_value_integer(CPUFREQ_PATH_PREFIX, res_name,
248                                 CPUFREQ_UP_THRESHOLD_PATH_SUFFIX, up_threshold);
249         return (ret < 0) ? ret : 0;
250 }
251
252 static struct pass_resource_dvfs_ops cpufreq_dvfs_ops =  {
253         .get_curr_governor = cpufreq_dvfs_get_curr_governor,
254         .set_curr_governor = cpufreq_dvfs_set_curr_governor,
255         .get_curr_freq = cpufreq_dvfs_get_curr_freq,
256         .get_min_freq = cpufreq_dvfs_get_min_freq,
257         .set_min_freq = cpufreq_dvfs_set_min_freq,
258         .get_max_freq = cpufreq_dvfs_get_max_freq,
259         .set_max_freq = cpufreq_dvfs_set_max_freq,
260         .get_available_min_freq = cpufreq_dvfs_get_available_min_freq,
261         .get_available_max_freq = cpufreq_dvfs_get_available_max_freq,
262         .get_up_threshold = cpufreq_dvfs_get_up_threshold,
263         .set_up_threshold = cpufreq_dvfs_set_up_threshold,
264 };
265
266 static int cpu_hotplug_get_online_state(char *res_name, int cpu)
267 {
268         char path[PATH_MAX];
269         int ret, online;
270
271         if ((!res_name))
272                 return -EINVAL;
273
274         snprintf(path, PATH_MAX, "%s%d%s",
275                 CPU_ONLINE_PATH_PREFIX,
276                 cpu,
277                 CPU_ONLINE_PATH_SUFFIX);
278
279         ret = sysfs_read_int(path, &online);
280         if (ret < 0)
281                 return ret;
282
283         return online;
284 }
285
286 static int cpu_hotplug_set_online_state(char *res_name, int cpu, int on)
287 {
288         char path[PATH_MAX];
289         int ret;
290
291         if ((!res_name))
292                 return -EINVAL;
293         if ((on != CPU_ONLINE_STATE_ON) && (on != CPU_ONLINE_STATE_OFF))
294                 return -EINVAL;
295
296         /*
297          * NOTE: Exynos SoC series cannot turn off the CPU0
298          * because of h/w design. To prevent the critical problem,
299          * if someone try to turn off the CPU0, just return without any
300          * opertaion.
301          */
302         if (on == 0 && cpu == 0) {
303                 return 0;
304         }
305
306         snprintf(path, PATH_MAX, "%s%d%s",
307                 CPU_ONLINE_PATH_PREFIX,
308                 cpu,
309                 CPU_ONLINE_PATH_SUFFIX);
310
311         ret = sysfs_write_int(path, on);
312         if (ret < 0)
313                 return ret;
314
315         return 0;
316 }
317
318 static struct pass_resource_hotplug_ops cpu_hotplus_ops = {
319         .get_online_state = cpu_hotplug_get_online_state,
320         .set_online_state = cpu_hotplug_set_online_state,
321 };
322
323 static int tmu_get_temp(char *res_thermal_name)
324 {
325         int temp, ret;
326
327         ret = get_value_integer(TMU_PATH_PREFIX, res_thermal_name,
328                                 TMU_TEMP_PATH_SUFFIX, &temp);
329         if (ret <  0)
330                 return ret;
331
332         /*
333          * Thermal framework provides the current temperature
334          * as five digits interger like 54430 when temperature is 54.430
335          * degrees centigrade. But, Thermal Monitor in Tizen usually
336          * use two digits interger without decimal point.
337          * So that round temperature value. It constraints the maximume
338          * temperature as 1048 degrees centigrade for preventing integer
339          * overflow. Usually, the embedded device never over 1000 degrees
340          * centigrade.
341          */
342         return (((temp & TMU_MAX_TEMP_MASK) + 500) / 1000);
343 }
344
345 static int tmu_get_policy(char *res_thermal_name, char *policy)
346 {
347         int ret;
348
349         if (!policy)
350                 return -EINVAL;
351
352         ret = get_value_string(TMU_PATH_PREFIX, res_thermal_name,
353                                 TMU_POLICY_PATH_SUFFIX, policy);
354         return (ret < 0) ? ret : 0;
355 }
356
357 static struct pass_resource_tmu_ops tmu_ops = {
358         .get_temp = tmu_get_temp,
359         .get_policy = tmu_get_policy,
360 };
361
362 /*****************************************************
363  * HAL backend implementation for BUS/GPU H/W Resource
364  */
365
366 static int bus_dvfs_get_curr_freq(char *res_name)
367 {
368         char buf[BUFF_MAX + 1];
369         char unit[BUFF_MAX + 1];
370         int ret, freq;
371
372         ret = sysfs_read_str("/sys/class/aml_ddr/freq", buf, BUFF_MAX);
373         if (ret < 0)
374                 return ret;
375
376         sscanf(buf, "%d %s", &freq, unit);
377
378         return (freq * 1000);
379 }
380
381 static int gpu_freq_table[] = {
382         400000,
383         400000,
384         500000,
385         666000,
386         800000,
387 };
388
389 static int gpu_dvfs_get_min_freq(char *res_name)
390 {
391         int freq, ret;
392
393         ret = sysfs_read_int("/sys/class/mpgpu/min_freq", &freq);
394         if (ret < 0)
395                 return ret;
396         return (ret < 0) ? ret : (gpu_freq_table[freq]);
397 }
398
399 static int gpu_dvfs_get_max_freq(char *res_name)
400 {
401         int freq, ret;
402
403         ret = sysfs_read_int("/sys/class/mpgpu/max_freq", &freq);
404         if (ret < 0)
405                 return ret;
406         return (ret < 0) ? ret : gpu_freq_table[freq];
407 }
408
409 static int gpu_dvfs_get_curr_freq(char *res_name)
410 {
411         int freq, ret;
412
413         ret = sysfs_read_int("/sys/class/mpgpu/cur_freq", &freq);
414         if (ret < 0)
415                 return ret;
416         return (ret < 0) ? ret : (freq * 1000);
417 }
418
419 static struct pass_resource_dvfs_ops bus_dvfs_ops =  {
420         .get_curr_freq = bus_dvfs_get_curr_freq,
421         .get_min_freq = bus_dvfs_get_curr_freq,
422         .get_max_freq = bus_dvfs_get_curr_freq,
423 };
424
425 static struct pass_resource_dvfs_ops gpu_dvfs_ops =  {
426         .get_curr_freq = gpu_dvfs_get_curr_freq,
427         .get_min_freq = gpu_dvfs_get_min_freq,
428         .get_max_freq = gpu_dvfs_get_max_freq,
429 };
430
431 /****************************************************
432  * HAL backend implementation for Memory H/W Resource
433  */
434 static int memory_get_fault_around_bytes(char *res_name)
435 {
436         int ret, fault_around_bytes;
437
438         if (!res_name)
439                 return -EINVAL;
440
441         ret = sysfs_read_int(FAULT_AROUND_BYTES_PATH, &fault_around_bytes);
442         if (ret < 0)
443                 return ret;
444
445         return fault_around_bytes;
446 }
447
448 static int memory_set_fault_around_bytes(char *res_name,
449                                                 int fault_around_bytes)
450 {
451         int ret;
452
453         if ((!res_name) || (fault_around_bytes <= 0))
454                 return -EINVAL;
455
456         ret = sysfs_write_int(FAULT_AROUND_BYTES_PATH, fault_around_bytes);
457         if (ret < 0)
458                 return ret;
459
460         return 0;
461 }
462
463 /************************
464  * HAL backend power data
465  */
466 static int power_init(void **data)
467 {
468         hal_backend_power_funcs *power_funcs = NULL;
469         struct pass_resource_cpu *cpu = NULL;
470         struct pass_resource_bus *bus = NULL;
471         struct pass_resource_gpu *gpu = NULL;
472         struct pass_resource_memory *memory = NULL;
473         int ret;
474
475         /* Allocate memory */
476         power_funcs = calloc(1, sizeof(hal_backend_power_funcs));
477         if (!power_funcs)
478                 return -ENOMEM;
479
480         cpu = calloc(1, sizeof(struct pass_resource_cpu));
481         if (!cpu) {
482                 ret = -ENOMEM;
483                 goto err_funcs;
484         }
485
486         bus = calloc(1, sizeof(struct pass_resource_bus));
487         if (!bus) {
488                 ret = -ENOMEM;
489                 goto err_cpu;
490         }
491
492         gpu = calloc(1, sizeof(struct pass_resource_gpu));
493         if (!gpu) {
494                 ret = -ENOMEM;
495                 goto err_bus;
496         }
497
498         memory = calloc(1, sizeof(struct pass_resource_memory));
499         if (!memory) {
500                 ret = -ENOMEM;
501                 goto err_gpu;
502         }
503
504         /* Initialize each h/w resource */
505         cpu->dvfs = cpufreq_dvfs_ops;
506         cpu->hotplug = cpu_hotplus_ops;
507         cpu->tmu = tmu_ops;
508
509         bus->dvfs = bus_dvfs_ops;
510         bus->tmu = tmu_ops;
511
512         gpu->dvfs = gpu_dvfs_ops;
513         gpu->tmu = tmu_ops;
514
515         memory->get_fault_around_bytes = memory_get_fault_around_bytes;
516         memory->set_fault_around_bytes = memory_set_fault_around_bytes;
517
518         /* Initialize hal_backend_power_funcs  */
519         power_funcs->cpu = cpu;
520         power_funcs->bus = bus;
521         power_funcs->gpu = gpu;
522         power_funcs->memory = memory;
523
524         *data = (void *)power_funcs;
525
526         return 0;
527
528 err_gpu:
529         if (gpu)
530                 free(gpu);
531 err_bus:
532         if (bus)
533                 free(bus);
534 err_cpu:
535         if (cpu)
536                 free(cpu);
537 err_funcs:
538         free(power_funcs);
539
540         return ret;
541 }
542
543 static int power_exit(void *data)
544 {
545         hal_backend_power_funcs *funcs;
546
547         if (!data)
548                 return -EINVAL;
549
550         funcs = (hal_backend_power_funcs *)data;
551
552         if (funcs->cpu)
553                 free(funcs->cpu);
554         if (funcs->bus)
555                 free(funcs->bus);
556         if (funcs->gpu)
557                 free(funcs->gpu);
558         if (funcs->memory)
559                 free(funcs->memory);
560
561         free(funcs);
562
563         return 0;
564 }
565
566 hal_backend hal_backend_power_data = {
567         .name = "hal-backend-power-vim3",
568         .vendor = "amlogic",
569         .abi_version = HAL_ABI_VERSION_TIZEN_6_5,
570         .init = power_init,
571         .exit = power_exit,
572 };