ARM: tegra: add sysfs support for tegra cpuquiet driver
Peter De Schrijver [Mon, 11 Jun 2012 17:43:59 +0000 (20:43 +0300)]
Change-Id: I215c5de8e98d139a93113978e1e27adb5a6b252c
Signed-off-by: Sai Charan Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-on: http://git-master/r/111283
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

arch/arm/mach-tegra/cpuquiet.c
drivers/cpuquiet/Makefile
drivers/cpuquiet/cpuquiet_attribute.c [new file with mode: 0644]
drivers/cpuquiet/governors/balanced.c
include/linux/cpuquiet.h

index 7ef217e..f0163e1 100644 (file)
@@ -20,7 +20,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/cpufreq.h>
@@ -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);
 }
index 0502d4f..e438def 100644 (file)
@@ -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 (file)
index 0000000..9f1aa43
--- /dev/null
@@ -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 <linux/cpuquiet.h>
+
+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);
+}
index 968344e..da98362 100644 (file)
@@ -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;
index 8459af7..fe5a037 100644 (file)
@@ -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