diff options
-rw-r--r-- | arch/avr32/Kconfig | 21 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/Makefile | 1 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap7000.c | 43 | ||||
-rw-r--r-- | arch/avr32/mach-at32ap/cpufreq.c | 112 |
4 files changed, 175 insertions, 2 deletions
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 3ec76586877e..8f37a14e6378 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig | |||
@@ -185,6 +185,27 @@ config CMDLINE | |||
185 | 185 | ||
186 | endmenu | 186 | endmenu |
187 | 187 | ||
188 | menu "Power managment options" | ||
189 | |||
190 | menu "CPU Frequency scaling" | ||
191 | |||
192 | source "drivers/cpufreq/Kconfig" | ||
193 | |||
194 | config CPU_FREQ_AT32AP | ||
195 | bool "CPU frequency driver for AT32AP" | ||
196 | depends on CPU_FREQ && PLATFORM_AT32AP | ||
197 | default n | ||
198 | help | ||
199 | This enables the CPU frequency driver for AT32AP processors. | ||
200 | |||
201 | For details, take a look in <file:Documentation/cpu-freq>. | ||
202 | |||
203 | If in doubt, say N. | ||
204 | |||
205 | endmenu | ||
206 | |||
207 | endmenu | ||
208 | |||
188 | menu "Bus options" | 209 | menu "Bus options" |
189 | 210 | ||
190 | config PCI | 211 | config PCI |
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile index f1d395724ac6..a8b445046e3e 100644 --- a/arch/avr32/mach-at32ap/Makefile +++ b/arch/avr32/mach-at32ap/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o | 1 | obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o |
2 | obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o | 2 | obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o |
3 | obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o | 3 | obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o |
4 | obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o | ||
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c index 5faa97e5ab16..c74f3715f3f1 100644 --- a/arch/avr32/mach-at32ap/at32ap7000.c +++ b/arch/avr32/mach-at32ap/at32ap7000.c | |||
@@ -219,6 +219,41 @@ static unsigned long cpu_clk_get_rate(struct clk *clk) | |||
219 | return bus_clk_get_rate(clk, shift); | 219 | return bus_clk_get_rate(clk, shift); |
220 | } | 220 | } |
221 | 221 | ||
222 | static long cpu_clk_set_rate(struct clk *clk, unsigned long rate, int apply) | ||
223 | { | ||
224 | u32 control; | ||
225 | unsigned long parent_rate, child_div, actual_rate, div; | ||
226 | |||
227 | parent_rate = clk->parent->get_rate(clk->parent); | ||
228 | control = pm_readl(CKSEL); | ||
229 | |||
230 | if (control & PM_BIT(HSBDIV)) | ||
231 | child_div = 1 << (PM_BFEXT(HSBSEL, control) + 1); | ||
232 | else | ||
233 | child_div = 1; | ||
234 | |||
235 | if (rate > 3 * (parent_rate / 4) || child_div == 1) { | ||
236 | actual_rate = parent_rate; | ||
237 | control &= ~PM_BIT(CPUDIV); | ||
238 | } else { | ||
239 | unsigned int cpusel; | ||
240 | div = (parent_rate + rate / 2) / rate; | ||
241 | if (div > child_div) | ||
242 | div = child_div; | ||
243 | cpusel = (div > 1) ? (fls(div) - 2) : 0; | ||
244 | control = PM_BIT(CPUDIV) | PM_BFINS(CPUSEL, cpusel, control); | ||
245 | actual_rate = parent_rate / (1 << (cpusel + 1)); | ||
246 | } | ||
247 | |||
248 | pr_debug("clk %s: new rate %lu (actual rate %lu)\n", | ||
249 | clk->name, rate, actual_rate); | ||
250 | |||
251 | if (apply) | ||
252 | pm_writel(CKSEL, control); | ||
253 | |||
254 | return actual_rate; | ||
255 | } | ||
256 | |||
222 | static void hsb_clk_mode(struct clk *clk, int enabled) | 257 | static void hsb_clk_mode(struct clk *clk, int enabled) |
223 | { | 258 | { |
224 | unsigned long flags; | 259 | unsigned long flags; |
@@ -300,6 +335,7 @@ static unsigned long pbb_clk_get_rate(struct clk *clk) | |||
300 | static struct clk cpu_clk = { | 335 | static struct clk cpu_clk = { |
301 | .name = "cpu", | 336 | .name = "cpu", |
302 | .get_rate = cpu_clk_get_rate, | 337 | .get_rate = cpu_clk_get_rate, |
338 | .set_rate = cpu_clk_set_rate, | ||
303 | .users = 1, | 339 | .users = 1, |
304 | }; | 340 | }; |
305 | static struct clk hsb_clk = { | 341 | static struct clk hsb_clk = { |
@@ -1152,10 +1188,13 @@ void __init at32_clock_init(void) | |||
1152 | u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0; | 1188 | u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0; |
1153 | int i; | 1189 | int i; |
1154 | 1190 | ||
1155 | if (pm_readl(MCCTRL) & PM_BIT(PLLSEL)) | 1191 | if (pm_readl(MCCTRL) & PM_BIT(PLLSEL)) { |
1156 | main_clock = &pll0; | 1192 | main_clock = &pll0; |
1157 | else | 1193 | cpu_clk.parent = &pll0; |
1194 | } else { | ||
1158 | main_clock = &osc0; | 1195 | main_clock = &osc0; |
1196 | cpu_clk.parent = &osc0; | ||
1197 | } | ||
1159 | 1198 | ||
1160 | if (pm_readl(PLL0) & PM_BIT(PLLOSC)) | 1199 | if (pm_readl(PLL0) & PM_BIT(PLLOSC)) |
1161 | pll0.parent = &osc1; | 1200 | pll0.parent = &osc1; |
diff --git a/arch/avr32/mach-at32ap/cpufreq.c b/arch/avr32/mach-at32ap/cpufreq.c new file mode 100644 index 000000000000..235524b79193 --- /dev/null +++ b/arch/avr32/mach-at32ap/cpufreq.c | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004-2007 Atmel Corporation | ||
3 | * | ||
4 | * Based on MIPS implementation arch/mips/kernel/time.c | ||
5 | * Copyright 2001 MontaVista Software Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | /*#define DEBUG*/ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/cpufreq.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <asm/system.h> | ||
22 | |||
23 | static struct clk *cpuclk; | ||
24 | |||
25 | static int at32_verify_speed(struct cpufreq_policy *policy) | ||
26 | { | ||
27 | if (policy->cpu != 0) | ||
28 | return -EINVAL; | ||
29 | |||
30 | cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, | ||
31 | policy->cpuinfo.max_freq); | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static unsigned int at32_get_speed(unsigned int cpu) | ||
36 | { | ||
37 | /* No SMP support */ | ||
38 | if (cpu) | ||
39 | return 0; | ||
40 | return (unsigned int)((clk_get_rate(cpuclk) + 500) / 1000); | ||
41 | } | ||
42 | |||
43 | static int at32_set_target(struct cpufreq_policy *policy, | ||
44 | unsigned int target_freq, | ||
45 | unsigned int relation) | ||
46 | { | ||
47 | struct cpufreq_freqs freqs; | ||
48 | long freq; | ||
49 | |||
50 | /* Convert target_freq from kHz to Hz */ | ||
51 | freq = clk_round_rate(cpuclk, target_freq * 1000); | ||
52 | |||
53 | /* Check if policy->min <= new_freq <= policy->max */ | ||
54 | if(freq < (policy->min * 1000) || freq > (policy->max * 1000)) | ||
55 | return -EINVAL; | ||
56 | |||
57 | pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000); | ||
58 | |||
59 | freqs.old = at32_get_speed(0); | ||
60 | freqs.new = (freq + 500) / 1000; | ||
61 | freqs.cpu = 0; | ||
62 | freqs.flags = 0; | ||
63 | |||
64 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
65 | clk_set_rate(cpuclk, freq); | ||
66 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
67 | |||
68 | pr_debug("cpufreq: set frequency %lu Hz\n", freq); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int __init at32_cpufreq_driver_init(struct cpufreq_policy *policy) | ||
74 | { | ||
75 | if (policy->cpu != 0) | ||
76 | return -EINVAL; | ||
77 | |||
78 | cpuclk = clk_get(NULL, "cpu"); | ||
79 | if (IS_ERR(cpuclk)) { | ||
80 | pr_debug("cpufreq: could not get CPU clk\n"); | ||
81 | return PTR_ERR(cpuclk); | ||
82 | } | ||
83 | |||
84 | policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000; | ||
85 | policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; | ||
86 | policy->cpuinfo.transition_latency = 0; | ||
87 | policy->cur = at32_get_speed(0); | ||
88 | policy->min = policy->cpuinfo.min_freq; | ||
89 | policy->max = policy->cpuinfo.max_freq; | ||
90 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | ||
91 | |||
92 | printk("cpufreq: AT32AP CPU frequency driver\n"); | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static struct cpufreq_driver at32_driver = { | ||
98 | .name = "at32ap", | ||
99 | .owner = THIS_MODULE, | ||
100 | .init = at32_cpufreq_driver_init, | ||
101 | .verify = at32_verify_speed, | ||
102 | .target = at32_set_target, | ||
103 | .get = at32_get_speed, | ||
104 | .flags = CPUFREQ_STICKY, | ||
105 | }; | ||
106 | |||
107 | static int __init at32_cpufreq_init(void) | ||
108 | { | ||
109 | return cpufreq_register_driver(&at32_driver); | ||
110 | } | ||
111 | |||
112 | arch_initcall(at32_cpufreq_init); | ||