mirror of
https://github.com/S3NEO/android_device_samsung_msm8226-common.git
synced 2024-11-06 21:55:45 +00:00
Custom PowerHAL implementation
PowerHAL highlights: * The CPU governor is always interactive. Governor changes are not expected and permissions are set once on boot. * Powersave profile: the CPU frequency is limited 787MHz and the CPU is never boosted on user interaction. This will save power without making the phone unbearably slow. * Performance profile: the CPU is constantly boosted. * Balanced profile: the CPU is boosted on user interaction. When the screen is turned off, the governor is tuned to lower the power consumption to save power in case of long lasting screen off activities such as music playback. * Currently there are no restrictions on the number of active cores. With this PowerHAL cpu-boost is not needed, so keep it disabled. In addition to that, drop all the properties based profiles and set config_perf_profile_prop to "powerhal" to make the framework send hints to PowerHAL when the power profile is changed. msm8226-common: PowerHAL: Initialize mutex Since the mutex is declared as static, this went unnoticed, but mutexes should always be initialized, so do it. msm8226-common: PowerHAL: Don't override user selected profiles Save and restore the last selected profile so that the user preference is not lost when transitioning out the low power mode. Also, don't actually change the profile if in low power mode, but don't discard the user preference. msm8226-common: Refactor PowerHAL Make the HAL generic by keeping the device dependent configuration separate. This allows to easily add or edit the profiles. The HAL should behave exactly as before, except for the fact that now there might be some useless writes when the screen is turned off. Change-Id: I6bb01a14f0058c59986989568e7766f4203150cc
This commit is contained in:
parent
bdeca00d33
commit
17591b33ae
6 changed files with 365 additions and 67 deletions
|
@ -98,10 +98,6 @@ TARGET_USE_CUSTOM_LUN_FILE_PATH := /sys/devices/platform/msm_hsusb/gadget/lun%d/
|
|||
TARGET_BOARD_PLATFORM := msm8226
|
||||
TARGET_BOARD_PLATFORM_GPU := qcom-adreno305
|
||||
|
||||
# Power
|
||||
TARGET_POWERHAL_SET_INTERACTIVE_EXT := $(VENDOR_PATH)/power/power_ext.c
|
||||
TARGET_POWERHAL_VARIANT := qcom
|
||||
|
||||
# Properties (reset them here, include more in device if needed)
|
||||
TARGET_SYSTEM_PROP := $(VENDOR_PATH)/system.prop
|
||||
|
||||
|
|
25
power/Android.mk
Normal file
25
power/Android.mk
Normal file
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Copyright 2015 The CyanogenMod Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE_RELATIVE_PATH := hw
|
||||
LOCAL_SRC_FILES := power.c
|
||||
LOCAL_SHARED_LIBRARIES := liblog libcutils
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE := power.$(TARGET_BOARD_PLATFORM)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
257
power/power.c
Normal file
257
power/power.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The CyanogenMod Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#define LOG_TAG "PowerHAL"
|
||||
|
||||
#include <hardware/hardware.h>
|
||||
#include <hardware/power.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
/* touchkeys */
|
||||
#define TK_POWER "/sys/class/input/input1/enabled"
|
||||
/* touchscreen */
|
||||
#define TS_POWER "/sys/class/input/input2/enabled"
|
||||
|
||||
#define CPUFREQ_PATH "/sys/devices/system/cpu/cpu0/cpufreq/"
|
||||
#define INTERACTIVE_PATH "/sys/devices/system/cpu/cpufreq/interactive/"
|
||||
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int boostpulse_fd = -1;
|
||||
|
||||
static int current_power_profile = -1;
|
||||
static int requested_power_profile = -1;
|
||||
|
||||
static int sysfs_write_str(char *path, char *s)
|
||||
{
|
||||
char buf[80];
|
||||
int len;
|
||||
int ret = 0;
|
||||
int fd;
|
||||
|
||||
fd = open(path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
ALOGE("Error opening %s: %s\n", path, buf);
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
len = write(fd, s, strlen(s));
|
||||
if (len < 0) {
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
ALOGE("Error writing to %s: %s\n", path, buf);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sysfs_write_int(char *path, int value)
|
||||
{
|
||||
char buf[80];
|
||||
snprintf(buf, 80, "%d", value);
|
||||
return sysfs_write_str(path, buf);
|
||||
}
|
||||
|
||||
static bool check_governor(void)
|
||||
{
|
||||
struct stat s;
|
||||
int err = stat(INTERACTIVE_PATH, &s);
|
||||
if (err != 0) return false;
|
||||
if (S_ISDIR(s.st_mode)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int is_profile_valid(int profile)
|
||||
{
|
||||
return profile >= 0 && profile < PROFILE_MAX;
|
||||
}
|
||||
|
||||
static void power_init(__attribute__((unused)) struct power_module *module)
|
||||
{
|
||||
ALOGI("%s", __func__);
|
||||
}
|
||||
|
||||
static int boostpulse_open()
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
if (boostpulse_fd < 0) {
|
||||
boostpulse_fd = open(INTERACTIVE_PATH "boostpulse", O_WRONLY);
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
return boostpulse_fd;
|
||||
}
|
||||
|
||||
static void power_set_interactive_ext(int on) {
|
||||
ALOGD("%s: %s input devices", __func__, on ? "enabling" : "disabling");
|
||||
sysfs_write(TK_POWER, on ? "1" : "0");
|
||||
sysfs_write(TS_POWER, on ? "1" : "0");
|
||||
}
|
||||
|
||||
static void power_set_interactive(__attribute__((unused)) struct power_module *module, int on)
|
||||
{
|
||||
if (!is_profile_valid(current_power_profile)) {
|
||||
ALOGD("%s: no power profile selected yet", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
power_set_interactive_ext(on);
|
||||
|
||||
// break out early if governor is not interactive
|
||||
if (!check_governor()) return;
|
||||
|
||||
if (on) {
|
||||
sysfs_write_int(INTERACTIVE_PATH "hispeed_freq",
|
||||
profiles[current_power_profile].hispeed_freq);
|
||||
sysfs_write_int(INTERACTIVE_PATH "go_hispeed_load",
|
||||
profiles[current_power_profile].go_hispeed_load);
|
||||
sysfs_write_str(INTERACTIVE_PATH "target_loads",
|
||||
profiles[current_power_profile].target_loads);
|
||||
} else {
|
||||
sysfs_write_int(INTERACTIVE_PATH "hispeed_freq",
|
||||
profiles[current_power_profile].hispeed_freq_off);
|
||||
sysfs_write_int(INTERACTIVE_PATH "go_hispeed_load",
|
||||
profiles[current_power_profile].go_hispeed_load_off);
|
||||
sysfs_write_str(INTERACTIVE_PATH "target_loads",
|
||||
profiles[current_power_profile].target_loads_off);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_power_profile(int profile)
|
||||
{
|
||||
if (!is_profile_valid(profile)) {
|
||||
ALOGE("%s: unknown profile: %d", __func__, profile);
|
||||
return;
|
||||
}
|
||||
|
||||
// break out early if governor is not interactive
|
||||
if (!check_governor()) return;
|
||||
|
||||
if (profile == current_power_profile)
|
||||
return;
|
||||
|
||||
ALOGD("%s: setting profile %d", __func__, profile);
|
||||
|
||||
sysfs_write_int(INTERACTIVE_PATH "boost",
|
||||
profiles[profile].boost);
|
||||
sysfs_write_int(INTERACTIVE_PATH "boostpulse_duration",
|
||||
profiles[profile].boostpulse_duration);
|
||||
sysfs_write_int(INTERACTIVE_PATH "go_hispeed_load",
|
||||
profiles[profile].go_hispeed_load);
|
||||
sysfs_write_int(INTERACTIVE_PATH "hispeed_freq",
|
||||
profiles[profile].hispeed_freq);
|
||||
sysfs_write_int(INTERACTIVE_PATH "io_is_busy",
|
||||
profiles[profile].io_is_busy);
|
||||
sysfs_write_int(INTERACTIVE_PATH "min_sample_time",
|
||||
profiles[profile].min_sample_time);
|
||||
sysfs_write_int(INTERACTIVE_PATH "sampling_down_factor",
|
||||
profiles[profile].sampling_down_factor);
|
||||
sysfs_write_str(INTERACTIVE_PATH "target_loads",
|
||||
profiles[profile].target_loads);
|
||||
sysfs_write_int(CPUFREQ_PATH "scaling_max_freq",
|
||||
profiles[profile].scaling_max_freq);
|
||||
|
||||
current_power_profile = profile;
|
||||
}
|
||||
|
||||
static void power_hint(__attribute__((unused)) struct power_module *module,
|
||||
power_hint_t hint, void *data)
|
||||
{
|
||||
char buf[80];
|
||||
int len;
|
||||
|
||||
switch (hint) {
|
||||
case POWER_HINT_INTERACTION:
|
||||
if (!is_profile_valid(current_power_profile)) {
|
||||
ALOGD("%s: no power profile selected yet", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!profiles[current_power_profile].boostpulse_duration)
|
||||
return;
|
||||
|
||||
// break out early if governor is not interactive
|
||||
if (!check_governor()) return;
|
||||
|
||||
if (boostpulse_open() >= 0) {
|
||||
snprintf(buf, sizeof(buf), "%d", 1);
|
||||
len = write(boostpulse_fd, &buf, sizeof(buf));
|
||||
if (len < 0) {
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
ALOGE("Error writing to boostpulse: %s\n", buf);
|
||||
|
||||
pthread_mutex_lock(&lock);
|
||||
close(boostpulse_fd);
|
||||
boostpulse_fd = -1;
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case POWER_HINT_SET_PROFILE:
|
||||
pthread_mutex_lock(&lock);
|
||||
set_power_profile(*(int32_t *)data);
|
||||
pthread_mutex_unlock(&lock);
|
||||
break;
|
||||
case POWER_HINT_LOW_POWER:
|
||||
/* This hint is handled by the framework */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct hw_module_methods_t power_module_methods = {
|
||||
.open = NULL,
|
||||
};
|
||||
|
||||
static int get_feature(__attribute__((unused)) struct power_module *module,
|
||||
feature_t feature)
|
||||
{
|
||||
if (feature == POWER_FEATURE_SUPPORTED_PROFILES) {
|
||||
return PROFILE_MAX;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct power_module HAL_MODULE_INFO_SYM = {
|
||||
.common = {
|
||||
.tag = HARDWARE_MODULE_TAG,
|
||||
.module_api_version = POWER_MODULE_API_VERSION_0_2,
|
||||
.hal_api_version = HARDWARE_HAL_API_VERSION,
|
||||
.id = POWER_HARDWARE_MODULE_ID,
|
||||
.name = "msm8226 Power HAL",
|
||||
.author = "Gabriele M",
|
||||
.methods = &power_module_methods,
|
||||
},
|
||||
|
||||
.init = power_init,
|
||||
.setInteractive = power_set_interactive,
|
||||
.powerHint = power_hint,
|
||||
.getFeature = get_feature
|
||||
};
|
83
power/power.h
Normal file
83
power/power.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The CyanogenMod Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROFILE_POWER_SAVE = 0,
|
||||
PROFILE_BALANCED,
|
||||
PROFILE_HIGH_PERFORMANCE,
|
||||
PROFILE_MAX
|
||||
};
|
||||
|
||||
typedef struct governor_settings {
|
||||
int is_interactive;
|
||||
int boost;
|
||||
int boostpulse_duration;
|
||||
int go_hispeed_load;
|
||||
int go_hispeed_load_off;
|
||||
int hispeed_freq;
|
||||
int hispeed_freq_off;
|
||||
int io_is_busy;
|
||||
int min_sample_time;
|
||||
int sampling_down_factor;
|
||||
char *target_loads;
|
||||
char *target_loads_off;
|
||||
int scaling_max_freq;
|
||||
} power_profile;
|
||||
|
||||
static power_profile profiles[PROFILE_MAX] = {
|
||||
[PROFILE_POWER_SAVE] = {
|
||||
.boost = 0,
|
||||
.boostpulse_duration = 0,
|
||||
.go_hispeed_load = 90,
|
||||
.go_hispeed_load_off = 90,
|
||||
.hispeed_freq = 787200,
|
||||
.hispeed_freq_off = 787200,
|
||||
.io_is_busy = 0,
|
||||
.min_sample_time = 60000,
|
||||
.sampling_down_factor = 100000,
|
||||
.target_loads = "95 1401600:99",
|
||||
.target_loads_off = "95 1401600:99",
|
||||
.scaling_max_freq = 787200,
|
||||
},
|
||||
[PROFILE_BALANCED] = {
|
||||
.boost = 0,
|
||||
.boostpulse_duration = 60000,
|
||||
.go_hispeed_load = 50,
|
||||
.go_hispeed_load_off = 90,
|
||||
.hispeed_freq = 998400,
|
||||
.hispeed_freq_off = 787200,
|
||||
.io_is_busy = 1,
|
||||
.min_sample_time = 60000,
|
||||
.sampling_down_factor = 100000,
|
||||
.target_loads = "80 998400:90 1401600:99",
|
||||
.target_loads_off = "95 1401600:99",
|
||||
.scaling_max_freq = 1401600,
|
||||
},
|
||||
[PROFILE_HIGH_PERFORMANCE] = {
|
||||
.boost = 1,
|
||||
.boostpulse_duration = 0, /* prevent unnecessary write */
|
||||
.go_hispeed_load = 50,
|
||||
.go_hispeed_load_off = 50,
|
||||
.hispeed_freq = 998400,
|
||||
.hispeed_freq_off = 998400,
|
||||
.io_is_busy = 1,
|
||||
.min_sample_time = 60000,
|
||||
.sampling_down_factor = 100000,
|
||||
.target_loads = "80",
|
||||
.target_loads_off = "80",
|
||||
.scaling_max_freq = 1401600,
|
||||
},
|
||||
};
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The CyanogenMod Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define LOG_TAG "PowerHAL_H_Ext"
|
||||
#include <utils/Log.h>
|
||||
|
||||
/* touchkeys */
|
||||
#define TK_POWER "/sys/class/input/input1/enabled"
|
||||
|
||||
/* touchscreen */
|
||||
#define TS_POWER "/sys/class/input/input2/enabled"
|
||||
|
||||
static void sysfs_write(char *path, char *s) {
|
||||
char buf[80];
|
||||
int len;
|
||||
int fd = open(path, O_WRONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
ALOGE("Error opening %s: %s\n", path, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
len = write(fd, s, strlen(s));
|
||||
if (len < 0) {
|
||||
strerror_r(errno, buf, sizeof(buf));
|
||||
ALOGE("Error writing to %s: %s\n", path, buf);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void cm_power_set_interactive_ext(int on) {
|
||||
ALOGD("%s: %s input devices", __func__, on ? "enabling" : "disabling");
|
||||
sysfs_write(TK_POWER, on ? "1" : "0");
|
||||
sysfs_write(TS_POWER, on ? "1" : "0");
|
||||
}
|
|
@ -47,15 +47,6 @@ on enable-low-power
|
|||
write /sys/devices/system/cpu/cpu2/online 1
|
||||
write /sys/devices/system/cpu/cpu3/online 1
|
||||
|
||||
# Configure interactive governor
|
||||
write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor interactive
|
||||
write /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load 90
|
||||
write /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq 998400
|
||||
write /sys/devices/system/cpu/cpufreq/interactive/io_is_busy 1
|
||||
write /sys/devices/system/cpu/cpufreq/interactive/min_sample_time 40000
|
||||
write /sys/devices/system/cpu/cpufreq/interactive/sampling_down_factor 100000
|
||||
write /sys/devices/system/cpu/cpufreq/interactive/timer_rate 30000
|
||||
|
||||
write /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 300000
|
||||
|
||||
write /sys/module/msm_thermal/core_control/enabled 1
|
||||
|
|
Loading…
Reference in a new issue