From 890a7b1b7409d3f867033533f691179f1daab633 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Mon, 11 Jun 2012 20:43:59 +0300 Subject: ARM: tegra: add sysfs support for tegra cpuquiet driver Change-Id: I215c5de8e98d139a93113978e1e27adb5a6b252c Signed-off-by: Sai Charan Gurrappadi Reviewed-on: http://git-master/r/111283 Reviewed-by: Simone Willett Tested-by: Simone Willett --- arch/arm/mach-tegra/cpuquiet.c | 119 +++++++++++++++++++++++++++--- drivers/cpuquiet/Makefile | 2 +- drivers/cpuquiet/cpuquiet_attribute.c | 133 ++++++++++++++++++++++++++++++++++ drivers/cpuquiet/governors/balanced.c | 84 +++++---------------- include/linux/cpuquiet.h | 52 +++++++++++++ 5 files changed, 311 insertions(+), 79 deletions(-) create mode 100644 drivers/cpuquiet/cpuquiet_attribute.c diff --git a/arch/arm/mach-tegra/cpuquiet.c b/arch/arm/mach-tegra/cpuquiet.c index 7ef217eb6872..f0163e1f3fc3 100644 --- a/arch/arm/mach-tegra/cpuquiet.c +++ b/arch/arm/mach-tegra/cpuquiet.c @@ -20,7 +20,6 @@ */ #include -#include #include #include #include @@ -47,20 +46,15 @@ static struct workqueue_struct *cpuquiet_wq; static struct delayed_work cpuquiet_work; static struct work_struct minmax_work; -static bool no_lp; -module_param(no_lp, bool, 0644); +static struct kobject *tegra_auto_sysfs_kobject; +static bool no_lp; +static bool enable; static unsigned long up_delay; -module_param(up_delay, ulong, 0644); static unsigned long down_delay; -module_param(down_delay, ulong, 0644); - static int mp_overhead = 10; -module_param(mp_overhead, int, 0644); static unsigned int idle_top_freq; -module_param(idle_top_freq, uint, 0644); static unsigned int idle_bottom_freq; -module_param(idle_bottom_freq, uint, 0644); static struct clk *cpu_clk; static struct clk *cpu_g_clk; @@ -222,8 +216,8 @@ static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p) if ((n >= 1) && is_lp_cluster()) { /* make sure cpu rate is within g-mode range before switching */ - unsigned int speed = max( - tegra_getspeed(0), clk_get_min_rate(cpu_g_clk) / 1000); + unsigned long speed = max((unsigned long)tegra_getspeed(0), + clk_get_min_rate(cpu_g_clk) / 1000); tegra_update_cpu_speed(speed); clk_set_parent(cpu_clk, cpu_g_clk); @@ -293,8 +287,94 @@ static struct notifier_block max_cpus_notifier = { .notifier_call = max_cpus_notify, }; +static void delay_callback(struct cpuquiet_attribute *attr) +{ + unsigned long val; + + if (attr) { + val = (*((unsigned long *)(attr->param))); + (*((unsigned long *)(attr->param))) = msecs_to_jiffies(val); + } +} + +static void enable_callback(struct cpuquiet_attribute *attr) +{ + mutex_lock(tegra3_cpu_lock); + + if (!enable && cpq_state != TEGRA_CPQ_DISABLED) { + cpq_state = TEGRA_CPQ_DISABLED; + mutex_unlock(tegra3_cpu_lock); + cancel_delayed_work_sync(&cpuquiet_work); + pr_info("Tegra cpuquiet clusterswitch disabled\n"); + mutex_lock(tegra3_cpu_lock); + } else if (enable && cpq_state == TEGRA_CPQ_DISABLED) { + cpq_state = TEGRA_CPQ_IDLE; + pr_info("Tegra cpuquiet clusterswitch enabled\n"); + tegra_cpu_set_speed_cap(NULL); + } + + mutex_unlock(tegra3_cpu_lock); +} + +CPQ_BASIC_ATTRIBUTE(no_lp, 0644, bool); +CPQ_BASIC_ATTRIBUTE(idle_top_freq, 0644, uint); +CPQ_BASIC_ATTRIBUTE(idle_bottom_freq, 0644, uint); +CPQ_BASIC_ATTRIBUTE(mp_overhead, 0644, int); +CPQ_ATTRIBUTE(up_delay, 0644, ulong, delay_callback); +CPQ_ATTRIBUTE(down_delay, 0644, ulong, delay_callback); +CPQ_ATTRIBUTE(enable, 0644, bool, enable_callback); + +static struct attribute *tegra_auto_attributes[] = { + &no_lp_attr.attr, + &up_delay_attr.attr, + &down_delay_attr.attr, + &idle_top_freq_attr.attr, + &idle_bottom_freq_attr.attr, + &mp_overhead_attr.attr, + &enable_attr.attr, + NULL, +}; + +static const struct sysfs_ops tegra_auto_sysfs_ops = { + .show = cpuquiet_auto_sysfs_show, + .store = cpuquiet_auto_sysfs_store, +}; + +static struct kobj_type ktype_sysfs = { + .sysfs_ops = &tegra_auto_sysfs_ops, + .default_attrs = tegra_auto_attributes, +}; + +static int tegra_auto_sysfs(void) +{ + int err; + + tegra_auto_sysfs_kobject = kzalloc(sizeof(*tegra_auto_sysfs_kobject), + GFP_KERNEL); + + if (!tegra_auto_sysfs_kobject) + return -ENOMEM; + + err = cpuquiet_kobject_init(tegra_auto_sysfs_kobject, &ktype_sysfs, + "tegra_cpuquiet"); + + if (err) + kfree(tegra_auto_sysfs_kobject); + + return err; +} + int tegra_auto_hotplug_init(struct mutex *cpu_lock) { + int err; + + cpu_clk = clk_get_sys(NULL, "cpu"); + cpu_g_clk = clk_get_sys(NULL, "cpu_g"); + cpu_lp_clk = clk_get_sys(NULL, "cpu_lp"); + + if (IS_ERR(cpu_clk) || IS_ERR(cpu_g_clk) || IS_ERR(cpu_lp_clk)) + return -ENOENT; + /* * Not bound to the issuer CPU (=> high-priority), has rescue worker * task, single-threaded, freezable. @@ -324,6 +404,8 @@ int tegra_auto_hotplug_init(struct mutex *cpu_lock) tegra3_cpu_lock = cpu_lock; cpq_state = INITIAL_STATE; + enable = cpq_state == TEGRA_CPQ_DISABLED ? false : true; + pr_info("Tegra cpuquiet initialized: %s\n", (cpq_state == TEGRA_CPQ_DISABLED) ? "disabled" : "enabled"); @@ -335,11 +417,24 @@ int tegra_auto_hotplug_init(struct mutex *cpu_lock) pr_err("%s: Failed to register max cpus PM QoS notifier\n", __func__); - return cpuquiet_register_driver(&tegra_cpuquiet_driver); + err = cpuquiet_register_driver(&tegra_cpuquiet_driver); + if (err) { + destroy_workqueue(cpuquiet_wq); + return err; + } + + err = tegra_auto_sysfs(); + if (err) { + cpuquiet_unregister_driver(&tegra_cpuquiet_driver); + destroy_workqueue(cpuquiet_wq); + } + + return err; } void tegra_auto_hotplug_exit(void) { destroy_workqueue(cpuquiet_wq); cpuquiet_unregister_driver(&tegra_cpuquiet_driver); + kobject_put(tegra_auto_sysfs_kobject); } diff --git a/drivers/cpuquiet/Makefile b/drivers/cpuquiet/Makefile index 0502d4f33012..e438defaacdd 100644 --- a/drivers/cpuquiet/Makefile +++ b/drivers/cpuquiet/Makefile @@ -1 +1 @@ -obj-$(CONFIG_CPUQUIET_FRAMEWORK) += cpuquiet.o driver.o sysfs.o governor.o governors/ +obj-$(CONFIG_CPUQUIET_FRAMEWORK) += cpuquiet.o driver.o sysfs.o cpuquiet_attribute.o governor.o governors/ diff --git a/drivers/cpuquiet/cpuquiet_attribute.c b/drivers/cpuquiet/cpuquiet_attribute.c new file mode 100644 index 000000000000..9f1aa430149d --- /dev/null +++ b/drivers/cpuquiet/cpuquiet_attribute.c @@ -0,0 +1,133 @@ +/* + * 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 + +ssize_t show_int_attribute(struct cpuquiet_attribute *cattr, char *buf) +{ + return sprintf(buf, "%d\n", *((int *)cattr->param)); +} + +ssize_t store_int_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err, val; + + err = kstrtoint(buf, 0, &val); + if (err < 0) + return err; + + *((int *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t show_bool_attribute(struct cpuquiet_attribute *cattr, char *buf) +{ + return sprintf(buf, "%d\n", *((bool *)cattr->param)); +} + +ssize_t store_bool_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err, val; + + err = kstrtoint(buf, 0, &val); + if (err < 0) + return err; + + if (val < 0 || val > 1) + return -EINVAL; + + *((bool *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t show_uint_attribute(struct cpuquiet_attribute *cattr, char *buf) +{ + return sprintf(buf, "%u\n", *((unsigned int *)cattr->param)); +} + +ssize_t store_uint_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err; + unsigned int val; + + err = kstrtouint(buf, 0, &val); + if (err < 0) + return err; + + *((unsigned int *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t store_ulong_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count) +{ + int err; + unsigned long val; + + err = kstrtoul(buf, 0, &val); + if (err < 0) + return err; + + *((unsigned long *)(cattr->param)) = val; + + if (cattr->store_callback) + cattr->store_callback(cattr); + + return count; +} + +ssize_t show_ulong_attribute(struct cpuquiet_attribute *cattr, + char *buf) +{ + return sprintf(buf, "%lu\n", *((unsigned long *)cattr->param)); +} + +ssize_t cpuquiet_auto_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct cpuquiet_attribute *cattr = + container_of(attr, struct cpuquiet_attribute, attr); + + if (cattr->store) + return cattr->store(cattr, buf, count); + + return -EINVAL; +} + +ssize_t cpuquiet_auto_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpuquiet_attribute *cattr = + container_of(attr, struct cpuquiet_attribute, attr); + + return cattr->show(cattr, buf); +} diff --git a/drivers/cpuquiet/governors/balanced.c b/drivers/cpuquiet/governors/balanced.c index 968344e220d9..da98362e5f24 100644 --- a/drivers/cpuquiet/governors/balanced.c +++ b/drivers/cpuquiet/governors/balanced.c @@ -55,30 +55,15 @@ static DEFINE_PER_CPU(unsigned int, cpu_load); static struct timer_list load_timer; static bool load_timer_active; -struct balanced_attribute { - struct attribute attr; - ssize_t (*show)(struct balanced_attribute *attr, char *buf); - ssize_t (*store)(struct balanced_attribute *attr, const char *buf, - size_t count); - unsigned long *param; -}; - -#define BALANCED_ATTRIBUTE(_name, _mode) \ - static struct balanced_attribute _name ## _attr = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = show_attribute, \ - .store = store_attribute, \ - .param = &_name, \ -} /* configurable parameters */ -static unsigned long balance_level = 60; -static unsigned long idle_bottom_freq; -static unsigned long idle_top_freq; +static unsigned int balance_level = 60; +static unsigned int idle_bottom_freq; +static unsigned int idle_top_freq; static unsigned long up_delay; static unsigned long down_delay; static unsigned long last_change_time; -static unsigned long load_sample_rate = 20; // msec +static unsigned int load_sample_rate = 20; /* msec */ static struct workqueue_struct *balanced_wq; static struct delayed_work balanced_work; static BALANCED_STATE balanced_state; @@ -339,53 +324,22 @@ static struct notifier_block balanced_cpufreq_nb = { .notifier_call = balanced_cpufreq_transition, }; -static ssize_t show_attribute(struct balanced_attribute *battr, char *buf) -{ - return sprintf(buf, "%lu\n", *(battr->param)); -} - -static ssize_t store_attribute(struct balanced_attribute *battr, - const char *buf, size_t count) +static void delay_callback(struct cpuquiet_attribute *attr) { - int err; unsigned long val; - err = strict_strtoul(buf, 0, &val); - if (err < 0) - return err; - - *(battr->param) = val; - - return count; -} - -static ssize_t balanced_sysfs_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) -{ - struct balanced_attribute *battr = - container_of(attr, struct balanced_attribute, attr); - - if (battr->store) - return battr->store(battr, buf, count); - - return -EINVAL; -} - -static ssize_t balanced_sysfs_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct balanced_attribute *battr = - container_of(attr, struct balanced_attribute, attr); - - return battr->show(battr, buf); + if (attr) { + val = (*((unsigned long *)(attr->param))); + (*((unsigned long *)(attr->param))) = msecs_to_jiffies(val); + } } -BALANCED_ATTRIBUTE(balance_level, 0644); -BALANCED_ATTRIBUTE(idle_bottom_freq, 0644); -BALANCED_ATTRIBUTE(idle_top_freq, 0644); -BALANCED_ATTRIBUTE(up_delay, 0644); -BALANCED_ATTRIBUTE(down_delay, 0644); -BALANCED_ATTRIBUTE(load_sample_rate, 0644); +CPQ_BASIC_ATTRIBUTE(balance_level, 0644, uint); +CPQ_BASIC_ATTRIBUTE(idle_bottom_freq, 0644, uint); +CPQ_BASIC_ATTRIBUTE(idle_top_freq, 0644, uint); +CPQ_BASIC_ATTRIBUTE(load_sample_rate, 0644, uint); +CPQ_ATTRIBUTE(up_delay, 0644, ulong, delay_callback); +CPQ_ATTRIBUTE(down_delay, 0644, ulong, delay_callback); static struct attribute *balanced_attributes[] = { &balance_level_attr.attr, @@ -397,8 +351,8 @@ static struct attribute *balanced_attributes[] = { }; static const struct sysfs_ops balanced_sysfs_ops = { - .show = balanced_sysfs_show, - .store = balanced_sysfs_store, + .show = cpuquiet_auto_sysfs_show, + .store = cpuquiet_auto_sysfs_store, }; static struct kobj_type ktype_balanced = { @@ -427,7 +381,6 @@ static int balanced_sysfs(void) static void balanced_stop(void) { - /* first unregister the notifiers. This ensures the governor state can't be modified by a cpufreq transition @@ -465,8 +418,7 @@ static int balanced_start(void) down_delay = msecs_to_jiffies(500); table = cpufreq_frequency_get_table(0); - for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++) - ; + for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++); idle_top_freq = table[(count / 2) - 1].frequency; idle_bottom_freq = table[(count / 2) - 2].frequency; diff --git a/include/linux/cpuquiet.h b/include/linux/cpuquiet.h index 8459af7aad74..fe5a03727739 100644 --- a/include/linux/cpuquiet.h +++ b/include/linux/cpuquiet.h @@ -50,4 +50,56 @@ extern void cpuquiet_remove_group(struct attribute_group *attrs); int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type, char *name); extern unsigned int nr_cluster_ids; + +/* Sysfs support */ +struct cpuquiet_attribute { + struct attribute attr; + ssize_t (*show)(struct cpuquiet_attribute *attr, char *buf); + ssize_t (*store)(struct cpuquiet_attribute *attr, const char *buf, + size_t count); + /* Optional. Called after store is called */ + void (*store_callback)(struct cpuquiet_attribute *attr); + void *param; +}; + +#define CPQ_ATTRIBUTE(_name, _mode, _type, _callback) \ + static struct cpuquiet_attribute _name ## _attr = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = show_ ## _type ## _attribute, \ + .store = store_ ## _type ## _attribute, \ + .store_callback = _callback, \ + .param = &_name, \ +} + +#define CPQ_BASIC_ATTRIBUTE(_name, _mode, _type) \ + CPQ_ATTRIBUTE(_name, _mode, _type, NULL) + +#define CPQ_ATTRIBUTE_CUSTOM(_name, _mode, _show, _store) \ + static struct cpuquiet_attribute _name ## _attr = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store \ + .store_callback = NULL, \ + .param = &_name, \ +} + + +extern ssize_t show_int_attribute(struct cpuquiet_attribute *cattr, char *buf); +extern ssize_t store_int_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count); +extern ssize_t show_bool_attribute(struct cpuquiet_attribute *cattr, char *buf); +extern ssize_t store_bool_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count); +extern ssize_t store_uint_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count); +extern ssize_t show_uint_attribute(struct cpuquiet_attribute *cattr, char *buf); +extern ssize_t store_ulong_attribute(struct cpuquiet_attribute *cattr, + const char *buf, size_t count); +extern ssize_t show_ulong_attribute(struct cpuquiet_attribute *cattr, + char *buf); +extern ssize_t cpuquiet_auto_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf); +extern ssize_t cpuquiet_auto_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count); #endif -- cgit v1.2.3