summaryrefslogtreecommitdiff
path: root/soc
diff options
context:
space:
mode:
authorJun Yu <yujun@marvell.com>2015-10-27 02:31:38 -0700
committerMohammed Habibulla <moch@google.com>2015-11-04 18:49:27 -0800
commit52f0fffa75b226b371965d2561f9d90067fbabfa (patch)
tree6ec4a6d34a05d9a129d3866cc922f580ae17e34c /soc
parent61758b94103e33189fbe9004604f1e82611de382 (diff)
downloadmarvell-52f0fffa75b226b371965d2561f9d90067fbabfa.tar.gz
Added the power hal module source code
BUG=25486837 Change-Id: I930f9b8a55a5127205bbe073923a3af2046b4f2e
Diffstat (limited to 'soc')
-rw-r--r--soc/common/power/Android.mk25
-rw-r--r--soc/common/power/power.c297
-rw-r--r--soc/iap140/modules/power_hal_module.mk19
3 files changed, 341 insertions, 0 deletions
diff --git a/soc/common/power/Android.mk b/soc/common/power/Android.mk
new file mode 100644
index 0000000..d5eba00
--- /dev/null
+++ b/soc/common/power/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 The Android Open Source 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 := power.mrvl
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := power.c
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/soc/common/power/power.c b/soc/common/power/power.c
new file mode 100644
index 0000000..4f94180
--- /dev/null
+++ b/soc/common/power/power.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2015. Marvell International Ltd
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <utils/Log.h>
+#include <hardware/hardware.h>
+#include <hardware/power.h>
+
+#define LOG_TAG "PowerHAL"
+
+#define POWER_HAL_CONF "/etc/powerhal.conf"
+
+#define DEV_MAX 256
+#define ERR_MAX 64
+#define STATUS_MAX 32
+
+static const char pm_control[] = "control";
+static const char pm_status[] = "runtime_status";
+static const char pm_state_auto[] = "auto";
+static const char pm_state_on[] = "on";
+
+struct node {
+ char device[DEV_MAX];
+ char status[DEV_MAX];
+ int fd;
+ struct node *next;
+ struct node *prev;
+};
+
+struct list {
+ struct node *first;
+ struct node *last;
+};
+
+static struct list devices;
+
+static int add_to_list(const char *device, int fd)
+{
+ struct node *node = malloc(sizeof(struct node));
+ if (node == NULL) {
+ ALOGE("Malloc failed for creating new node");
+ return -1;
+ }
+
+ memset(node, 0, sizeof(struct node));
+ if ((strlcpy(node->device, device,
+ sizeof(node->device))) >= sizeof(node->device)) {
+ ALOGE("Invalid name of the device %s", device);
+ free(node);
+ return -1;
+ }
+
+ int n = snprintf(node->status, sizeof(node->status),
+ "%s/%s", device, pm_status);
+ if (n < 0 || n >= sizeof(node->status)) {
+ ALOGE("Invalid status of the device %s", device);
+ free(node);
+ return -1;
+ }
+
+ node->fd = fd;
+
+ if (devices.first == NULL) {
+ devices.first = devices.last = node;
+ } else {
+ devices.last->next = node;
+ node->prev = devices.last;
+ devices.last = node;
+ }
+
+ return 0;
+}
+
+/**
+ * Return 0 if the device is PM supported, or -1 if unsupported.
+ */
+static int is_pm_supported(const char *control, char *status)
+{
+ char buf[ERR_MAX] = {0};
+ char sbuf[STATUS_MAX] = {0};
+
+ int fd = open(status, O_RDONLY);
+ if (fd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGW("Error opening %s: %s", status, buf);
+ return -1;
+ }
+
+ int ret = read(fd, sbuf, sizeof(sbuf) - 1);
+ close(fd);
+
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGW("Error reading %s: %s", status, buf);
+ return -1;
+ }
+
+ if (strcmp(sbuf, "unsupported") == 0) {
+ ALOGW("%s is unsupported", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void wait_for_set_complete(struct node *node, int on)
+{
+ char buf[80] = {0};
+ char sbuf[64] = {0};
+ int count = 1000;
+ const char *status = node->status;
+ const char *device = node->device;
+
+ do {
+ int fd = open(status, O_RDONLY);
+ if (fd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error opening %s of %s: %s",
+ status, device, buf);
+ return;
+ }
+
+ int ret = read(fd, sbuf, sizeof(sbuf) - 1);
+ close(fd);
+
+ if (ret < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error reading %s of %s: %s", status, device, buf);
+ continue;
+ }
+
+ if (strcmp(sbuf, "unsupported") == 0) {
+ ALOGE("%s of %s is unsupported", status, device);
+ break;
+ }
+
+ if (strcmp(sbuf, on ? "active" : "suspended") == 0) {
+ ALOGI("Set %s active/suspend successfully, "
+ "current status is %s", device, sbuf);
+ break;
+ }
+
+ usleep(1000);
+ } while (--count > 0);
+
+ if (count == 0) {
+ ALOGE("Device %s suspend/resume failed", device);
+ return;
+ }
+}
+
+static int pm_set_for_device(struct node *node, int on)
+{
+ char buf[ERR_MAX] = {0};
+
+ const char *pm_value = on ? pm_state_on : pm_state_auto;
+
+ int ret = write(node->fd, pm_value, strlen(pm_value));
+ if (ret != strlen(pm_value)) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error writing %s to %s: %s",
+ pm_value, node->device, buf);
+ return -1;
+ }
+
+ ALOGD("Set %s to %s done", node->device, pm_value);
+
+ return 0;
+}
+
+static void power_init(struct power_module *module)
+{
+ FILE *conf = fopen(POWER_HAL_CONF, "r");
+
+ if (conf == NULL) {
+ ALOGE("%s does not exist! No devices for "
+ "interactive mode power management", POWER_HAL_CONF);
+ return;
+ }
+
+ char line[DEV_MAX] = {0};
+ char control[DEV_MAX] = {0};
+ char status[DEV_MAX] = {0};
+ char buf[ERR_MAX] = {0};
+ int fd = -1;
+
+ while (fgets(line, DEV_MAX, conf) != NULL) {
+ if (line[0] == '#' || line[0] == '\n' ||
+ line[0] == ' ' || line[0] == '\0')
+ continue;
+
+ size_t line_len = strlen(line);
+ if (line[line_len - 1] == '\n') {
+ line[line_len - 1] = '\0';
+ line_len--;
+ }
+
+ int n = snprintf(control, sizeof(control), "%s/%s", line, pm_control);
+ if (n < 0 || n >= sizeof(control)) {
+ ALOGE("Error copying control of the device %s", line);
+ continue;
+ }
+
+ n = snprintf(status, sizeof(status), "%s/%s", line, pm_status);
+ if (n < 0 || n >= sizeof(status)) {
+ ALOGE("Error copying status of the device %s", line);
+ continue;
+ }
+
+ if (is_pm_supported(control, status) != 0) {
+ ALOGE("Device %s does not support PM", line);
+ continue;
+ }
+
+ fd = open(control, O_RDWR);
+ if (fd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error opening %s: %s", control, buf);
+ continue;
+ }
+
+ ALOGD("Add device %s to list", line);
+ if (add_to_list(line, fd) != 0) {
+ ALOGE("Add device %s to list failed", line);
+ close(fd);
+ }
+ }
+
+ fclose(conf);
+
+ return;
+}
+
+static void power_set_interactive(struct power_module *module, int on)
+{
+ struct node *node;
+
+ ALOGI("power_set_interactive called with on %d", on);
+ if (on) {
+ for (node = devices.last; node != NULL; node = node->prev)
+ pm_set_for_device(node, on);
+ } else {
+ for (node = devices.first; node != NULL; node = node->next)
+ pm_set_for_device(node, on);
+ }
+
+ for (node = devices.last; node != NULL; node = node->prev)
+ wait_for_set_complete(node, on);
+
+ return;
+}
+
+static void power_hint(struct power_module *module,
+ power_hint_t hint, void *data)
+{
+}
+
+static struct hw_module_methods_t power_module_methods = {
+ .open = NULL,
+};
+
+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 = "Marvell Power HAL",
+ .author = "Marvell SEEDS",
+ .methods = &power_module_methods,
+ },
+
+ .init = power_init,
+ .setInteractive = power_set_interactive,
+ .powerHint = power_hint,
+};
diff --git a/soc/iap140/modules/power_hal_module.mk b/soc/iap140/modules/power_hal_module.mk
new file mode 100644
index 0000000..68ae567
--- /dev/null
+++ b/soc/iap140/modules/power_hal_module.mk
@@ -0,0 +1,19 @@
+#
+# Copyright 2015 The Android Open Source 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.
+#
+
+# power-hal modules
+DEVICE_PACKAGES += \
+ power.mrvl