Root/
1 | /* linux/arch/arm/plat-s3c24xx/cpu-freq.c |
2 | * |
3 | * Copyright (c) 2006-2008 Simtec Electronics |
4 | * http://armlinux.simtec.co.uk/ |
5 | * Ben Dooks <ben@simtec.co.uk> |
6 | * |
7 | * S3C24XX CPU Frequency scaling |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. |
12 | */ |
13 | |
14 | #include <linux/init.h> |
15 | #include <linux/module.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/ioport.h> |
18 | #include <linux/cpufreq.h> |
19 | #include <linux/cpu.h> |
20 | #include <linux/clk.h> |
21 | #include <linux/err.h> |
22 | #include <linux/io.h> |
23 | #include <linux/sysdev.h> |
24 | #include <linux/kobject.h> |
25 | #include <linux/sysfs.h> |
26 | #include <linux/slab.h> |
27 | |
28 | #include <asm/mach/arch.h> |
29 | #include <asm/mach/map.h> |
30 | |
31 | #include <plat/cpu.h> |
32 | #include <plat/clock.h> |
33 | #include <plat/cpu-freq-core.h> |
34 | |
35 | #include <mach/regs-clock.h> |
36 | |
37 | /* note, cpufreq support deals in kHz, no Hz */ |
38 | |
39 | static struct cpufreq_driver s3c24xx_driver; |
40 | static struct s3c_cpufreq_config cpu_cur; |
41 | static struct s3c_iotimings s3c24xx_iotiming; |
42 | static struct cpufreq_frequency_table *pll_reg; |
43 | static unsigned int last_target = ~0; |
44 | static unsigned int ftab_size; |
45 | static struct cpufreq_frequency_table *ftab; |
46 | |
47 | static struct clk *_clk_mpll; |
48 | static struct clk *_clk_xtal; |
49 | static struct clk *clk_fclk; |
50 | static struct clk *clk_hclk; |
51 | static struct clk *clk_pclk; |
52 | static struct clk *clk_arm; |
53 | |
54 | #ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS |
55 | struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void) |
56 | { |
57 | return &cpu_cur; |
58 | } |
59 | |
60 | struct s3c_iotimings *s3c_cpufreq_getiotimings(void) |
61 | { |
62 | return &s3c24xx_iotiming; |
63 | } |
64 | #endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */ |
65 | |
66 | static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg) |
67 | { |
68 | unsigned long fclk, pclk, hclk, armclk; |
69 | |
70 | cfg->freq.fclk = fclk = clk_get_rate(clk_fclk); |
71 | cfg->freq.hclk = hclk = clk_get_rate(clk_hclk); |
72 | cfg->freq.pclk = pclk = clk_get_rate(clk_pclk); |
73 | cfg->freq.armclk = armclk = clk_get_rate(clk_arm); |
74 | |
75 | cfg->pll.index = __raw_readl(S3C2410_MPLLCON); |
76 | cfg->pll.frequency = fclk; |
77 | |
78 | cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10); |
79 | |
80 | cfg->divs.h_divisor = fclk / hclk; |
81 | cfg->divs.p_divisor = fclk / pclk; |
82 | } |
83 | |
84 | static inline void s3c_cpufreq_calc(struct s3c_cpufreq_config *cfg) |
85 | { |
86 | unsigned long pll = cfg->pll.frequency; |
87 | |
88 | cfg->freq.fclk = pll; |
89 | cfg->freq.hclk = pll / cfg->divs.h_divisor; |
90 | cfg->freq.pclk = pll / cfg->divs.p_divisor; |
91 | |
92 | /* convert hclk into 10ths of nanoseconds for io calcs */ |
93 | cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10); |
94 | } |
95 | |
96 | static inline int closer(unsigned int target, unsigned int n, unsigned int c) |
97 | { |
98 | int diff_cur = abs(target - c); |
99 | int diff_new = abs(target - n); |
100 | |
101 | return (diff_new < diff_cur); |
102 | } |
103 | |
104 | static void s3c_cpufreq_show(const char *pfx, |
105 | struct s3c_cpufreq_config *cfg) |
106 | { |
107 | s3c_freq_dbg("%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)\n", |
108 | pfx, cfg->pll.frequency, cfg->freq.fclk, cfg->freq.armclk, |
109 | cfg->freq.hclk, cfg->divs.h_divisor, |
110 | cfg->freq.pclk, cfg->divs.p_divisor); |
111 | } |
112 | |
113 | /* functions to wrapper the driver info calls to do the cpu specific work */ |
114 | |
115 | static void s3c_cpufreq_setio(struct s3c_cpufreq_config *cfg) |
116 | { |
117 | if (cfg->info->set_iotiming) |
118 | (cfg->info->set_iotiming)(cfg, &s3c24xx_iotiming); |
119 | } |
120 | |
121 | static int s3c_cpufreq_calcio(struct s3c_cpufreq_config *cfg) |
122 | { |
123 | if (cfg->info->calc_iotiming) |
124 | return (cfg->info->calc_iotiming)(cfg, &s3c24xx_iotiming); |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static void s3c_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg) |
130 | { |
131 | (cfg->info->set_refresh)(cfg); |
132 | } |
133 | |
134 | static void s3c_cpufreq_setdivs(struct s3c_cpufreq_config *cfg) |
135 | { |
136 | (cfg->info->set_divs)(cfg); |
137 | } |
138 | |
139 | static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) |
140 | { |
141 | return (cfg->info->calc_divs)(cfg); |
142 | } |
143 | |
144 | static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg) |
145 | { |
146 | (cfg->info->set_fvco)(cfg); |
147 | } |
148 | |
149 | static inline void s3c_cpufreq_resume_clocks(void) |
150 | { |
151 | cpu_cur.info->resume_clocks(); |
152 | } |
153 | |
154 | static inline void s3c_cpufreq_updateclk(struct clk *clk, |
155 | unsigned int freq) |
156 | { |
157 | clk_set_rate(clk, freq); |
158 | } |
159 | |
160 | static int s3c_cpufreq_settarget(struct cpufreq_policy *policy, |
161 | unsigned int target_freq, |
162 | struct cpufreq_frequency_table *pll) |
163 | { |
164 | struct s3c_cpufreq_freqs freqs; |
165 | struct s3c_cpufreq_config cpu_new; |
166 | unsigned long flags; |
167 | |
168 | cpu_new = cpu_cur; /* copy new from current */ |
169 | |
170 | s3c_cpufreq_show("cur", &cpu_cur); |
171 | |
172 | /* TODO - check for DMA currently outstanding */ |
173 | |
174 | cpu_new.pll = pll ? *pll : cpu_cur.pll; |
175 | |
176 | if (pll) |
177 | freqs.pll_changing = 1; |
178 | |
179 | /* update our frequencies */ |
180 | |
181 | cpu_new.freq.armclk = target_freq; |
182 | cpu_new.freq.fclk = cpu_new.pll.frequency; |
183 | |
184 | if (s3c_cpufreq_calcdivs(&cpu_new) < 0) { |
185 | printk(KERN_ERR "no divisors for %d\n", target_freq); |
186 | goto err_notpossible; |
187 | } |
188 | |
189 | s3c_freq_dbg("%s: got divs\n", __func__); |
190 | |
191 | s3c_cpufreq_calc(&cpu_new); |
192 | |
193 | s3c_freq_dbg("%s: calculated frequencies for new\n", __func__); |
194 | |
195 | if (cpu_new.freq.hclk != cpu_cur.freq.hclk) { |
196 | if (s3c_cpufreq_calcio(&cpu_new) < 0) { |
197 | printk(KERN_ERR "%s: no IO timings\n", __func__); |
198 | goto err_notpossible; |
199 | } |
200 | } |
201 | |
202 | s3c_cpufreq_show("new", &cpu_new); |
203 | |
204 | /* setup our cpufreq parameters */ |
205 | |
206 | freqs.old = cpu_cur.freq; |
207 | freqs.new = cpu_new.freq; |
208 | |
209 | freqs.freqs.cpu = 0; |
210 | freqs.freqs.old = cpu_cur.freq.armclk / 1000; |
211 | freqs.freqs.new = cpu_new.freq.armclk / 1000; |
212 | |
213 | /* update f/h/p clock settings before we issue the change |
214 | * notification, so that drivers do not need to do anything |
215 | * special if they want to recalculate on CPUFREQ_PRECHANGE. */ |
216 | |
217 | s3c_cpufreq_updateclk(_clk_mpll, cpu_new.pll.frequency); |
218 | s3c_cpufreq_updateclk(clk_fclk, cpu_new.freq.fclk); |
219 | s3c_cpufreq_updateclk(clk_hclk, cpu_new.freq.hclk); |
220 | s3c_cpufreq_updateclk(clk_pclk, cpu_new.freq.pclk); |
221 | |
222 | /* start the frequency change */ |
223 | |
224 | if (policy) |
225 | cpufreq_notify_transition(&freqs.freqs, CPUFREQ_PRECHANGE); |
226 | |
227 | /* If hclk is staying the same, then we do not need to |
228 | * re-write the IO or the refresh timings whilst we are changing |
229 | * speed. */ |
230 | |
231 | local_irq_save(flags); |
232 | |
233 | /* is our memory clock slowing down? */ |
234 | if (cpu_new.freq.hclk < cpu_cur.freq.hclk) { |
235 | s3c_cpufreq_setrefresh(&cpu_new); |
236 | s3c_cpufreq_setio(&cpu_new); |
237 | } |
238 | |
239 | if (cpu_new.freq.fclk == cpu_cur.freq.fclk) { |
240 | /* not changing PLL, just set the divisors */ |
241 | |
242 | s3c_cpufreq_setdivs(&cpu_new); |
243 | } else { |
244 | if (cpu_new.freq.fclk < cpu_cur.freq.fclk) { |
245 | /* slow the cpu down, then set divisors */ |
246 | |
247 | s3c_cpufreq_setfvco(&cpu_new); |
248 | s3c_cpufreq_setdivs(&cpu_new); |
249 | } else { |
250 | /* set the divisors, then speed up */ |
251 | |
252 | s3c_cpufreq_setdivs(&cpu_new); |
253 | s3c_cpufreq_setfvco(&cpu_new); |
254 | } |
255 | } |
256 | |
257 | /* did our memory clock speed up */ |
258 | if (cpu_new.freq.hclk > cpu_cur.freq.hclk) { |
259 | s3c_cpufreq_setrefresh(&cpu_new); |
260 | s3c_cpufreq_setio(&cpu_new); |
261 | } |
262 | |
263 | /* update our current settings */ |
264 | cpu_cur = cpu_new; |
265 | |
266 | local_irq_restore(flags); |
267 | |
268 | /* notify everyone we've done this */ |
269 | if (policy) |
270 | cpufreq_notify_transition(&freqs.freqs, CPUFREQ_POSTCHANGE); |
271 | |
272 | s3c_freq_dbg("%s: finished\n", __func__); |
273 | return 0; |
274 | |
275 | err_notpossible: |
276 | printk(KERN_ERR "no compatible settings for %d\n", target_freq); |
277 | return -EINVAL; |
278 | } |
279 | |
280 | /* s3c_cpufreq_target |
281 | * |
282 | * called by the cpufreq core to adjust the frequency that the CPU |
283 | * is currently running at. |
284 | */ |
285 | |
286 | static int s3c_cpufreq_target(struct cpufreq_policy *policy, |
287 | unsigned int target_freq, |
288 | unsigned int relation) |
289 | { |
290 | struct cpufreq_frequency_table *pll; |
291 | unsigned int index; |
292 | |
293 | /* avoid repeated calls which cause a needless amout of duplicated |
294 | * logging output (and CPU time as the calculation process is |
295 | * done) */ |
296 | if (target_freq == last_target) |
297 | return 0; |
298 | |
299 | last_target = target_freq; |
300 | |
301 | s3c_freq_dbg("%s: policy %p, target %u, relation %u\n", |
302 | __func__, policy, target_freq, relation); |
303 | |
304 | if (ftab) { |
305 | if (cpufreq_frequency_table_target(policy, ftab, |
306 | target_freq, relation, |
307 | &index)) { |
308 | s3c_freq_dbg("%s: table failed\n", __func__); |
309 | return -EINVAL; |
310 | } |
311 | |
312 | s3c_freq_dbg("%s: adjust %d to entry %d (%u)\n", __func__, |
313 | target_freq, index, ftab[index].frequency); |
314 | target_freq = ftab[index].frequency; |
315 | } |
316 | |
317 | target_freq *= 1000; /* convert target to Hz */ |
318 | |
319 | /* find the settings for our new frequency */ |
320 | |
321 | if (!pll_reg || cpu_cur.lock_pll) { |
322 | /* either we've not got any PLL values, or we've locked |
323 | * to the current one. */ |
324 | pll = NULL; |
325 | } else { |
326 | struct cpufreq_policy tmp_policy; |
327 | int ret; |
328 | |
329 | /* we keep the cpu pll table in Hz, to ensure we get an |
330 | * accurate value for the PLL output. */ |
331 | |
332 | tmp_policy.min = policy->min * 1000; |
333 | tmp_policy.max = policy->max * 1000; |
334 | tmp_policy.cpu = policy->cpu; |
335 | |
336 | /* cpufreq_frequency_table_target uses a pointer to 'index' |
337 | * which is the number of the table entry, not the value of |
338 | * the table entry's index field. */ |
339 | |
340 | ret = cpufreq_frequency_table_target(&tmp_policy, pll_reg, |
341 | target_freq, relation, |
342 | &index); |
343 | |
344 | if (ret < 0) { |
345 | printk(KERN_ERR "%s: no PLL available\n", __func__); |
346 | goto err_notpossible; |
347 | } |
348 | |
349 | pll = pll_reg + index; |
350 | |
351 | s3c_freq_dbg("%s: target %u => %u\n", |
352 | __func__, target_freq, pll->frequency); |
353 | |
354 | target_freq = pll->frequency; |
355 | } |
356 | |
357 | return s3c_cpufreq_settarget(policy, target_freq, pll); |
358 | |
359 | err_notpossible: |
360 | printk(KERN_ERR "no compatible settings for %d\n", target_freq); |
361 | return -EINVAL; |
362 | } |
363 | |
364 | static unsigned int s3c_cpufreq_get(unsigned int cpu) |
365 | { |
366 | return clk_get_rate(clk_arm) / 1000; |
367 | } |
368 | |
369 | struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name) |
370 | { |
371 | struct clk *clk; |
372 | |
373 | clk = clk_get(dev, name); |
374 | if (IS_ERR(clk)) |
375 | printk(KERN_ERR "cpufreq: failed to get clock '%s'\n", name); |
376 | |
377 | return clk; |
378 | } |
379 | |
380 | static int s3c_cpufreq_init(struct cpufreq_policy *policy) |
381 | { |
382 | printk(KERN_INFO "%s: initialising policy %p\n", __func__, policy); |
383 | |
384 | if (policy->cpu != 0) |
385 | return -EINVAL; |
386 | |
387 | policy->cur = s3c_cpufreq_get(0); |
388 | policy->min = policy->cpuinfo.min_freq = 0; |
389 | policy->max = policy->cpuinfo.max_freq = cpu_cur.info->max.fclk / 1000; |
390 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; |
391 | |
392 | /* feed the latency information from the cpu driver */ |
393 | policy->cpuinfo.transition_latency = cpu_cur.info->latency; |
394 | |
395 | if (ftab) |
396 | cpufreq_frequency_table_cpuinfo(policy, ftab); |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | static __init int s3c_cpufreq_initclks(void) |
402 | { |
403 | _clk_mpll = s3c_cpufreq_clk_get(NULL, "mpll"); |
404 | _clk_xtal = s3c_cpufreq_clk_get(NULL, "xtal"); |
405 | clk_fclk = s3c_cpufreq_clk_get(NULL, "fclk"); |
406 | clk_hclk = s3c_cpufreq_clk_get(NULL, "hclk"); |
407 | clk_pclk = s3c_cpufreq_clk_get(NULL, "pclk"); |
408 | clk_arm = s3c_cpufreq_clk_get(NULL, "armclk"); |
409 | |
410 | if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) || |
411 | IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) { |
412 | printk(KERN_ERR "%s: could not get clock(s)\n", __func__); |
413 | return -ENOENT; |
414 | } |
415 | |
416 | printk(KERN_INFO "%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", __func__, |
417 | clk_get_rate(clk_fclk) / 1000, |
418 | clk_get_rate(clk_hclk) / 1000, |
419 | clk_get_rate(clk_pclk) / 1000, |
420 | clk_get_rate(clk_arm) / 1000); |
421 | |
422 | return 0; |
423 | } |
424 | |
425 | static int s3c_cpufreq_verify(struct cpufreq_policy *policy) |
426 | { |
427 | if (policy->cpu != 0) |
428 | return -EINVAL; |
429 | |
430 | return 0; |
431 | } |
432 | |
433 | #ifdef CONFIG_PM |
434 | static struct cpufreq_frequency_table suspend_pll; |
435 | static unsigned int suspend_freq; |
436 | |
437 | static int s3c_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg) |
438 | { |
439 | suspend_pll.frequency = clk_get_rate(_clk_mpll); |
440 | suspend_pll.index = __raw_readl(S3C2410_MPLLCON); |
441 | suspend_freq = s3c_cpufreq_get(0) * 1000; |
442 | |
443 | return 0; |
444 | } |
445 | |
446 | static int s3c_cpufreq_resume(struct cpufreq_policy *policy) |
447 | { |
448 | int ret; |
449 | |
450 | s3c_freq_dbg("%s: resuming with policy %p\n", __func__, policy); |
451 | |
452 | last_target = ~0; /* invalidate last_target setting */ |
453 | |
454 | /* first, find out what speed we resumed at. */ |
455 | s3c_cpufreq_resume_clocks(); |
456 | |
457 | /* whilst we will be called later on, we try and re-set the |
458 | * cpu frequencies as soon as possible so that we do not end |
459 | * up resuming devices and then immediatley having to re-set |
460 | * a number of settings once these devices have restarted. |
461 | * |
462 | * as a note, it is expected devices are not used until they |
463 | * have been un-suspended and at that time they should have |
464 | * used the updated clock settings. |
465 | */ |
466 | |
467 | ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll); |
468 | if (ret) { |
469 | printk(KERN_ERR "%s: failed to reset pll/freq\n", __func__); |
470 | return ret; |
471 | } |
472 | |
473 | return 0; |
474 | } |
475 | #else |
476 | #define s3c_cpufreq_resume NULL |
477 | #define s3c_cpufreq_suspend NULL |
478 | #endif |
479 | |
480 | static struct cpufreq_driver s3c24xx_driver = { |
481 | .flags = CPUFREQ_STICKY, |
482 | .verify = s3c_cpufreq_verify, |
483 | .target = s3c_cpufreq_target, |
484 | .get = s3c_cpufreq_get, |
485 | .init = s3c_cpufreq_init, |
486 | .suspend = s3c_cpufreq_suspend, |
487 | .resume = s3c_cpufreq_resume, |
488 | .name = "s3c24xx", |
489 | }; |
490 | |
491 | |
492 | int __init s3c_cpufreq_register(struct s3c_cpufreq_info *info) |
493 | { |
494 | if (!info || !info->name) { |
495 | printk(KERN_ERR "%s: failed to pass valid information\n", |
496 | __func__); |
497 | return -EINVAL; |
498 | } |
499 | |
500 | printk(KERN_INFO "S3C24XX CPU Frequency driver, %s cpu support\n", |
501 | info->name); |
502 | |
503 | /* check our driver info has valid data */ |
504 | |
505 | BUG_ON(info->set_refresh == NULL); |
506 | BUG_ON(info->set_divs == NULL); |
507 | BUG_ON(info->calc_divs == NULL); |
508 | |
509 | /* info->set_fvco is optional, depending on whether there |
510 | * is a need to set the clock code. */ |
511 | |
512 | cpu_cur.info = info; |
513 | |
514 | /* Note, driver registering should probably update locktime */ |
515 | |
516 | return 0; |
517 | } |
518 | |
519 | int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) |
520 | { |
521 | struct s3c_cpufreq_board *ours; |
522 | |
523 | if (!board) { |
524 | printk(KERN_INFO "%s: no board data\n", __func__); |
525 | return -EINVAL; |
526 | } |
527 | |
528 | /* Copy the board information so that each board can make this |
529 | * initdata. */ |
530 | |
531 | ours = kzalloc(sizeof(struct s3c_cpufreq_board), GFP_KERNEL); |
532 | if (ours == NULL) { |
533 | printk(KERN_ERR "%s: no memory\n", __func__); |
534 | return -ENOMEM; |
535 | } |
536 | |
537 | *ours = *board; |
538 | cpu_cur.board = ours; |
539 | |
540 | return 0; |
541 | } |
542 | |
543 | int __init s3c_cpufreq_auto_io(void) |
544 | { |
545 | int ret; |
546 | |
547 | if (!cpu_cur.info->get_iotiming) { |
548 | printk(KERN_ERR "%s: get_iotiming undefined\n", __func__); |
549 | return -ENOENT; |
550 | } |
551 | |
552 | printk(KERN_INFO "%s: working out IO settings\n", __func__); |
553 | |
554 | ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming); |
555 | if (ret) |
556 | printk(KERN_ERR "%s: failed to get timings\n", __func__); |
557 | |
558 | return ret; |
559 | } |
560 | |
561 | /* if one or is zero, then return the other, otherwise return the min */ |
562 | #define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b)) |
563 | |
564 | /** |
565 | * s3c_cpufreq_freq_min - find the minimum settings for the given freq. |
566 | * @dst: The destination structure |
567 | * @a: One argument. |
568 | * @b: The other argument. |
569 | * |
570 | * Create a minimum of each frequency entry in the 'struct s3c_freq', |
571 | * unless the entry is zero when it is ignored and the non-zero argument |
572 | * used. |
573 | */ |
574 | static void s3c_cpufreq_freq_min(struct s3c_freq *dst, |
575 | struct s3c_freq *a, struct s3c_freq *b) |
576 | { |
577 | dst->fclk = do_min(a->fclk, b->fclk); |
578 | dst->hclk = do_min(a->hclk, b->hclk); |
579 | dst->pclk = do_min(a->pclk, b->pclk); |
580 | dst->armclk = do_min(a->armclk, b->armclk); |
581 | } |
582 | |
583 | static inline u32 calc_locktime(u32 freq, u32 time_us) |
584 | { |
585 | u32 result; |
586 | |
587 | result = freq * time_us; |
588 | result = DIV_ROUND_UP(result, 1000 * 1000); |
589 | |
590 | return result; |
591 | } |
592 | |
593 | static void s3c_cpufreq_update_loctkime(void) |
594 | { |
595 | unsigned int bits = cpu_cur.info->locktime_bits; |
596 | u32 rate = (u32)clk_get_rate(_clk_xtal); |
597 | u32 val; |
598 | |
599 | if (bits == 0) { |
600 | WARN_ON(1); |
601 | return; |
602 | } |
603 | |
604 | val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits; |
605 | val |= calc_locktime(rate, cpu_cur.info->locktime_m); |
606 | |
607 | printk(KERN_INFO "%s: new locktime is 0x%08x\n", __func__, val); |
608 | __raw_writel(val, S3C2410_LOCKTIME); |
609 | } |
610 | |
611 | static int s3c_cpufreq_build_freq(void) |
612 | { |
613 | int size, ret; |
614 | |
615 | if (!cpu_cur.info->calc_freqtable) |
616 | return -EINVAL; |
617 | |
618 | kfree(ftab); |
619 | ftab = NULL; |
620 | |
621 | size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0); |
622 | size++; |
623 | |
624 | ftab = kmalloc(sizeof(struct cpufreq_frequency_table) * size, GFP_KERNEL); |
625 | if (!ftab) { |
626 | printk(KERN_ERR "%s: no memory for tables\n", __func__); |
627 | return -ENOMEM; |
628 | } |
629 | |
630 | ftab_size = size; |
631 | |
632 | ret = cpu_cur.info->calc_freqtable(&cpu_cur, ftab, size); |
633 | s3c_cpufreq_addfreq(ftab, ret, size, CPUFREQ_TABLE_END); |
634 | |
635 | return 0; |
636 | } |
637 | |
638 | static int __init s3c_cpufreq_initcall(void) |
639 | { |
640 | int ret = 0; |
641 | |
642 | if (cpu_cur.info && cpu_cur.board) { |
643 | ret = s3c_cpufreq_initclks(); |
644 | if (ret) |
645 | goto out; |
646 | |
647 | /* get current settings */ |
648 | s3c_cpufreq_getcur(&cpu_cur); |
649 | s3c_cpufreq_show("cur", &cpu_cur); |
650 | |
651 | if (cpu_cur.board->auto_io) { |
652 | ret = s3c_cpufreq_auto_io(); |
653 | if (ret) { |
654 | printk(KERN_ERR "%s: failed to get io timing\n", |
655 | __func__); |
656 | goto out; |
657 | } |
658 | } |
659 | |
660 | if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) { |
661 | printk(KERN_ERR "%s: no IO support registered\n", |
662 | __func__); |
663 | ret = -EINVAL; |
664 | goto out; |
665 | } |
666 | |
667 | if (!cpu_cur.info->need_pll) |
668 | cpu_cur.lock_pll = 1; |
669 | |
670 | s3c_cpufreq_update_loctkime(); |
671 | |
672 | s3c_cpufreq_freq_min(&cpu_cur.max, &cpu_cur.board->max, |
673 | &cpu_cur.info->max); |
674 | |
675 | if (cpu_cur.info->calc_freqtable) |
676 | s3c_cpufreq_build_freq(); |
677 | |
678 | ret = cpufreq_register_driver(&s3c24xx_driver); |
679 | } |
680 | |
681 | out: |
682 | return ret; |
683 | } |
684 | |
685 | late_initcall(s3c_cpufreq_initcall); |
686 | |
687 | /** |
688 | * s3c_plltab_register - register CPU PLL table. |
689 | * @plls: The list of PLL entries. |
690 | * @plls_no: The size of the PLL entries @plls. |
691 | * |
692 | * Register the given set of PLLs with the system. |
693 | */ |
694 | int __init s3c_plltab_register(struct cpufreq_frequency_table *plls, |
695 | unsigned int plls_no) |
696 | { |
697 | struct cpufreq_frequency_table *vals; |
698 | unsigned int size; |
699 | |
700 | size = sizeof(struct cpufreq_frequency_table) * (plls_no + 1); |
701 | |
702 | vals = kmalloc(size, GFP_KERNEL); |
703 | if (vals) { |
704 | memcpy(vals, plls, size); |
705 | pll_reg = vals; |
706 | |
707 | /* write a terminating entry, we don't store it in the |
708 | * table that is stored in the kernel */ |
709 | vals += plls_no; |
710 | vals->frequency = CPUFREQ_TABLE_END; |
711 | |
712 | printk(KERN_INFO "cpufreq: %d PLL entries\n", plls_no); |
713 | } else |
714 | printk(KERN_ERR "cpufreq: no memory for PLL tables\n"); |
715 | |
716 | return vals ? 0 : -ENOMEM; |
717 | } |
718 |
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