aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans-Christian Egtvedt <hcegtvedt@atmel.com>2007-06-04 10:10:57 -0400
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2007-07-18 14:45:51 -0400
commit9e58e1855c9815ad4944df90f695a7645c50f463 (patch)
tree22bf85cd9a42039726de7c5250f30207dad36b5f
parent7a5b80590772c29bba1d54d3685622177d6fe39f (diff)
[AVR32] CPU frequency scaling for AT32AP
This patch enables CPU frequency scaling for AT32AP devices. This will enable the CPU to scale between the speed of the high speed bus and the master clock and thus save some power. The patch also adds a parent to cpu_clk and a cpu_clk_set_rate to enable changing the CPU clock divider in a sane way. The driver does not check if the given rate is 0, thus resulting in a div by 0. I think this check should be go into the clk_set_rate framework, and not here. Tested on AT32AP7000/ATSTK1000. Hardware documentation can be found in the AT32AP7000 datasheet. Signed-off-by: Hans-Christian Egtvedt <hcegtvedt@atmel.com> Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
-rw-r--r--arch/avr32/Kconfig21
-rw-r--r--arch/avr32/mach-at32ap/Makefile1
-rw-r--r--arch/avr32/mach-at32ap/at32ap7000.c43
-rw-r--r--arch/avr32/mach-at32ap/cpufreq.c112
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
186endmenu 186endmenu
187 187
188menu "Power managment options"
189
190menu "CPU Frequency scaling"
191
192source "drivers/cpufreq/Kconfig"
193
194config 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
205endmenu
206
207endmenu
208
188menu "Bus options" 209menu "Bus options"
189 210
190config PCI 211config 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 @@
1obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o 1obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o
2obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o 2obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o
3obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o 3obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o
4obj-$(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
222static 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
222static void hsb_clk_mode(struct clk *clk, int enabled) 257static 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)
300static struct clk cpu_clk = { 335static 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};
305static struct clk hsb_clk = { 341static 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
23static struct clk *cpuclk;
24
25static 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
35static 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
43static 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
73static 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
97static 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
107static int __init at32_cpufreq_init(void)
108{
109 return cpufreq_register_driver(&at32_driver);
110}
111
112arch_initcall(at32_cpufreq_init);