summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/cpuquiet.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/cpuquiet.c')
-rw-r--r--arch/arm/mach-tegra/cpuquiet.c119
1 files changed, 107 insertions, 12 deletions
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 <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);
}