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