mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 21:07:02 +09:00
[ARM] tegra: cpufreq: Support for tegra cpu throttling
Change-Id: I28d69d22437b6ba2d22e4ce12746630786006071 Signed-off-by: Dmitriy Gruzman <dmitriy.gruzman@motorola.com>
This commit is contained in:
committed by
Todd Poynor
parent
3cf8cfddc7
commit
b01b6774ca
@@ -29,6 +29,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <asm/smp_twd.h>
|
||||
#include <asm/system.h>
|
||||
@@ -36,7 +37,7 @@
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/clk.h>
|
||||
|
||||
/* Frequency table index must be sequential starting at 0 */
|
||||
/* Frequency table index must be sequential starting at 0 and frequencies must be ascending*/
|
||||
static struct cpufreq_frequency_table freq_table[] = {
|
||||
{ 0, 216000 },
|
||||
{ 1, 312000 },
|
||||
@@ -49,14 +50,27 @@ static struct cpufreq_frequency_table freq_table[] = {
|
||||
{ 8, CPUFREQ_TABLE_END },
|
||||
};
|
||||
|
||||
/* CPU frequency is gradually lowered when throttling is enabled */
|
||||
#define THROTTLE_START_INDEX 2
|
||||
#define THROTTLE_END_INDEX 6
|
||||
#define THROTTLE_DELAY msecs_to_jiffies(2000)
|
||||
#define NO_DELAY msecs_to_jiffies(0)
|
||||
|
||||
#define NUM_CPUS 2
|
||||
|
||||
static struct clk *cpu_clk;
|
||||
|
||||
static struct workqueue_struct *workqueue;
|
||||
|
||||
static unsigned long target_cpu_speed[NUM_CPUS];
|
||||
static DEFINE_MUTEX(tegra_cpu_lock);
|
||||
static bool is_suspended;
|
||||
|
||||
static DEFINE_MUTEX(throttling_lock);
|
||||
static bool is_throttling;
|
||||
static struct delayed_work throttle_work;
|
||||
|
||||
|
||||
int tegra_verify_speed(struct cpufreq_policy *policy)
|
||||
{
|
||||
return cpufreq_frequency_table_verify(policy, freq_table);
|
||||
@@ -143,6 +157,8 @@ static int tegra_target(struct cpufreq_policy *policy,
|
||||
{
|
||||
int idx;
|
||||
unsigned int freq;
|
||||
unsigned int highest_speed;
|
||||
unsigned int limit_when_throttling;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&tegra_cpu_lock);
|
||||
@@ -159,13 +175,137 @@ static int tegra_target(struct cpufreq_policy *policy,
|
||||
|
||||
target_cpu_speed[policy->cpu] = freq;
|
||||
|
||||
ret = tegra_update_cpu_speed(tegra_cpu_highest_speed());
|
||||
highest_speed = tegra_cpu_highest_speed();
|
||||
|
||||
/* Do not go above this frequency when throttling */
|
||||
limit_when_throttling = freq_table[THROTTLE_START_INDEX].frequency;
|
||||
|
||||
if (is_throttling && highest_speed > limit_when_throttling) {
|
||||
if (tegra_getspeed(0) < limit_when_throttling) {
|
||||
ret = tegra_update_cpu_speed(limit_when_throttling);
|
||||
goto out;
|
||||
} else {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = tegra_update_cpu_speed(highest_speed);
|
||||
out:
|
||||
mutex_unlock(&tegra_cpu_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool tegra_throttling_needed(unsigned long *rate)
|
||||
{
|
||||
unsigned int current_freq = tegra_getspeed(0);
|
||||
int i;
|
||||
|
||||
for (i = THROTTLE_END_INDEX; i >= THROTTLE_START_INDEX; i--) {
|
||||
if (freq_table[i].frequency < current_freq) {
|
||||
*rate = freq_table[i].frequency;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tegra_throttle_work_func(struct work_struct *work)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
mutex_lock(&tegra_cpu_lock);
|
||||
|
||||
if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) {
|
||||
queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY);
|
||||
}
|
||||
|
||||
mutex_unlock(&tegra_cpu_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_throttling_enable
|
||||
* This functions may sleep
|
||||
*/
|
||||
void tegra_throttling_enable(void)
|
||||
{
|
||||
mutex_lock(&throttling_lock);
|
||||
|
||||
if (!is_throttling) {
|
||||
is_throttling = true;
|
||||
queue_delayed_work(workqueue, &throttle_work, NO_DELAY);
|
||||
}
|
||||
|
||||
mutex_unlock(&throttling_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_throttling_enable);
|
||||
|
||||
/**
|
||||
* tegra_throttling_disable
|
||||
* This functions may sleep
|
||||
*/
|
||||
void tegra_throttling_disable(void)
|
||||
{
|
||||
mutex_lock(&throttling_lock);
|
||||
|
||||
if (is_throttling) {
|
||||
cancel_delayed_work_sync(&throttle_work);
|
||||
is_throttling = false;
|
||||
}
|
||||
|
||||
mutex_unlock(&throttling_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_throttling_disable);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int throttle_debug_set(void *data, u64 val)
|
||||
{
|
||||
if (val) {
|
||||
tegra_throttling_enable();
|
||||
} else {
|
||||
tegra_throttling_disable();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int throttle_debug_get(void *data, u64 *val)
|
||||
{
|
||||
*val = (u64) is_throttling;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set, "%llu\n");
|
||||
|
||||
static struct dentry *cpu_tegra_debugfs_root;
|
||||
|
||||
static int __init tegra_cpu_debug_init(void)
|
||||
{
|
||||
cpu_tegra_debugfs_root = debugfs_create_dir("cpu-tegra", 0);
|
||||
|
||||
if (!cpu_tegra_debugfs_root)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, NULL, &throttle_fops))
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
debugfs_remove_recursive(cpu_tegra_debugfs_root);
|
||||
return -ENOMEM;
|
||||
|
||||
}
|
||||
|
||||
static void __exit tegra_cpu_debug_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(cpu_tegra_debugfs_root);
|
||||
}
|
||||
|
||||
late_initcall(tegra_cpu_debug_init);
|
||||
module_exit(tegra_cpu_debug_exit);
|
||||
#endif
|
||||
|
||||
static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
|
||||
void *dummy)
|
||||
{
|
||||
@@ -207,8 +347,10 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
|
||||
policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
|
||||
cpumask_copy(policy->related_cpus, cpu_possible_mask);
|
||||
|
||||
if (policy->cpu == 0)
|
||||
if (policy->cpu == 0) {
|
||||
INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);
|
||||
register_pm_notifier(&tegra_cpu_pm_notifier);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -237,11 +379,15 @@ static struct cpufreq_driver tegra_cpufreq_driver = {
|
||||
|
||||
static int __init tegra_cpufreq_init(void)
|
||||
{
|
||||
workqueue = create_singlethread_workqueue("cpu-tegra");
|
||||
if (!workqueue)
|
||||
return -ENOMEM;
|
||||
return cpufreq_register_driver(&tegra_cpufreq_driver);
|
||||
}
|
||||
|
||||
static void __exit tegra_cpufreq_exit(void)
|
||||
{
|
||||
destroy_workqueue(workqueue);
|
||||
cpufreq_unregister_driver(&tegra_cpufreq_driver);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user