aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32/mach-at32ap/at32ap7000.c
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 /arch/avr32/mach-at32ap/at32ap7000.c
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>
Diffstat (limited to 'arch/avr32/mach-at32ap/at32ap7000.c')
-rw-r--r--arch/avr32/mach-at32ap/at32ap7000.c43
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
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;