blob: b5db5f52540448e62ea91b6c2fa60e10803857ef [file] [log] [blame]
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -07001/*
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
22#define LOG_TAG "Grouper PowerHAL"
23#include <utils/Log.h>
24
25#include <hardware/hardware.h>
26#include <hardware/power.h>
27
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -050028#define BOOSTPULSE_PATH "/sys/devices/system/cpu/cpufreq/interactive/boostpulse"
29#define SCALING_MAX_BUF_SZ 10
30static char scaling_max_freq[SCALING_MAX_BUF_SZ] = "1300000";
31static char screenoff_max_freq[SCALING_MAX_BUF_SZ] = "640000";
32
33struct grouper_power_module {
34 struct power_module base;
35 pthread_mutex_t lock;
36 int boostpulse_fd;
37 int boostpulse_warned;
38};
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -070039
40static void sysfs_write(char *path, char *s)
41{
42 char buf[80];
43 int len;
44 int fd = open(path, O_WRONLY);
45
46 if (fd < 0) {
47 strerror_r(errno, buf, sizeof(buf));
48 ALOGE("Error opening %s: %s\n", path, buf);
49 return;
50 }
51
52 len = write(fd, s, strlen(s));
53 if (len < 0) {
54 strerror_r(errno, buf, sizeof(buf));
55 ALOGE("Error writing to %s: %s\n", path, buf);
56 }
57
58 close(fd);
59}
60
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -050061static int sysfs_read(const char *path, char *s, size_t l)
62{
63 char buf[80];
64 int ret = -1;
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -050065 int count;
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -050066 int fd = open(path, O_RDONLY);
67
68 if (fd < 0) {
69 strerror_r(errno, buf, sizeof(buf));
70 ALOGE("Error opening %s: %s\n", path, buf);
71 return ret;
72 }
73
74 do {
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -050075 count = read(fd, s, l - 1);
76 } while (count < 0 && errno == EINTR); /* Retry if interrupted */
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -050077
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -050078 if (count < 0) {
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -050079 strerror_r(errno, buf, sizeof(buf));
80 ALOGE("Error reading from %s: %s\n", path, buf);
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -050081 } else {
82 s[count] = '\0';
83 ret = count;
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -050084 }
85
86 close(fd);
87 return ret;
88}
89
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -050090static int boostpulse_open(struct grouper_power_module *grouper)
91{
92 char buf[80];
93
94 pthread_mutex_lock(&grouper->lock);
95
96 if (grouper->boostpulse_fd < 0) {
97 grouper->boostpulse_fd = open(BOOSTPULSE_PATH, O_WRONLY);
98
99 if (grouper->boostpulse_fd < 0) {
100 if (!grouper->boostpulse_warned) {
101 strerror_r(errno, buf, sizeof(buf));
102 ALOGE("Error opening %s: %s\n", BOOSTPULSE_PATH, buf);
103 grouper->boostpulse_warned = 1;
104 }
105 }
106 }
107
108 pthread_mutex_unlock(&grouper->lock);
109 return grouper->boostpulse_fd;
110}
111
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700112static void grouper_power_init(struct power_module *module)
113{
114 /*
Andrew Sutherland033000f2012-10-15 21:29:17 -0500115 * cpufreq interactive governor: timer 20ms, min sample 30ms,
116 * hispeed 1GHz
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700117 */
118
119 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_rate",
120 "20000");
121 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/min_sample_time",
122 "30000");
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700123 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load",
124 "85");
Andrew Sutherland033000f2012-10-15 21:29:17 -0500125 sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq",
126 "1000000");
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700127}
128
129static void grouper_power_set_interactive(struct power_module *module, int on)
130{
faux123d95de762012-07-29 10:50:35 -0700131 int len;
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -0500132 char buf[SCALING_MAX_BUF_SZ];
faux123d95de762012-07-29 10:50:35 -0700133
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700134 /*
135 * Lower maximum frequency when screen is off. CPU 0 and 1 share a
136 * cpufreq policy.
137 */
138
faux123d95de762012-07-29 10:50:35 -0700139 if (!on) {
140 /* read the current scaling max freq and save it before updating */
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -0500141 len = sysfs_read("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq",
142 buf, sizeof(buf));
Andrew Sutherland31fb8c02012-10-10 21:45:10 -0500143 if (len > 0 && strncmp(buf, screenoff_max_freq,
144 strlen(screenoff_max_freq)) != 0) {
Andrew Sutherland026db012012-10-02 00:04:47 -0500145 strcpy(scaling_max_freq, buf);
Andrew Sutherland31fb8c02012-10-10 21:45:10 -0500146 }
faux123d95de762012-07-29 10:50:35 -0700147 }
Ramanan Rajeswaran3d95d142012-05-29 15:48:02 -0700148
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -0500149 sysfs_write("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq",
Andrew Sutherland026db012012-10-02 00:04:47 -0500150 on ? scaling_max_freq : screenoff_max_freq);
Andrew Sutherland54d5f6c2012-08-24 22:28:25 -0500151
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700152}
153
154static void grouper_power_hint(struct power_module *module, power_hint_t hint,
155 void *data)
156{
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -0500157 struct grouper_power_module *grouper = (struct grouper_power_module *) module;
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700158 char buf[80];
159 int len;
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -0500160 int duration = 1;
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700161
162 switch (hint) {
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -0500163 case POWER_HINT_INTERACTION:
164 case POWER_HINT_CPU_BOOST:
165 if (boostpulse_open(grouper) >= 0) {
166 if (data != NULL)
167 duration = (int) data;
168 snprintf(buf, sizeof(buf), "%d", duration);
169 len = write(grouper->boostpulse_fd, buf, strlen(buf));
170 if (len < 0) {
171 strerror_r(errno, buf, sizeof(buf));
172 ALOGE("Error writing to %s: %s\n", BOOSTPULSE_PATH, buf);
173 pthread_mutex_lock(&grouper->lock);
174 close(grouper->boostpulse_fd);
175 grouper->boostpulse_fd = -1;
176 grouper->boostpulse_warned = 0;
177 pthread_mutex_unlock(&grouper->lock);
178 }
179 }
180 break;
181
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700182 case POWER_HINT_VSYNC:
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700183 break;
184
185 default:
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -0500186 break;
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700187 }
188}
189
190static struct hw_module_methods_t power_module_methods = {
191 .open = NULL,
192};
193
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -0500194struct grouper_power_module HAL_MODULE_INFO_SYM = {
195 base: {
196 common: {
197 tag: HARDWARE_MODULE_TAG,
198 module_api_version: POWER_MODULE_API_VERSION_0_2,
199 hal_api_version: HARDWARE_HAL_API_VERSION,
200 id: POWER_HARDWARE_MODULE_ID,
201 name: "Grouper Power HAL",
202 author: "The Android Open Source Project",
203 methods: &power_module_methods,
204 },
205
206 init: grouper_power_init,
207 setInteractive: grouper_power_set_interactive,
208 powerHint: grouper_power_hint,
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700209 },
210
Andrew Sutherlandee59e6c2012-10-05 23:08:53 -0500211 lock: PTHREAD_MUTEX_INITIALIZER,
212 boostpulse_fd: -1,
213 boostpulse_warned: 0,
Jean-Baptiste Queru671476b2012-05-29 10:40:37 -0700214};