From patchwork Tue Mar 12 00:42:19 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Heiko_St=C3=BCbner?= X-Patchwork-Id: 2252251 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 4B4663FCF6 for ; Tue, 12 Mar 2013 00:42:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754599Ab3CLAmY (ORCPT ); Mon, 11 Mar 2013 20:42:24 -0400 Received: from gloria.sntech.de ([95.129.55.99]:40392 "EHLO gloria.sntech.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754446Ab3CLAmY (ORCPT ); Mon, 11 Mar 2013 20:42:24 -0400 Received: from 146-52-45-75-dynip.superkabel.de ([146.52.45.75] helo=marty.localnet) by gloria.sntech.de with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.72) (envelope-from ) id 1UFDIL-0003ec-JP; Tue, 12 Mar 2013 01:42:21 +0100 From: Heiko =?utf-8?q?St=C3=BCbner?= To: Kukjin Kim Subject: [PATCH 1/7] clk: samsung: add plls used in s3c2416 and s3c2443 Date: Tue, 12 Mar 2013 01:42:19 +0100 User-Agent: KMail/1.13.7 (Linux/3.2.0-3-686-pae; KDE/4.8.4; i686; ; ) Cc: mturquette@linaro.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, Thomas Abraham , Sylwester Nawrocki , t.figa@samsung.com References: <201303120141.31262.heiko@sntech.de> In-Reply-To: <201303120141.31262.heiko@sntech.de> MIME-Version: 1.0 Message-Id: <201303120142.19842.heiko@sntech.de> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org This adds support for pll2126x, pll3000x, pll6552x and pll6553x. Signed-off-by: Heiko Stuebner --- drivers/clk/samsung/clk-pll.c | 376 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk-pll.h | 8 + 2 files changed, 384 insertions(+), 0 deletions(-) diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 4b24511..b772f9e 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -400,6 +400,97 @@ struct clk * __init samsung_clk_register_pll46xx(const char *name, } /* + * PLL2126x Clock Type + */ + +#define PLL2126X_MDIV_MASK (0xFF) +#define PLL2126X_PDIV_MASK (0x3) +#define PLL2126X_SDIV_MASK (0x3) +#define PLL2126X_MDIV_SHIFT (16) +#define PLL2126X_PDIV_SHIFT (8) +#define PLL2126X_SDIV_SHIFT (0) + +struct samsung_clk_pll2126x { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll2126x(_hw) container_of(_hw, struct samsung_clk_pll2126x, hw) + +static unsigned long samsung_pll2126x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll2126x *pll = to_clk_pll2126x(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL2126X_MDIV_SHIFT) & PLL2126X_MDIV_MASK; + pdiv = (pll_con >> PLL2126X_PDIV_SHIFT) & PLL2126X_PDIV_MASK; + sdiv = (pll_con >> PLL2126X_SDIV_SHIFT) & PLL2126X_SDIV_MASK; + + fvco *= (mdiv + 8); + do_div(fvco, (pdiv + 2) << sdiv); + + return (unsigned long)fvco; +} + +/* todo: implement pll2126x clock round rate operation */ +static long samsung_pll2126x_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pll2126x clock set rate */ +static int samsung_pll2126x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll2126x_clk_ops = { + .recalc_rate = samsung_pll2126x_recalc_rate, + .round_rate = samsung_pll2126x_round_rate, + .set_rate = samsung_pll2126x_set_rate, +}; + +struct clk * __init samsung_clk_register_pll2126x(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll2126x *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_pll2126x_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; +} + +/* * PLL2550x Clock Type */ @@ -497,3 +588,288 @@ struct clk * __init samsung_clk_register_pll2550x(const char *name, return clk; } + +/* + * PLL3000x Clock Type + */ + +#define PLL3000X_MDIV_MASK (0xFF) +#define PLL3000X_PDIV_MASK (0x3) +#define PLL3000X_SDIV_MASK (0x3) +#define PLL3000X_MDIV_SHIFT (16) +#define PLL3000X_PDIV_SHIFT (8) +#define PLL3000X_SDIV_SHIFT (0) + +struct samsung_clk_pll3000x { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll3000x(_hw) container_of(_hw, struct samsung_clk_pll3000x, hw) + +static unsigned long samsung_pll3000x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll3000x *pll = to_clk_pll3000x(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL3000X_MDIV_SHIFT) & PLL3000X_MDIV_MASK; + pdiv = (pll_con >> PLL3000X_PDIV_SHIFT) & PLL3000X_PDIV_MASK; + sdiv = (pll_con >> PLL3000X_SDIV_SHIFT) & PLL3000X_SDIV_MASK; + + fvco *= (2 * (mdiv + 8)); + do_div(fvco, pdiv << sdiv); + + return (unsigned long)fvco; +} + +/* todo: implement pll3000x clock round rate operation */ +static long samsung_pll3000x_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pll3000x clock set rate */ +static int samsung_pll3000x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll3000x_clk_ops = { + .recalc_rate = samsung_pll3000x_recalc_rate, + .round_rate = samsung_pll3000x_round_rate, + .set_rate = samsung_pll3000x_set_rate, +}; + +struct clk * __init samsung_clk_register_pll3000x(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll3000x *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_pll3000x_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; +} + +/* + * PLL6552x Clock Type + */ + +#define PLL6552X_MDIV_MASK (0x3FF) +#define PLL6552X_PDIV_MASK (0x3F) +#define PLL6552X_SDIV_MASK (0x7) +#define PLL6552X_MDIV_SHIFT (14) +#define PLL6552X_PDIV_SHIFT (5) +#define PLL6552X_SDIV_SHIFT (0) + +struct samsung_clk_pll6552x { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll6552x(_hw) container_of(_hw, struct samsung_clk_pll6552x, hw) + +static unsigned long samsung_pll6552x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll6552x *pll = to_clk_pll6552x(hw); + u32 pll_con, mdiv, pdiv, sdiv; + u64 fvco = parent_rate; + + pll_con = __raw_readl(pll->con_reg); + mdiv = (pll_con >> PLL6552X_MDIV_SHIFT) & PLL6552X_MDIV_MASK; + pdiv = (pll_con >> PLL6552X_PDIV_SHIFT) & PLL6552X_PDIV_MASK; + sdiv = (pll_con >> PLL6552X_SDIV_SHIFT) & PLL6552X_SDIV_MASK; + + fvco *= mdiv; + do_div(fvco, (pdiv << sdiv)); + + return (unsigned long)fvco; +} + +/* todo: implement pll6552x clock round rate operation */ +static long samsung_pll6552x_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pll6552x clock set rate */ +static int samsung_pll6552x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll6552x_clk_ops = { + .recalc_rate = samsung_pll6552x_recalc_rate, + .round_rate = samsung_pll6552x_round_rate, + .set_rate = samsung_pll6552x_set_rate, +}; + +struct clk * __init samsung_clk_register_pll6552x(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll6552x *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_pll6552x_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; +} + +/* + * PLL6553x Clock Type + */ + +#define PLL6553X_MDIV_MASK (0x7F) +#define PLL6553X_PDIV_MASK (0x1F) +#define PLL6553X_SDIV_MASK (0x3) +#define PLL6553X_KDIV_MASK (0xFFFF) +#define PLL6553X_MDIV_SHIFT (16) +#define PLL6553X_PDIV_SHIFT (8) +#define PLL6553X_SDIV_SHIFT (0) + +struct samsung_clk_pll6553x { + struct clk_hw hw; + const void __iomem *con_reg; +}; + +#define to_clk_pll6553x(_hw) container_of(_hw, struct samsung_clk_pll6553x, hw) + +static unsigned long samsung_pll6553x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct samsung_clk_pll6553x *pll = to_clk_pll6553x(hw); + u32 pll_con0, pll_con1, mdiv, pdiv, sdiv, kdiv; + u64 fvco = parent_rate; + + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); + mdiv = (pll_con0 >> PLL6553X_MDIV_SHIFT) & PLL6553X_MDIV_MASK; + pdiv = (pll_con0 >> PLL6553X_PDIV_SHIFT) & PLL6553X_PDIV_MASK; + sdiv = (pll_con0 >> PLL6553X_SDIV_SHIFT) & PLL6553X_SDIV_MASK; + kdiv = pll_con1 & PLL6553X_KDIV_MASK; + + /* + * We need to multiple parent_rate by mdiv (the integer part) and kdiv + * which is in 2^16ths, so shift mdiv up (does not overflow) and + * add kdiv before multiplying. The use of tmp is to avoid any + * overflows before shifting bac down into result when multipling + * by the mdiv and kdiv pair. + */ + + fvco *= (mdiv << 16) + kdiv; + do_div(fvco, (pdiv << sdiv)); + fvco >>= 16; + + return (unsigned long)fvco; +} + +/* todo: implement pll6553x clock round rate operation */ +static long samsung_pll6553x_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + return -ENOTSUPP; +} + +/* todo: implement pll6553x clock set rate */ +static int samsung_pll6553x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + return -ENOTSUPP; +} + +static const struct clk_ops samsung_pll6553x_clk_ops = { + .recalc_rate = samsung_pll6553x_recalc_rate, + .round_rate = samsung_pll6553x_round_rate, + .set_rate = samsung_pll6553x_set_rate, +}; + +struct clk * __init samsung_clk_register_pll6553x(const char *name, + const char *pname, const void __iomem *con_reg) +{ + struct samsung_clk_pll6553x *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_pll6553x_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; +} diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index f33786e..465ee6f 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -34,8 +34,16 @@ extern struct clk * __init samsung_clk_register_pll45xx(const char *name, extern struct clk * __init samsung_clk_register_pll46xx(const char *name, const char *pname, const void __iomem *con_reg, enum pll46xx_type type); +extern struct clk * __init samsung_clk_register_pll2126x(const char *name, + const char *pname, const void __iomem *con_reg); extern struct clk * __init samsung_clk_register_pll2550x(const char *name, const char *pname, const void __iomem *reg_base, const unsigned long offset); +extern struct clk * __init samsung_clk_register_pll3000x(const char *name, + const char *pname, const void __iomem *con_reg); +extern struct clk * __init samsung_clk_register_pll6552x(const char *name, + const char *pname, const void __iomem *con_reg); +extern struct clk * __init samsung_clk_register_pll6553x(const char *name, + const char *pname, const void __iomem *con_reg); #endif /* __SAMSUNG_CLK_PLL_H */