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