mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
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> Rebase-Id: R23fdadebbf0644ed585f4d4d5ff8dad556773d2a
This commit is contained in:
committed by
Huang, Tao
parent
b25f8f9e49
commit
780c6ada7d
200
drivers/cpuquiet/driver.c
Normal file
200
drivers/cpuquiet/driver.c
Normal file
@@ -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);
|
||||
Reference in New Issue
Block a user