aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-ep93xx/clock.c
diff options
context:
space:
mode:
authorRyan Mallon <ryan@bluewatersys.com>2009-09-22 19:47:09 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:51 -0400
commitc6012189a40d33213ead5d15769fab615443206f (patch)
tree56682b59b54f18f0134d373e309121f87a907297 /arch/arm/mach-ep93xx/clock.c
parentd63870db3c41086d7f13ec8b41def4331db32327 (diff)
ep93xx video driver platform support
Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com> Cc: Daniele Venzano <linux@brownhat.org> Cc: Russell King <rmk@arm.linux.org.uk> Cc: Krzysztof Helt <krzysztof.h1@poczta.fm> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/arm/mach-ep93xx/clock.c')
-rw-r--r--arch/arm/mach-ep93xx/clock.c88
1 files changed, 87 insertions, 1 deletions
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index 3dd0e2a23095..dda19cd76194 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -37,7 +37,7 @@ struct clk {
37static unsigned long get_uart_rate(struct clk *clk); 37static unsigned long get_uart_rate(struct clk *clk);
38 38
39static int set_keytchclk_rate(struct clk *clk, unsigned long rate); 39static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
40 40static int set_div_rate(struct clk *clk, unsigned long rate);
41 41
42static struct clk clk_uart1 = { 42static struct clk clk_uart1 = {
43 .sw_locked = 1, 43 .sw_locked = 1,
@@ -76,6 +76,13 @@ static struct clk clk_pwm = {
76 .rate = EP93XX_EXT_CLK_RATE, 76 .rate = EP93XX_EXT_CLK_RATE,
77}; 77};
78 78
79static struct clk clk_video = {
80 .sw_locked = 1,
81 .enable_reg = EP93XX_SYSCON_VIDCLKDIV,
82 .enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE,
83 .set_rate = set_div_rate,
84};
85
79/* DMA Clocks */ 86/* DMA Clocks */
80static struct clk clk_m2p0 = { 87static struct clk clk_m2p0 = {
81 .enable_reg = EP93XX_SYSCON_PWRCNT, 88 .enable_reg = EP93XX_SYSCON_PWRCNT,
@@ -140,6 +147,7 @@ static struct clk_lookup clocks[] = {
140 INIT_CK(NULL, "pll2", &clk_pll2), 147 INIT_CK(NULL, "pll2", &clk_pll2),
141 INIT_CK("ep93xx-ohci", NULL, &clk_usb_host), 148 INIT_CK("ep93xx-ohci", NULL, &clk_usb_host),
142 INIT_CK("ep93xx-keypad", NULL, &clk_keypad), 149 INIT_CK("ep93xx-keypad", NULL, &clk_keypad),
150 INIT_CK("ep93xx-fb", NULL, &clk_video),
143 INIT_CK(NULL, "pwm_clk", &clk_pwm), 151 INIT_CK(NULL, "pwm_clk", &clk_pwm),
144 INIT_CK(NULL, "m2p0", &clk_m2p0), 152 INIT_CK(NULL, "m2p0", &clk_m2p0),
145 INIT_CK(NULL, "m2p1", &clk_m2p1), 153 INIT_CK(NULL, "m2p1", &clk_m2p1),
@@ -236,6 +244,84 @@ static int set_keytchclk_rate(struct clk *clk, unsigned long rate)
236 return 0; 244 return 0;
237} 245}
238 246
247static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel,
248 int *pdiv, int *div)
249{
250 unsigned long max_rate, best_rate = 0,
251 actual_rate = 0, mclk_rate = 0, rate_err = -1;
252 int i, found = 0, __div = 0, __pdiv = 0;
253
254 /* Don't exceed the maximum rate */
255 max_rate = max(max(clk_pll1.rate / 4, clk_pll2.rate / 4),
256 (unsigned long)EP93XX_EXT_CLK_RATE / 4);
257 rate = min(rate, max_rate);
258
259 /*
260 * Try the two pll's and the external clock
261 * Because the valid predividers are 2, 2.5 and 3, we multiply
262 * all the clocks by 2 to avoid floating point math.
263 *
264 * This is based on the algorithm in the ep93xx raster guide:
265 * http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf
266 *
267 */
268 for (i = 0; i < 3; i++) {
269 if (i == 0)
270 mclk_rate = EP93XX_EXT_CLK_RATE * 2;
271 else if (i == 1)
272 mclk_rate = clk_pll1.rate * 2;
273 else if (i == 2)
274 mclk_rate = clk_pll2.rate * 2;
275
276 /* Try each predivider value */
277 for (__pdiv = 4; __pdiv <= 6; __pdiv++) {
278 __div = mclk_rate / (rate * __pdiv);
279 if (__div < 2 || __div > 127)
280 continue;
281
282 actual_rate = mclk_rate / (__pdiv * __div);
283
284 if (!found || abs(actual_rate - rate) < rate_err) {
285 *pdiv = __pdiv - 3;
286 *div = __div;
287 *psel = (i == 2);
288 *esel = (i != 0);
289 best_rate = actual_rate;
290 rate_err = abs(actual_rate - rate);
291 found = 1;
292 }
293 }
294 }
295
296 if (!found)
297 return 0;
298
299 return best_rate;
300}
301
302static int set_div_rate(struct clk *clk, unsigned long rate)
303{
304 unsigned long actual_rate;
305 int psel = 0, esel = 0, pdiv = 0, div = 0;
306 u32 val;
307
308 actual_rate = calc_clk_div(rate, &psel, &esel, &pdiv, &div);
309 if (actual_rate == 0)
310 return -EINVAL;
311 clk->rate = actual_rate;
312
313 /* Clear the esel, psel, pdiv and div bits */
314 val = __raw_readl(clk->enable_reg);
315 val &= ~0x7fff;
316
317 /* Set the new esel, psel, pdiv and div bits for the new clock rate */
318 val |= (esel ? EP93XX_SYSCON_CLKDIV_ESEL : 0) |
319 (psel ? EP93XX_SYSCON_CLKDIV_PSEL : 0) |
320 (pdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | div;
321 ep93xx_syscon_swlocked_write(val, clk->enable_reg);
322 return 0;
323}
324
239int clk_set_rate(struct clk *clk, unsigned long rate) 325int clk_set_rate(struct clk *clk, unsigned long rate)
240{ 326{
241 if (clk->set_rate) 327 if (clk->set_rate)