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 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 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 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 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]*********************************************/ 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 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 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 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 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 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 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 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 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 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 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 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 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