linux/drivers/clk/samsung/clk-pll.c
Tomasz Figa a8b5a39ecb clk: samsung: Remove unimplemented ops for pll
Unimplemented clock operations should be simply omitted instead of returning
error values.

This patch removes unimplemented PLL operations to fix problems caused
by returning error code in round_rate callback.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
2013-04-04 15:51:09 +09:00

420 lines
10 KiB
C

/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro 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.
*
* This file contains the utility functions to register the pll clocks.
*/
#include <linux/errno.h>
#include "clk.h"
#include "clk-pll.h"
/*
* PLL35xx Clock Type
*/
#define PLL35XX_MDIV_MASK (0x3FF)
#define PLL35XX_PDIV_MASK (0x3F)
#define PLL35XX_SDIV_MASK (0x7)
#define PLL35XX_MDIV_SHIFT (16)
#define PLL35XX_PDIV_SHIFT (8)
#define PLL35XX_SDIV_SHIFT (0)
struct samsung_clk_pll35xx {
struct clk_hw hw;
const void __iomem *con_reg;
};
#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw)
static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw);
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
pll_con = __raw_readl(pll->con_reg);
mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
fvco *= mdiv;
do_div(fvco, (pdiv << sdiv));
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll35xx_clk_ops = {
.recalc_rate = samsung_pll35xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll35xx(const char *name,
const char *pname, const void __iomem *con_reg)
{
struct samsung_clk_pll35xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll35xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL36xx Clock Type
*/
#define PLL36XX_KDIV_MASK (0xFFFF)
#define PLL36XX_MDIV_MASK (0x1FF)
#define PLL36XX_PDIV_MASK (0x3F)
#define PLL36XX_SDIV_MASK (0x7)
#define PLL36XX_MDIV_SHIFT (16)
#define PLL36XX_PDIV_SHIFT (8)
#define PLL36XX_SDIV_SHIFT (0)
struct samsung_clk_pll36xx {
struct clk_hw hw;
const void __iomem *con_reg;
};
#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw)
static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw);
u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1;
u64 fvco = parent_rate;
pll_con0 = __raw_readl(pll->con_reg);
pll_con1 = __raw_readl(pll->con_reg + 4);
mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
kdiv = pll_con1 & PLL36XX_KDIV_MASK;
fvco *= (mdiv << 16) + kdiv;
do_div(fvco, (pdiv << sdiv));
fvco >>= 16;
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll36xx_clk_ops = {
.recalc_rate = samsung_pll36xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll36xx(const char *name,
const char *pname, const void __iomem *con_reg)
{
struct samsung_clk_pll36xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll36xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL45xx Clock Type
*/
#define PLL45XX_MDIV_MASK (0x3FF)
#define PLL45XX_PDIV_MASK (0x3F)
#define PLL45XX_SDIV_MASK (0x7)
#define PLL45XX_MDIV_SHIFT (16)
#define PLL45XX_PDIV_SHIFT (8)
#define PLL45XX_SDIV_SHIFT (0)
struct samsung_clk_pll45xx {
struct clk_hw hw;
enum pll45xx_type type;
const void __iomem *con_reg;
};
#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
pll_con = __raw_readl(pll->con_reg);
mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
if (pll->type == pll_4508)
sdiv = sdiv - 1;
fvco *= mdiv;
do_div(fvco, (pdiv << sdiv));
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll45xx_clk_ops = {
.recalc_rate = samsung_pll45xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll45xx(const char *name,
const char *pname, const void __iomem *con_reg,
enum pll45xx_type type)
{
struct samsung_clk_pll45xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll45xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
pll->type = type;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL46xx Clock Type
*/
#define PLL46XX_MDIV_MASK (0x1FF)
#define PLL46XX_PDIV_MASK (0x3F)
#define PLL46XX_SDIV_MASK (0x7)
#define PLL46XX_MDIV_SHIFT (16)
#define PLL46XX_PDIV_SHIFT (8)
#define PLL46XX_SDIV_SHIFT (0)
#define PLL46XX_KDIV_MASK (0xFFFF)
#define PLL4650C_KDIV_MASK (0xFFF)
#define PLL46XX_KDIV_SHIFT (0)
struct samsung_clk_pll46xx {
struct clk_hw hw;
enum pll46xx_type type;
const void __iomem *con_reg;
};
#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw)
static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw);
u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
u64 fvco = parent_rate;
pll_con0 = __raw_readl(pll->con_reg);
pll_con1 = __raw_readl(pll->con_reg + 4);
mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK :
pll_con1 & PLL46XX_KDIV_MASK;
shift = pll->type == pll_4600 ? 16 : 10;
fvco *= (mdiv << shift) + kdiv;
do_div(fvco, (pdiv << sdiv));
fvco >>= shift;
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll46xx_clk_ops = {
.recalc_rate = samsung_pll46xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll46xx(const char *name,
const char *pname, const void __iomem *con_reg,
enum pll46xx_type type)
{
struct samsung_clk_pll46xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll46xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
pll->type = type;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL2550x Clock Type
*/
#define PLL2550X_R_MASK (0x1)
#define PLL2550X_P_MASK (0x3F)
#define PLL2550X_M_MASK (0x3FF)
#define PLL2550X_S_MASK (0x7)
#define PLL2550X_R_SHIFT (20)
#define PLL2550X_P_SHIFT (14)
#define PLL2550X_M_SHIFT (4)
#define PLL2550X_S_SHIFT (0)
struct samsung_clk_pll2550x {
struct clk_hw hw;
const void __iomem *reg_base;
unsigned long offset;
};
#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
u32 r, p, m, s, pll_stat;
u64 fvco = parent_rate;
pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
if (!r)
return 0;
p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
fvco *= m;
do_div(fvco, (p << s));
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll2550x_clk_ops = {
.recalc_rate = samsung_pll2550x_recalc_rate,
};
struct clk * __init samsung_clk_register_pll2550x(const char *name,
const char *pname, const void __iomem *reg_base,
const unsigned long offset)
{
struct samsung_clk_pll2550x *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll2550x_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->reg_base = reg_base;
pll->offset = offset;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}