| /* |
| * Copyright 2017-2018 NXP. |
| * |
| * The code contained herein is licensed under the GNU General Public |
| * License. You may obtain a copy of the GNU General Public License |
| * Version 2 or later at the following locations: |
| * |
| * http://www.opensource.org/licenses/gpl-license.html |
| * http://www.gnu.org/copyleft/gpl.html |
| */ |
| #include <linux/clk.h> |
| #include <linux/err.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| |
| #include "clk.h" |
| |
| /* PLL CFGs */ |
| #define PLL_CFG0 0x0 |
| #define PLL_CFG1 0x4 |
| #define PLL_CFG2 0x8 |
| |
| #define PLL_DIVF1_SHIFT 13 |
| #define PLL_DIVF2_SHIFT 7 |
| #define PLL_DIVF_MASK 0x3f |
| |
| #define PLL_DIVR1_SHIFT 25 |
| #define PLL_DIVR2_SHIFT 19 |
| #define PLL_DIVR1_MASK 0x3 |
| #define PLL_DIVR2_MASK 0x3f |
| #define PLL_REF_SHIFT 0 |
| #define PLL_REF_MASK 0x3 |
| #define PLL_REF_OSC_25M 0 |
| #define PLL_REF_OSC_27M 1 |
| #define PLL_REF_PHY_27M 2 |
| |
| #define PLL_LOCK 31 |
| #define PLL_PD 7 |
| |
| #define OSC_25M 25000000 |
| #define OSC_27M 27000000 |
| |
| struct clk_sccg_pll { |
| struct clk_hw hw; |
| void __iomem *base; |
| }; |
| |
| #define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw) |
| |
| static int clk_pll1_is_prepared(struct clk_hw *hw) |
| { |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| u32 val; |
| |
| val = readl_relaxed(pll->base + PLL_CFG0); |
| return (val & (1 << PLL_PD)) ? 0 : 1; |
| } |
| |
| static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw, |
| unsigned long parent_rate) |
| { |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| u32 val, divf; |
| |
| val = readl_relaxed(pll->base + PLL_CFG2); |
| divf = (val >> PLL_DIVF1_SHIFT) & PLL_DIVF_MASK; |
| |
| return parent_rate * 2 * (divf + 1); |
| } |
| |
| static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long *prate) |
| { |
| unsigned long parent_rate = *prate; |
| u32 div; |
| |
| div = rate / (parent_rate * 2); |
| |
| return parent_rate * div * 2; |
| } |
| |
| static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long parent_rate) |
| { |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| u32 val; |
| u32 divf; |
| |
| divf = rate / (parent_rate * 2); |
| |
| val = readl_relaxed(pll->base + PLL_CFG2); |
| val &= ~(PLL_DIVF_MASK << PLL_DIVF1_SHIFT); |
| val |= (divf - 1) << PLL_DIVF1_SHIFT; |
| writel_relaxed(val, pll->base + PLL_CFG2); |
| /* FIXME PLL lock check */ |
| |
| return 0; |
| } |
| |
| static int clk_pll1_prepare(struct clk_hw *hw) |
| { |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| u32 val; |
| |
| val = readl_relaxed(pll->base); |
| val &= ~(1 << PLL_PD); |
| writel_relaxed(val, pll->base); |
| |
| /* wait for PLL locked */ |
| |
| return 0; |
| } |
| |
| static void clk_pll1_unprepare(struct clk_hw *hw) |
| { |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| u32 val; |
| |
| val = readl_relaxed(pll->base); |
| val |= (1 << PLL_PD); |
| writel_relaxed(val, pll->base); |
| } |
| |
| static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw, |
| unsigned long parent_rate) |
| { |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| u32 val, ref, divr1, divf1, divr2, divf2; |
| u64 temp64; |
| |
| val = readl_relaxed(pll->base + PLL_CFG0); |
| switch ((val >> PLL_REF_SHIFT) & PLL_REF_MASK) { |
| case PLL_REF_OSC_25M: |
| ref = OSC_25M; |
| break; |
| case PLL_REF_OSC_27M: |
| case PLL_REF_PHY_27M: |
| ref = OSC_27M; |
| break; |
| default: |
| ref = OSC_25M; |
| break; |
| } |
| |
| val = readl_relaxed(pll->base + PLL_CFG2); |
| divr1 = (val >> PLL_DIVR1_SHIFT) & PLL_DIVR1_MASK; |
| divr2 = (val >> PLL_DIVR2_SHIFT) & PLL_DIVR2_MASK; |
| divf1 = (val >> PLL_DIVF1_SHIFT) & PLL_DIVF_MASK; |
| divf2 = (val >> PLL_DIVF2_SHIFT) & PLL_DIVF_MASK; |
| |
| temp64 = ref * 2; |
| temp64 *= (divf1 + 1) * (divf2 + 1); |
| |
| do_div(temp64, (divr1 + 1) * (divr2 + 1)); |
| |
| return (unsigned long)temp64; |
| } |
| |
| static long clk_pll2_round_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long *prate) |
| { |
| u32 div; |
| unsigned long parent_rate = *prate; |
| |
| /* FIXME */ |
| div = rate / (parent_rate * 2); |
| |
| return parent_rate * div * 2; |
| } |
| |
| static int clk_pll2_set_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long parent_rate) |
| { |
| u32 val; |
| u32 divf; |
| struct clk_sccg_pll *pll = to_clk_sccg_pll(hw); |
| |
| divf = rate / (parent_rate * 2); |
| |
| val = readl_relaxed(pll->base + PLL_CFG2); |
| val &= ~(PLL_DIVF_MASK << PLL_DIVF2_SHIFT); |
| val |= (divf - 1) << PLL_DIVF2_SHIFT; |
| writel_relaxed(val, pll->base + PLL_CFG2); |
| /* FIXME PLL lock check */ |
| |
| return 0; |
| } |
| |
| static const struct clk_ops clk_sccg_pll1_ops = { |
| .is_prepared = clk_pll1_is_prepared, |
| .recalc_rate = clk_pll1_recalc_rate, |
| .round_rate = clk_pll1_round_rate, |
| .set_rate = clk_pll1_set_rate, |
| }; |
| |
| static const struct clk_ops clk_sccg_pll2_ops = { |
| .prepare = clk_pll1_prepare, |
| .unprepare = clk_pll1_unprepare, |
| .recalc_rate = clk_pll2_recalc_rate, |
| .round_rate = clk_pll2_round_rate, |
| .set_rate = clk_pll2_set_rate, |
| }; |
| |
| struct clk *imx_clk_sccg_pll(const char *name, const char *parent_name, |
| void __iomem *base, enum imx_sccg_pll_type pll_type) |
| { |
| struct clk_sccg_pll *pll; |
| struct clk *clk; |
| struct clk_init_data init; |
| |
| pll = kzalloc(sizeof(*pll), GFP_KERNEL); |
| if (!pll) |
| return ERR_PTR(-ENOMEM); |
| |
| pll->base = base; |
| init.name = name; |
| switch (pll_type) { |
| case SCCG_PLL1: |
| init.ops = &clk_sccg_pll1_ops; |
| break; |
| case SCCG_PLL2: |
| init.ops = &clk_sccg_pll2_ops; |
| break; |
| } |
| |
| init.flags = CLK_SET_RATE_GATE | CLK_GET_RATE_NOCACHE; |
| init.parent_names = &parent_name; |
| init.num_parents = 1; |
| |
| pll->hw.init = &init; |
| |
| clk = clk_register(NULL, &pll->hw); |
| if (IS_ERR(clk)) |
| kfree(pll); |
| |
| return clk; |
| } |