Mariusz Chilmon
m.chilmon@amw.gdynia.pl
uc.vmario.org
| Źródło | Dokładność | Odchyłka |
|---|---|---|
| RC | ±1% | 7h/miesiąc |
| XO | ±20ppm | 51s/miesiąc |
| OCXO | ±100ppb | 3s/rok |
| Rb | ±1,5ppb | 47ms/rok |
int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate)
{
unsigned v;
struct pll_data *pll;
unsigned long input;
unsigned ratio = 0;
/* If this is the PLL base clock, wrong function to call */
if (clk->pll_data)
return -EINVAL;
/* There must be a parent... */
if (WARN_ON(!clk->parent))
return -EINVAL;
/* ... the parent must be a PLL... */
if (WARN_ON(!clk->parent->pll_data))
return -EINVAL;
/* ... and this clock must have a divider. */
if (WARN_ON(!clk->div_reg))
return -EINVAL;
pll = clk->parent->pll_data;
input = clk->parent->rate;
/* If pre-PLL, source clock is before the multiplier and divider(s) */
if (clk->flags & PRE_PLL)
input = pll->input_rate;
if (input > rate) {
/*
* Can afford to provide an output little higher than requested
* only if maximum rate supported by hardware on this sysclk
* is known.
*/
if (clk->maxrate) {
ratio = DIV_ROUND_CLOSEST(input, rate);
if (input / ratio > clk->maxrate)
ratio = 0;
}
if (ratio == 0)
ratio = DIV_ROUND_UP(input, rate);
ratio--;
}
if (ratio > pll->div_ratio_mask)
return -EINVAL;
do {
v = __raw_readl(pll->base + PLLSTAT);
} while (v & PLLSTAT_GOSTAT);
v = __raw_readl(pll->base + clk->div_reg);
v &= ~pll->div_ratio_mask;
v |= ratio | PLLDIV_EN;
__raw_writel(v, pll->base + clk->div_reg);
v = __raw_readl(pll->base + PLLCMD);
v |= PLLCMD_GOSET;
__raw_writel(v, pll->base + PLLCMD);
do {
v = __raw_readl(pll->base + PLLSTAT);
} while (v & PLLSTAT_GOSTAT);
return 0;
}