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 <utils/Log.h> 35 36#include <hardware/hardware.h> 37#include <hardware/power.h> 38 39#define SCHEDTUNE_BOOST_PATH "/dev/stune/top-app/schedtune.boost" 40#define SCHEDTUNE_BOOST_NORM "10" 41#define SCHEDTUNE_BOOST_INTERACTIVE "40" 42#define SCHEDTUNE_BOOST_TIME_NS 1000000000LL 43#define INTERACTIVE_BOOSTPULSE_PATH "/sys/devices/system/cpu/cpufreq/interactive/boostpulse" 44#define INTERACTIVE_IO_IS_BUSY_PATH "/sys/devices/system/cpu/cpufreq/interactive/io_is_busy" 45#define CPU_MAX_FREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" 46#define LOW_POWER_MAX_FREQ "729000" 47#define NORMAL_MAX_FREQ "1200000" 48#define SVELTE_PROP "ro.boot.svelte" 49#define SVELTE_MAX_FREQ_PROP "ro.config.svelte.max_cpu_freq" 50#define SVELTE_LOW_POWER_MAX_FREQ_PROP "ro.config.svelte.low_power_max_cpu_freq" 51 52struct hikey_power_module { 53 struct power_module base; 54 pthread_mutex_t lock; 55 /* interactive gov boost values */ 56 int boostpulse_fd; 57 int boostpulse_warned; 58 /* EAS schedtune values */ 59 int schedtune_boost_fd; 60 long long deboost_time; 61 sem_t signal_lock; 62}; 63 64static bool low_power_mode = false; 65 66static char *max_cpu_freq = NORMAL_MAX_FREQ; 67static char *low_power_max_cpu_freq = LOW_POWER_MAX_FREQ; 68 69 70#define container_of(addr, struct_name, field_name) \ 71 ((struct_name *)((char *)(addr) - offsetof(struct_name, field_name))) 72 73 74static int sysfs_write(const char *path, char *s) 75{ 76 char buf[80]; 77 int len; 78 int fd = open(path, O_WRONLY); 79 80 if (fd < 0) { 81 strerror_r(errno, buf, sizeof(buf)); 82 ALOGE("Error opening %s: %s\n", path, buf); 83 return fd; 84 } 85 86 len = write(fd, s, strlen(s)); 87 if (len < 0) { 88 strerror_r(errno, buf, sizeof(buf)); 89 ALOGE("Error writing to %s: %s\n", path, buf); 90 } 91 92 close(fd); 93 return len; 94} 95 96#define NSEC_PER_SEC 1000000000LL 97static long long gettime_ns(void) 98{ 99 struct timespec ts; 100 101 clock_gettime(CLOCK_MONOTONIC, &ts); 102 return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; 103} 104 105static void nanosleep_ns(long long ns) 106{ 107 struct timespec ts; 108 ts.tv_sec = ns/NSEC_PER_SEC; 109 ts.tv_nsec = ns%NSEC_PER_SEC; 110 nanosleep(&ts, NULL); 111} 112 113/*[interactive cpufreq gov funcs]*********************************************/ 114static void interactive_power_init(struct hikey_power_module __unused *hikey) 115{ 116 int32_t is_svelte = property_get_int32(SVELTE_PROP, 0); 117 118 if (sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate", 119 "20000") < 0) 120 return; 121 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_slack", 122 "20000"); 123 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time", 124 "80000"); 125 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq", 126 "1200000"); 127 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load", 128 "99"); 129 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/target_loads", 130 "65 729000:75 960000:85"); 131 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay", 132 "20000"); 133 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration", 134 "1000000"); 135 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/io_is_busy", "0"); 136 137 if (is_svelte) { 138 char prop_buffer[PROPERTY_VALUE_MAX]; 139 int len = property_get(SVELTE_MAX_FREQ_PROP, prop_buffer, 140 LOW_POWER_MAX_FREQ); 141 142 max_cpu_freq = strndup(prop_buffer, len); 143 len = property_get(SVELTE_LOW_POWER_MAX_FREQ_PROP, prop_buffer, 144 LOW_POWER_MAX_FREQ); 145 low_power_max_cpu_freq = strndup(prop_buffer, len); 146 } 147} 148 149static void power_set_interactive(struct power_module __unused *module, int on) 150{ 151 ALOGV("power_set_interactive: %d\n", on); 152 153 /* 154 * Lower maximum frequency when screen is off. 155 */ 156 sysfs_write(CPU_MAX_FREQ_PATH, 157 (!on || low_power_mode) ? low_power_max_cpu_freq : max_cpu_freq); 158 sysfs_write(INTERACTIVE_IO_IS_BUSY_PATH, on ? "1" : "0"); 159 ALOGV("power_set_interactive: %d done\n", on); 160} 161 162static int interactive_boostpulse(struct hikey_power_module *hikey) 163{ 164 char buf[80]; 165 int len; 166 167 if (hikey->boostpulse_fd < 0) 168 hikey->boostpulse_fd = open(INTERACTIVE_BOOSTPULSE_PATH, O_WRONLY); 169 170 if (hikey->boostpulse_fd < 0) { 171 if (!hikey->boostpulse_warned) { 172 strerror_r(errno, buf, sizeof(buf)); 173 ALOGE("Error opening %s: %s\n", INTERACTIVE_BOOSTPULSE_PATH, 174 buf); 175 hikey->boostpulse_warned = 1; 176 } 177 return hikey->boostpulse_fd; 178 } 179 180 len = write(hikey->boostpulse_fd, "1", 1); 181 if (len < 0) { 182 strerror_r(errno, buf, sizeof(buf)); 183 ALOGE("Error writing to %s: %s\n", 184 INTERACTIVE_BOOSTPULSE_PATH, buf); 185 return -1; 186 } 187 return 0; 188} 189 190/*[schedtune functions]*******************************************************/ 191 192int schedtune_sysfs_boost(struct hikey_power_module *hikey, char* booststr) 193{ 194 char buf[80]; 195 int len; 196 197 if (hikey->schedtune_boost_fd < 0) 198 return hikey->schedtune_boost_fd; 199 200 len = write(hikey->schedtune_boost_fd, booststr, 2); 201 if (len < 0) { 202 strerror_r(errno, buf, sizeof(buf)); 203 ALOGE("Error writing to %s: %s\n", SCHEDTUNE_BOOST_PATH, buf); 204 } 205 return len; 206} 207 208static void* schedtune_deboost_thread(void* arg) 209{ 210 struct hikey_power_module *hikey = (struct hikey_power_module *)arg; 211 212 while(1) { 213 sem_wait(&hikey->signal_lock); 214 while(1) { 215 long long now, sleeptime = 0; 216 217 pthread_mutex_lock(&hikey->lock); 218 now = gettime_ns(); 219 if (hikey->deboost_time > now) { 220 sleeptime = hikey->deboost_time - now; 221 pthread_mutex_unlock(&hikey->lock); 222 nanosleep_ns(sleeptime); 223 continue; 224 } 225 226 schedtune_sysfs_boost(hikey, SCHEDTUNE_BOOST_NORM); 227 hikey->deboost_time = 0; 228 pthread_mutex_unlock(&hikey->lock); 229 break; 230 } 231 } 232 return NULL; 233} 234 235static int schedtune_boost(struct hikey_power_module *hikey) 236{ 237 long long now; 238 239 if (hikey->schedtune_boost_fd < 0) 240 return hikey->schedtune_boost_fd; 241 242 now = gettime_ns(); 243 if (!hikey->deboost_time) { 244 schedtune_sysfs_boost(hikey, SCHEDTUNE_BOOST_INTERACTIVE); 245 sem_post(&hikey->signal_lock); 246 } 247 hikey->deboost_time = now + SCHEDTUNE_BOOST_TIME_NS; 248 249 return 0; 250} 251 252static void schedtune_power_init(struct hikey_power_module *hikey) 253{ 254 char buf[50]; 255 pthread_t tid; 256 257 258 hikey->deboost_time = 0; 259 sem_init(&hikey->signal_lock, 0, 1); 260 261 hikey->schedtune_boost_fd = open(SCHEDTUNE_BOOST_PATH, O_WRONLY); 262 if (hikey->schedtune_boost_fd < 0) { 263 strerror_r(errno, buf, sizeof(buf)); 264 ALOGE("Error opening %s: %s\n", SCHEDTUNE_BOOST_PATH, buf); 265 } 266 267 pthread_create(&tid, NULL, schedtune_deboost_thread, hikey); 268} 269 270/*[generic functions]*********************************************************/ 271static void hikey_power_init(struct power_module __unused *module) 272{ 273 struct hikey_power_module *hikey = container_of(module, 274 struct hikey_power_module, base); 275 interactive_power_init(hikey); 276 schedtune_power_init(hikey); 277} 278 279static void hikey_hint_interaction(struct hikey_power_module *mod) 280{ 281 /* Try interactive cpufreq boosting first */ 282 if(!interactive_boostpulse(mod)) 283 return; 284 /* Then try EAS schedtune boosting */ 285 if(!schedtune_boost(mod)) 286 return; 287} 288 289static void hikey_power_hint(struct power_module *module, power_hint_t hint, 290 void *data) 291{ 292 struct hikey_power_module *hikey = container_of(module, 293 struct hikey_power_module, base); 294 295 pthread_mutex_lock(&hikey->lock); 296 switch (hint) { 297 case POWER_HINT_INTERACTION: 298 hikey_hint_interaction(hikey); 299 break; 300 301 case POWER_HINT_VSYNC: 302 break; 303 304 case POWER_HINT_LOW_POWER: 305 if (data) { 306 sysfs_write(CPU_MAX_FREQ_PATH, low_power_max_cpu_freq); 307 } else { 308 sysfs_write(CPU_MAX_FREQ_PATH, max_cpu_freq); 309 } 310 low_power_mode = data; 311 break; 312 313 default: 314 break; 315 } 316 pthread_mutex_unlock(&hikey->lock); 317} 318 319static void set_feature(struct power_module *module, feature_t feature, int state) 320{ 321 struct hikey_power_module *hikey = container_of(module, 322 struct hikey_power_module, base); 323 switch (feature) { 324 default: 325 ALOGW("Error setting the feature %d and state %d, it doesn't exist\n", 326 feature, state); 327 break; 328 } 329} 330 331static int power_open(const hw_module_t* __unused module, const char* name, 332 hw_device_t** device) 333{ 334 int retval = 0; /* 0 is ok; -1 is error */ 335 ALOGD("%s: enter; name=%s", __FUNCTION__, name); 336 337 if (strcmp(name, POWER_HARDWARE_MODULE_ID) == 0) { 338 power_module_t *dev = (power_module_t *)calloc(1, 339 sizeof(power_module_t)); 340 341 if (dev) { 342 /* Common hw_device_t fields */ 343 dev->common.tag = HARDWARE_DEVICE_TAG; 344 dev->common.module_api_version = POWER_MODULE_API_VERSION_0_5; 345 dev->common.hal_api_version = HARDWARE_HAL_API_VERSION; 346 347 dev->init = hikey_power_init; 348 dev->powerHint = hikey_power_hint; 349 dev->setInteractive = power_set_interactive; 350 dev->setFeature = set_feature; 351 352 *device = (hw_device_t*)dev; 353 } else 354 retval = -ENOMEM; 355 } else { 356 retval = -EINVAL; 357 } 358 359 ALOGD("%s: exit %d", __FUNCTION__, retval); 360 return retval; 361} 362 363static struct hw_module_methods_t power_module_methods = { 364 .open = power_open, 365}; 366 367struct hikey_power_module HAL_MODULE_INFO_SYM = { 368 .base = { 369 .common = { 370 .tag = HARDWARE_MODULE_TAG, 371 .module_api_version = POWER_MODULE_API_VERSION_0_2, 372 .hal_api_version = HARDWARE_HAL_API_VERSION, 373 .id = POWER_HARDWARE_MODULE_ID, 374 .name = "HiKey Power HAL", 375 .author = "The Android Open Source Project", 376 .methods = &power_module_methods, 377 }, 378 379 .init = hikey_power_init, 380 .setInteractive = power_set_interactive, 381 .powerHint = hikey_power_hint, 382 .setFeature = set_feature, 383 }, 384 385 .lock = PTHREAD_MUTEX_INITIALIZER, 386 .boostpulse_fd = -1, 387 .boostpulse_warned = 0, 388}; 389