Home | History | Annotate | Download | only in power
      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 
     52 struct 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 
     64 static bool low_power_mode = false;
     65 
     66 static char *max_cpu_freq = NORMAL_MAX_FREQ;
     67 static 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 
     74 static 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
     97 static 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 
    105 static 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]*********************************************/
    114 static 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 
    149 static 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 
    162 static 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 
    192 int 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 
    208 static 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 
    235 static 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 
    252 static 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]*********************************************************/
    271 static 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 
    279 static 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 
    289 static 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 
    319 static 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 
    331 static 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 
    363 static struct hw_module_methods_t power_module_methods = {
    364     .open = power_open,
    365 };
    366 
    367 struct 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