diff options
Diffstat (limited to 'drivers/cpufreq/omap-cpufreq.c')
-rw-r--r-- | drivers/cpufreq/omap-cpufreq.c | 72 |
1 files changed, 67 insertions, 5 deletions
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 5d04c57aae30..67bbb06d0460 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/opp.h> | 25 | #include <linux/opp.h> |
26 | #include <linux/cpu.h> | 26 | #include <linux/cpu.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/regulator/consumer.h> | ||
28 | 29 | ||
29 | #include <asm/system.h> | 30 | #include <asm/system.h> |
30 | #include <asm/smp_plat.h> | 31 | #include <asm/smp_plat.h> |
@@ -37,6 +38,9 @@ | |||
37 | 38 | ||
38 | #include <mach/hardware.h> | 39 | #include <mach/hardware.h> |
39 | 40 | ||
41 | /* OPP tolerance in percentage */ | ||
42 | #define OPP_TOLERANCE 4 | ||
43 | |||
40 | #ifdef CONFIG_SMP | 44 | #ifdef CONFIG_SMP |
41 | struct lpj_info { | 45 | struct lpj_info { |
42 | unsigned long ref; | 46 | unsigned long ref; |
@@ -52,6 +56,7 @@ static atomic_t freq_table_users = ATOMIC_INIT(0); | |||
52 | static struct clk *mpu_clk; | 56 | static struct clk *mpu_clk; |
53 | static char *mpu_clk_name; | 57 | static char *mpu_clk_name; |
54 | static struct device *mpu_dev; | 58 | static struct device *mpu_dev; |
59 | static struct regulator *mpu_reg; | ||
55 | 60 | ||
56 | static int omap_verify_speed(struct cpufreq_policy *policy) | 61 | static int omap_verify_speed(struct cpufreq_policy *policy) |
57 | { | 62 | { |
@@ -76,8 +81,10 @@ static int omap_target(struct cpufreq_policy *policy, | |||
76 | unsigned int relation) | 81 | unsigned int relation) |
77 | { | 82 | { |
78 | unsigned int i; | 83 | unsigned int i; |
79 | int ret = 0; | 84 | int r, ret = 0; |
80 | struct cpufreq_freqs freqs; | 85 | struct cpufreq_freqs freqs; |
86 | struct opp *opp; | ||
87 | unsigned long freq, volt = 0, volt_old = 0, tol = 0; | ||
81 | 88 | ||
82 | if (!freq_table) { | 89 | if (!freq_table) { |
83 | dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__, | 90 | dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__, |
@@ -111,13 +118,50 @@ static int omap_target(struct cpufreq_policy *policy, | |||
111 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | 118 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); |
112 | } | 119 | } |
113 | 120 | ||
114 | #ifdef CONFIG_CPU_FREQ_DEBUG | 121 | freq = freqs.new * 1000; |
115 | pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); | 122 | |
116 | #endif | 123 | if (mpu_reg) { |
124 | opp = opp_find_freq_ceil(mpu_dev, &freq); | ||
125 | if (IS_ERR(opp)) { | ||
126 | dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n", | ||
127 | __func__, freqs.new); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | volt = opp_get_voltage(opp); | ||
131 | tol = volt * OPP_TOLERANCE / 100; | ||
132 | volt_old = regulator_get_voltage(mpu_reg); | ||
133 | } | ||
134 | |||
135 | dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n", | ||
136 | freqs.old / 1000, volt_old ? volt_old / 1000 : -1, | ||
137 | freqs.new / 1000, volt ? volt / 1000 : -1); | ||
138 | |||
139 | /* scaling up? scale voltage before frequency */ | ||
140 | if (mpu_reg && (freqs.new > freqs.old)) { | ||
141 | r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); | ||
142 | if (r < 0) { | ||
143 | dev_warn(mpu_dev, "%s: unable to scale voltage up.\n", | ||
144 | __func__); | ||
145 | freqs.new = freqs.old; | ||
146 | goto done; | ||
147 | } | ||
148 | } | ||
117 | 149 | ||
118 | ret = clk_set_rate(mpu_clk, freqs.new * 1000); | 150 | ret = clk_set_rate(mpu_clk, freqs.new * 1000); |
119 | freqs.new = omap_getspeed(policy->cpu); | ||
120 | 151 | ||
152 | /* scaling down? scale voltage after frequency */ | ||
153 | if (mpu_reg && (freqs.new < freqs.old)) { | ||
154 | r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); | ||
155 | if (r < 0) { | ||
156 | dev_warn(mpu_dev, "%s: unable to scale voltage down.\n", | ||
157 | __func__); | ||
158 | ret = clk_set_rate(mpu_clk, freqs.old * 1000); | ||
159 | freqs.new = freqs.old; | ||
160 | goto done; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | freqs.new = omap_getspeed(policy->cpu); | ||
121 | #ifdef CONFIG_SMP | 165 | #ifdef CONFIG_SMP |
122 | /* | 166 | /* |
123 | * Note that loops_per_jiffy is not updated on SMP systems in | 167 | * Note that loops_per_jiffy is not updated on SMP systems in |
@@ -144,6 +188,7 @@ static int omap_target(struct cpufreq_policy *policy, | |||
144 | freqs.new); | 188 | freqs.new); |
145 | #endif | 189 | #endif |
146 | 190 | ||
191 | done: | ||
147 | /* notifiers */ | 192 | /* notifiers */ |
148 | for_each_cpu(i, policy->cpus) { | 193 | for_each_cpu(i, policy->cpus) { |
149 | freqs.cpu = i; | 194 | freqs.cpu = i; |
@@ -260,6 +305,23 @@ static int __init omap_cpufreq_init(void) | |||
260 | return -EINVAL; | 305 | return -EINVAL; |
261 | } | 306 | } |
262 | 307 | ||
308 | mpu_reg = regulator_get(mpu_dev, "vcc"); | ||
309 | if (IS_ERR(mpu_reg)) { | ||
310 | pr_warning("%s: unable to get MPU regulator\n", __func__); | ||
311 | mpu_reg = NULL; | ||
312 | } else { | ||
313 | /* | ||
314 | * Ensure physical regulator is present. | ||
315 | * (e.g. could be dummy regulator.) | ||
316 | */ | ||
317 | if (regulator_get_voltage(mpu_reg) < 0) { | ||
318 | pr_warn("%s: physical regulator not present for MPU\n", | ||
319 | __func__); | ||
320 | regulator_put(mpu_reg); | ||
321 | mpu_reg = NULL; | ||
322 | } | ||
323 | } | ||
324 | |||
263 | return cpufreq_register_driver(&omap_driver); | 325 | return cpufreq_register_driver(&omap_driver); |
264 | } | 326 | } |
265 | 327 | ||