diff options
author | Sivaram Nair <sivaramn@nvidia.com> | 2012-08-28 19:08:29 +0300 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2012-09-10 12:08:40 -0700 |
commit | e1ad6b452b65dc574f58f9ffc9641a78a569f509 (patch) | |
tree | 314a62267c84240010ce54968ce458ec6a2fa0be /drivers/edp | |
parent | 44bb3f4026196433027e8002952a79f3683c9725 (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/Makefile | 1 | ||||
-rw-r--r-- | drivers/edp/edp.c | 6 | ||||
-rw-r--r-- | drivers/edp/edp_internal.h | 5 | ||||
-rw-r--r-- | drivers/edp/sysfs.c | 382 |
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); |