• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
sysfs_write(const char * path,char * s)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
gettime_ns(void)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 
nanosleep_ns(long long ns)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]*********************************************/
interactive_power_init(struct hikey_power_module __unused * hikey)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 
power_set_interactive(struct power_module __unused * module,int on)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 
interactive_boostpulse(struct hikey_power_module * hikey)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 
schedtune_sysfs_boost(struct hikey_power_module * hikey,char * booststr)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 
schedtune_deboost_thread(void * arg)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 
schedtune_boost(struct hikey_power_module * hikey)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 
schedtune_power_init(struct hikey_power_module * hikey)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]*********************************************************/
hikey_power_init(struct power_module __unused * module)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 
hikey_hint_interaction(struct hikey_power_module * mod)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 
hikey_power_hint(struct power_module * module,power_hint_t hint,void * data)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 
set_feature(struct power_module * module,feature_t feature,int state)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 
power_open(const hw_module_t * __unused module,const char * name,hw_device_t ** device)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