Merge branch 'perfcounters-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6] / arch / arm / plat-omap / cpu-omap.c
1 /*
2  *  linux/arch/arm/plat-omap/cpu-omap.c
3  *
4  *  CPU frequency scaling for OMAP
5  *
6  *  Copyright (C) 2005 Nokia Corporation
7  *  Written by Tony Lindgren <tony@atomide.com>
8  *
9  *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/sched.h>
18 #include <linux/cpufreq.h>
19 #include <linux/delay.h>
20 #include <linux/init.h>
21 #include <linux/err.h>
22 #include <linux/clk.h>
23 #include <linux/io.h>
24
25 #include <mach/hardware.h>
26 #include <mach/clock.h>
27 #include <asm/system.h>
28
29 #define VERY_HI_RATE    900000000
30
31 static struct cpufreq_frequency_table *freq_table;
32
33 #ifdef CONFIG_ARCH_OMAP1
34 #define MPU_CLK         "mpu"
35 #else
36 #define MPU_CLK         "virt_prcm_set"
37 #endif
38
39 static struct clk *mpu_clk;
40
41 /* TODO: Add support for SDRAM timing changes */
42
43 int omap_verify_speed(struct cpufreq_policy *policy)
44 {
45         if (freq_table)
46                 return cpufreq_frequency_table_verify(policy, freq_table);
47
48         if (policy->cpu)
49                 return -EINVAL;
50
51         cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
52                                      policy->cpuinfo.max_freq);
53
54         policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000;
55         policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000;
56         cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
57                                      policy->cpuinfo.max_freq);
58         return 0;
59 }
60
61 unsigned int omap_getspeed(unsigned int cpu)
62 {
63         unsigned long rate;
64
65         if (cpu)
66                 return 0;
67
68         rate = clk_get_rate(mpu_clk) / 1000;
69         return rate;
70 }
71
72 static int omap_target(struct cpufreq_policy *policy,
73                        unsigned int target_freq,
74                        unsigned int relation)
75 {
76         struct cpufreq_freqs freqs;
77         int ret = 0;
78
79         /* Ensure desired rate is within allowed range.  Some govenors
80          * (ondemand) will just pass target_freq=0 to get the minimum. */
81         if (target_freq < policy->cpuinfo.min_freq)
82                 target_freq = policy->cpuinfo.min_freq;
83         if (target_freq > policy->cpuinfo.max_freq)
84                 target_freq = policy->cpuinfo.max_freq;
85
86         freqs.old = omap_getspeed(0);
87         freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
88         freqs.cpu = 0;
89
90         if (freqs.old == freqs.new)
91                 return ret;
92
93         cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
94 #ifdef CONFIG_CPU_FREQ_DEBUG
95         printk(KERN_DEBUG "cpufreq-omap: transition: %u --> %u\n",
96                freqs.old, freqs.new);
97 #endif
98         ret = clk_set_rate(mpu_clk, freqs.new * 1000);
99         cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
100
101         return ret;
102 }
103
104 static int __init omap_cpu_init(struct cpufreq_policy *policy)
105 {
106         int result = 0;
107
108         mpu_clk = clk_get(NULL, MPU_CLK);
109         if (IS_ERR(mpu_clk))
110                 return PTR_ERR(mpu_clk);
111
112         if (policy->cpu != 0)
113                 return -EINVAL;
114
115         policy->cur = policy->min = policy->max = omap_getspeed(0);
116
117         clk_init_cpufreq_table(&freq_table);
118         if (freq_table) {
119                 result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
120                 if (!result)
121                         cpufreq_frequency_table_get_attr(freq_table,
122                                                         policy->cpu);
123         } else {
124                 policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000;
125                 policy->cpuinfo.max_freq = clk_round_rate(mpu_clk,
126                                                         VERY_HI_RATE) / 1000;
127         }
128
129         /* FIXME: what's the actual transition time? */
130         policy->cpuinfo.transition_latency = 10 * 1000 * 1000;
131
132         return 0;
133 }
134
135 static int omap_cpu_exit(struct cpufreq_policy *policy)
136 {
137         clk_put(mpu_clk);
138         return 0;
139 }
140
141 static struct freq_attr *omap_cpufreq_attr[] = {
142         &cpufreq_freq_attr_scaling_available_freqs,
143         NULL,
144 };
145
146 static struct cpufreq_driver omap_driver = {
147         .flags          = CPUFREQ_STICKY,
148         .verify         = omap_verify_speed,
149         .target         = omap_target,
150         .get            = omap_getspeed,
151         .init           = omap_cpu_init,
152         .exit           = omap_cpu_exit,
153         .name           = "omap",
154         .attr           = omap_cpufreq_attr,
155 };
156
157 static int __init omap_cpufreq_init(void)
158 {
159         return cpufreq_register_driver(&omap_driver);
160 }
161
162 arch_initcall(omap_cpufreq_init);
163
164 /*
165  * if ever we want to remove this, upon cleanup call:
166  *
167  * cpufreq_unregister_driver()
168  * cpufreq_frequency_table_put_attr()
169  */
170