Blame view
drivers/cpufreq/omap-cpufreq.c
6.42 KB
ec6bced6c [PATCH] ARM: 2803... |
1 |
/* |
ffe4f0f11 cpufreq: OMAP: on... |
2 |
* CPU frequency scaling for OMAP using OPP information |
ec6bced6c [PATCH] ARM: 2803... |
3 4 5 6 7 8 |
* * Copyright (C) 2005 Nokia Corporation * Written by Tony Lindgren <tony@atomide.com> * * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King * |
731e0cc63 cpufreq: OMAP: cl... |
9 10 11 |
* Copyright (C) 2007-2011 Texas Instruments, Inc. * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar * |
ec6bced6c [PATCH] ARM: 2803... |
12 13 14 15 16 17 18 19 20 21 22 |
* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/cpufreq.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/err.h> |
f8ce25476 [ARM] Move asm/ha... |
23 |
#include <linux/clk.h> |
fced80c73 [ARM] Convert asm... |
24 |
#include <linux/io.h> |
731e0cc63 cpufreq: OMAP: cl... |
25 |
#include <linux/opp.h> |
46c12216c cpufreq: OMAP: Ad... |
26 |
#include <linux/cpu.h> |
c1b547bc2 cpufreq: OMAP: fi... |
27 |
#include <linux/module.h> |
ec6bced6c [PATCH] ARM: 2803... |
28 |
|
ec6bced6c [PATCH] ARM: 2803... |
29 |
#include <asm/system.h> |
731e0cc63 cpufreq: OMAP: cl... |
30 |
#include <asm/smp_plat.h> |
46c12216c cpufreq: OMAP: Ad... |
31 |
#include <asm/cpu.h> |
ec6bced6c [PATCH] ARM: 2803... |
32 |
|
731e0cc63 cpufreq: OMAP: cl... |
33 34 35 |
#include <plat/clock.h> #include <plat/omap-pm.h> #include <plat/common.h> |
c1b547bc2 cpufreq: OMAP: fi... |
36 |
#include <plat/omap_device.h> |
a7ca9d2b0 ARM: OMAP: Update... |
37 |
|
731e0cc63 cpufreq: OMAP: cl... |
38 |
#include <mach/hardware.h> |
aeec29901 [ARM] OMAP2: Impl... |
39 |
|
46c12216c cpufreq: OMAP: Ad... |
40 41 42 43 44 45 46 47 48 |
#ifdef CONFIG_SMP struct lpj_info { unsigned long ref; unsigned int freq; }; static DEFINE_PER_CPU(struct lpj_info, lpj_ref); static struct lpj_info global_lpj_ref; #endif |
731e0cc63 cpufreq: OMAP: cl... |
49 |
static struct cpufreq_frequency_table *freq_table; |
1c78217fc cpufreq: OMAP: fi... |
50 |
static atomic_t freq_table_users = ATOMIC_INIT(0); |
b8488fbe6 ARM: OMAP: Fix sl... |
51 |
static struct clk *mpu_clk; |
08ca3e3b8 cpufreq: OMAP: mo... |
52 |
static char *mpu_clk_name; |
a820ffa8f cpufreq: OMAP: de... |
53 |
static struct device *mpu_dev; |
b8488fbe6 ARM: OMAP: Fix sl... |
54 |
|
b0a330dc5 OMAP: plat-omap: ... |
55 |
static int omap_verify_speed(struct cpufreq_policy *policy) |
ec6bced6c [PATCH] ARM: 2803... |
56 |
{ |
bf2a359d5 cpufreq: OMAP: do... |
57 |
if (!freq_table) |
ec6bced6c [PATCH] ARM: 2803... |
58 |
return -EINVAL; |
bf2a359d5 cpufreq: OMAP: do... |
59 |
return cpufreq_frequency_table_verify(policy, freq_table); |
ec6bced6c [PATCH] ARM: 2803... |
60 |
} |
b0a330dc5 OMAP: plat-omap: ... |
61 |
static unsigned int omap_getspeed(unsigned int cpu) |
ec6bced6c [PATCH] ARM: 2803... |
62 |
{ |
ec6bced6c [PATCH] ARM: 2803... |
63 |
unsigned long rate; |
46c12216c cpufreq: OMAP: Ad... |
64 |
if (cpu >= NR_CPUS) |
ec6bced6c [PATCH] ARM: 2803... |
65 |
return 0; |
ec6bced6c [PATCH] ARM: 2803... |
66 |
rate = clk_get_rate(mpu_clk) / 1000; |
ec6bced6c [PATCH] ARM: 2803... |
67 68 69 70 71 72 73 |
return rate; } static int omap_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { |
bf2a359d5 cpufreq: OMAP: do... |
74 75 |
unsigned int i; int ret = 0; |
731e0cc63 cpufreq: OMAP: cl... |
76 |
struct cpufreq_freqs freqs; |
ec6bced6c [PATCH] ARM: 2803... |
77 |
|
bf2a359d5 cpufreq: OMAP: do... |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
if (!freq_table) { dev_err(mpu_dev, "%s: cpu%d: no freq table! ", __func__, policy->cpu); return -EINVAL; } ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, relation, &i); if (ret) { dev_dbg(mpu_dev, "%s: cpu%d: no freq match for %d(ret=%d) ", __func__, policy->cpu, target_freq, ret); return ret; } freqs.new = freq_table[i].frequency; if (!freqs.new) { dev_err(mpu_dev, "%s: cpu%d: no match for freq %d ", __func__, policy->cpu, target_freq); return -EINVAL; } |
aeec29901 [ARM] OMAP2: Impl... |
100 |
|
46c12216c cpufreq: OMAP: Ad... |
101 |
freqs.old = omap_getspeed(policy->cpu); |
46c12216c cpufreq: OMAP: Ad... |
102 |
freqs.cpu = policy->cpu; |
ec6bced6c [PATCH] ARM: 2803... |
103 |
|
022ac03b4 cpufreq: OMAP: no... |
104 |
if (freqs.old == freqs.new && policy->cur == freqs.new) |
aeec29901 [ARM] OMAP2: Impl... |
105 |
return ret; |
46c12216c cpufreq: OMAP: Ad... |
106 107 108 109 110 |
/* notifiers */ for_each_cpu(i, policy->cpus) { freqs.cpu = i; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); } |
731e0cc63 cpufreq: OMAP: cl... |
111 |
|
aeec29901 [ARM] OMAP2: Impl... |
112 |
#ifdef CONFIG_CPU_FREQ_DEBUG |
731e0cc63 cpufreq: OMAP: cl... |
113 114 |
pr_info("cpufreq-omap: transition: %u --> %u ", freqs.old, freqs.new); |
aeec29901 [ARM] OMAP2: Impl... |
115 |
#endif |
731e0cc63 cpufreq: OMAP: cl... |
116 |
|
aeec29901 [ARM] OMAP2: Impl... |
117 |
ret = clk_set_rate(mpu_clk, freqs.new * 1000); |
46c12216c cpufreq: OMAP: Ad... |
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
freqs.new = omap_getspeed(policy->cpu); #ifdef CONFIG_SMP /* * Note that loops_per_jiffy is not updated on SMP systems in * cpufreq driver. So, update the per-CPU loops_per_jiffy value * on frequency transition. We need to update all dependent CPUs. */ for_each_cpu(i, policy->cpus) { struct lpj_info *lpj = &per_cpu(lpj_ref, i); if (!lpj->freq) { lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy; lpj->freq = freqs.old; } per_cpu(cpu_data, i).loops_per_jiffy = cpufreq_scale(lpj->ref, lpj->freq, freqs.new); } |
731e0cc63 cpufreq: OMAP: cl... |
136 |
|
46c12216c cpufreq: OMAP: Ad... |
137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
/* And don't forget to adjust the global one */ if (!global_lpj_ref.freq) { global_lpj_ref.ref = loops_per_jiffy; global_lpj_ref.freq = freqs.old; } loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq, freqs.new); #endif /* notifiers */ for_each_cpu(i, policy->cpus) { freqs.cpu = i; cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); } |
ec6bced6c [PATCH] ARM: 2803... |
151 152 153 |
return ret; } |
1c78217fc cpufreq: OMAP: fi... |
154 155 156 157 158 |
static inline void freq_table_free(void) { if (atomic_dec_and_test(&freq_table_users)) opp_free_cpufreq_table(mpu_dev, &freq_table); } |
790ab7e92 arm: omap: fix se... |
159 |
static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) |
ec6bced6c [PATCH] ARM: 2803... |
160 |
{ |
aeec29901 [ARM] OMAP2: Impl... |
161 |
int result = 0; |
731e0cc63 cpufreq: OMAP: cl... |
162 |
|
08ca3e3b8 cpufreq: OMAP: mo... |
163 |
mpu_clk = clk_get(NULL, mpu_clk_name); |
ec6bced6c [PATCH] ARM: 2803... |
164 165 |
if (IS_ERR(mpu_clk)) return PTR_ERR(mpu_clk); |
11e04fdd9 cpufreq: OMAP: pu... |
166 167 168 169 |
if (policy->cpu >= NR_CPUS) { result = -EINVAL; goto fail_ck; } |
aeec29901 [ARM] OMAP2: Impl... |
170 |
|
46c12216c cpufreq: OMAP: Ad... |
171 |
policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); |
1c78217fc cpufreq: OMAP: fi... |
172 173 174 |
if (atomic_inc_return(&freq_table_users) == 1) result = opp_init_cpufreq_table(mpu_dev, &freq_table); |
bf2a359d5 cpufreq: OMAP: do... |
175 176 177 178 179 |
if (result) { dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d] ", __func__, policy->cpu, result); |
11e04fdd9 cpufreq: OMAP: pu... |
180 |
goto fail_ck; |
aeec29901 [ARM] OMAP2: Impl... |
181 |
} |
bf2a359d5 cpufreq: OMAP: do... |
182 |
result = cpufreq_frequency_table_cpuinfo(policy, freq_table); |
1c78217fc cpufreq: OMAP: fi... |
183 184 185 186 |
if (result) goto fail_table; cpufreq_frequency_table_get_attr(freq_table, policy->cpu); |
bf2a359d5 cpufreq: OMAP: do... |
187 |
|
731e0cc63 cpufreq: OMAP: cl... |
188 189 |
policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; |
46c12216c cpufreq: OMAP: Ad... |
190 191 192 193 194 195 196 197 198 199 200 |
policy->cur = omap_getspeed(policy->cpu); /* * On OMAP SMP configuartion, both processors share the voltage * and clock. So both CPUs needs to be scaled together and hence * needs software co-ordination. Use cpufreq affected_cpus * interface to handle this scenario. Additional is_smp() check * is to keep SMP_ON_UP build working. */ if (is_smp()) { policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; |
ed8ce00c5 cpufreq: OMAP: En... |
201 |
cpumask_setall(policy->cpus); |
46c12216c cpufreq: OMAP: Ad... |
202 |
} |
731e0cc63 cpufreq: OMAP: cl... |
203 |
|
aeec29901 [ARM] OMAP2: Impl... |
204 |
/* FIXME: what's the actual transition time? */ |
b029839cf omap3: Decrease c... |
205 |
policy->cpuinfo.transition_latency = 300 * 1000; |
ec6bced6c [PATCH] ARM: 2803... |
206 207 |
return 0; |
11e04fdd9 cpufreq: OMAP: pu... |
208 |
|
1c78217fc cpufreq: OMAP: fi... |
209 210 |
fail_table: freq_table_free(); |
11e04fdd9 cpufreq: OMAP: pu... |
211 212 213 |
fail_ck: clk_put(mpu_clk); return result; |
ec6bced6c [PATCH] ARM: 2803... |
214 |
} |
b8488fbe6 ARM: OMAP: Fix sl... |
215 216 |
static int omap_cpu_exit(struct cpufreq_policy *policy) { |
1c78217fc cpufreq: OMAP: fi... |
217 |
freq_table_free(); |
b8488fbe6 ARM: OMAP: Fix sl... |
218 219 220 |
clk_put(mpu_clk); return 0; } |
aeec29901 [ARM] OMAP2: Impl... |
221 222 223 224 |
static struct freq_attr *omap_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, }; |
ec6bced6c [PATCH] ARM: 2803... |
225 226 227 228 229 230 |
static struct cpufreq_driver omap_driver = { .flags = CPUFREQ_STICKY, .verify = omap_verify_speed, .target = omap_target, .get = omap_getspeed, .init = omap_cpu_init, |
b8488fbe6 ARM: OMAP: Fix sl... |
231 |
.exit = omap_cpu_exit, |
ec6bced6c [PATCH] ARM: 2803... |
232 |
.name = "omap", |
aeec29901 [ARM] OMAP2: Impl... |
233 |
.attr = omap_cpufreq_attr, |
ec6bced6c [PATCH] ARM: 2803... |
234 235 236 237 |
}; static int __init omap_cpufreq_init(void) { |
08ca3e3b8 cpufreq: OMAP: mo... |
238 239 240 241 242 243 244 245 246 247 248 249 |
if (cpu_is_omap24xx()) mpu_clk_name = "virt_prcm_set"; else if (cpu_is_omap34xx()) mpu_clk_name = "dpll1_ck"; else if (cpu_is_omap44xx()) mpu_clk_name = "dpll_mpu_ck"; if (!mpu_clk_name) { pr_err("%s: unsupported Silicon? ", __func__); return -EINVAL; } |
a820ffa8f cpufreq: OMAP: de... |
250 |
|
c1b547bc2 cpufreq: OMAP: fi... |
251 |
mpu_dev = omap_device_get_by_hwmod_name("mpu"); |
a820ffa8f cpufreq: OMAP: de... |
252 253 254 255 256 |
if (!mpu_dev) { pr_warning("%s: unable to get the mpu device ", __func__); return -EINVAL; } |
ec6bced6c [PATCH] ARM: 2803... |
257 258 |
return cpufreq_register_driver(&omap_driver); } |
731e0cc63 cpufreq: OMAP: cl... |
259 260 261 262 |
static void __exit omap_cpufreq_exit(void) { cpufreq_unregister_driver(&omap_driver); } |
aeec29901 [ARM] OMAP2: Impl... |
263 |
|
731e0cc63 cpufreq: OMAP: cl... |
264 265 266 267 |
MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs"); MODULE_LICENSE("GPL"); module_init(omap_cpufreq_init); module_exit(omap_cpufreq_exit); |