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>
This commit is contained in:
Hans J. Koch 2011-02-17 16:42:59 +01:00 committed by Thomas Gleixner
parent 85922e54a3
commit fe03a9f7bb
1 changed files with 12 additions and 4 deletions

View File

@ -50,6 +50,8 @@
#define ACLKTCX (CKC_BASE + ACLKTCX_OFFS) #define ACLKTCX (CKC_BASE + ACLKTCX_OFFS)
#define ACLKTCZ (CKC_BASE + ACLKTCZ_OFFS) #define ACLKTCZ (CKC_BASE + ACLKTCZ_OFFS)
#define ACLK_MAX_DIV (0xfff + 1)
/* Crystal frequencies */ /* Crystal frequencies */
static unsigned long xi_rate, xti_rate; static unsigned long xi_rate, xti_rate;
@ -258,14 +260,19 @@ static unsigned long aclk_best_div(struct clk *clk, unsigned long rate)
{ {
unsigned long div, src, freq, r1, r2; unsigned long div, src, freq, r1, r2;
if (!rate)
return ACLK_MAX_DIV;
src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT; src = __raw_readl(clk->aclkreg) >> ACLK_SEL_SHIFT;
src &= CLK_SRC_MASK; src &= CLK_SRC_MASK;
freq = root_clk_get_rate(src); freq = root_clk_get_rate(src);
div = freq / rate + 1; div = freq / rate;
if (!div)
return 1;
if (div >= ACLK_MAX_DIV)
return ACLK_MAX_DIV;
r1 = freq / div; r1 = freq / div;
r2 = freq / (div + 1); r2 = freq / (div + 1);
if (r2 >= rate)
return div + 1;
if ((rate - r2) < (r1 - rate)) if ((rate - r2) < (r1 - rate))
return div + 1; return div + 1;
@ -287,7 +294,8 @@ static int aclk_set_rate(struct clk *clk, unsigned long rate)
u32 reg; u32 reg;
reg = __raw_readl(clk->aclkreg) & ~ACLK_DIV_MASK; reg = __raw_readl(clk->aclkreg) & ~ACLK_DIV_MASK;
reg |= aclk_best_div(clk, rate); reg |= aclk_best_div(clk, rate) - 1;
__raw_writel(reg, clk->aclkreg);
return 0; return 0;
} }