Files
device_google_wahoo/power/power.c
Ahmed ElArabawy e9e903f222 Revert "power hal: Add power HAL API 1.1 impl for Wahoo"
This reverts commit c043816b3c.
The reason of the revert is to check for the application start time
regression that may have been caused by the CL

Bug: 38317397
Test: None

Change-Id: I7edaf75eeebcecb848b38a9b7eff2bdb1f233cdb
Signed-off-by: Ahmed ElArabawy <arabawy@google.com>
2017-05-17 15:21:36 -07:00

675 lines
23 KiB
C

/*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_NIDEBUG 0
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <stdlib.h>
#define LOG_TAG "QCOM PowerHAL"
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/power.h>
#include "utils.h"
#include "metadata-defs.h"
#include "hint-data.h"
#include "performance.h"
#include "power-common.h"
#ifndef RPM_SYSTEM_STAT
#define RPM_SYSTEM_STAT "/d/system_stats"
#endif
/* RPM runs at 19.2Mhz. Divide by 19200 for msec */
#define RPM_CLK 19200
#define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
#define LINE_SIZE 128
#define MAX_RPM_PARAMS 2
#define PLATFORM_SLEEP_MODES RPM_MODE_MAX
#define XO_VOTERS (MAX_STATS - XO_VOTERS_START)
#define VMIN_VOTERS 0
enum stats_type {
RPM_MODE_XO,
RPM_MODE_VMIN,
RPM_MODE_MAX,
XO_VOTERS_START = RPM_MODE_MAX,
VOTER_APSS = XO_VOTERS_START,
VOTER_MPSS,
VOTER_ADSP,
VOTER_SLPI,
MAX_STATS,
};
struct stat_pair {
enum stats_type stat;
const char *label;
const char **parameters;
size_t num_parameters;
};
const char *rpm_stat_params[MAX_RPM_PARAMS] = {
"count",
"actual last sleep(msec)",
};
const char *master_stat_params[MAX_RPM_PARAMS] = {
"Accumulated XO duration",
"XO Count",
};
struct stat_pair rpm_stat_map[] = {
{ RPM_MODE_XO, "RPM Mode:vlow", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
{ RPM_MODE_VMIN, "RPM Mode:vmin", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
{ VOTER_APSS, "APSS", master_stat_params, ARRAY_SIZE(master_stat_params) },
{ VOTER_MPSS, "MPSS", master_stat_params, ARRAY_SIZE(master_stat_params) },
{ VOTER_ADSP, "ADSP", master_stat_params, ARRAY_SIZE(master_stat_params) },
{ VOTER_SLPI, "SLPI", master_stat_params, ARRAY_SIZE(master_stat_params) },
};
static int saved_dcvs_cpu0_slack_max = -1;
static int saved_dcvs_cpu0_slack_min = -1;
static int saved_mpdecision_slack_max = -1;
static int saved_mpdecision_slack_min = -1;
static int saved_interactive_mode = -1;
static int slack_node_rw_failed = 0;
static int display_hint_sent;
static void power_init(struct power_module *UNUSED(module))
{
ALOGI("QCOM power HAL initing.");
}
static void process_video_decode_hint(void *metadata)
{
char governor[80];
struct video_decode_metadata_t video_decode_metadata;
if (get_scaling_governor(governor, sizeof(governor)) == -1) {
ALOGE("Can't obtain scaling governor.");
return;
}
if (metadata) {
ALOGI("Processing video decode hint. Metadata: %s", (char *)metadata);
}
/* Initialize encode metadata struct fields. */
memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t));
video_decode_metadata.state = -1;
video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID;
if (metadata) {
if (parse_video_decode_metadata((char *)metadata, &video_decode_metadata) ==
-1) {
ALOGE("Error occurred while parsing metadata.");
return;
}
} else {
return;
}
if (video_decode_metadata.state == 1) {
if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
int resource_values[] = {THREAD_MIGRATION_SYNC_OFF};
perform_hint_action(video_decode_metadata.hint_id,
resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
} else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF};
perform_hint_action(video_decode_metadata.hint_id,
resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
}
} else if (video_decode_metadata.state == 0) {
if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
} else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
undo_hint_action(video_decode_metadata.hint_id);
}
}
}
static void process_video_encode_hint(void *metadata)
{
char governor[80];
struct video_encode_metadata_t video_encode_metadata;
if (get_scaling_governor(governor, sizeof(governor)) == -1) {
ALOGE("Can't obtain scaling governor.");
return;
}
/* Initialize encode metadata struct fields. */
memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t));
video_encode_metadata.state = -1;
video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID;
if (metadata) {
if (parse_video_encode_metadata((char *)metadata, &video_encode_metadata) ==
-1) {
ALOGE("Error occurred while parsing metadata.");
return;
}
} else {
return;
}
if (video_encode_metadata.state == 1) {
if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1, THREAD_MIGRATION_SYNC_OFF};
perform_hint_action(video_encode_metadata.hint_id,
resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
} else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF,
INTERACTIVE_IO_BUSY_OFF};
perform_hint_action(video_encode_metadata.hint_id,
resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
}
} else if (video_encode_metadata.state == 0) {
if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
undo_hint_action(video_encode_metadata.hint_id);
} else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
undo_hint_action(video_encode_metadata.hint_id);
}
}
}
int __attribute__ ((weak)) power_hint_override(struct power_module *UNUSED(module),
power_hint_t UNUSED(hint),
void *UNUSED(data))
{
return HINT_NONE;
}
/* Declare function before use */
void interaction(int duration, int num_args, int opt_list[]);
static void power_hint(struct power_module *module, power_hint_t hint,
void *data)
{
/* Check if this hint has been overridden. */
if (power_hint_override(module, hint, data) == HINT_HANDLED) {
/* The power_hint has been handled. We can skip the rest. */
return;
}
switch(hint) {
case POWER_HINT_VSYNC:
break;
case POWER_HINT_SUSTAINED_PERFORMANCE:
ALOGI("Sustained perf power hint not handled in power_hint_override");
break;
case POWER_HINT_VR_MODE:
ALOGI("VR mode power hint not handled in power_hint_override");
break;
case POWER_HINT_INTERACTION:
{
int resources[] = {0x702, 0x20F, 0x30F};
int duration = 3000;
interaction(duration, sizeof(resources)/sizeof(resources[0]), resources);
}
break;
case POWER_HINT_VIDEO_ENCODE:
process_video_encode_hint(data);
break;
case POWER_HINT_VIDEO_DECODE:
process_video_decode_hint(data);
break;
default:
break;
}
}
int __attribute__ ((weak)) set_interactive_override(struct power_module *UNUSED(module),
int UNUSED(on))
{
return HINT_NONE;
}
void set_interactive(struct power_module *module, int on)
{
char governor[80];
char tmp_str[NODE_MAX];
struct video_encode_metadata_t video_encode_metadata;
int rc = 0;
if (set_interactive_override(module, on) == HINT_HANDLED) {
return;
}
ALOGI("Got set_interactive hint");
if (get_scaling_governor(governor, sizeof(governor)) == -1) {
ALOGE("Can't obtain scaling governor.");
return;
}
if (!on) {
/* Display off. */
if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF};
if (!display_hint_sent) {
perform_hint_action(DISPLAY_STATE_HINT_ID,
resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
display_hint_sent = 1;
}
} else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF};
if (!display_hint_sent) {
perform_hint_action(DISPLAY_STATE_HINT_ID,
resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
display_hint_sent = 1;
}
} else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
if (saved_interactive_mode == 1){
/* Display turned off. */
if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
if (!slack_node_rw_failed) {
ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE);
}
rc = 1;
} else {
saved_dcvs_cpu0_slack_max = atoi(tmp_str);
}
if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
if (!slack_node_rw_failed) {
ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE);
}
rc = 1;
} else {
saved_dcvs_cpu0_slack_min = atoi(tmp_str);
}
if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
if (!slack_node_rw_failed) {
ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE);
}
rc = 1;
} else {
saved_mpdecision_slack_max = atoi(tmp_str);
}
if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
if(!slack_node_rw_failed) {
ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE);
}
rc = 1;
} else {
saved_mpdecision_slack_min = atoi(tmp_str);
}
/* Write new values. */
if (saved_dcvs_cpu0_slack_max != -1) {
snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max);
if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
if (!slack_node_rw_failed) {
ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
}
rc = 1;
}
}
if (saved_dcvs_cpu0_slack_min != -1) {
snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min);
if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
if(!slack_node_rw_failed) {
ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
}
rc = 1;
}
}
if (saved_mpdecision_slack_max != -1) {
snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max);
if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
if(!slack_node_rw_failed) {
ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
}
rc = 1;
}
}
if (saved_mpdecision_slack_min != -1) {
snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min);
if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
if(!slack_node_rw_failed) {
ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
}
rc = 1;
}
}
}
slack_node_rw_failed = rc;
}
} else {
/* Display on. */
if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
undo_hint_action(DISPLAY_STATE_HINT_ID);
display_hint_sent = 0;
} else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
undo_hint_action(DISPLAY_STATE_HINT_ID);
display_hint_sent = 0;
} else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
if (saved_interactive_mode == -1 || saved_interactive_mode == 0) {
/* Display turned on. Restore if possible. */
if (saved_dcvs_cpu0_slack_max != -1) {
snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max);
if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
if (!slack_node_rw_failed) {
ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
}
rc = 1;
}
}
if (saved_dcvs_cpu0_slack_min != -1) {
snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min);
if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
if (!slack_node_rw_failed) {
ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
}
rc = 1;
}
}
if (saved_mpdecision_slack_max != -1) {
snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max);
if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
if (!slack_node_rw_failed) {
ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
}
rc = 1;
}
}
if (saved_mpdecision_slack_min != -1) {
snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min);
if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
if (!slack_node_rw_failed) {
ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
}
rc = 1;
}
}
}
slack_node_rw_failed = rc;
}
}
saved_interactive_mode = !!on;
}
static ssize_t get_number_of_platform_modes(struct power_module *UNUSED(module)) {
return PLATFORM_SLEEP_MODES;
}
static int get_voter_list(struct power_module *UNUSED(module), size_t *voter) {
voter[0] = XO_VOTERS;
voter[1] = VMIN_VOTERS;
return 0;
}
static int parse_stats(const char **params, size_t params_size,
uint64_t *list, FILE *fp) {
ssize_t nread;
size_t len = LINE_SIZE;
char *line;
size_t params_read = 0;
size_t i;
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
return -ENOMEM;
}
while ((params_read < params_size) &&
(nread = getline(&line, &len, fp) > 0)) {
char *key = line + strspn(line, " \t");
char *value = strchr(key, ':');
if (!value || (value > (line + len)))
continue;
*value++ = '\0';
for (i = 0; i < params_size; i++) {
if (!strcmp(key, params[i])) {
list[i] = strtoull(value, NULL, 0);
params_read++;
break;
}
}
}
free(line);
return 0;
}
static int extract_stats(uint64_t *list, char *file,
struct stat_pair *map, size_t map_size) {
FILE *fp;
ssize_t read;
size_t len = LINE_SIZE;
char *line;
size_t i, stats_read = 0;
int ret = 0;
fp = fopen(file, "re");
if (fp == NULL) {
ALOGE("%s: failed to open '%s': %s", __func__, file, strerror(errno));
return -errno;
}
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
fclose(fp);
return -ENOMEM;
}
while ((stats_read < map_size) && (read = getline(&line, &len, fp) != -1)) {
size_t begin = strspn(line, " \t");
for (i = 0; i < map_size; i++) {
if (!strncmp(line + begin, map[i].label, strlen(map[i].label))) {
stats_read++;
break;
}
}
if (i == map_size)
continue;
ret = parse_stats(map[i].parameters, map[i].num_parameters,
&list[map[i].stat * MAX_RPM_PARAMS], fp);
if (ret < 0)
break;
}
free(line);
fclose(fp);
return ret;
}
static int get_platform_low_power_stats(struct power_module *UNUSED(module),
power_state_platform_sleep_state_t *list) {
uint64_t stats[MAX_STATS * MAX_RPM_PARAMS] = {0};
uint64_t *values;
int ret;
unsigned i;
if (!list) {
return -EINVAL;
}
ret = extract_stats(stats, RPM_SYSTEM_STAT,
rpm_stat_map, ARRAY_SIZE(rpm_stat_map));
if (ret) {
return ret;
}
/* Update statistics for XO_shutdown */
strcpy(list[0].name, "XO_shutdown");
values = stats + (RPM_MODE_XO * MAX_RPM_PARAMS);
list[0].total_transitions = values[0];
list[0].residency_in_msec_since_boot = values[1];
list[0].supported_only_in_suspend = false;
list[0].number_of_voters = XO_VOTERS;
for (i = 0; i < XO_VOTERS; i++) {
int voter = i + XO_VOTERS_START;
strlcpy(list[0].voters[i].name, rpm_stat_map[voter].label,
POWER_STATE_VOTER_NAME_MAX_LENGTH);
values = stats + (voter * MAX_RPM_PARAMS);
list[0].voters[i].total_time_in_msec_voted_for_since_boot = values[0] / RPM_CLK;
list[0].voters[i].total_number_of_times_voted_since_boot = values[1];
}
/* Update statistics for VMIN state */
strcpy(list[1].name, "VMIN");
values = stats + (RPM_MODE_VMIN * MAX_RPM_PARAMS);
list[1].total_transitions = values[0];
list[1].residency_in_msec_since_boot = values[1];
list[1].supported_only_in_suspend = false;
list[1].number_of_voters = VMIN_VOTERS;
return 0;
}
static int power_open(const hw_module_t* UNUSED(module), const char* name,
hw_device_t** device)
{
ALOGD("%s: enter; name=%s", __FUNCTION__, name);
int retval = 0; /* 0 is ok; -1 is error */
if (strcmp(name, POWER_HARDWARE_MODULE_ID) == 0) {
power_module_t *dev = (power_module_t *)calloc(1,
sizeof(power_module_t));
if (dev) {
/* Common hw_device_t fields */
dev->common.tag = HARDWARE_MODULE_TAG;
dev->common.module_api_version = POWER_MODULE_API_VERSION_0_5;
dev->common.module_api_version = HARDWARE_HAL_API_VERSION;
dev->init = power_init;
dev->powerHint = power_hint;
dev->setInteractive = set_interactive;
dev->get_number_of_platform_modes = get_number_of_platform_modes;
dev->get_platform_low_power_stats = get_platform_low_power_stats;
dev->get_voter_list = get_voter_list;
*device = (hw_device_t*)dev;
} else
retval = -ENOMEM;
} else {
retval = -EINVAL;
}
ALOGD("%s: exit %d", __FUNCTION__, retval);
return retval;
}
static struct hw_module_methods_t power_module_methods = {
.open = power_open,
};
struct power_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = POWER_MODULE_API_VERSION_0_5,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = POWER_HARDWARE_MODULE_ID,
.name = "QCOM Power HAL",
.author = "Qualcomm",
.methods = &power_module_methods,
},
.init = power_init,
.powerHint = power_hint,
.setInteractive = set_interactive,
.get_number_of_platform_modes = get_number_of_platform_modes,
.get_platform_low_power_stats = get_platform_low_power_stats,
.get_voter_list = get_voter_list
};