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 <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