diff options
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; |