diff options
author | Hans-Christian Egtvedt <hcegtvedt@atmel.com> | 2007-06-04 10:10:57 -0400 |
---|---|---|
committer | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2007-07-18 14:45:51 -0400 |
commit | 9e58e1855c9815ad4944df90f695a7645c50f463 (patch) | |
tree | 22bf85cd9a42039726de7c5250f30207dad36b5f /arch/avr32/mach-at32ap/at32ap7000.c | |
parent | 7a5b80590772c29bba1d54d3685622177d6fe39f (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>
Diffstat (limited to 'arch/avr32/mach-at32ap/at32ap7000.c')
-rw-r--r-- | arch/avr32/mach-at32ap/at32ap7000.c | 43 |
1 files changed, 41 insertions, 2 deletions
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; |