blob: e9a5248618ae3db3823fdb0d3c4b150386ea2a6e [file] [log] [blame]
John Stultz7266c5f2016-06-09 14:18:57 -07001/*
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>
John Stultz4c1a82b2016-08-18 14:49:01 -070028#include <pthread.h>
29#include <semaphore.h>
John Stultz7266c5f2016-06-09 14:18:57 -070030#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
John Stultz884c7132016-08-24 20:19:35 -070039#define SCHEDTUNE_BOOST_PATH "/dev/stune/foreground/schedtune.boost"
John Stultz4c1a82b2016-08-18 14:49:01 -070040#define SCHEDTUNE_BOOST_NORM "10"
41#define SCHEDTUNE_BOOST_INTERACTIVE "40"
42#define SCHEDTUNE_BOOST_TIME_NS 1000000000LL
John Stultz127e1cc2016-08-18 12:20:40 -070043#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"
John Stultz7266c5f2016-06-09 14:18:57 -070045#define CPU_MAX_FREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
John Stultz7266c5f2016-06-09 14:18:57 -070046#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;
John Stultz4c1a82b2016-08-18 14:49:01 -070055 /* interactive gov boost values */
John Stultz7266c5f2016-06-09 14:18:57 -070056 int boostpulse_fd;
57 int boostpulse_warned;
John Stultz4c1a82b2016-08-18 14:49:01 -070058 /* EAS schedtune values */
59 int schedtune_boost_fd;
60 long long deboost_time;
61 sem_t signal_lock;
John Stultz7266c5f2016-06-09 14:18:57 -070062};
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
John Stultze150ab32016-08-18 14:04:34 -070069
70#define container_of(addr, struct_name, field_name) \
71 ((struct_name *)((char *)(addr) - offsetof(struct_name, field_name)))
72
73
Dmitry Shmidtcc147582016-11-07 13:13:34 -080074static int sysfs_write(const char *path, char *s)
John Stultz7266c5f2016-06-09 14:18:57 -070075{
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);
Dmitry Shmidtcc147582016-11-07 13:13:34 -080083 return fd;
John Stultz7266c5f2016-06-09 14:18:57 -070084 }
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);
Dmitry Shmidtcc147582016-11-07 13:13:34 -080093 return len;
John Stultz7266c5f2016-06-09 14:18:57 -070094}
95
John Stultz4c1a82b2016-08-18 14:49:01 -070096#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
John Stultzc5793712016-08-18 14:00:49 -0700113/*[interactive cpufreq gov funcs]*********************************************/
John Stultze150ab32016-08-18 14:04:34 -0700114static void interactive_power_init(struct hikey_power_module __unused *hikey)
John Stultz7266c5f2016-06-09 14:18:57 -0700115{
John Stultz65de27c2016-08-18 11:50:42 -0700116 int32_t is_svelte = property_get_int32(SVELTE_PROP, 0);
117
Dmitry Shmidtcc147582016-11-07 13:13:34 -0800118 if (sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate",
119 "20000") < 0)
120 return;
John Stultz7266c5f2016-06-09 14:18:57 -0700121 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
John Stultz65de27c2016-08-18 11:50:42 -0700137 if (is_svelte) {
138 char prop_buffer[PROPERTY_VALUE_MAX];
John Stultz127e1cc2016-08-18 12:20:40 -0700139 int len = property_get(SVELTE_MAX_FREQ_PROP, prop_buffer,
140 LOW_POWER_MAX_FREQ);
John Stultz65de27c2016-08-18 11:50:42 -0700141
142 max_cpu_freq = strndup(prop_buffer, len);
John Stultz127e1cc2016-08-18 12:20:40 -0700143 len = property_get(SVELTE_LOW_POWER_MAX_FREQ_PROP, prop_buffer,
144 LOW_POWER_MAX_FREQ);
John Stultz65de27c2016-08-18 11:50:42 -0700145 low_power_max_cpu_freq = strndup(prop_buffer, len);
146 }
John Stultz7266c5f2016-06-09 14:18:57 -0700147}
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);
John Stultz127e1cc2016-08-18 12:20:40 -0700158 sysfs_write(INTERACTIVE_IO_IS_BUSY_PATH, on ? "1" : "0");
John Stultz7266c5f2016-06-09 14:18:57 -0700159 ALOGV("power_set_interactive: %d done\n", on);
160}
161
John Stultz5e1546b2016-08-18 11:56:31 -0700162static int interactive_boostpulse(struct hikey_power_module *hikey)
163{
164 char buf[80];
165 int len;
166
John Stultz64b54ca2016-08-18 15:01:33 -0700167 if (hikey->boostpulse_fd < 0)
John Stultz127e1cc2016-08-18 12:20:40 -0700168 hikey->boostpulse_fd = open(INTERACTIVE_BOOSTPULSE_PATH, O_WRONLY);
John Stultz5e1546b2016-08-18 11:56:31 -0700169
John Stultz64b54ca2016-08-18 15:01:33 -0700170 if (hikey->boostpulse_fd < 0) {
171 if (!hikey->boostpulse_warned) {
John Stultz5e1546b2016-08-18 11:56:31 -0700172 strerror_r(errno, buf, sizeof(buf));
John Stultz127e1cc2016-08-18 12:20:40 -0700173 ALOGE("Error opening %s: %s\n", INTERACTIVE_BOOSTPULSE_PATH,
John Stultz64b54ca2016-08-18 15:01:33 -0700174 buf);
175 hikey->boostpulse_warned = 1;
John Stultz5e1546b2016-08-18 11:56:31 -0700176 }
John Stultz64b54ca2016-08-18 15:01:33 -0700177 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",
John Stultz127e1cc2016-08-18 12:20:40 -0700184 INTERACTIVE_BOOSTPULSE_PATH, buf);
John Stultz64b54ca2016-08-18 15:01:33 -0700185 return -1;
John Stultz5e1546b2016-08-18 11:56:31 -0700186 }
187 return 0;
188}
189
John Stultz4c1a82b2016-08-18 14:49:01 -0700190/*[schedtune functions]*******************************************************/
John Stultzc5793712016-08-18 14:00:49 -0700191
John Stultz4c1a82b2016-08-18 14:49:01 -0700192int 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]*********************************************************/
John Stultzc5793712016-08-18 14:00:49 -0700271static void hikey_power_init(struct power_module __unused *module)
272{
John Stultze150ab32016-08-18 14:04:34 -0700273 struct hikey_power_module *hikey = container_of(module,
274 struct hikey_power_module, base);
275 interactive_power_init(hikey);
John Stultz4c1a82b2016-08-18 14:49:01 -0700276 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;
John Stultzc5793712016-08-18 14:00:49 -0700287}
288
John Stultz7266c5f2016-06-09 14:18:57 -0700289static void hikey_power_hint(struct power_module *module, power_hint_t hint,
290 void *data)
291{
John Stultze150ab32016-08-18 14:04:34 -0700292 struct hikey_power_module *hikey = container_of(module,
293 struct hikey_power_module, base);
John Stultz7266c5f2016-06-09 14:18:57 -0700294
295 pthread_mutex_lock(&hikey->lock);
296 switch (hint) {
297 case POWER_HINT_INTERACTION:
John Stultz4c1a82b2016-08-18 14:49:01 -0700298 hikey_hint_interaction(hikey);
John Stultz7266c5f2016-06-09 14:18:57 -0700299 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
John Stultzab1f6a72016-08-18 12:28:55 -0700319static void set_feature(struct power_module *module, feature_t feature, int state)
320{
John Stultze150ab32016-08-18 14:04:34 -0700321 struct hikey_power_module *hikey = container_of(module,
322 struct hikey_power_module, base);
John Stultzab1f6a72016-08-18 12:28:55 -0700323 switch (feature) {
324 default:
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800325 ALOGW("Error setting the feature %d and state %d, it doesn't exist\n",
326 feature, state);
John Stultzab1f6a72016-08-18 12:28:55 -0700327 break;
328 }
329}
330
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800331static int power_open(const hw_module_t* __unused module, const char* name,
Ruchi Kandoi96c621c2016-11-03 12:46:10 -0700332 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
John Stultz7266c5f2016-06-09 14:18:57 -0700363static struct hw_module_methods_t power_module_methods = {
Ruchi Kandoi96c621c2016-11-03 12:46:10 -0700364 .open = power_open,
John Stultz7266c5f2016-06-09 14:18:57 -0700365};
366
367struct hikey_power_module HAL_MODULE_INFO_SYM = {
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800368 .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,
John Stultz7266c5f2016-06-09 14:18:57 -0700377 },
378
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800379 .init = hikey_power_init,
380 .setInteractive = power_set_interactive,
381 .powerHint = hikey_power_hint,
382 .setFeature = set_feature,
John Stultz7266c5f2016-06-09 14:18:57 -0700383 },
384
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800385 .lock = PTHREAD_MUTEX_INITIALIZER,
386 .boostpulse_fd = -1,
387 .boostpulse_warned = 0,
John Stultz7266c5f2016-06-09 14:18:57 -0700388};