Skip to content

Commit 9e58e18

Browse files
Hans-Christian EgtvedtHaavard Skinnemoen
authored andcommitted
[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 <[email protected]> Signed-off-by: Haavard Skinnemoen <[email protected]>
1 parent 7a5b805 commit 9e58e18

File tree

4 files changed

+175
-2
lines changed

4 files changed

+175
-2
lines changed

arch/avr32/Kconfig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,27 @@ config CMDLINE
185185

186186
endmenu
187187

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+
188209
menu "Bus options"
189210

190211
config PCI

arch/avr32/mach-at32ap/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o
22
obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o
33
obj-$(CONFIG_CPU_AT32AP7000) += time-tc.o
4+
obj-$(CONFIG_CPU_FREQ_AT32AP) += cpufreq.o

arch/avr32/mach-at32ap/at32ap7000.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,41 @@ static unsigned long cpu_clk_get_rate(struct clk *clk)
219219
return bus_clk_get_rate(clk, shift);
220220
}
221221

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+
222257
static void hsb_clk_mode(struct clk *clk, int enabled)
223258
{
224259
unsigned long flags;
@@ -300,6 +335,7 @@ static unsigned long pbb_clk_get_rate(struct clk *clk)
300335
static struct clk cpu_clk = {
301336
.name = "cpu",
302337
.get_rate = cpu_clk_get_rate,
338+
.set_rate = cpu_clk_set_rate,
303339
.users = 1,
304340
};
305341
static struct clk hsb_clk = {
@@ -1152,10 +1188,13 @@ void __init at32_clock_init(void)
11521188
u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0;
11531189
int i;
11541190

1155-
if (pm_readl(MCCTRL) & PM_BIT(PLLSEL))
1191+
if (pm_readl(MCCTRL) & PM_BIT(PLLSEL)) {
11561192
main_clock = &pll0;
1157-
else
1193+
cpu_clk.parent = &pll0;
1194+
} else {
11581195
main_clock = &osc0;
1196+
cpu_clk.parent = &osc0;
1197+
}
11591198

11601199
if (pm_readl(PLL0) & PM_BIT(PLLOSC))
11611200
pll0.parent = &osc1;

arch/avr32/mach-at32ap/cpufreq.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

0 commit comments

Comments
 (0)