aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32
diff options
context:
space:
mode:
Diffstat (limited to 'arch/avr32')
-rw-r--r--arch/avr32/mach-at32ap/at32ap700x.c153
1 files changed, 150 insertions, 3 deletions
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index 22c302ad9b3f..0f24b4f85c17 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -6,6 +6,7 @@
6 * published by the Free Software Foundation. 6 * published by the Free Software Foundation.
7 */ 7 */
8#include <linux/clk.h> 8#include <linux/clk.h>
9#include <linux/delay.h>
9#include <linux/fb.h> 10#include <linux/fb.h>
10#include <linux/init.h> 11#include <linux/init.h>
11#include <linux/platform_device.h> 12#include <linux/platform_device.h>
@@ -99,6 +100,9 @@ unsigned long at32ap7000_osc_rates[3] = {
99 [2] = 12000000, 100 [2] = 12000000,
100}; 101};
101 102
103static struct clk osc0;
104static struct clk osc1;
105
102static unsigned long osc_get_rate(struct clk *clk) 106static unsigned long osc_get_rate(struct clk *clk)
103{ 107{
104 return at32ap7000_osc_rates[clk->index]; 108 return at32ap7000_osc_rates[clk->index];
@@ -108,9 +112,6 @@ static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
108{ 112{
109 unsigned long div, mul, rate; 113 unsigned long div, mul, rate;
110 114
111 if (!(control & PM_BIT(PLLEN)))
112 return 0;
113
114 div = PM_BFEXT(PLLDIV, control) + 1; 115 div = PM_BFEXT(PLLDIV, control) + 1;
115 mul = PM_BFEXT(PLLMUL, control) + 1; 116 mul = PM_BFEXT(PLLMUL, control) + 1;
116 117
@@ -121,6 +122,71 @@ static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
121 return rate; 122 return rate;
122} 123}
123 124
125static long pll_set_rate(struct clk *clk, unsigned long rate,
126 u32 *pll_ctrl)
127{
128 unsigned long mul;
129 unsigned long mul_best_fit = 0;
130 unsigned long div;
131 unsigned long div_min;
132 unsigned long div_max;
133 unsigned long div_best_fit = 0;
134 unsigned long base;
135 unsigned long pll_in;
136 unsigned long actual = 0;
137 unsigned long rate_error;
138 unsigned long rate_error_prev = ~0UL;
139 u32 ctrl;
140
141 /* Rate must be between 80 MHz and 200 Mhz. */
142 if (rate < 80000000UL || rate > 200000000UL)
143 return -EINVAL;
144
145 ctrl = PM_BF(PLLOPT, 4);
146 base = clk->parent->get_rate(clk->parent);
147
148 /* PLL input frequency must be between 6 MHz and 32 MHz. */
149 div_min = DIV_ROUND_UP(base, 32000000UL);
150 div_max = base / 6000000UL;
151
152 if (div_max < div_min)
153 return -EINVAL;
154
155 for (div = div_min; div <= div_max; div++) {
156 pll_in = (base + div / 2) / div;
157 mul = (rate + pll_in / 2) / pll_in;
158
159 if (mul == 0)
160 continue;
161
162 actual = pll_in * mul;
163 rate_error = abs(actual - rate);
164
165 if (rate_error < rate_error_prev) {
166 mul_best_fit = mul;
167 div_best_fit = div;
168 rate_error_prev = rate_error;
169 }
170
171 if (rate_error == 0)
172 break;
173 }
174
175 if (div_best_fit == 0)
176 return -EINVAL;
177
178 ctrl |= PM_BF(PLLMUL, mul_best_fit - 1);
179 ctrl |= PM_BF(PLLDIV, div_best_fit - 1);
180 ctrl |= PM_BF(PLLCOUNT, 16);
181
182 if (clk->parent == &osc1)
183 ctrl |= PM_BIT(PLLOSC);
184
185 *pll_ctrl = ctrl;
186
187 return actual;
188}
189
124static unsigned long pll0_get_rate(struct clk *clk) 190static unsigned long pll0_get_rate(struct clk *clk)
125{ 191{
126 u32 control; 192 u32 control;
@@ -130,6 +196,41 @@ static unsigned long pll0_get_rate(struct clk *clk)
130 return pll_get_rate(clk, control); 196 return pll_get_rate(clk, control);
131} 197}
132 198
199static void pll1_mode(struct clk *clk, int enabled)
200{
201 unsigned long timeout;
202 u32 status;
203 u32 ctrl;
204
205 ctrl = pm_readl(PLL1);
206
207 if (enabled) {
208 if (!PM_BFEXT(PLLMUL, ctrl) && !PM_BFEXT(PLLDIV, ctrl)) {
209 pr_debug("clk %s: failed to enable, rate not set\n",
210 clk->name);
211 return;
212 }
213
214 ctrl |= PM_BIT(PLLEN);
215 pm_writel(PLL1, ctrl);
216
217 /* Wait for PLL lock. */
218 for (timeout = 10000; timeout; timeout--) {
219 status = pm_readl(ISR);
220 if (status & PM_BIT(LOCK1))
221 break;
222 udelay(10);
223 }
224
225 if (!(status & PM_BIT(LOCK1)))
226 printk(KERN_ERR "clk %s: timeout waiting for lock\n",
227 clk->name);
228 } else {
229 ctrl &= ~PM_BIT(PLLEN);
230 pm_writel(PLL1, ctrl);
231 }
232}
233
133static unsigned long pll1_get_rate(struct clk *clk) 234static unsigned long pll1_get_rate(struct clk *clk)
134{ 235{
135 u32 control; 236 u32 control;
@@ -139,6 +240,49 @@ static unsigned long pll1_get_rate(struct clk *clk)
139 return pll_get_rate(clk, control); 240 return pll_get_rate(clk, control);
140} 241}
141 242
243static long pll1_set_rate(struct clk *clk, unsigned long rate, int apply)
244{
245 u32 ctrl = 0;
246 unsigned long actual_rate;
247
248 actual_rate = pll_set_rate(clk, rate, &ctrl);
249
250 if (apply) {
251 if (actual_rate != rate)
252 return -EINVAL;
253 if (clk->users > 0)
254 return -EBUSY;
255 pr_debug(KERN_INFO "clk %s: new rate %lu (actual rate %lu)\n",
256 clk->name, rate, actual_rate);
257 pm_writel(PLL1, ctrl);
258 }
259
260 return actual_rate;
261}
262
263static int pll1_set_parent(struct clk *clk, struct clk *parent)
264{
265 u32 ctrl;
266
267 if (clk->users > 0)
268 return -EBUSY;
269
270 ctrl = pm_readl(PLL1);
271 WARN_ON(ctrl & PM_BIT(PLLEN));
272
273 if (parent == &osc0)
274 ctrl &= ~PM_BIT(PLLOSC);
275 else if (parent == &osc1)
276 ctrl |= PM_BIT(PLLOSC);
277 else
278 return -EINVAL;
279
280 pm_writel(PLL1, ctrl);
281 clk->parent = parent;
282
283 return 0;
284}
285
142/* 286/*
143 * The AT32AP7000 has five primary clock sources: One 32kHz 287 * The AT32AP7000 has five primary clock sources: One 32kHz
144 * oscillator, two crystal oscillators and two PLLs. 288 * oscillator, two crystal oscillators and two PLLs.
@@ -167,7 +311,10 @@ static struct clk pll0 = {
167}; 311};
168static struct clk pll1 = { 312static struct clk pll1 = {
169 .name = "pll1", 313 .name = "pll1",
314 .mode = pll1_mode,
170 .get_rate = pll1_get_rate, 315 .get_rate = pll1_get_rate,
316 .set_rate = pll1_set_rate,
317 .set_parent = pll1_set_parent,
171 .parent = &osc0, 318 .parent = &osc0,
172}; 319};
173 320