blob: 5143cb794f1f33e740011016e9683d6226385bd9 [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
John Stultz7266c5f2016-06-09 14:18:57 -070074static void 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;
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}
94
John Stultz4c1a82b2016-08-18 14:49:01 -070095#define NSEC_PER_SEC 1000000000LL
96static long long gettime_ns(void)
97{
98 struct timespec ts;
99
100 clock_gettime(CLOCK_MONOTONIC, &ts);
101 return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
102}
103
104static void nanosleep_ns(long long ns)
105{
106 struct timespec ts;
107 ts.tv_sec = ns/NSEC_PER_SEC;
108 ts.tv_nsec = ns%NSEC_PER_SEC;
109 nanosleep(&ts, NULL);
110}
111
John Stultzc5793712016-08-18 14:00:49 -0700112/*[interactive cpufreq gov funcs]*********************************************/
John Stultze150ab32016-08-18 14:04:34 -0700113static void interactive_power_init(struct hikey_power_module __unused *hikey)
John Stultz7266c5f2016-06-09 14:18:57 -0700114{
John Stultz65de27c2016-08-18 11:50:42 -0700115 int32_t is_svelte = property_get_int32(SVELTE_PROP, 0);
116
John Stultz7266c5f2016-06-09 14:18:57 -0700117 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate",
118 "20000");
119 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_slack",
120 "20000");
121 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time",
122 "80000");
123 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq",
124 "1200000");
125 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load",
126 "99");
127 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/target_loads",
128 "65 729000:75 960000:85");
129 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay",
130 "20000");
131 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration",
132 "1000000");
133 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/io_is_busy", "0");
134
John Stultz65de27c2016-08-18 11:50:42 -0700135 if (is_svelte) {
136 char prop_buffer[PROPERTY_VALUE_MAX];
John Stultz127e1cc2016-08-18 12:20:40 -0700137 int len = property_get(SVELTE_MAX_FREQ_PROP, prop_buffer,
138 LOW_POWER_MAX_FREQ);
John Stultz65de27c2016-08-18 11:50:42 -0700139
140 max_cpu_freq = strndup(prop_buffer, len);
John Stultz127e1cc2016-08-18 12:20:40 -0700141 len = property_get(SVELTE_LOW_POWER_MAX_FREQ_PROP, prop_buffer,
142 LOW_POWER_MAX_FREQ);
John Stultz65de27c2016-08-18 11:50:42 -0700143 low_power_max_cpu_freq = strndup(prop_buffer, len);
144 }
John Stultz7266c5f2016-06-09 14:18:57 -0700145}
146
147static void power_set_interactive(struct power_module __unused *module, int on)
148{
149 ALOGV("power_set_interactive: %d\n", on);
150
151 /*
152 * Lower maximum frequency when screen is off.
153 */
154 sysfs_write(CPU_MAX_FREQ_PATH,
155 (!on || low_power_mode) ? low_power_max_cpu_freq : max_cpu_freq);
John Stultz127e1cc2016-08-18 12:20:40 -0700156 sysfs_write(INTERACTIVE_IO_IS_BUSY_PATH, on ? "1" : "0");
John Stultz7266c5f2016-06-09 14:18:57 -0700157 ALOGV("power_set_interactive: %d done\n", on);
158}
159
John Stultz5e1546b2016-08-18 11:56:31 -0700160static int interactive_boostpulse(struct hikey_power_module *hikey)
161{
162 char buf[80];
163 int len;
164
John Stultz64b54ca2016-08-18 15:01:33 -0700165 if (hikey->boostpulse_fd < 0)
John Stultz127e1cc2016-08-18 12:20:40 -0700166 hikey->boostpulse_fd = open(INTERACTIVE_BOOSTPULSE_PATH, O_WRONLY);
John Stultz5e1546b2016-08-18 11:56:31 -0700167
John Stultz64b54ca2016-08-18 15:01:33 -0700168 if (hikey->boostpulse_fd < 0) {
169 if (!hikey->boostpulse_warned) {
John Stultz5e1546b2016-08-18 11:56:31 -0700170 strerror_r(errno, buf, sizeof(buf));
John Stultz127e1cc2016-08-18 12:20:40 -0700171 ALOGE("Error opening %s: %s\n", INTERACTIVE_BOOSTPULSE_PATH,
John Stultz64b54ca2016-08-18 15:01:33 -0700172 buf);
173 hikey->boostpulse_warned = 1;
John Stultz5e1546b2016-08-18 11:56:31 -0700174 }
John Stultz64b54ca2016-08-18 15:01:33 -0700175 return hikey->boostpulse_fd;
176 }
177
178 len = write(hikey->boostpulse_fd, "1", 1);
179 if (len < 0) {
180 strerror_r(errno, buf, sizeof(buf));
181 ALOGE("Error writing to %s: %s\n",
John Stultz127e1cc2016-08-18 12:20:40 -0700182 INTERACTIVE_BOOSTPULSE_PATH, buf);
John Stultz64b54ca2016-08-18 15:01:33 -0700183 return -1;
John Stultz5e1546b2016-08-18 11:56:31 -0700184 }
185 return 0;
186}
187
John Stultz4c1a82b2016-08-18 14:49:01 -0700188/*[schedtune functions]*******************************************************/
John Stultzc5793712016-08-18 14:00:49 -0700189
John Stultz4c1a82b2016-08-18 14:49:01 -0700190int schedtune_sysfs_boost(struct hikey_power_module *hikey, char* booststr)
191{
192 char buf[80];
193 int len;
194
195 if (hikey->schedtune_boost_fd < 0)
196 return hikey->schedtune_boost_fd;
197
198 len = write(hikey->schedtune_boost_fd, booststr, 2);
199 if (len < 0) {
200 strerror_r(errno, buf, sizeof(buf));
201 ALOGE("Error writing to %s: %s\n", SCHEDTUNE_BOOST_PATH, buf);
202 }
203 return len;
204}
205
206static void* schedtune_deboost_thread(void* arg)
207{
208 struct hikey_power_module *hikey = (struct hikey_power_module *)arg;
209
210 while(1) {
211 sem_wait(&hikey->signal_lock);
212 while(1) {
213 long long now, sleeptime = 0;
214
215 pthread_mutex_lock(&hikey->lock);
216 now = gettime_ns();
217 if (hikey->deboost_time > now) {
218 sleeptime = hikey->deboost_time - now;
219 pthread_mutex_unlock(&hikey->lock);
220 nanosleep_ns(sleeptime);
221 continue;
222 }
223
224 schedtune_sysfs_boost(hikey, SCHEDTUNE_BOOST_NORM);
225 hikey->deboost_time = 0;
226 pthread_mutex_unlock(&hikey->lock);
227 break;
228 }
229 }
230 return NULL;
231}
232
233static int schedtune_boost(struct hikey_power_module *hikey)
234{
235 long long now;
236
237 if (hikey->schedtune_boost_fd < 0)
238 return hikey->schedtune_boost_fd;
239
240 now = gettime_ns();
241 if (!hikey->deboost_time) {
242 schedtune_sysfs_boost(hikey, SCHEDTUNE_BOOST_INTERACTIVE);
243 sem_post(&hikey->signal_lock);
244 }
245 hikey->deboost_time = now + SCHEDTUNE_BOOST_TIME_NS;
246
247 return 0;
248}
249
250static void schedtune_power_init(struct hikey_power_module *hikey)
251{
252 char buf[50];
253 pthread_t tid;
254
255
256 hikey->deboost_time = 0;
257 sem_init(&hikey->signal_lock, 0, 1);
258
259 hikey->schedtune_boost_fd = open(SCHEDTUNE_BOOST_PATH, O_WRONLY);
260 if (hikey->schedtune_boost_fd < 0) {
261 strerror_r(errno, buf, sizeof(buf));
262 ALOGE("Error opening %s: %s\n", SCHEDTUNE_BOOST_PATH, buf);
263 }
264
265 pthread_create(&tid, NULL, schedtune_deboost_thread, hikey);
266}
267
268/*[generic functions]*********************************************************/
John Stultzc5793712016-08-18 14:00:49 -0700269static void hikey_power_init(struct power_module __unused *module)
270{
John Stultze150ab32016-08-18 14:04:34 -0700271 struct hikey_power_module *hikey = container_of(module,
272 struct hikey_power_module, base);
273 interactive_power_init(hikey);
John Stultz4c1a82b2016-08-18 14:49:01 -0700274 schedtune_power_init(hikey);
275}
276
277static void hikey_hint_interaction(struct hikey_power_module *mod)
278{
279 /* Try interactive cpufreq boosting first */
280 if(!interactive_boostpulse(mod))
281 return;
282 /* Then try EAS schedtune boosting */
283 if(!schedtune_boost(mod))
284 return;
John Stultzc5793712016-08-18 14:00:49 -0700285}
286
John Stultz7266c5f2016-06-09 14:18:57 -0700287static void hikey_power_hint(struct power_module *module, power_hint_t hint,
288 void *data)
289{
John Stultze150ab32016-08-18 14:04:34 -0700290 struct hikey_power_module *hikey = container_of(module,
291 struct hikey_power_module, base);
John Stultz7266c5f2016-06-09 14:18:57 -0700292
293 pthread_mutex_lock(&hikey->lock);
294 switch (hint) {
295 case POWER_HINT_INTERACTION:
John Stultz4c1a82b2016-08-18 14:49:01 -0700296 hikey_hint_interaction(hikey);
John Stultz7266c5f2016-06-09 14:18:57 -0700297 break;
298
299 case POWER_HINT_VSYNC:
300 break;
301
302 case POWER_HINT_LOW_POWER:
303 if (data) {
304 sysfs_write(CPU_MAX_FREQ_PATH, low_power_max_cpu_freq);
305 } else {
306 sysfs_write(CPU_MAX_FREQ_PATH, max_cpu_freq);
307 }
308 low_power_mode = data;
309 break;
310
311 default:
312 break;
313 }
314 pthread_mutex_unlock(&hikey->lock);
315}
316
John Stultzab1f6a72016-08-18 12:28:55 -0700317static void set_feature(struct power_module *module, feature_t feature, int state)
318{
John Stultze150ab32016-08-18 14:04:34 -0700319 struct hikey_power_module *hikey = container_of(module,
320 struct hikey_power_module, base);
John Stultzab1f6a72016-08-18 12:28:55 -0700321 switch (feature) {
322 default:
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800323 ALOGW("Error setting the feature %d and state %d, it doesn't exist\n",
324 feature, state);
John Stultzab1f6a72016-08-18 12:28:55 -0700325 break;
326 }
327}
328
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800329static int power_open(const hw_module_t* __unused module, const char* name,
Ruchi Kandoi96c621c2016-11-03 12:46:10 -0700330 hw_device_t** device)
331{
332 int retval = 0; /* 0 is ok; -1 is error */
333 ALOGD("%s: enter; name=%s", __FUNCTION__, name);
334
335 if (strcmp(name, POWER_HARDWARE_MODULE_ID) == 0) {
336 power_module_t *dev = (power_module_t *)calloc(1,
337 sizeof(power_module_t));
338
339 if (dev) {
340 /* Common hw_device_t fields */
341 dev->common.tag = HARDWARE_DEVICE_TAG;
342 dev->common.module_api_version = POWER_MODULE_API_VERSION_0_5;
343 dev->common.hal_api_version = HARDWARE_HAL_API_VERSION;
344
345 dev->init = hikey_power_init;
346 dev->powerHint = hikey_power_hint;
347 dev->setInteractive = power_set_interactive;
348 dev->setFeature = set_feature;
349
350 *device = (hw_device_t*)dev;
351 } else
352 retval = -ENOMEM;
353 } else {
354 retval = -EINVAL;
355 }
356
357 ALOGD("%s: exit %d", __FUNCTION__, retval);
358 return retval;
359}
360
John Stultz7266c5f2016-06-09 14:18:57 -0700361static struct hw_module_methods_t power_module_methods = {
Ruchi Kandoi96c621c2016-11-03 12:46:10 -0700362 .open = power_open,
John Stultz7266c5f2016-06-09 14:18:57 -0700363};
364
365struct hikey_power_module HAL_MODULE_INFO_SYM = {
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800366 .base = {
367 .common = {
368 .tag = HARDWARE_MODULE_TAG,
369 .module_api_version = POWER_MODULE_API_VERSION_0_2,
370 .hal_api_version = HARDWARE_HAL_API_VERSION,
371 .id = POWER_HARDWARE_MODULE_ID,
372 .name = "HiKey Power HAL",
373 .author = "The Android Open Source Project",
374 .methods = &power_module_methods,
John Stultz7266c5f2016-06-09 14:18:57 -0700375 },
376
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800377 .init = hikey_power_init,
378 .setInteractive = power_set_interactive,
379 .powerHint = hikey_power_hint,
380 .setFeature = set_feature,
John Stultz7266c5f2016-06-09 14:18:57 -0700381 },
382
Dmitry Shmidt0ea8f4d2016-11-07 12:55:16 -0800383 .lock = PTHREAD_MUTEX_INITIALIZER,
384 .boostpulse_fd = -1,
385 .boostpulse_warned = 0,
John Stultz7266c5f2016-06-09 14:18:57 -0700386};