aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tcc8k/clock.c
diff options
context:
space:
mode:
authorHans J. Koch <hjk@linutronix.de>2011-02-17 10:42:59 -0500
committerThomas Gleixner <tglx@linutronix.de>2011-03-11 04:06:06 -0500
commitfe03a9f7bb89e920e60fd1bb074adab2eed2bf48 (patch)
tree4389363bf88c7d8d04b1bfc5f3421333470decb4 /arch/arm/mach-tcc8k/clock.c
parent85922e54a3a14a6aee6c0b1fc67d81ef0c60fc9c (diff)
arm: tcc8k: Fix clock rate calculation
The calculation of the best divider value for a requested clock rate always returned a value that was slightly too large. It was also not protected against possible divisions by zero. Request for very low, but non zero rates would cause the ACLK divisor field to overflow. Catch this situation by using the maximum value. The internal function aclk_set_rate() calculates the correct divider value, but doesn't write it back to the register. Add the write back. Signed-off-by: Hans J. Koch <hjk@linutronix.de> Signed-off-by: Oskar Schirmer <oskar@linutronix.de> Cc: bigeasy@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/arm/mach-tcc8k/clock.c')
-rw-r--r--arch/arm/mach-tcc8k/clock.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/arch/arm/mach-tcc8k/clock.c b/arch/arm/mach-tcc8k/clock.c
index 3970a9cdce26..7ebcbff4652a 100644
--- a/arch/arm/mach-tcc8k/clock.c
+++ b/arch/arm/mach-tcc8k/clock.c
@@ -50,6 +50,8 @@
50#define ACLKTCX (CKC_BASE + ACLKTCX_OFFS) 50#define ACLKTCX (CKC_BASE + ACLKTCX_OFFS)
51#define ACLKTCZ (CKC_BASE + ACLKTCZ_OFFS) 51#define ACLKTCZ (CKC_BASE + ACLKTCZ_OFFS)
52 52
53#define ACLK_MAX_DIV (0xfff + 1)
54
53/* Crystal frequencies */ 55/* Crystal frequencies */
54static unsigned long xi_rate, xti_rate; 56static unsigned long xi_rate, xti_rate;
55 57
@@ -258,14 +260,19 @@ static unsigned long aclk_best_div(struct clk *clk, unsigned long rate)
258{ 260{
259 unsigned long div, src, freq, r1, r2; 261 unsigned long div, src, freq, r1, r2;
260 262
263 if (!rate)
264 return ACLK_MAX_DIV;
265
261 src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT; 266 src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT;
262 src &= CLK_SRC_MASK; 267 src &= CLK_SRC_MASK;
263 freq = root_clk_get_rate(src); 268 freq = root_clk_get_rate(src);
264 div = freq / rate + 1; 269 div = freq / rate;
270 if (!div)
271 return 1;
272 if (div >= ACLK_MAX_DIV)
273 return ACLK_MAX_DIV;
265 r1 = freq / div; 274 r1 = freq / div;
266 r2 = freq / (div + 1); 275 r2 = freq / (div + 1);
267 if (r2 >= rate)
268 return div + 1;
269 if ((rate - r2) < (r1 - rate)) 276 if ((rate - r2) < (r1 - rate))
270 return div + 1; 277 return div + 1;
271 278
@@ -287,7 +294,8 @@ static int aclk_set_rate(struct clk *clk, unsigned long rate)
287 u32 reg; 294 u32 reg;
288 295
289 reg = __raw_readl(clk->aclkreg) & ~ACLK_DIV_MASK; 296 reg = __raw_readl(clk->aclkreg) & ~ACLK_DIV_MASK;
290 reg |= aclk_best_div(clk, rate); 297 reg |= aclk_best_div(clk, rate) - 1;
298 __raw_writel(reg, clk->aclkreg);
291 return 0; 299 return 0;
292} 300}
293 301