aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-08-05 20:40:39 -0400
committerColin Cross <ccross@android.com>2011-02-10 20:50:40 -0500
commit1eb2ecf1d5b3c29ce86f098de4ad21fa757d2160 (patch)
treeafe011518ccff676794ad459bf500009964d49c3 /arch/arm/mach-tegra
parent537f5af0f63eea9cd06f477e1c0fe363d1656e08 (diff)
ARM: tegra: cpufreq: Disable cpufreq during suspend
On Tegra, calling clk_set_rate on the CPU clock may call into the regulator API. If the regulator driver that controls the CPU voltage rail has been suspended, this can lead to attempted communication with a hardware block that has already been turned off. Adds a SUSPEND_PREPARE notification hook to drop the frequency to the lowest possible during suspend. Also adds 216MHz (off of PLLP) as the lowest CPU frequency, which allows PLLX to be turned off. Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c75
1 files changed, 60 insertions, 15 deletions
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index fea5719c7072..ad26a9f3a134 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -28,6 +28,7 @@
28#include <linux/err.h> 28#include <linux/err.h>
29#include <linux/clk.h> 29#include <linux/clk.h>
30#include <linux/io.h> 30#include <linux/io.h>
31#include <linux/suspend.h>
31 32
32#include <asm/system.h> 33#include <asm/system.h>
33 34
@@ -36,14 +37,15 @@
36 37
37/* Frequency table index must be sequential starting at 0 */ 38/* Frequency table index must be sequential starting at 0 */
38static struct cpufreq_frequency_table freq_table[] = { 39static struct cpufreq_frequency_table freq_table[] = {
39 { 0, 312000 }, 40 { 0, 216000 },
40 { 1, 456000 }, 41 { 1, 312000 },
41 { 2, 608000 }, 42 { 2, 456000 },
42 { 3, 760000 }, 43 { 3, 608000 },
43 { 4, 816000 }, 44 { 4, 760000 },
44 { 5, 912000 }, 45 { 5, 816000 },
45 { 6, 1000000 }, 46 { 6, 912000 },
46 { 7, CPUFREQ_TABLE_END }, 47 { 7, 1000000 },
48 { 8, CPUFREQ_TABLE_END },
47}; 49};
48 50
49#define NUM_CPUS 2 51#define NUM_CPUS 2
@@ -51,6 +53,8 @@ static struct cpufreq_frequency_table freq_table[] = {
51static struct clk *cpu_clk; 53static struct clk *cpu_clk;
52 54
53static unsigned long target_cpu_speed[NUM_CPUS]; 55static unsigned long target_cpu_speed[NUM_CPUS];
56static DEFINE_MUTEX(tegra_cpu_lock);
57static bool is_suspended;
54 58
55int tegra_verify_speed(struct cpufreq_policy *policy) 59int tegra_verify_speed(struct cpufreq_policy *policy)
56{ 60{
@@ -68,16 +72,11 @@ unsigned int tegra_getspeed(unsigned int cpu)
68 return rate; 72 return rate;
69} 73}
70 74
71static int tegra_update_cpu_speed(void) 75static int tegra_update_cpu_speed(unsigned long rate)
72{ 76{
73 int i;
74 unsigned long rate = 0;
75 int ret = 0; 77 int ret = 0;
76 struct cpufreq_freqs freqs; 78 struct cpufreq_freqs freqs;
77 79
78 for_each_online_cpu(i)
79 rate = max(rate, target_cpu_speed[i]);
80
81 freqs.old = tegra_getspeed(0); 80 freqs.old = tegra_getspeed(0);
82 freqs.new = rate; 81 freqs.new = rate;
83 82
@@ -105,12 +104,30 @@ static int tegra_update_cpu_speed(void)
105 return 0; 104 return 0;
106} 105}
107 106
107static unsigned long tegra_cpu_highest_speed(void)
108{
109 unsigned long rate = 0;
110 int i;
111
112 for_each_online_cpu(i)
113 rate = max(rate, target_cpu_speed[i]);
114 return rate;
115}
116
108static int tegra_target(struct cpufreq_policy *policy, 117static int tegra_target(struct cpufreq_policy *policy,
109 unsigned int target_freq, 118 unsigned int target_freq,
110 unsigned int relation) 119 unsigned int relation)
111{ 120{
112 int idx; 121 int idx;
113 unsigned int freq; 122 unsigned int freq;
123 int ret = 0;
124
125 mutex_lock(&tegra_cpu_lock);
126
127 if (is_suspended) {
128 ret = -EBUSY;
129 goto out;
130 }
114 131
115 cpufreq_frequency_table_target(policy, freq_table, target_freq, 132 cpufreq_frequency_table_target(policy, freq_table, target_freq,
116 relation, &idx); 133 relation, &idx);
@@ -119,9 +136,34 @@ static int tegra_target(struct cpufreq_policy *policy,
119 136
120 target_cpu_speed[policy->cpu] = freq; 137 target_cpu_speed[policy->cpu] = freq;
121 138
122 return tegra_update_cpu_speed(); 139 ret = tegra_update_cpu_speed(tegra_cpu_highest_speed());
140
141out:
142 mutex_unlock(&tegra_cpu_lock);
143 return ret;
123} 144}
124 145
146static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
147 void *dummy)
148{
149 mutex_lock(&tegra_cpu_lock);
150 if (event == PM_SUSPEND_PREPARE) {
151 is_suspended = true;
152 pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n",
153 freq_table[0].frequency);
154 tegra_update_cpu_speed(freq_table[0].frequency);
155 } else if (event == PM_POST_SUSPEND) {
156 is_suspended = false;
157 }
158 mutex_unlock(&tegra_cpu_lock);
159
160 return NOTIFY_OK;
161}
162
163static struct notifier_block tegra_cpu_pm_notifier = {
164 .notifier_call = tegra_pm_notify,
165};
166
125static int tegra_cpu_init(struct cpufreq_policy *policy) 167static int tegra_cpu_init(struct cpufreq_policy *policy)
126{ 168{
127 if (policy->cpu >= NUM_CPUS) 169 if (policy->cpu >= NUM_CPUS)
@@ -142,6 +184,9 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
142 policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; 184 policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
143 cpumask_copy(policy->related_cpus, cpu_possible_mask); 185 cpumask_copy(policy->related_cpus, cpu_possible_mask);
144 186
187 if (policy->cpu == 0)
188 register_pm_notifier(&tegra_cpu_pm_notifier);
189
145 return 0; 190 return 0;
146} 191}
147 192