summaryrefslogtreecommitdiff
path: root/drivers/cpuquiet
diff options
context:
space:
mode:
authorPeter De Schrijver <pdeschrijver@nvidia.com>2012-03-30 11:40:44 +0300
committerSimone Willett <swillett@nvidia.com>2012-06-06 17:54:05 -0700
commit3fa0319f24d9e3a6762fb866a9222d1f43791246 (patch)
tree0d7f6088da4d87e9610429790e0248f1c8c416ef /drivers/cpuquiet
parentf425d20cc9c016bcb51312dc8736d23ade618ae2 (diff)
cpuquiet: driver support
Change-Id: I4f3f67d4459eeda519efdfd80e1283bef2d597e3 Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com> Reviewed-on: http://git-master/r/105266 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com> Tested-by: Sai Gurrappadi <sgurrappadi@nvidia.com> Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Diffstat (limited to 'drivers/cpuquiet')
-rw-r--r--drivers/cpuquiet/driver.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/drivers/cpuquiet/driver.c b/drivers/cpuquiet/driver.c
new file mode 100644
index 000000000000..f9dcdf018f58
--- /dev/null
+++ b/drivers/cpuquiet/driver.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/cpuquiet.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <asm/cputime.h>
+
+#include "cpuquiet.h"
+
+struct cpuquiet_cpu_stat {
+ cputime64_t time_up_total;
+ u64 last_update;
+ unsigned int up_down_count;
+ struct kobject cpu_kobject;
+};
+
+struct cpu_attribute {
+ struct attribute attr;
+ enum { up_down_count, time_up_total } type;
+};
+
+static struct cpuquiet_driver *cpuquiet_curr_driver;
+struct cpuquiet_cpu_stat *stats;
+
+#define CPU_ATTRIBUTE(_name) \
+ static struct cpu_attribute _name ## _attr = { \
+ .attr = {.name = __stringify(_name), .mode = 0444 }, \
+ .type = _name, \
+}
+
+CPU_ATTRIBUTE(up_down_count);
+CPU_ATTRIBUTE(time_up_total);
+
+static struct attribute *cpu_attributes[] = {
+ &up_down_count_attr.attr,
+ &time_up_total_attr.attr,
+ NULL,
+};
+
+static void stats_update(struct cpuquiet_cpu_stat *stat, bool up)
+{
+ u64 cur_jiffies = get_jiffies_64();
+ bool was_up = stat->up_down_count & 0x1;
+
+ if (was_up)
+ stat->time_up_total = cputime64_add(stat->time_up_total,
+ cputime64_sub(cur_jiffies, stat->last_update));
+
+ if (was_up != up)
+ stat->up_down_count++;
+
+ stat->last_update = cur_jiffies;
+}
+
+int cpuquiet_quiesence_cpu(unsigned int cpunumber)
+{
+ int err = -EPERM;
+
+ if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu)
+ err = cpuquiet_curr_driver->quiesence_cpu(cpunumber);
+
+ stats_update(stats + cpunumber, 0);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_quiesence_cpu);
+
+int cpuquiet_wake_cpu(unsigned int cpunumber)
+{
+ int err = -EPERM;
+
+ if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu)
+ err = cpuquiet_curr_driver->wake_cpu(cpunumber);
+
+ stats_update(stats + cpunumber, 1);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_wake_cpu);
+
+static ssize_t stats_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpu_attribute *cattr =
+ container_of(attr, struct cpu_attribute, attr);
+ struct cpuquiet_cpu_stat *stat =
+ container_of(kobj, struct cpuquiet_cpu_stat, cpu_kobject);
+ ssize_t len = 0;
+ bool was_up = stat->up_down_count & 0x1;
+
+ stats_update(stat, was_up);
+
+ switch (cattr->type) {
+ case up_down_count:
+ len = sprintf(buf, "%u\n", stat->up_down_count);
+ break;
+ case time_up_total:
+ len = sprintf(buf, "%llu\n", stat->time_up_total);
+ break;
+ }
+
+ return len;
+}
+
+static const struct sysfs_ops stats_sysfs_ops = {
+ .show = stats_sysfs_show,
+};
+
+static struct kobj_type ktype_cpu_stats = {
+ .sysfs_ops = &stats_sysfs_ops,
+ .default_attrs = cpu_attributes,
+};
+
+int cpuquiet_register_driver(struct cpuquiet_driver *drv)
+{
+ int err = -EBUSY;
+ unsigned int cpu;
+ struct sys_device *sys_dev;
+ u64 cur_jiffies;
+
+ if (!drv)
+ return -EINVAL;
+
+ stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL);
+ if (!stats)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ cur_jiffies = get_jiffies_64();
+ stats[cpu].last_update = cur_jiffies;
+ if (cpu_online(cpu))
+ stats[cpu].up_down_count = 1;
+ sys_dev = get_cpu_sysdev(cpu);
+ if (sys_dev) {
+ cpuquiet_add_dev(sys_dev, cpu);
+ cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject,
+ &ktype_cpu_stats, "stats", cpu);
+ }
+ }
+
+ mutex_lock(&cpuquiet_lock);
+ if (!cpuquiet_curr_driver) {
+ err = 0;
+ cpuquiet_curr_driver = drv;
+ cpuquiet_switch_governor(cpuquiet_get_first_governor());
+ }
+ mutex_unlock(&cpuquiet_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(cpuquiet_register_driver);
+
+struct cpuquiet_driver *cpuquiet_get_driver(void)
+{
+ return cpuquiet_curr_driver;
+}
+
+void cpuquiet_unregister_driver(struct cpuquiet_driver *drv)
+{
+ unsigned int cpu;
+
+ if (drv != cpuquiet_curr_driver) {
+ WARN(1, "invalid cpuquiet_unregister_driver(%s)\n",
+ drv->name);
+ return;
+ }
+
+ /* stop current governor first */
+ cpuquiet_switch_governor(NULL);
+
+ mutex_lock(&cpuquiet_lock);
+ cpuquiet_curr_driver = NULL;
+
+ for_each_possible_cpu(cpu) {
+ kobject_put(&stats[cpu].cpu_kobject);
+ cpuquiet_remove_dev(cpu);
+ }
+
+ mutex_unlock(&cpuquiet_lock);
+}
+EXPORT_SYMBOL(cpuquiet_unregister_driver);