blob: ca8260b55a192b5294fe4757e0fe1c5266916083 [file] [log] [blame]
/*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
* Copyright (C) 2013-2014 Imagination Technologies Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* True Circuits PLL in TZ1090 SoC.
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
#include "clk.h"
/* Register definitions */
#define PLL_CTL0 0
#define PLL_CTL0_BWADJ_M 0xfff
#define PLL_CTL0_BWADJ_S 20
#define PLL_CTL0_CLKF_M 0x1fff
#define PLL_CTL0_CLKF_S 4
#define PLL_CTL0_CLKOD_M 0x7
#define PLL_CTL0_CLKOD_S 0
#define PLL_CTL1 4
#define PLL_CTL1_RESET_B BIT(28)
#define PLL_CTL1_FASTEN_B BIT(27)
#define PLL_CTL1_ENSAT_B BIT(26)
#define PLL_CTL1_BYPASS_B BIT(25)
#define PLL_CTL1_PWRDN_B BIT(24)
#define PLL_CTL1_CLKR_M 0x3f
#define PLL_CTL1_CLKR_S 0
/**
* struct tz1090_clk_pll_priv - PLL in TZ1090
*
* @hw: handle between common and hardware-specific interfaces
* @reg: first of two registers
*
* PLL in TZ1090.
*/
struct tz1090_clk_pll_priv {
struct clk_hw hw;
void __iomem *reg;
};
#define to_tz1090_clk_pll(_hw) container_of(_hw, struct tz1090_clk_pll_priv, hw)
static unsigned long tz1090_clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long f_in)
{
struct tz1090_clk_pll_priv *pll = to_tz1090_clk_pll(hw);
u32 ctl0, ctl1;
unsigned int clk_f; /* feedback divide */
unsigned int clk_od; /* output divide */
unsigned int clk_r; /* reference divide */
unsigned long f_out;
ctl0 = readl(pll->reg + PLL_CTL0);
ctl1 = readl(pll->reg + PLL_CTL1);
/* Bypass? */
if (ctl1 & PLL_CTL1_BYPASS_B)
return f_in;
/* Get divider values */
clk_f = 1 + ((ctl0 >> PLL_CTL0_CLKF_S) & PLL_CTL0_CLKF_M);
clk_od = 1 + ((ctl0 >> PLL_CTL0_CLKOD_S) & PLL_CTL0_CLKOD_M);
clk_r = 1 + ((ctl1 >> PLL_CTL1_CLKR_S) & PLL_CTL1_CLKR_M);
/*
* formula:
* f_out = (f_in / clk_r) * (clk_f / 2) / clk_od
* = (f_in * clk_f) / (2 * clk_r * clk_od)
*/
f_out = div_u64((u64)f_in * clk_f,
2 * clk_r * clk_od);
return f_out;
}
/* finds best pll parameters and returns rate on success (or 0) */
static int tz1090_clk_pll_bestvals(struct clk_hw *hw, unsigned long parent_rate,
unsigned long rate, unsigned long *clkf,
unsigned long *clkr, unsigned long *clkod)
{
unsigned long odmin, odmax;
unsigned long bestf = 1, bestr = 1, bestod = 1;
unsigned long rod2, cur, best = 0;
unsigned long f, r, od;
/* 120MHz/freq < od < 600MHz/freq */
odmin = 120000000/rate + 1;
odmax = 600000000/rate;
if (odmin < 1)
odmin = 1;
if (odmax > PLL_CTL0_CLKOD_M + 1)
odmax = PLL_CTL0_CLKOD_M + 1;
/*
* Search through valid combinations of od and r, starting with lower
* output divider values to get a lower intermediate frequency.
*/
for (od = odmin; od <= odmax; ++od) {
for (r = 1; r <= PLL_CTL1_CLKR_M + 1; ++r) {
/*
* Calculate best f for given r and od, rounding down
* So for f, freq <= rate
* And for f+1, freq > rate
* We have to do rate+1 because rate may have itself
* been rounded down.
*/
rod2 = 2 * r * od;
f = div_u64((u64)(rate + 1) * rod2, parent_rate);
if (f < 1)
continue;
if (f > PLL_CTL0_CLKF_M + 1)
f = PLL_CTL0_CLKF_M + 1;
/* Calculate final rate and see if it's the best */
cur = div_u64((u64)parent_rate * f, rod2);
if (cur > best) {
bestf = f;
bestr = r;
bestod = od;
best = cur;
/* Can't improve on a perfect match */
if (cur == rate)
goto done;
}
}
}
if (!best)
return 0;
done:
pr_debug("tz1090_clk_pll: final %lu/%lu * %lu/2/%lu=%lu (req=%lu, err=%ld)\n",
parent_rate, bestr, bestf, bestod, best, rate, best - rate);
*clkf = bestf;
*clkr = bestr;
*clkod = bestod;
return best;
}
static long tz1090_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
unsigned long clkf, clkr, clkod;
unsigned long parent_rate = *prate;
return tz1090_clk_pll_bestvals(hw, parent_rate, rate, &clkf, &clkr,
&clkod);
}
static int tz1090_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct tz1090_clk_pll_priv *pll = to_tz1090_clk_pll(hw);
unsigned long clkf, clkr, clkod, bwadj;
u32 ctl0, ctl1;
if (!tz1090_clk_pll_bestvals(hw, parent_rate, rate,
&clkf, &clkr, &clkod))
return -EINVAL;
/* offset the values ready to go in the PLL registers */
--clkr;
--clkf;
--clkod;
bwadj = clkf / 2;
/* bypass, reset and configure PLL */
ctl0 = (bwadj << PLL_CTL0_BWADJ_S) |
(clkf << PLL_CTL0_CLKF_S) |
(clkod << PLL_CTL0_CLKOD_S);
ctl1 = PLL_CTL1_RESET_B |
PLL_CTL1_ENSAT_B |
PLL_CTL1_BYPASS_B |
(clkr << PLL_CTL1_CLKR_S);
writel(ctl1, pll->reg + PLL_CTL1);
writel(ctl0, pll->reg + PLL_CTL0);
/* allow 5us after clkf before deasserting reset */
udelay(5);
/* take PLL out of reset and enable fasten */
ctl1 &= ~PLL_CTL1_RESET_B;
ctl1 |= PLL_CTL1_FASTEN_B;
writel(ctl1, pll->reg + PLL_CTL1);
/* count at least 500 divided ref clks to allow time to lock */
msleep(1 + 500*1000*(clkr+1)/parent_rate);
/* take PLL out of fasten / bypass */
ctl1 &= ~PLL_CTL1_FASTEN_B;
ctl1 &= ~PLL_CTL1_BYPASS_B;
writel(ctl1, pll->reg + PLL_CTL1);
return 0;
}
static const struct clk_ops tz1090_clk_pll_ops = {
.recalc_rate = tz1090_clk_pll_recalc_rate,
.round_rate = tz1090_clk_pll_round_rate,
.set_rate = tz1090_clk_pll_set_rate,
};
/**
* __register_pll() - register a PLL with the clock framework
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust PLL
*
* Register a TZ1090 PLL clock to the clock framework.
*/
static struct clk *__init __register_pll(const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *reg)
{
struct tz1090_clk_pll_priv *pll;
struct clk *clk;
struct clk_init_data init;
/* allocate the pll */
pll = kzalloc(sizeof(struct tz1090_clk_pll_priv), GFP_KERNEL);
if (!pll)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &tz1090_clk_pll_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct tz1090_clk_pll_priv assignments */
pll->reg = reg;
pll->hw.init = &init;
/* register the clock */
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk))
kfree(pll);
return clk;
}
/**
* tz1090_clk_register_plls() - Register set of PLLs with a provider.
* @p: TZ1090 clock provider.
* @plls: Array of PLL descriptions.
* @count Number of PLLs described in the array.
*/
void __init tz1090_clk_register_plls(struct tz1090_clk_provider *p,
const struct tz1090_clk_pll *plls,
unsigned int count)
{
const struct tz1090_clk_pll *pll;
struct clk *clk;
unsigned int i;
for (pll = plls, i = 0; i < count; ++pll, ++i) {
clk = __register_pll(tz1090_clk_xlate(p, pll->name),
tz1090_clk_xlate(p, pll->parent), 0,
p->base + pll->reg_base);
p->clk_data.clks[pll->id] = clk;
}
}