diff options
author | Kevin Hilman <khilman@mvista.com> | 2009-01-27 21:13:38 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-02-08 12:50:28 -0500 |
commit | aeec299011da8c3f07a47fe5d988f0eafda53906 (patch) | |
tree | 8661476adf90325972e5feab723f1272ab04fc6b /arch/arm/plat-omap | |
parent | ae8578c0194695bd37435249dfed720769a6bbf3 (diff) |
[ARM] OMAP2: Implement CPUfreq frequency table based on PRCM table
This patch adds a CPUfreq frequency-table implementation for OMAP2 by
walking the PRCM rate-table for available entries and adding them to a
CPUfreq table.
CPUfreq can then be used to manage switching between all the available
entries in the PRCM rate table. Either use the CPUfreq sysfs
interface directly, (see Section 3 of Documentation/cpu-freq/user-guide.txt)
or use the cpufrequtils package:
http://www.kernel.org/pub/linux/utils/kernel/cpufreq/cpufrequtils.html
Signed-off-by: Kevin Hilman <khilman@mvista.com>
Updated to try to use cpufreq_table if it exists.
linux-omap source commit is 77ce544fa48deb7a2003f454624e3ca10d37ab87.
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/plat-omap')
-rw-r--r-- | arch/arm/plat-omap/cpu-omap.c | 57 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/mach/clock.h | 3 |
2 files changed, 56 insertions, 4 deletions
diff --git a/arch/arm/plat-omap/cpu-omap.c b/arch/arm/plat-omap/cpu-omap.c index b2690242a390..843e8af64066 100644 --- a/arch/arm/plat-omap/cpu-omap.c +++ b/arch/arm/plat-omap/cpu-omap.c | |||
@@ -23,10 +23,13 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | 24 | ||
25 | #include <mach/hardware.h> | 25 | #include <mach/hardware.h> |
26 | #include <mach/clock.h> | ||
26 | #include <asm/system.h> | 27 | #include <asm/system.h> |
27 | 28 | ||
28 | #define VERY_HI_RATE 900000000 | 29 | #define VERY_HI_RATE 900000000 |
29 | 30 | ||
31 | static struct cpufreq_frequency_table *freq_table; | ||
32 | |||
30 | #ifdef CONFIG_ARCH_OMAP1 | 33 | #ifdef CONFIG_ARCH_OMAP1 |
31 | #define MPU_CLK "mpu" | 34 | #define MPU_CLK "mpu" |
32 | #else | 35 | #else |
@@ -39,6 +42,9 @@ static struct clk *mpu_clk; | |||
39 | 42 | ||
40 | int omap_verify_speed(struct cpufreq_policy *policy) | 43 | int omap_verify_speed(struct cpufreq_policy *policy) |
41 | { | 44 | { |
45 | if (freq_table) | ||
46 | return cpufreq_frequency_table_verify(policy, freq_table); | ||
47 | |||
42 | if (policy->cpu) | 48 | if (policy->cpu) |
43 | return -EINVAL; | 49 | return -EINVAL; |
44 | 50 | ||
@@ -70,12 +76,26 @@ static int omap_target(struct cpufreq_policy *policy, | |||
70 | struct cpufreq_freqs freqs; | 76 | struct cpufreq_freqs freqs; |
71 | int ret = 0; | 77 | int ret = 0; |
72 | 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 | |||
73 | freqs.old = omap_getspeed(0); | 86 | freqs.old = omap_getspeed(0); |
74 | freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; | 87 | freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; |
75 | freqs.cpu = 0; | 88 | freqs.cpu = 0; |
76 | 89 | ||
90 | if (freqs.old == freqs.new) | ||
91 | return ret; | ||
92 | |||
77 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | 93 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); |
78 | ret = clk_set_rate(mpu_clk, target_freq * 1000); | 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); | ||
79 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | 99 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
80 | 100 | ||
81 | return ret; | 101 | return ret; |
@@ -83,16 +103,31 @@ static int omap_target(struct cpufreq_policy *policy, | |||
83 | 103 | ||
84 | static int __init omap_cpu_init(struct cpufreq_policy *policy) | 104 | static int __init omap_cpu_init(struct cpufreq_policy *policy) |
85 | { | 105 | { |
106 | int result = 0; | ||
107 | |||
86 | mpu_clk = clk_get(NULL, MPU_CLK); | 108 | mpu_clk = clk_get(NULL, MPU_CLK); |
87 | if (IS_ERR(mpu_clk)) | 109 | if (IS_ERR(mpu_clk)) |
88 | return PTR_ERR(mpu_clk); | 110 | return PTR_ERR(mpu_clk); |
89 | 111 | ||
90 | if (policy->cpu != 0) | 112 | if (policy->cpu != 0) |
91 | return -EINVAL; | 113 | return -EINVAL; |
114 | |||
92 | policy->cur = policy->min = policy->max = omap_getspeed(0); | 115 | policy->cur = policy->min = policy->max = omap_getspeed(0); |
93 | policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; | 116 | |
94 | policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, VERY_HI_RATE) / 1000; | 117 | clk_init_cpufreq_table(&freq_table); |
95 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | 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; | ||
96 | 131 | ||
97 | return 0; | 132 | return 0; |
98 | } | 133 | } |
@@ -103,6 +138,11 @@ static int omap_cpu_exit(struct cpufreq_policy *policy) | |||
103 | return 0; | 138 | return 0; |
104 | } | 139 | } |
105 | 140 | ||
141 | static struct freq_attr *omap_cpufreq_attr[] = { | ||
142 | &cpufreq_freq_attr_scaling_available_freqs, | ||
143 | NULL, | ||
144 | }; | ||
145 | |||
106 | static struct cpufreq_driver omap_driver = { | 146 | static struct cpufreq_driver omap_driver = { |
107 | .flags = CPUFREQ_STICKY, | 147 | .flags = CPUFREQ_STICKY, |
108 | .verify = omap_verify_speed, | 148 | .verify = omap_verify_speed, |
@@ -111,6 +151,7 @@ static struct cpufreq_driver omap_driver = { | |||
111 | .init = omap_cpu_init, | 151 | .init = omap_cpu_init, |
112 | .exit = omap_cpu_exit, | 152 | .exit = omap_cpu_exit, |
113 | .name = "omap", | 153 | .name = "omap", |
154 | .attr = omap_cpufreq_attr, | ||
114 | }; | 155 | }; |
115 | 156 | ||
116 | static int __init omap_cpufreq_init(void) | 157 | static int __init omap_cpufreq_init(void) |
@@ -119,3 +160,11 @@ static int __init omap_cpufreq_init(void) | |||
119 | } | 160 | } |
120 | 161 | ||
121 | arch_initcall(omap_cpufreq_init); | 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 | |||
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h index 6f49a3332890..681c65105e25 100644 --- a/arch/arm/plat-omap/include/mach/clock.h +++ b/arch/arm/plat-omap/include/mach/clock.h | |||
@@ -121,6 +121,9 @@ extern void recalculate_root_clocks(void); | |||
121 | extern void followparent_recalc(struct clk *clk); | 121 | extern void followparent_recalc(struct clk *clk); |
122 | extern int clk_get_usecount(struct clk *clk); | 122 | extern int clk_get_usecount(struct clk *clk); |
123 | extern void clk_enable_init_clocks(void); | 123 | extern void clk_enable_init_clocks(void); |
124 | #ifdef CONFIG_CPU_FREQ | ||
125 | extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table); | ||
126 | #endif | ||
124 | 127 | ||
125 | extern const struct clkops clkops_null; | 128 | extern const struct clkops clkops_null; |
126 | 129 | ||