summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra3_throttle.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_throttle.c')
-rw-r--r--arch/arm/mach-tegra/tegra3_throttle.c229
1 files changed, 147 insertions, 82 deletions
diff --git a/arch/arm/mach-tegra/tegra3_throttle.c b/arch/arm/mach-tegra/tegra3_throttle.c
index 9e8d32fb79fa..61dc5cdb6a7d 100644
--- a/arch/arm/mach-tegra/tegra3_throttle.c
+++ b/arch/arm/mach-tegra/tegra3_throttle.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/tegra3_throttle.c
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* 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
@@ -27,37 +27,28 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/thermal.h>
+#include <mach/thermal.h>
#include "clock.h"
#include "cpu-tegra.h"
#include "dvfs.h"
-/* tegra throttling require frequencies in the table to be in ascending order */
-static struct cpufreq_frequency_table *cpu_freq_table;
-static struct {
- unsigned int cpu_freq;
- int core_cap_level;
-} throttle_table[] = {
- { 0, 1000 }, /* placeholder for cpu floor rate */
- { 640000, 1000 },
- { 640000, 1000 },
- { 640000, 1000 },
- { 640000, 1000 },
- { 640000, 1000 },
- { 760000, 1000 },
- { 760000, 1050 },
- {1000000, 1050 },
- {1000000, 1100 },
-};
-
-static int is_throttling;
-static int throttle_index;
-static struct thermal_cooling_device *cdev;
+static struct mutex *cpu_throttle_lock;
+static DEFINE_MUTEX(bthrot_list_lock);
+static LIST_HEAD(bthrot_list);
static unsigned int clip_to_table(unsigned int cpu_freq)
{
int i;
+ struct cpufreq_frequency_table *cpu_freq_table;
+ struct tegra_cpufreq_table_data *table_data =
+ tegra_cpufreq_table_get();
+
+ if (IS_ERR_OR_NULL(table_data))
+ return -EINVAL;
+
+ cpu_freq_table = table_data->freq_table;
for (i = 0; cpu_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
if (cpu_freq_table[i].frequency > cpu_freq)
@@ -69,13 +60,37 @@ static unsigned int clip_to_table(unsigned int cpu_freq)
unsigned int tegra_throttle_governor_speed(unsigned int requested_speed)
{
- return is_throttling ?
- min(requested_speed, throttle_table[throttle_index].cpu_freq) :
- requested_speed;
+ struct balanced_throttle *bthrot;
+ unsigned int throttle_speed = requested_speed;
+ int index;
+
+ mutex_lock(&bthrot_list_lock);
+ list_for_each_entry(bthrot, &bthrot_list, node) {
+ if (bthrot->is_throttling) {
+ index = bthrot->throttle_index;
+ throttle_speed = min(throttle_speed,
+ bthrot->throt_tab[index].cpu_freq);
+ }
+ }
+ mutex_unlock(&bthrot_list_lock);
+
+ return throttle_speed;
}
bool tegra_is_throttling(void)
{
+ struct balanced_throttle *bthrot;
+ bool is_throttling = false;
+
+ mutex_lock(&bthrot_list_lock);
+ list_for_each_entry(bthrot, &bthrot_list, node) {
+ if (bthrot->is_throttling) {
+ is_throttling = true;
+ break;
+ }
+ }
+ mutex_unlock(&bthrot_list_lock);
+
return is_throttling;
}
@@ -83,7 +98,10 @@ static int
tegra_throttle_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *max_state)
{
- *max_state = ARRAY_SIZE(throttle_table);
+ struct balanced_throttle *bthrot = cdev->devdata;
+
+ *max_state = bthrot->throt_tab_size;
+
return 0;
}
@@ -91,9 +109,13 @@ static int
tegra_throttle_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *cur_state)
{
- *cur_state = is_throttling ?
- (ARRAY_SIZE(throttle_table) - throttle_index) :
+ struct balanced_throttle *bthrot = cdev->devdata;
+
+ mutex_lock(cpu_throttle_lock);
+ *cur_state = bthrot->is_throttling ?
+ (bthrot->throt_tab_size - bthrot->throttle_index) :
0;
+ mutex_unlock(cpu_throttle_lock);
return 0;
}
@@ -102,84 +124,55 @@ static int
tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long cur_state)
{
+ struct balanced_throttle *bthrot = cdev->devdata;
int core_level;
+ int index;
+ mutex_lock(cpu_throttle_lock);
if (cur_state == 0) {
/* restore speed requested by governor */
- if (is_throttling) {
+ if (bthrot->is_throttling) {
tegra_dvfs_core_cap_enable(false);
- is_throttling = false;
+ bthrot->is_throttling = false;
}
tegra_cpu_set_speed_cap(NULL);
} else {
- if (!is_throttling) {
+ if (!bthrot->is_throttling) {
tegra_dvfs_core_cap_enable(true);
- is_throttling = true;
+ bthrot->is_throttling = true;
}
- throttle_index = ARRAY_SIZE(throttle_table) - cur_state;
- core_level = throttle_table[throttle_index].core_cap_level;
+ bthrot->throttle_index = bthrot->throt_tab_size - cur_state;
+ index = bthrot->throttle_index;
+ core_level = bthrot->throt_tab[index].core_cap_level;
tegra_dvfs_core_cap_level_set(core_level);
tegra_cpu_set_speed_cap(NULL);
}
+ mutex_unlock(cpu_throttle_lock);
return 0;
}
-struct thermal_cooling_device_ops tegra_throttle_cooling_ops = {
+static struct thermal_cooling_device_ops tegra_throttle_cooling_ops = {
.get_max_state = tegra_throttle_get_max_state,
.get_cur_state = tegra_throttle_get_cur_state,
.set_cur_state = tegra_throttle_set_cur_state,
};
-int __init tegra_throttle_init(struct mutex *cpu_lock)
-{
- int i;
- struct tegra_cpufreq_table_data *table_data =
- tegra_cpufreq_table_get();
- if (IS_ERR_OR_NULL(table_data))
- return -EINVAL;
-
- cpu_freq_table = table_data->freq_table;
- throttle_table[0].cpu_freq =
- cpu_freq_table[table_data->throttle_lowest_index].frequency;
-
- for (i = 0; i < ARRAY_SIZE(throttle_table); i++) {
- unsigned int cpu_freq = throttle_table[i].cpu_freq;
- throttle_table[i].cpu_freq = clip_to_table(cpu_freq);
- }
-
- cdev = thermal_cooling_device_register("Throttle", NULL,
- &tegra_throttle_cooling_ops);
-
- if (IS_ERR(cdev)) {
- cdev = NULL;
- return -ENODEV;
- }
-
- return 0;
-}
-
-void tegra_throttle_exit(void)
-{
- if (cdev) {
- thermal_cooling_device_unregister(cdev);
- cdev = NULL;
- }
-}
-
#ifdef CONFIG_DEBUG_FS
static int table_show(struct seq_file *s, void *data)
{
+ struct balanced_throttle *bthrot = s->private;
int i;
- for (i = 0; i < ARRAY_SIZE(throttle_table); i++)
+ for (i = 0; i < bthrot->throt_tab_size; i++)
seq_printf(s, "[%d] = %7u %4d\n",
- i, throttle_table[i].cpu_freq,
- throttle_table[i].core_cap_level);
+ i, bthrot->throt_tab[i].cpu_freq,
+ bthrot->throt_tab[i].core_cap_level);
+
return 0;
}
@@ -191,6 +184,7 @@ static int table_open(struct inode *inode, struct file *file)
static ssize_t table_write(struct file *file,
const char __user *userbuf, size_t count, loff_t *ppos)
{
+ struct balanced_throttle *bthrot = file->private_data;
char buf[80];
int table_idx;
unsigned int cpu_freq;
@@ -211,12 +205,12 @@ static ssize_t table_write(struct file *file,
&table_idx, &cpu_freq, &core_cap_level) != 3)
return -1;
- if ((table_idx < 0) || (table_idx >= ARRAY_SIZE(throttle_table)))
+ if ((table_idx < 0) || (table_idx >= bthrot->throt_tab_size))
return -EINVAL;
/* round new settings before updating table */
- throttle_table[table_idx].cpu_freq = clip_to_table(cpu_freq);
- throttle_table[table_idx].core_cap_level = (core_cap_level / 50) * 50;
+ bthrot->throt_tab[table_idx].cpu_freq = clip_to_table(cpu_freq);
+ bthrot->throt_tab[table_idx].core_cap_level = (core_cap_level / 50) * 50;
return count;
}
@@ -229,14 +223,85 @@ static const struct file_operations table_fops = {
.release = single_release,
};
+static struct dentry *throttle_debugfs_root;
+#endif /* CONFIG_DEBUG_FS */
-int __init tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root)
+
+struct balanced_throttle *balanced_throttle_register(
+ int id,
+ struct throttle_table *table,
+ int tab_size)
{
- if (!debugfs_create_file("throttle_table", 0644, cpu_tegra_debugfs_root,
- NULL, &table_fops))
- return -ENOMEM;
+ struct balanced_throttle *bthrot;
+ char name[32];
+ int i, index;
+ struct cpufreq_frequency_table *cpu_freq_table;
+ struct tegra_cpufreq_table_data *table_data =
+ tegra_cpufreq_table_get();
+ if (IS_ERR_OR_NULL(table_data))
+ return ERR_PTR(-EINVAL);
+
+ cpu_freq_table = table_data->freq_table;
+
+ bthrot = kzalloc(sizeof(struct balanced_throttle), GFP_KERNEL);
+
+ if (!bthrot)
+ return ERR_PTR(-ENOMEM);
+
+ bthrot->id = id;
+ bthrot->throt_tab = table;
+ bthrot->throt_tab_size = tab_size;
+
+
+ for (i = 0; i < bthrot->throt_tab_size; i++) {
+ unsigned int cpu_freq = bthrot->throt_tab[i].cpu_freq;
+ if (cpu_freq == 0) {
+ index = table_data->throttle_lowest_index;
+ cpu_freq = cpu_freq_table[index].frequency;
+ } else {
+ cpu_freq = clip_to_table(cpu_freq);
+ }
+ bthrot->throt_tab[i].cpu_freq = cpu_freq;
+ }
+
+ bthrot->cdev = thermal_cooling_device_register(
+ "balanced",
+ bthrot,
+ &tegra_throttle_cooling_ops);
+
+ if (IS_ERR(bthrot->cdev)) {
+ bthrot->cdev = NULL;
+ kfree(bthrot);
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&bthrot_list_lock);
+ list_add(&bthrot->node, &bthrot_list);
+ mutex_unlock(&bthrot_list_lock);
+
+#ifdef CONFIG_DEBUG_FS
+ sprintf(name, "throttle_table%d", id);
+ debugfs_create_file(name,0644, throttle_debugfs_root,
+ bthrot, &table_fops);
+#endif
+
+ return bthrot;
+}
+
+int __init tegra_throttle_init(struct mutex *cpu_lock)
+{
+ cpu_throttle_lock = cpu_lock;
+#ifdef CONFIG_DEBUG_FS
+ throttle_debugfs_root = debugfs_create_dir("tegra_throttle", 0);
+#endif
return 0;
}
-#endif /* CONFIG_DEBUG_FS */
+
+void tegra_throttle_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(throttle_debugfs_root);
+#endif
+}