提交 47727beb 编写于 作者: L Loc Ho 提交者: Stephen Boyd

clk: xgene: Add SoC and PMD PLL clocks with v2 hardware

Add X-Gene SoC and PMD PLL clocks support for v2 hardware.
X-Gene SoC v2 and above use an slightly different SoC
and PMD PLL hardware logic.
Signed-off-by: NLoc Ho <lho@apm.com>
Signed-off-by: NStephen Boyd <sboyd@codeaurora.org>
上级 7b63c567
......@@ -29,7 +29,9 @@
#include <linux/of_address.h>
/* Register SCU_PCPPLL bit fields */
#define N_DIV_RD(src) (((src) & 0x000001ff))
#define N_DIV_RD(src) ((src) & 0x000001ff)
#define SC_N_DIV_RD(src) ((src) & 0x0000007f)
#define SC_OUTDIV2(src) (((src) & 0x00000100) >> 8)
/* Register SCU_SOCPLL bit fields */
#define CLKR_RD(src) (((src) & 0x07000000)>>24)
......@@ -63,6 +65,7 @@ struct xgene_clk_pll {
spinlock_t *lock;
u32 pll_offset;
enum xgene_pll_type type;
int version;
};
#define to_xgene_clk_pll(_hw) container_of(_hw, struct xgene_clk_pll, hw)
......@@ -92,27 +95,37 @@ static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw,
pll = xgene_clk_read(pllclk->reg + pllclk->pll_offset);
if (pllclk->type == PLL_TYPE_PCP) {
/*
* PLL VCO = Reference clock * NF
* PCP PLL = PLL_VCO / 2
*/
nout = 2;
fvco = parent_rate * (N_DIV_RD(pll) + 4);
if (pllclk->version <= 1) {
if (pllclk->type == PLL_TYPE_PCP) {
/*
* PLL VCO = Reference clock * NF
* PCP PLL = PLL_VCO / 2
*/
nout = 2;
fvco = parent_rate * (N_DIV_RD(pll) + 4);
} else {
/*
* Fref = Reference Clock / NREF;
* Fvco = Fref * NFB;
* Fout = Fvco / NOUT;
*/
nref = CLKR_RD(pll) + 1;
nout = CLKOD_RD(pll) + 1;
nfb = CLKF_RD(pll);
fref = parent_rate / nref;
fvco = fref * nfb;
}
} else {
/*
* Fref = Reference Clock / NREF;
* Fvco = Fref * NFB;
* Fout = Fvco / NOUT;
* fvco = Reference clock * FBDIVC
* PLL freq = fvco / NOUT
*/
nref = CLKR_RD(pll) + 1;
nout = CLKOD_RD(pll) + 1;
nfb = CLKF_RD(pll);
fref = parent_rate / nref;
fvco = fref * nfb;
nout = SC_OUTDIV2(pll) ? 2 : 3;
fvco = parent_rate * SC_N_DIV_RD(pll);
}
pr_debug("%s pll recalc rate %ld parent %ld\n", clk_hw_get_name(hw),
fvco / nout, parent_rate);
pr_debug("%s pll recalc rate %ld parent %ld version %d\n",
clk_hw_get_name(hw), fvco / nout, parent_rate,
pllclk->version);
return fvco / nout;
}
......@@ -125,7 +138,7 @@ static const struct clk_ops xgene_clk_pll_ops = {
static struct clk *xgene_register_clk_pll(struct device *dev,
const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg, u32 pll_offset,
u32 type, spinlock_t *lock)
u32 type, spinlock_t *lock, int version)
{
struct xgene_clk_pll *apmclk;
struct clk *clk;
......@@ -144,6 +157,7 @@ static struct clk *xgene_register_clk_pll(struct device *dev,
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
apmclk->version = version;
apmclk->reg = reg;
apmclk->lock = lock;
apmclk->pll_offset = pll_offset;
......@@ -160,26 +174,37 @@ static struct clk *xgene_register_clk_pll(struct device *dev,
return clk;
}
static int xgene_pllclk_version(struct device_node *np)
{
if (of_device_is_compatible(np, "apm,xgene-socpll-clock"))
return 1;
if (of_device_is_compatible(np, "apm,xgene-pcppll-clock"))
return 1;
return 2;
}
static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_type)
{
const char *clk_name = np->full_name;
struct clk *clk;
void __iomem *reg;
const char *clk_name = np->full_name;
struct clk *clk;
void __iomem *reg;
int version = xgene_pllclk_version(np);
reg = of_iomap(np, 0);
if (reg == NULL) {
pr_err("Unable to map CSR register for %s\n", np->full_name);
return;
}
of_property_read_string(np, "clock-output-names", &clk_name);
clk = xgene_register_clk_pll(NULL,
clk_name, of_clk_get_parent_name(np, 0),
CLK_IS_ROOT, reg, 0, pll_type, &clk_lock);
if (!IS_ERR(clk)) {
of_clk_add_provider(np, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
pr_debug("Add %s clock PLL\n", clk_name);
}
reg = of_iomap(np, 0);
if (reg == NULL) {
pr_err("Unable to map CSR register for %s\n", np->full_name);
return;
}
of_property_read_string(np, "clock-output-names", &clk_name);
clk = xgene_register_clk_pll(NULL,
clk_name, of_clk_get_parent_name(np, 0),
CLK_IS_ROOT, reg, 0, pll_type, &clk_lock,
version);
if (!IS_ERR(clk)) {
of_clk_add_provider(np, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
pr_debug("Add %s clock PLL\n", clk_name);
}
}
static void xgene_socpllclk_init(struct device_node *np)
......@@ -460,7 +485,7 @@ static void __init xgene_devclk_init(struct device_node *np)
rc = of_address_to_resource(np, i, &res);
if (rc != 0) {
if (i == 0) {
pr_err("no DTS register for %s\n",
pr_err("no DTS register for %s\n",
np->full_name);
return;
}
......@@ -518,4 +543,8 @@ static void __init xgene_devclk_init(struct device_node *np)
CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init);
CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init);
CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock",
xgene_socpllclk_init);
CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock",
xgene_pcppllclk_init);
CLK_OF_DECLARE(xgene_dev_clock, "apm,xgene-device-clock", xgene_devclk_init);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册