1 /*
2 * Copyright (C) 2014 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
17 #include <dirent.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <cutils/properties.h>
27 //#define LOG_NDEBUG 0
28
29 #define LOG_TAG "DragonPowerHAL"
30 #include <utils/Log.h>
31
32 #include <hardware/hardware.h>
33 #include <hardware/power.h>
34
35 #include "timed_qos_manager.h"
36
37 #define BOOSTPULSE_PATH "/sys/devices/system/cpu/cpufreq/interactive/boostpulse"
38 #define CPU_MAX_FREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
39 #define IO_IS_BUSY_PATH "/sys/devices/system/cpu/cpufreq/interactive/io_is_busy"
40 #define LIGHTBAR_SEQUENCE_PATH "/sys/class/chromeos/cros_ec/lightbar/sequence"
41 #define IIO_ACTIVITY_DEVICE_PATH "/sys/class/chromeos/cros_ec/device/cros-ec-activity.0"
42 #define IIO_DOUBLE_TAP_EVENT "events/in_activity_double_tap_change_falling_en"
43 #define IIO_DEVICE_PREFIX "iio:device"
44 #define EXT_VOLTAGE_LIM_PATH "/sys/class/chromeos/cros_ec/usb-pd-charger/ext_voltage_lim"
45 #define EC_POWER_LIMIT_NONE "0xffff"
46 #define LOW_POWER_MAX_FREQ "1020000"
47 #define NORMAL_MAX_FREQ "1912500"
48 #define GPU_CAP_PATH "/sys/kernel/debug/system_edp/capping/force_gpu"
49 #define LOW_POWER_GPU_CAP "3000"
50 #define NORMAL_GPU_CAP "0"
51 #define GPU_BOOST_PATH "/sys/devices/57000000.gpu/pstate"
52 #define GPU_BOOST_ENTER_CMD "06,0C" // boost GPU to work at least on 06 - 460MHz
53 #define GPU_BOOST_DURATION_MS 2000
54 #define GPU_BOOST_EXIT_CMD "auto"
55 #define GPU_FREQ_CONSTRAINT "852000 852000 -1 2000"
56
57 struct dragon_power_module {
58 struct power_module base;
59 pthread_mutex_t boost_pulse_lock;
60 pthread_mutex_t low_power_lock;
61 int boostpulse_fd;
62 int boostpulse_warned;
63 TimedQosManager *gpu_qos_manager;
64 };
65
66 static bool low_power_mode = false;
67 static char *iio_activity_device = NULL;
68
69 static const char *max_cpu_freq = NORMAL_MAX_FREQ;
70 static const char *low_power_max_cpu_freq = LOW_POWER_MAX_FREQ;
71
72 static const char *normal_gpu_cap = NORMAL_GPU_CAP;
73 static const char *low_power_gpu_cap = LOW_POWER_GPU_CAP;
74
75
sysfs_write(const char * path,const char * s)76 void sysfs_write(const char *path, const char *s)
77 {
78 char buf[80];
79 int len;
80 int fd = open(path, O_WRONLY);
81
82 if (fd < 0) {
83 strerror_r(errno, buf, sizeof(buf));
84 ALOGE("Error opening %s: %s\n", path, buf);
85 return;
86 }
87
88 len = write(fd, s, strlen(s));
89 if (len < 0) {
90 strerror_r(errno, buf, sizeof(buf));
91 ALOGE("Error writing to %s: %s\n", path, buf);
92 }
93
94 close(fd);
95 }
96
power_init(struct power_module __unused * module)97 static void power_init(struct power_module __unused *module)
98 {
99 struct dragon_power_module *dragon =
100 (struct dragon_power_module *) module;
101
102 dragon->gpu_qos_manager = new TimedQosManager("GPU",
103 new SysfsQosObject(GPU_BOOST_PATH, GPU_BOOST_ENTER_CMD, GPU_BOOST_EXIT_CMD),
104 false);
105 dragon->gpu_qos_manager->run("GpuTimedQosManager", PRIORITY_FOREGROUND);
106
107 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate",
108 "20000");
109 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_slack",
110 "20000");
111 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time",
112 "80000");
113 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq",
114 "1530000");
115 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load",
116 "99");
117 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/target_loads",
118 "65 228000:75 624000:85");
119 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay",
120 "20000");
121 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration",
122 "1000000");
123 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/io_is_busy", "0");
124 sysfs_write("/sys/class/chromeos/cros_ec/lightbar/userspace_control", "1");
125
126 /* Find the iio device used for activities/estures recognition */
127 DIR *iio_activity_dir = opendir(IIO_ACTIVITY_DEVICE_PATH);
128 if (iio_activity_dir == NULL) {
129 ALOGE("%s is not available.\n", iio_activity_dir);
130 return;
131 }
132 const struct dirent *ent_device;
133 while (ent_device = readdir(iio_activity_dir), ent_device != NULL) {
134 if (!strncmp(ent_device->d_name, IIO_DEVICE_PREFIX, strlen(IIO_DEVICE_PREFIX))) {
135 iio_activity_device = strdup(ent_device->d_name);
136 break;
137 }
138 }
139 if (iio_activity_device == NULL)
140 ALOGE("Activity device not found");
141 }
142
power_set_interactive(struct power_module __unused * module,int on)143 static void power_set_interactive(struct power_module __unused *module, int on)
144 {
145 ALOGV("power_set_interactive: %d\n", on);
146
147 /*
148 * Lower maximum frequency when screen is off.
149 */
150 sysfs_write(CPU_MAX_FREQ_PATH,
151 (!on || low_power_mode) ? low_power_max_cpu_freq : max_cpu_freq);
152 sysfs_write(IO_IS_BUSY_PATH, on ? "1" : "0");
153 sysfs_write(LIGHTBAR_SEQUENCE_PATH, on ? "s3s0" : "s0s3");
154 /* limit charging voltage to 5V when interactive otherwise no limit */
155 sysfs_write(EXT_VOLTAGE_LIM_PATH, on ? "5000" : EC_POWER_LIMIT_NONE);
156 if (iio_activity_device != NULL) {
157 char buf[128];
158 snprintf(buf, sizeof(buf), "%s/%s/%s", IIO_ACTIVITY_DEVICE_PATH,
159 iio_activity_device, IIO_DOUBLE_TAP_EVENT);
160 sysfs_write(buf, on ? "0" : "1");
161 }
162 ALOGV("power_set_interactive: %d done\n", on);
163 }
164
boostpulse_open(struct dragon_power_module * dragon)165 static int boostpulse_open(struct dragon_power_module *dragon)
166 {
167 char buf[80];
168 int len;
169
170 pthread_mutex_lock(&dragon->boost_pulse_lock);
171
172 if (dragon->boostpulse_fd < 0) {
173 dragon->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
174
175 if (dragon->boostpulse_fd < 0) {
176 if (!dragon->boostpulse_warned) {
177 strerror_r(errno, buf, sizeof(buf));
178 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, buf);
179 dragon->boostpulse_warned = 1;
180 }
181 }
182 }
183
184 pthread_mutex_unlock(&dragon->boost_pulse_lock);
185 return dragon->boostpulse_fd;
186 }
187
dragon_power_hint(struct power_module * module,power_hint_t hint,void * data)188 static void dragon_power_hint(struct power_module *module, power_hint_t hint,
189 void *data)
190 {
191 struct dragon_power_module *dragon =
192 (struct dragon_power_module *) module;
193 char buf[80];
194 int len;
195
196 switch (hint) {
197 case POWER_HINT_INTERACTION:
198 if (boostpulse_open(dragon) >= 0) {
199 len = write(dragon->boostpulse_fd, "1", 1);
200
201 if (len < 0) {
202 strerror_r(errno, buf, sizeof(buf));
203 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, buf);
204 }
205 }
206 if (dragon->gpu_qos_manager != NULL)
207 dragon->gpu_qos_manager->requestTimedQos(ms2ns(GPU_BOOST_DURATION_MS));
208
209 break;
210
211 case POWER_HINT_VSYNC:
212 break;
213
214 case POWER_HINT_LOW_POWER:
215 pthread_mutex_lock(&dragon->low_power_lock);
216 if (data) {
217 sysfs_write(CPU_MAX_FREQ_PATH, low_power_max_cpu_freq);
218 sysfs_write(GPU_CAP_PATH, low_power_gpu_cap);
219 } else {
220 sysfs_write(CPU_MAX_FREQ_PATH, max_cpu_freq);
221 sysfs_write(GPU_CAP_PATH, normal_gpu_cap);
222 }
223 low_power_mode = data;
224 pthread_mutex_unlock(&dragon->low_power_lock);
225 break;
226
227 default:
228 break;
229 }
230 }
231
dragon_power_open(const hw_module_t * module,const char * name,hw_device_t ** device)232 static int dragon_power_open(const hw_module_t *module, const char *name,
233 hw_device_t **device)
234 {
235 ALOGD("%s: enter; name=%s", __FUNCTION__, name);
236 int retval = 0; /* 0 is ok; -1 is error */
237
238 if (strcmp(name, POWER_HARDWARE_MODULE_ID) == 0) {
239 dragon_power_module *dev = (dragon_power_module *)calloc(1,
240 sizeof(dragon_power_module));
241
242 if (dev) {
243 /* Common hw_device_t fields */
244 dev->base.common.tag = HARDWARE_MODULE_TAG;
245 dev->base.common.module_api_version = POWER_MODULE_API_VERSION_0_2;
246 dev->base.common.hal_api_version = HARDWARE_HAL_API_VERSION;
247
248 dev->base.init = power_init;
249 dev->base.powerHint = dragon_power_hint;
250 dev->base.setInteractive = power_set_interactive;
251 dev->boost_pulse_lock = PTHREAD_MUTEX_INITIALIZER;
252 dev->low_power_lock = PTHREAD_MUTEX_INITIALIZER;
253 dev->boostpulse_fd = -1;
254 dev->boostpulse_warned = 0;
255 dev->gpu_qos_manager = NULL;
256
257 *device = (hw_device_t*)dev;
258 } else
259 retval = -ENOMEM;
260 } else {
261 retval = -EINVAL;
262 }
263
264 ALOGD("%s: exit %d", __FUNCTION__, retval);
265 return retval;
266 }
267
268
269 static struct hw_module_methods_t power_module_methods = {
270 .open = dragon_power_open,
271 };
272
273 struct dragon_power_module HAL_MODULE_INFO_SYM = {
274 base: {
275 common: {
276 tag: HARDWARE_MODULE_TAG,
277 module_api_version: POWER_MODULE_API_VERSION_0_2,
278 hal_api_version: HARDWARE_HAL_API_VERSION,
279 id: POWER_HARDWARE_MODULE_ID,
280 name: "Dragon Power HAL",
281 author: "The Android Open Source Project",
282 methods: &power_module_methods,
283 dso: NULL,
284 reserved: {0},
285 },
286
287 init: power_init,
288 setInteractive: power_set_interactive,
289 powerHint: dragon_power_hint,
290 },
291
292 boost_pulse_lock: PTHREAD_MUTEX_INITIALIZER,
293 low_power_lock: PTHREAD_MUTEX_INITIALIZER,
294 boostpulse_fd: -1,
295 boostpulse_warned: 0,
296 gpu_qos_manager: NULL,
297 };
298
299