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 | |
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>
-rw-r--r-- | arch/arm/mach-omap2/clock24xx.c | 42 | ||||
-rw-r--r-- | arch/arm/plat-omap/cpu-omap.c | 57 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/mach/clock.h | 3 |
3 files changed, 98 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/clock24xx.c b/arch/arm/mach-omap2/clock24xx.c index 421728a7f903..b9902666e4b7 100644 --- a/arch/arm/mach-omap2/clock24xx.c +++ b/arch/arm/mach-omap2/clock24xx.c | |||
@@ -574,6 +574,45 @@ static int omap2_select_table_rate(struct clk *clk, unsigned long rate) | |||
574 | return 0; | 574 | return 0; |
575 | } | 575 | } |
576 | 576 | ||
577 | #ifdef CONFIG_CPU_FREQ | ||
578 | /* | ||
579 | * Walk PRCM rate table and fillout cpufreq freq_table | ||
580 | */ | ||
581 | static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(rate_table)]; | ||
582 | |||
583 | void omap2_clk_init_cpufreq_table(struct cpufreq_frequency_table **table) | ||
584 | { | ||
585 | struct prcm_config *prcm; | ||
586 | int i = 0; | ||
587 | |||
588 | for (prcm = rate_table; prcm->mpu_speed; prcm++) { | ||
589 | if (!(prcm->flags & cpu_mask)) | ||
590 | continue; | ||
591 | if (prcm->xtal_speed != sys_ck.rate) | ||
592 | continue; | ||
593 | |||
594 | /* don't put bypass rates in table */ | ||
595 | if (prcm->dpll_speed == prcm->xtal_speed) | ||
596 | continue; | ||
597 | |||
598 | freq_table[i].index = i; | ||
599 | freq_table[i].frequency = prcm->mpu_speed / 1000; | ||
600 | i++; | ||
601 | } | ||
602 | |||
603 | if (i == 0) { | ||
604 | printk(KERN_WARNING "%s: failed to initialize frequency " | ||
605 | "table\n", __func__); | ||
606 | return; | ||
607 | } | ||
608 | |||
609 | freq_table[i].index = i; | ||
610 | freq_table[i].frequency = CPUFREQ_TABLE_END; | ||
611 | |||
612 | *table = &freq_table[0]; | ||
613 | } | ||
614 | #endif | ||
615 | |||
577 | static struct clk_functions omap2_clk_functions = { | 616 | static struct clk_functions omap2_clk_functions = { |
578 | .clk_enable = omap2_clk_enable, | 617 | .clk_enable = omap2_clk_enable, |
579 | .clk_disable = omap2_clk_disable, | 618 | .clk_disable = omap2_clk_disable, |
@@ -581,6 +620,9 @@ static struct clk_functions omap2_clk_functions = { | |||
581 | .clk_set_rate = omap2_clk_set_rate, | 620 | .clk_set_rate = omap2_clk_set_rate, |
582 | .clk_set_parent = omap2_clk_set_parent, | 621 | .clk_set_parent = omap2_clk_set_parent, |
583 | .clk_disable_unused = omap2_clk_disable_unused, | 622 | .clk_disable_unused = omap2_clk_disable_unused, |
623 | #ifdef CONFIG_CPU_FREQ | ||
624 | .clk_init_cpufreq_table = omap2_clk_init_cpufreq_table, | ||
625 | #endif | ||
584 | }; | 626 | }; |
585 | 627 | ||
586 | static u32 omap2_get_apll_clkin(void) | 628 | static u32 omap2_get_apll_clkin(void) |
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 | ||