Root/
1 | /* |
2 | * drivers/cpufreq/spear-cpufreq.c |
3 | * |
4 | * CPU Frequency Scaling for SPEAr platform |
5 | * |
6 | * Copyright (C) 2012 ST Microelectronics |
7 | * Deepak Sikri <deepak.sikri@st.com> |
8 | * |
9 | * This file is licensed under the terms of the GNU General Public |
10 | * License version 2. This program is licensed "as is" without any |
11 | * warranty of any kind, whether express or implied. |
12 | */ |
13 | |
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
15 | |
16 | #include <linux/clk.h> |
17 | #include <linux/cpufreq.h> |
18 | #include <linux/err.h> |
19 | #include <linux/init.h> |
20 | #include <linux/module.h> |
21 | #include <linux/of.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/types.h> |
24 | |
25 | /* SPEAr CPUFreq driver data structure */ |
26 | static struct { |
27 | struct clk *clk; |
28 | unsigned int transition_latency; |
29 | struct cpufreq_frequency_table *freq_tbl; |
30 | u32 cnt; |
31 | } spear_cpufreq; |
32 | |
33 | static int spear_cpufreq_verify(struct cpufreq_policy *policy) |
34 | { |
35 | return cpufreq_frequency_table_verify(policy, spear_cpufreq.freq_tbl); |
36 | } |
37 | |
38 | static unsigned int spear_cpufreq_get(unsigned int cpu) |
39 | { |
40 | return clk_get_rate(spear_cpufreq.clk) / 1000; |
41 | } |
42 | |
43 | static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq) |
44 | { |
45 | struct clk *sys_pclk; |
46 | int pclk; |
47 | /* |
48 | * In SPEAr1340, cpu clk's parent sys clk can take input from |
49 | * following sources |
50 | */ |
51 | const char *sys_clk_src[] = { |
52 | "sys_syn_clk", |
53 | "pll1_clk", |
54 | "pll2_clk", |
55 | "pll3_clk", |
56 | }; |
57 | |
58 | /* |
59 | * As sys clk can have multiple source with their own range |
60 | * limitation so we choose possible sources accordingly |
61 | */ |
62 | if (newfreq <= 300000000) |
63 | pclk = 0; /* src is sys_syn_clk */ |
64 | else if (newfreq > 300000000 && newfreq <= 500000000) |
65 | pclk = 3; /* src is pll3_clk */ |
66 | else if (newfreq == 600000000) |
67 | pclk = 1; /* src is pll1_clk */ |
68 | else |
69 | return ERR_PTR(-EINVAL); |
70 | |
71 | /* Get parent to sys clock */ |
72 | sys_pclk = clk_get(NULL, sys_clk_src[pclk]); |
73 | if (IS_ERR(sys_pclk)) |
74 | pr_err("Failed to get %s clock\n", sys_clk_src[pclk]); |
75 | |
76 | return sys_pclk; |
77 | } |
78 | |
79 | /* |
80 | * In SPEAr1340, we cannot use newfreq directly because we need to actually |
81 | * access a source clock (clk) which might not be ancestor of cpu at present. |
82 | * Hence in SPEAr1340 we would operate on source clock directly before switching |
83 | * cpu clock to it. |
84 | */ |
85 | static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq) |
86 | { |
87 | struct clk *sys_clk; |
88 | int ret = 0; |
89 | |
90 | sys_clk = clk_get_parent(spear_cpufreq.clk); |
91 | if (IS_ERR(sys_clk)) { |
92 | pr_err("failed to get cpu's parent (sys) clock\n"); |
93 | return PTR_ERR(sys_clk); |
94 | } |
95 | |
96 | /* Set the rate of the source clock before changing the parent */ |
97 | ret = clk_set_rate(sys_pclk, newfreq); |
98 | if (ret) { |
99 | pr_err("Failed to set sys clk rate to %lu\n", newfreq); |
100 | return ret; |
101 | } |
102 | |
103 | ret = clk_set_parent(sys_clk, sys_pclk); |
104 | if (ret) { |
105 | pr_err("Failed to set sys clk parent\n"); |
106 | return ret; |
107 | } |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static int spear_cpufreq_target(struct cpufreq_policy *policy, |
113 | unsigned int target_freq, unsigned int relation) |
114 | { |
115 | struct cpufreq_freqs freqs; |
116 | unsigned long newfreq; |
117 | struct clk *srcclk; |
118 | int index, ret, mult = 1; |
119 | |
120 | if (cpufreq_frequency_table_target(policy, spear_cpufreq.freq_tbl, |
121 | target_freq, relation, &index)) |
122 | return -EINVAL; |
123 | |
124 | freqs.cpu = policy->cpu; |
125 | freqs.old = spear_cpufreq_get(0); |
126 | |
127 | newfreq = spear_cpufreq.freq_tbl[index].frequency * 1000; |
128 | if (of_machine_is_compatible("st,spear1340")) { |
129 | /* |
130 | * SPEAr1340 is special in the sense that due to the possibility |
131 | * of multiple clock sources for cpu clk's parent we can have |
132 | * different clock source for different frequency of cpu clk. |
133 | * Hence we need to choose one from amongst these possible clock |
134 | * sources. |
135 | */ |
136 | srcclk = spear1340_cpu_get_possible_parent(newfreq); |
137 | if (IS_ERR(srcclk)) { |
138 | pr_err("Failed to get src clk\n"); |
139 | return PTR_ERR(srcclk); |
140 | } |
141 | |
142 | /* SPEAr1340: src clk is always 2 * intended cpu clk */ |
143 | mult = 2; |
144 | } else { |
145 | /* |
146 | * src clock to be altered is ancestor of cpu clock. Hence we |
147 | * can directly work on cpu clk |
148 | */ |
149 | srcclk = spear_cpufreq.clk; |
150 | } |
151 | |
152 | newfreq = clk_round_rate(srcclk, newfreq * mult); |
153 | if (newfreq < 0) { |
154 | pr_err("clk_round_rate failed for cpu src clock\n"); |
155 | return newfreq; |
156 | } |
157 | |
158 | freqs.new = newfreq / 1000; |
159 | freqs.new /= mult; |
160 | |
161 | for_each_cpu(freqs.cpu, policy->cpus) |
162 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); |
163 | |
164 | if (mult == 2) |
165 | ret = spear1340_set_cpu_rate(srcclk, newfreq); |
166 | else |
167 | ret = clk_set_rate(spear_cpufreq.clk, newfreq); |
168 | |
169 | /* Get current rate after clk_set_rate, in case of failure */ |
170 | if (ret) { |
171 | pr_err("CPU Freq: cpu clk_set_rate failed: %d\n", ret); |
172 | freqs.new = clk_get_rate(spear_cpufreq.clk) / 1000; |
173 | } |
174 | |
175 | for_each_cpu(freqs.cpu, policy->cpus) |
176 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
177 | return ret; |
178 | } |
179 | |
180 | static int spear_cpufreq_init(struct cpufreq_policy *policy) |
181 | { |
182 | int ret; |
183 | |
184 | ret = cpufreq_frequency_table_cpuinfo(policy, spear_cpufreq.freq_tbl); |
185 | if (ret) { |
186 | pr_err("cpufreq_frequency_table_cpuinfo() failed"); |
187 | return ret; |
188 | } |
189 | |
190 | cpufreq_frequency_table_get_attr(spear_cpufreq.freq_tbl, policy->cpu); |
191 | policy->cpuinfo.transition_latency = spear_cpufreq.transition_latency; |
192 | policy->cur = spear_cpufreq_get(0); |
193 | |
194 | cpumask_setall(policy->cpus); |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static int spear_cpufreq_exit(struct cpufreq_policy *policy) |
200 | { |
201 | cpufreq_frequency_table_put_attr(policy->cpu); |
202 | return 0; |
203 | } |
204 | |
205 | static struct freq_attr *spear_cpufreq_attr[] = { |
206 | &cpufreq_freq_attr_scaling_available_freqs, |
207 | NULL, |
208 | }; |
209 | |
210 | static struct cpufreq_driver spear_cpufreq_driver = { |
211 | .name = "cpufreq-spear", |
212 | .flags = CPUFREQ_STICKY, |
213 | .verify = spear_cpufreq_verify, |
214 | .target = spear_cpufreq_target, |
215 | .get = spear_cpufreq_get, |
216 | .init = spear_cpufreq_init, |
217 | .exit = spear_cpufreq_exit, |
218 | .attr = spear_cpufreq_attr, |
219 | }; |
220 | |
221 | static int spear_cpufreq_driver_init(void) |
222 | { |
223 | struct device_node *np; |
224 | const struct property *prop; |
225 | struct cpufreq_frequency_table *freq_tbl; |
226 | const __be32 *val; |
227 | int cnt, i, ret; |
228 | |
229 | np = of_find_node_by_path("/cpus/cpu@0"); |
230 | if (!np) { |
231 | pr_err("No cpu node found"); |
232 | return -ENODEV; |
233 | } |
234 | |
235 | if (of_property_read_u32(np, "clock-latency", |
236 | &spear_cpufreq.transition_latency)) |
237 | spear_cpufreq.transition_latency = CPUFREQ_ETERNAL; |
238 | |
239 | prop = of_find_property(np, "cpufreq_tbl", NULL); |
240 | if (!prop || !prop->value) { |
241 | pr_err("Invalid cpufreq_tbl"); |
242 | ret = -ENODEV; |
243 | goto out_put_node; |
244 | } |
245 | |
246 | cnt = prop->length / sizeof(u32); |
247 | val = prop->value; |
248 | |
249 | freq_tbl = kmalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL); |
250 | if (!freq_tbl) { |
251 | ret = -ENOMEM; |
252 | goto out_put_node; |
253 | } |
254 | |
255 | for (i = 0; i < cnt; i++) { |
256 | freq_tbl[i].index = i; |
257 | freq_tbl[i].frequency = be32_to_cpup(val++); |
258 | } |
259 | |
260 | freq_tbl[i].index = i; |
261 | freq_tbl[i].frequency = CPUFREQ_TABLE_END; |
262 | |
263 | spear_cpufreq.freq_tbl = freq_tbl; |
264 | |
265 | of_node_put(np); |
266 | |
267 | spear_cpufreq.clk = clk_get(NULL, "cpu_clk"); |
268 | if (IS_ERR(spear_cpufreq.clk)) { |
269 | pr_err("Unable to get CPU clock\n"); |
270 | ret = PTR_ERR(spear_cpufreq.clk); |
271 | goto out_put_mem; |
272 | } |
273 | |
274 | ret = cpufreq_register_driver(&spear_cpufreq_driver); |
275 | if (!ret) |
276 | return 0; |
277 | |
278 | pr_err("failed register driver: %d\n", ret); |
279 | clk_put(spear_cpufreq.clk); |
280 | |
281 | out_put_mem: |
282 | kfree(freq_tbl); |
283 | return ret; |
284 | |
285 | out_put_node: |
286 | of_node_put(np); |
287 | return ret; |
288 | } |
289 | late_initcall(spear_cpufreq_driver_init); |
290 | |
291 | MODULE_AUTHOR("Deepak Sikri <deepak.sikri@st.com>"); |
292 | MODULE_DESCRIPTION("SPEAr CPUFreq driver"); |
293 | MODULE_LICENSE("GPL"); |
294 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9