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 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 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 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 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 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 232 static int dragon_power_open(const hw_module_t *module, const char *name, 233 hw_device_t **device) 234 { 235 return 0; 236 } 237 238 239 static struct hw_module_methods_t power_module_methods = { 240 .open = dragon_power_open, 241 }; 242 243 struct dragon_power_module HAL_MODULE_INFO_SYM = { 244 base: { 245 common: { 246 tag: HARDWARE_MODULE_TAG, 247 module_api_version: POWER_MODULE_API_VERSION_0_2, 248 hal_api_version: HARDWARE_HAL_API_VERSION, 249 id: POWER_HARDWARE_MODULE_ID, 250 name: "Dragon Power HAL", 251 author: "The Android Open Source Project", 252 methods: &power_module_methods, 253 dso: NULL, 254 reserved: {0}, 255 }, 256 257 init: power_init, 258 setInteractive: power_set_interactive, 259 powerHint: dragon_power_hint, 260 }, 261 262 boost_pulse_lock: PTHREAD_MUTEX_INITIALIZER, 263 low_power_lock: PTHREAD_MUTEX_INITIALIZER, 264 boostpulse_fd: -1, 265 boostpulse_warned: 0, 266 gpu_qos_manager: NULL, 267 }; 268 269