summaryrefslogtreecommitdiff
path: root/drivers/edp
diff options
context:
space:
mode:
authorSivaram Nair <sivaramn@nvidia.com>2012-08-28 19:08:29 +0300
committerVarun Colbert <vcolbert@nvidia.com>2012-09-10 12:08:40 -0700
commite1ad6b452b65dc574f58f9ffc9641a78a569f509 (patch)
tree314a62267c84240010ce54968ce458ec6a2fa0be /drivers/edp
parent44bb3f4026196433027e8002952a79f3683c9725 (diff)
pm: EDP: adding sysfs entries
This patch adds sysfs entries to represent the EDP framework. The sysfs is at /sys/power/edp. Change-Id: If562d7745a99e5324a982f91991d2dfeacacb84e Signed-off-by: Sivaram Nair <sivaramn@nvidia.com> Reviewed-on: http://git-master/r/130510 Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Diffstat (limited to 'drivers/edp')
-rw-r--r--drivers/edp/Makefile1
-rw-r--r--drivers/edp/edp.c6
-rw-r--r--drivers/edp/edp_internal.h5
-rw-r--r--drivers/edp/sysfs.c382
4 files changed, 394 insertions, 0 deletions
diff --git a/drivers/edp/Makefile b/drivers/edp/Makefile
index 134381f47c98..2f8d84cb16c7 100644
--- a/drivers/edp/Makefile
+++ b/drivers/edp/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_EDP_FRAMEWORK) += edp.o
obj-$(CONFIG_EDP_FRAMEWORK) += edp_priority.o
+obj-$(CONFIG_EDP_FRAMEWORK) += sysfs.o
diff --git a/drivers/edp/edp.c b/drivers/edp/edp.c
index f08e3caed8d1..13b77a25e429 100644
--- a/drivers/edp/edp.c
+++ b/drivers/edp/edp.c
@@ -64,6 +64,8 @@ int edp_register_manager(struct edp_manager *mgr)
mgr->gov = NULL;
INIT_LIST_HEAD(&mgr->clients);
INIT_WORK(&mgr->work, promote);
+ mgr->kobj = NULL;
+ edp_manager_add_kobject(mgr);
r = 0;
}
mutex_unlock(&edp_lock);
@@ -122,6 +124,7 @@ int edp_unregister_manager(struct edp_manager *mgr)
} else if (!list_empty(&mgr->clients)) {
r = -EBUSY;
} else {
+ edp_manager_remove_kobject(mgr);
edp_set_governor_unlocked(mgr, NULL);
list_del(&mgr->link);
mgr->registered = false;
@@ -228,6 +231,8 @@ static int register_client(struct edp_manager *mgr, struct edp_client *client)
client->num_borrowers = 0;
client->num_loans = 0;
client->ithreshold = client->states[0];
+ client->kobj = NULL;
+ edp_client_add_kobject(client);
return 0;
}
@@ -305,6 +310,7 @@ static int unregister_client(struct edp_client *client)
if (client->num_loans)
return -EBUSY;
+ edp_client_remove_kobject(client);
close_all_loans(client);
mod_request(client, NULL);
list_del(&client->link);
diff --git a/drivers/edp/edp_internal.h b/drivers/edp/edp_internal.h
index 227e42c585a2..e0d890279744 100644
--- a/drivers/edp/edp_internal.h
+++ b/drivers/edp/edp_internal.h
@@ -58,4 +58,9 @@ struct edp_governor *edp_find_governor_unlocked(const char *s);
int edp_set_governor_unlocked(struct edp_manager *mgr,
struct edp_governor *gov);
+void edp_manager_add_kobject(struct edp_manager *mgr);
+void edp_manager_remove_kobject(struct edp_manager *mgr);
+void edp_client_add_kobject(struct edp_client *client);
+void edp_client_remove_kobject(struct edp_client *client);
+
#endif
diff --git a/drivers/edp/sysfs.c b/drivers/edp/sysfs.c
new file mode 100644
index 000000000000..083aa1729a9e
--- /dev/null
+++ b/drivers/edp/sysfs.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/edp.h>
+#include <linux/slab.h>
+#include "edp_internal.h"
+
+static struct kobject edp_kobj;
+
+struct manager_entry {
+ struct edp_manager *manager;
+ struct kobject kobj;
+};
+
+struct manager_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct edp_manager *, char *);
+ ssize_t (*store)(struct edp_manager *, const char *, size_t);
+};
+
+static ssize_t cap_show(struct edp_manager *m, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", m->imax);
+}
+
+static ssize_t remaining_show(struct edp_manager *m, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", m->remaining);
+}
+
+static ssize_t manager_governor_show(struct edp_manager *m, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%s\n", m->gov ? m->gov->name : "");
+}
+
+static ssize_t manager_governor_store(struct edp_manager *m, const char *s,
+ size_t count)
+{
+ char name[EDP_NAME_LEN];
+ struct edp_governor *gov;
+
+ if (!count || count >= sizeof(name))
+ return -EINVAL;
+
+ memcpy(name, s, count);
+ name[count] = 0;
+ strim(name);
+ gov = edp_find_governor_unlocked(name);
+ if (!gov)
+ return -EINVAL;
+
+ return edp_set_governor_unlocked(m, gov) ?: count;
+}
+
+struct manager_attr attr_cap = __ATTR_RO(cap);
+struct manager_attr attr_remaining = __ATTR_RO(remaining);
+struct manager_attr attr_mgr_gov = __ATTR(governor, 0644,
+ manager_governor_show, manager_governor_store);
+
+static struct attribute *manager_attrs[] = {
+ &attr_cap.attr,
+ &attr_remaining.attr,
+ &attr_mgr_gov.attr,
+ NULL
+};
+
+static struct edp_manager *to_manager(struct kobject *kobj)
+{
+ struct manager_entry *me = container_of(kobj, struct manager_entry,
+ kobj);
+ return me ? me->manager : NULL;
+}
+
+static ssize_t manager_state_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ ssize_t r;
+ struct edp_manager *m;
+ struct manager_attr *mattr;
+
+ mutex_lock(&edp_lock);
+ m = to_manager(kobj);
+ mattr = container_of(attr, struct manager_attr, attr);
+ r = m && mattr ? mattr->show(m, buf) : -EINVAL;
+ mutex_unlock(&edp_lock);
+
+ return r;
+}
+
+static ssize_t manager_state_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ ssize_t r;
+ struct edp_manager *m;
+ struct manager_attr *mattr;
+
+ mutex_lock(&edp_lock);
+ m = to_manager(kobj);
+ mattr = container_of(attr, struct manager_attr, attr);
+ r = m && mattr ? mattr->store(m, buf, count) : -EINVAL;
+ mutex_unlock(&edp_lock);
+
+ return r;
+}
+
+static const struct sysfs_ops manager_sysfs_ops = {
+ .show = manager_state_show,
+ .store = manager_state_store
+};
+
+static struct kobj_type ktype_manager = {
+ .sysfs_ops = &manager_sysfs_ops,
+ .default_attrs = manager_attrs
+};
+
+void edp_manager_add_kobject(struct edp_manager *mgr)
+{
+ struct manager_entry *me;
+
+ me = kzalloc(sizeof(*me), GFP_KERNEL);
+ if (!me) {
+ pr_err("%s: failed to alloc sysfs manager entry\n",
+ mgr->name);
+ return;
+ }
+
+ if (kobject_init_and_add(&me->kobj, &ktype_manager, &edp_kobj,
+ mgr->name)) {
+ pr_err("%s: failed to init & add sysfs manager entry\n",
+ mgr->name);
+ kfree(me);
+ return;
+ }
+
+ me->manager = mgr;
+ mgr->kobj = &me->kobj;
+ kobject_uevent(&me->kobj, KOBJ_ADD);
+ return;
+}
+
+void edp_manager_remove_kobject(struct edp_manager *mgr)
+{
+ struct manager_entry *me;
+
+ if (!mgr->kobj)
+ return;
+
+ me = container_of(mgr->kobj, struct manager_entry, kobj);
+ mgr->kobj = NULL;
+ kobject_put(&me->kobj);
+ kfree(me);
+}
+
+struct client_entry {
+ struct edp_client *client;
+ struct kobject kobj;
+};
+
+struct client_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct edp_client *, char *);
+};
+
+static ssize_t states_show(struct edp_client *c, char *s)
+{
+ unsigned int i;
+ int cnt = 0;
+ const int sz = sizeof(*c->states) * 3 + 2;
+
+ for (i = 0; i < c->num_states && (cnt + sz) < PAGE_SIZE; i++)
+ cnt += sprintf(s + cnt, "%s%u", i ? " " : "", c->states[i]);
+
+ cnt += sprintf(s + cnt, "\n");
+ return cnt;
+}
+
+static ssize_t num_states_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", c->num_states);
+}
+
+static ssize_t e0_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", c->states[c->e0_index]);
+}
+
+static ssize_t max_borrowers_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", c->max_borrowers);
+}
+
+static ssize_t priority_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%d\n", c->priority);
+}
+
+static ssize_t request_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", req_level(c));
+}
+
+static ssize_t current_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", cur_level(c));
+}
+
+static ssize_t threshold_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", c->ithreshold);
+}
+
+static ssize_t borrowers_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", c->num_borrowers);
+}
+
+static ssize_t loans_show(struct edp_client *c, char *s)
+{
+ return scnprintf(s, PAGE_SIZE, "%u\n", c->num_loans);
+}
+
+struct client_attr attr_states = __ATTR_RO(states);
+struct client_attr attr_num_states = __ATTR_RO(num_states);
+struct client_attr attr_e0 = __ATTR_RO(e0);
+struct client_attr attr_max_borrowers = __ATTR_RO(max_borrowers);
+struct client_attr attr_priority = __ATTR_RO(priority);
+struct client_attr attr_request = __ATTR_RO(request);
+struct client_attr attr_current = __ATTR_RO(current);
+struct client_attr attr_threshold = __ATTR_RO(threshold);
+struct client_attr attr_borrowers = __ATTR_RO(borrowers);
+struct client_attr attr_loans = __ATTR_RO(loans);
+
+static struct attribute *client_attrs[] = {
+ &attr_states.attr,
+ &attr_num_states.attr,
+ &attr_e0.attr,
+ &attr_max_borrowers.attr,
+ &attr_priority.attr,
+ &attr_request.attr,
+ &attr_current.attr,
+ &attr_threshold.attr,
+ &attr_borrowers.attr,
+ &attr_loans.attr,
+ NULL
+};
+
+static struct edp_client *to_client(struct kobject *kobj)
+{
+ struct client_entry *ce = container_of(kobj, struct client_entry,
+ kobj);
+ return ce ? ce->client : NULL;
+}
+
+static ssize_t client_state_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ ssize_t r;
+ struct edp_client *c;
+ struct client_attr *cattr;
+
+ mutex_lock(&edp_lock);
+ c = to_client(kobj);
+ cattr = container_of(attr, struct client_attr, attr);
+ r = c && cattr ? cattr->show(c, buf) : -EINVAL;
+ mutex_unlock(&edp_lock);
+
+ return r;
+}
+
+static const struct sysfs_ops client_sysfs_ops = {
+ .show = client_state_show
+};
+
+static struct kobj_type ktype_client = {
+ .sysfs_ops = &client_sysfs_ops,
+ .default_attrs = client_attrs
+};
+
+void edp_client_add_kobject(struct edp_client *client)
+{
+ struct client_entry *ce;
+ struct kobject *parent = client->manager->kobj;
+
+ if (!parent)
+ return;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ pr_err("%s: failed to alloc sysfs client entry\n",
+ client->name);
+ return;
+ }
+
+ if (kobject_init_and_add(&ce->kobj, &ktype_client, parent,
+ client->name)) {
+ pr_err("%s: failed to init & add sysfs client entry\n",
+ client->name);
+ kfree(ce);
+ return;
+ }
+
+ ce->client = client;
+ client->kobj = &ce->kobj;
+ kobject_uevent(&ce->kobj, KOBJ_ADD);
+ return;
+}
+
+void edp_client_remove_kobject(struct edp_client *client)
+{
+ struct client_entry *ce;
+
+ if (!client->kobj)
+ return;
+
+ ce = container_of(client->kobj, struct client_entry, kobj);
+ client->kobj = NULL;
+ kobject_put(&ce->kobj);
+ kfree(ce);
+}
+
+static ssize_t governors_show(struct kobject *kobj, struct attribute *attr,
+ char *s)
+{
+ struct edp_governor *g;
+ int cnt = 0;
+
+ mutex_lock(&edp_lock);
+
+ list_for_each_entry(g, &edp_governors, link) {
+ if (cnt + EDP_NAME_LEN + 2 >= PAGE_SIZE)
+ break;
+ cnt += sprintf(s + cnt, "%s%s", cnt ? " " : "", g->name);
+ }
+
+ cnt += sprintf(s + cnt, "\n");
+
+ mutex_unlock(&edp_lock);
+
+ return cnt;
+}
+
+static const struct sysfs_ops edp_sysfs_ops = {
+ .show = governors_show
+};
+
+static struct attribute attr_governors = {
+ .name = "governors",
+ .mode = 0444
+};
+
+static struct attribute *edp_attrs[] = {
+ &attr_governors,
+ NULL
+};
+
+static struct kobj_type ktype_edp = {
+ .sysfs_ops = &edp_sysfs_ops,
+ .default_attrs = edp_attrs
+};
+
+static int __init edp_sysfs_init(void)
+{
+ return kobject_init_and_add(&edp_kobj, &ktype_edp, power_kobj, "edp");
+}
+
+MODULE_LICENSE("GPL");
+module_init(edp_sysfs_init);