1 /* 2 * Copyright (C) 2012 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 #include <errno.h> 17 #include <string.h> 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 #include <fcntl.h> 21 #include <sys/socket.h> 22 #include <cutils/uevent.h> 23 #include <sys/poll.h> 24 #include <pthread.h> 25 #include <linux/netlink.h> 26 #include <stdlib.h> 27 #include <stdbool.h> 28 29 #define LOG_TAG "Grouper PowerHAL" 30 #include <utils/Log.h> 31 32 #include <hardware/hardware.h> 33 #include <hardware/power.h> 34 35 #define BOOST_PATH "/sys/devices/system/cpu/cpufreq/interactive/boost" 36 #define UEVENT_MSG_LEN 2048 37 #define TOTAL_CPUS 4 38 #define RETRY_TIME_CHANGING_FREQ 20 39 #define SLEEP_USEC_BETWN_RETRY 200 40 #define LOW_POWER_MAX_FREQ "640000" 41 #define LOW_POWER_MIN_FREQ "51000" 42 #define NORMAL_MAX_FREQ "1300000" 43 #define UEVENT_STRING "online@/devices/system/cpu/" 44 45 static int boost_fd = -1; 46 static int boost_warned; 47 48 static struct pollfd pfd; 49 static char *cpu_path_min[] = { 50 "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq", 51 "/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq", 52 "/sys/devices/system/cpu/cpu2/cpufreq/scaling_min_freq", 53 "/sys/devices/system/cpu/cpu3/cpufreq/scaling_min_freq", 54 }; 55 static char *cpu_path_max[] = { 56 "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", 57 "/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq", 58 "/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq", 59 "/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq", 60 }; 61 static bool freq_set[TOTAL_CPUS]; 62 static bool low_power_mode = false; 63 static pthread_mutex_t low_power_mode_lock = PTHREAD_MUTEX_INITIALIZER; 64 65 static int sysfs_write(char *path, char *s) 66 { 67 char buf[80]; 68 int len; 69 int fd = open(path, O_WRONLY); 70 71 if (fd < 0) { 72 strerror_r(errno, buf, sizeof(buf)); 73 ALOGE("Error opening %s: %s\n", path, buf); 74 return -1; 75 } 76 77 len = write(fd, s, strlen(s)); 78 if (len < 0) { 79 strerror_r(errno, buf, sizeof(buf)); 80 ALOGE("Error writing to %s: %s\n", path, buf); 81 return -1; 82 } 83 84 close(fd); 85 return 0; 86 } 87 88 static int uevent_event() 89 { 90 char msg[UEVENT_MSG_LEN]; 91 char *cp; 92 int n, cpu, ret, retry = RETRY_TIME_CHANGING_FREQ; 93 94 n = recv(pfd.fd, msg, UEVENT_MSG_LEN, MSG_DONTWAIT); 95 if (n <= 0) { 96 return -1; 97 } 98 if (n >= UEVENT_MSG_LEN) { /* overflow -- discard */ 99 return -1; 100 } 101 102 cp = msg; 103 104 if (strstr(cp, UEVENT_STRING)) { 105 n = strlen(cp); 106 errno = 0; 107 cpu = strtol(cp + n - 1, NULL, 10); 108 109 if (errno == EINVAL || errno == ERANGE || cpu < 0 || cpu >= TOTAL_CPUS) { 110 return -1; 111 } 112 113 pthread_mutex_lock(&low_power_mode_lock); 114 if (low_power_mode && !freq_set[cpu]) { 115 while (retry) { 116 sysfs_write(cpu_path_min[cpu], LOW_POWER_MIN_FREQ); 117 ret = sysfs_write(cpu_path_max[cpu], LOW_POWER_MAX_FREQ); 118 if (!ret) { 119 freq_set[cpu] = true; 120 break; 121 } 122 usleep(SLEEP_USEC_BETWN_RETRY); 123 retry--; 124 } 125 } else if (!low_power_mode && freq_set[cpu]) { 126 while (retry) { 127 ret = sysfs_write(cpu_path_max[cpu], NORMAL_MAX_FREQ); 128 if (!ret) { 129 freq_set[cpu] = false; 130 break; 131 } 132 usleep(SLEEP_USEC_BETWN_RETRY); 133 retry--; 134 } 135 } 136 pthread_mutex_unlock(&low_power_mode_lock); 137 } 138 return 0; 139 } 140 141 void *thread_uevent(__attribute__((unused)) void *x) 142 { 143 while (1) { 144 int nevents, ret; 145 146 nevents = poll(&pfd, 1, -1); 147 148 if (nevents == -1) { 149 if (errno == EINTR) 150 continue; 151 ALOGE("powerhal: thread_uevent: poll_wait failed\n"); 152 break; 153 } 154 ret = uevent_event(); 155 if (ret < 0) 156 ALOGE("Error processing the uevent event"); 157 } 158 return NULL; 159 } 160 161 162 static void uevent_init() 163 { 164 struct sockaddr_nl client; 165 pthread_t tid; 166 pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 167 168 if (pfd.fd < 0) { 169 ALOGE("%s: failed to open: %s", __func__, strerror(errno)); 170 return; 171 } 172 memset(&client, 0, sizeof(struct sockaddr_nl)); 173 pthread_create(&tid, NULL, thread_uevent, NULL); 174 client.nl_family = AF_NETLINK; 175 client.nl_pid = tid; 176 client.nl_groups = -1; 177 pfd.events = POLLIN; 178 bind(pfd.fd, (void *)&client, sizeof(struct sockaddr_nl)); 179 return; 180 } 181 182 static void grouper_power_init( __attribute__((unused)) struct power_module *module) 183 { 184 /* 185 * cpufreq interactive governor: timer 20ms, min sample 100ms, 186 * hispeed 700MHz at load 40% 187 */ 188 189 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate", 190 "20000"); 191 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time", 192 "30000"); 193 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load", 194 "85"); 195 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boost_factor", 196 "0"); 197 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/input_boost", 198 "1"); 199 uevent_init(); 200 } 201 202 static void grouper_power_set_interactive( __attribute__((unused)) struct power_module *module, int on) 203 { 204 /* 205 * Lower maximum frequency when screen is off. CPU 0 and 1 share a 206 * cpufreq policy. 207 */ 208 209 sysfs_write("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", 210 on ? "1300000" : "700000"); 211 212 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/input_boost", 213 on ? "1" : "0"); 214 215 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/boost_factor", 216 on ? "0" : "2"); 217 218 } 219 220 static void grouper_power_hint(__attribute__((unused)) struct power_module *module, power_hint_t hint, 221 void *data) 222 { 223 char buf[80]; 224 int len, cpu, ret; 225 226 switch (hint) { 227 case POWER_HINT_VSYNC: 228 break; 229 230 case POWER_HINT_LOW_POWER: 231 pthread_mutex_lock(&low_power_mode_lock); 232 if (data) { 233 low_power_mode = true; 234 for (cpu = 0; cpu < TOTAL_CPUS; cpu++) { 235 sysfs_write(cpu_path_min[cpu], LOW_POWER_MIN_FREQ); 236 ret = sysfs_write(cpu_path_max[cpu], LOW_POWER_MAX_FREQ); 237 if (!ret) { 238 freq_set[cpu] = true; 239 } 240 } 241 } else { 242 low_power_mode = false; 243 for (cpu = 0; cpu < TOTAL_CPUS; cpu++) { 244 ret = sysfs_write(cpu_path_max[cpu], NORMAL_MAX_FREQ); 245 if (!ret) { 246 freq_set[cpu] = false; 247 } 248 } 249 } 250 pthread_mutex_unlock(&low_power_mode_lock); 251 break; 252 default: 253 break; 254 } 255 } 256 257 static struct hw_module_methods_t power_module_methods = { 258 .open = NULL, 259 }; 260 261 struct power_module HAL_MODULE_INFO_SYM = { 262 .common = { 263 .tag = HARDWARE_MODULE_TAG, 264 .module_api_version = POWER_MODULE_API_VERSION_0_2, 265 .hal_api_version = HARDWARE_HAL_API_VERSION, 266 .id = POWER_HARDWARE_MODULE_ID, 267 .name = "Grouper Power HAL", 268 .author = "The Android Open Source Project", 269 .methods = &power_module_methods, 270 }, 271 272 .init = grouper_power_init, 273 .setInteractive = grouper_power_set_interactive, 274 .powerHint = grouper_power_hint, 275 }; 276