1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * Based on the FlounderPowerHAL
17  */
18 
19 #include <dirent.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <pthread.h>
29 #include <semaphore.h>
30 #include <cutils/properties.h>
31 //#define LOG_NDEBUG 0
32 
33 #define LOG_TAG "HiKeyPowerHAL"
34 #include <log/log.h>
35 
36 #include <hardware/hardware.h>
37 #include <hardware/power.h>
38 
39 #include "power-helper.h"
40 
41 #define SCHEDTUNE_BOOST_PATH "/dev/stune/top-app/schedtune.boost"
42 #define SCHEDTUNE_BOOST_VAL_PROP "ro.config.schetune.touchboost.value"
43 #define SCHEDTUNE_BOOST_TIME_PROP "ro.config.schetune.touchboost.time_ns"
44 
45 #define SCHEDTUNE_BOOST_VAL_DEFAULT "40"
46 
47 char schedtune_boost_norm[PROPERTY_VALUE_MAX] = "10";
48 char schedtune_boost_interactive[PROPERTY_VALUE_MAX] = SCHEDTUNE_BOOST_VAL_DEFAULT;
49 long long schedtune_boost_time_ns = 1000000000LL;
50 
51 #define DEVFREQ_DDR_MIN_FREQ_PATH_PROP \
52 	"ro.config.devfreq.ddr.min_freq.path"
53 #define DEVFREQ_DDR_MIN_FREQ_BOOST_PROP \
54 	"ro.config.devfreq.ddr.min_freq.boost"
55 
56 char devfreq_ddr_min_path[PROPERTY_VALUE_MAX];
57 char devfreq_ddr_min_orig[PROPERTY_VALUE_MAX];
58 char devfreq_ddr_min_boost[PROPERTY_VALUE_MAX];
59 
60 #define DEVFREQ_GPU_MIN_FREQ_PATH_PROP \
61 	"ro.config.devfreq.gpu.min_freq.path"
62 #define DEVFREQ_GPU_MIN_FREQ_BOOST_PROP \
63 	"ro.config.devfreq.gpu.min_freq.boost"
64 
65 char devfreq_gpu_min_path[PROPERTY_VALUE_MAX];
66 char devfreq_gpu_min_orig[PROPERTY_VALUE_MAX];
67 char devfreq_gpu_min_boost[PROPERTY_VALUE_MAX];
68 
69 #define INTERACTIVE_BOOSTPULSE_PATH "/sys/devices/system/cpu/cpufreq/interactive/boostpulse"
70 #define INTERACTIVE_IO_IS_BUSY_PATH "/sys/devices/system/cpu/cpufreq/interactive/io_is_busy"
71 
72 struct hikey_power_module {
73     struct power_module base;
74     pthread_mutex_t lock;
75     /* interactive gov boost values */
76     int boostpulse_fd;
77     int boostpulse_warned;
78     /* EAS schedtune values */
79     int schedtune_boost_fd;
80     long long deboost_time;
81     sem_t signal_lock;
82 };
83 
84 struct hikey_power_module this_power_module;
85 
86 
87 static bool low_power_mode = false;
88 
89 
90 #define CPUFREQ_CLUST_MAX_FREQ_PATH_PROP "ro.config.cpufreq.max_freq.cluster"
91 #define CPUFREQ_CLUST_LOW_POWER_MAX_FREQ_PROP "ro.config.cpufreq.low_power_max.cluster"
92 #define CPUFREQ_CLUST0_MAX_FREQ_PATH_DEFAULT "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
93 
94 #define NR_CLUSTERS 4
95 static int max_clusters = 1;
96 static struct hikey_cpufreq_t {
97 	char path[PROPERTY_VALUE_MAX];
98 	char normal_max[PROPERTY_VALUE_MAX];
99 	char low_power_max[PROPERTY_VALUE_MAX];
100 } hikey_cpufreq_clusters[NR_CLUSTERS];
101 
102 #define container_of(addr, struct_name, field_name) \
103     ((struct_name *)((char *)(addr) - offsetof(struct_name, field_name)))
104 
105 
sysfs_write(const char * path,char * s)106 static int sysfs_write(const char *path, char *s)
107 {
108     char buf[80];
109     int len;
110     int fd = open(path, O_WRONLY);
111 
112     if (fd < 0) {
113         strerror_r(errno, buf, sizeof(buf));
114         ALOGE("Error opening %s: %s\n", path, buf);
115         return fd;
116     }
117 
118     len = write(fd, s, strlen(s));
119     if (len < 0) {
120         strerror_r(errno, buf, sizeof(buf));
121         ALOGE("Error writing to %s: %s\n", path, buf);
122     }
123 
124     close(fd);
125     return len;
126 }
127 
sysfs_read(const char * path,char * s,int slen)128 static int sysfs_read(const char *path, char *s, int slen)
129 {
130     int len;
131     int fd = open(path, O_RDONLY);
132 
133     if (fd < 0) {
134         ALOGE("Error opening %s\n", path);
135         return fd;
136     }
137 
138     len = read(fd, s, slen);
139     if (len < 0) {
140         ALOGE("Error reading %s\n", path);
141     }
142 
143     close(fd);
144     return len;
145 }
146 
147 #define NSEC_PER_SEC 1000000000LL
gettime_ns(void)148 static long long gettime_ns(void)
149 {
150     struct timespec ts;
151 
152     clock_gettime(CLOCK_MONOTONIC, &ts);
153     return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
154 }
155 
nanosleep_ns(long long ns)156 static void nanosleep_ns(long long ns)
157 {
158     struct timespec ts;
159     ts.tv_sec = ns/NSEC_PER_SEC;
160     ts.tv_nsec = ns%NSEC_PER_SEC;
161     nanosleep(&ts, NULL);
162 }
163 
164 /*[interactive cpufreq gov funcs]*********************************************/
interactive_power_init(struct hikey_power_module __unused * hikey)165 static void interactive_power_init(struct hikey_power_module __unused *hikey)
166 {
167     if (sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate",
168                 "20000") < 0)
169         return;
170     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_slack",
171                 "20000");
172     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time",
173                 "80000");
174     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq",
175                 "1200000");
176     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load",
177                 "99");
178     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/target_loads",
179                 "65 729000:75 960000:85");
180     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay",
181                 "20000");
182     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration",
183                 "1000000");
184     sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/io_is_busy", "0");
185 
186 }
187 
interactive_boostpulse(struct hikey_power_module * hikey)188 static int interactive_boostpulse(struct hikey_power_module *hikey)
189 {
190     char buf[80];
191     int len;
192 
193    if (hikey->boostpulse_fd < 0)
194         hikey->boostpulse_fd = open(INTERACTIVE_BOOSTPULSE_PATH, O_WRONLY);
195 
196     if (hikey->boostpulse_fd < 0) {
197         if (!hikey->boostpulse_warned) {
198             strerror_r(errno, buf, sizeof(buf));
199             ALOGE("Error opening %s: %s\n", INTERACTIVE_BOOSTPULSE_PATH,
200                       buf);
201             hikey->boostpulse_warned = 1;
202         }
203         return hikey->boostpulse_fd;
204     }
205 
206     len = write(hikey->boostpulse_fd, "1", 1);
207     if (len < 0) {
208         strerror_r(errno, buf, sizeof(buf));
209         ALOGE("Error writing to %s: %s\n",
210                                  INTERACTIVE_BOOSTPULSE_PATH, buf);
211         return -1;
212     }
213     return 0;
214 }
215 
216 static void
hikey_devfreq_set_interactive(struct hikey_power_module __unused * hikey,int on)217 hikey_devfreq_set_interactive(struct hikey_power_module __unused *hikey, int on)
218 {
219     if (!on || low_power_mode) {
220         if (devfreq_ddr_min_path[0] != '\0')
221             sysfs_write(devfreq_ddr_min_path, devfreq_ddr_min_orig);
222 
223         if (devfreq_gpu_min_path[0] != '\0')
224             sysfs_write(devfreq_gpu_min_path, devfreq_gpu_min_orig);
225     } else {
226         if (devfreq_ddr_min_path[0] != '\0')
227             sysfs_write(devfreq_ddr_min_path, devfreq_ddr_min_boost);
228 
229         if (devfreq_gpu_min_path[0] != '\0')
230             sysfs_write(devfreq_gpu_min_path, devfreq_gpu_min_boost);
231     }
232 }
233 
hikey_devfreq_init(struct hikey_power_module __unused * hikey)234 static void hikey_devfreq_init(struct hikey_power_module __unused *hikey)
235 {
236     property_get(DEVFREQ_DDR_MIN_FREQ_PATH_PROP, devfreq_ddr_min_path, "");
237     if (devfreq_ddr_min_path[0] != '\0') {
238         sysfs_read(devfreq_ddr_min_path, devfreq_ddr_min_orig,
239                    PROPERTY_VALUE_MAX);
240         property_get(DEVFREQ_DDR_MIN_FREQ_BOOST_PROP,
241                      devfreq_ddr_min_boost, "");
242     }
243 
244     property_get(DEVFREQ_GPU_MIN_FREQ_PATH_PROP, devfreq_gpu_min_path, "");
245     if (devfreq_gpu_min_path[0] != '\0') {
246         sysfs_read(devfreq_gpu_min_path, devfreq_gpu_min_orig,
247                    PROPERTY_VALUE_MAX);
248         property_get(DEVFREQ_GPU_MIN_FREQ_BOOST_PROP,
249                      devfreq_gpu_min_boost, "");
250     }
251 }
252 
253 /*[schedtune functions]*******************************************************/
254 
schedtune_sysfs_boost(struct hikey_power_module * hikey,char * booststr)255 static int schedtune_sysfs_boost(struct hikey_power_module *hikey, char* booststr)
256 {
257     char buf[80];
258     int len;
259 
260     if (hikey->schedtune_boost_fd < 0)
261         return hikey->schedtune_boost_fd;
262 
263     len = write(hikey->schedtune_boost_fd, booststr, strlen(booststr));
264     if (len < 0) {
265         strerror_r(errno, buf, sizeof(buf));
266         ALOGE("Error writing to %s: %s\n", SCHEDTUNE_BOOST_PATH, buf);
267     }
268     return len;
269 }
270 
schedtune_deboost_thread(void * arg)271 static void* schedtune_deboost_thread(void* arg)
272 {
273     struct hikey_power_module *hikey = (struct hikey_power_module *)arg;
274 
275     while(1) {
276         sem_wait(&hikey->signal_lock);
277         while(1) {
278             long long now, sleeptime = 0;
279 
280             pthread_mutex_lock(&hikey->lock);
281             now = gettime_ns();
282             if (hikey->deboost_time > now) {
283                 sleeptime = hikey->deboost_time - now;
284                 pthread_mutex_unlock(&hikey->lock);
285                 nanosleep_ns(sleeptime);
286                 continue;
287             }
288 
289             schedtune_sysfs_boost(hikey, schedtune_boost_norm);
290             hikey_devfreq_set_interactive(hikey, 0);
291             hikey->deboost_time = 0;
292             pthread_mutex_unlock(&hikey->lock);
293             break;
294         }
295     }
296     return NULL;
297 }
298 
schedtune_boost(struct hikey_power_module * hikey)299 static int schedtune_boost(struct hikey_power_module *hikey)
300 {
301     long long now;
302 
303     if (hikey->schedtune_boost_fd < 0)
304         return hikey->schedtune_boost_fd;
305 
306     now = gettime_ns();
307     if (!hikey->deboost_time) {
308         schedtune_sysfs_boost(hikey, schedtune_boost_interactive);
309         hikey_devfreq_set_interactive(hikey, 1);
310         sem_post(&hikey->signal_lock);
311     }
312     hikey->deboost_time = now + schedtune_boost_time_ns;
313 
314     return 0;
315 }
316 
schedtune_power_init(struct hikey_power_module * hikey)317 static void schedtune_power_init(struct hikey_power_module *hikey)
318 {
319     char buf[50];
320     pthread_t tid;
321 
322     hikey->deboost_time = 0;
323     sem_init(&hikey->signal_lock, 0, 1);
324 
325     hikey->schedtune_boost_fd = open(SCHEDTUNE_BOOST_PATH, O_RDWR);
326     if (hikey->schedtune_boost_fd < 0) {
327         strerror_r(errno, buf, sizeof(buf));
328         ALOGE("Error opening %s: %s\n", SCHEDTUNE_BOOST_PATH, buf);
329         return;
330     }
331 
332     schedtune_boost_time_ns = property_get_int64(SCHEDTUNE_BOOST_TIME_PROP,
333                                                  1000000000LL);
334     property_get(SCHEDTUNE_BOOST_VAL_PROP, schedtune_boost_interactive,
335                  SCHEDTUNE_BOOST_VAL_DEFAULT);
336 
337     if (hikey->schedtune_boost_fd >= 0) {
338         size_t len = read(hikey->schedtune_boost_fd, schedtune_boost_norm,
339                           PROPERTY_VALUE_MAX);
340 	if (len <= 0)
341             ALOGE("Error reading normal boost value\n");
342 	else if (schedtune_boost_norm[len] == '\n')
343             schedtune_boost_norm[len] = '\0';
344 
345     }
346 
347     ALOGV("Starting with schedtune boost norm: %s touchboost: %s and boosttime: %lld\n",
348 	  schedtune_boost_norm, schedtune_boost_interactive, schedtune_boost_time_ns);
349 
350     pthread_create(&tid, NULL, schedtune_deboost_thread, hikey);
351 }
352 
353 /*[generic functions]*********************************************************/
354 
power_set_interactive(int on)355 void power_set_interactive(int on)
356 {
357     int i;
358 
359     /*
360      * Lower maximum frequency when screen is off.
361      */
362     for (i=0; i < max_clusters; i++) {
363         if ((!on || low_power_mode) && hikey_cpufreq_clusters[i].low_power_max[0] != '\0')
364 		sysfs_write(hikey_cpufreq_clusters[i].path, hikey_cpufreq_clusters[i].low_power_max);
365 	else
366 		sysfs_write(hikey_cpufreq_clusters[i].path, hikey_cpufreq_clusters[i].normal_max);
367     }
368     sysfs_write(INTERACTIVE_IO_IS_BUSY_PATH, on ? "1" : "0");
369 }
370 
371 
hikey_cpufreq_init(struct hikey_power_module __unused * hikey)372 static void hikey_cpufreq_init(struct hikey_power_module __unused *hikey)
373 {
374     char buf[128];
375     int len, i;
376 
377     for (i=0; i < NR_CLUSTERS; i++) {
378         sprintf(buf,"%s%d", CPUFREQ_CLUST_MAX_FREQ_PATH_PROP, i);
379         property_get(buf, hikey_cpufreq_clusters[i].path, "");
380 
381         if (hikey_cpufreq_clusters[i].path[0] == '\0') {
382             if (i == 0) {
383                 /* In case no property was set, pick cpu0's cluster */
384                 strncpy(hikey_cpufreq_clusters[i].path,
385                         CPUFREQ_CLUST0_MAX_FREQ_PATH_DEFAULT,
386                         PROPERTY_VALUE_MAX);
387             } else
388                 break;
389         }
390         sprintf(buf,"%s%d", CPUFREQ_CLUST_LOW_POWER_MAX_FREQ_PROP, i);
391         property_get(buf, hikey_cpufreq_clusters[i].low_power_max, "");
392         len = sysfs_read(hikey_cpufreq_clusters[i].path,
393                          hikey_cpufreq_clusters[i].normal_max,
394                          PROPERTY_VALUE_MAX);
395         ALOGV("Cluster: %d path: %s  low: %s norm: %s\n", i,
396               hikey_cpufreq_clusters[i].path,
397               hikey_cpufreq_clusters[i].low_power_max,
398               hikey_cpufreq_clusters[i].normal_max);
399     }
400     max_clusters = i;
401 }
402 
power_init(void)403 void power_init(void)
404 {
405     struct hikey_power_module *hikey = &this_power_module;
406     memset(hikey, 0, sizeof(struct hikey_power_module));
407     pthread_mutex_init(&hikey->lock, NULL);
408     hikey->boostpulse_fd = -1;
409     hikey->boostpulse_warned = 0;
410 
411     hikey_cpufreq_init(hikey);
412     hikey_devfreq_init(hikey);
413     interactive_power_init(hikey);
414     schedtune_power_init(hikey);
415 }
416 
hikey_hint_interaction(struct hikey_power_module * mod)417 static void hikey_hint_interaction(struct hikey_power_module *mod)
418 {
419     /* Try interactive cpufreq boosting first */
420     if(!interactive_boostpulse(mod))
421         return;
422     /* Then try EAS schedtune boosting */
423     if(!schedtune_boost(mod))
424         return;
425 }
426 
power_hint(power_hint_t hint,void * data)427 void power_hint(power_hint_t hint, void *data)
428 {
429     struct hikey_power_module *hikey = &this_power_module;
430 
431     pthread_mutex_lock(&hikey->lock);
432     switch (hint) {
433      case POWER_HINT_INTERACTION:
434         hikey_hint_interaction(hikey);
435         break;
436 
437    case POWER_HINT_VSYNC:
438         break;
439 
440     case POWER_HINT_LOW_POWER:
441         low_power_mode = data;
442         power_set_interactive(1);
443         break;
444 
445     default:
446             break;
447     }
448     pthread_mutex_unlock(&hikey->lock);
449 }
450