diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_throttle.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra3_throttle.c | 229 |
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 +} |