Blame view
drivers/clk/imx/clk-pllv4.c
4.01 KB
14a5e140a MLK-13441-5 ARM: ... |
1 |
/* |
d29959f0f MLK-13568: ARM: c... |
2 |
* Copyright (C) 2016 Freescale Semiconductor, Inc. |
14a5e140a MLK-13441-5 ARM: ... |
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
* * 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/clk-provider.h> #include <linux/err.h> #include <linux/slab.h> #include "clk.h" #define PLL_EN BIT(0) #define BP_PLL_DIV 16 #define BM_PLL_DIV (0x7f << 16) #define PLL_CFG_OFFSET 0x08 #define PLL_NUM_OFFSET 0x10 #define PLL_DENOM_OFFSET 0x14 struct clk_pllv4 { struct clk_hw hw; void __iomem *base; u32 div_mask; u32 div_shift; u32 cfg_offset; u32 num_offset; u32 denom_offset; }; #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw) static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_pllv4 *pll = to_clk_pllv4(hw); u32 mfn = readl_relaxed(pll->base + pll->num_offset); u32 mfd = readl_relaxed(pll->base + pll->denom_offset); u32 div = (readl_relaxed(pll->base + pll->cfg_offset) & pll->div_mask) >> pll->div_shift; u64 temp64 = (u64)parent_rate; temp64 *= mfn; do_div(temp64, mfd); return (parent_rate * div) + (u32)temp64; } static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { unsigned long parent_rate = *prate; unsigned long min_rate = parent_rate * 16; unsigned long max_rate = parent_rate * 30; u32 div; u32 mfn, mfd = 1000000; u64 temp64; if (rate > max_rate) rate = max_rate; else if (rate < min_rate) rate = min_rate; div = rate / parent_rate; temp64 = (u64) (rate - div * parent_rate); temp64 *= mfd; do_div(temp64, parent_rate); mfn = temp64; return parent_rate * div + parent_rate / mfd * mfn; } static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_pllv4 *pll = to_clk_pllv4(hw); unsigned long min_rate = parent_rate * 16; unsigned long max_rate = parent_rate * 30; u32 val, div; u32 mfn, mfd = 1000000; u64 temp64; if (rate < min_rate || rate > max_rate) return -EINVAL; div = rate / parent_rate; temp64 = (u64) (rate - div * parent_rate); temp64 *= mfd; do_div(temp64, parent_rate); mfn = temp64; val = readl_relaxed(pll->base + pll->cfg_offset); val &= ~pll->div_mask; |
d29959f0f MLK-13568: ARM: c... |
99 |
val |= (div << pll->div_shift); |
14a5e140a MLK-13441-5 ARM: ... |
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
writel_relaxed(val, pll->base + pll->cfg_offset); writel_relaxed(mfn, pll->base + pll->num_offset); writel_relaxed(mfd, pll->base + pll->denom_offset); return 0; } static int clk_pllv4_enable(struct clk_hw *hw) { u32 val; struct clk_pllv4 *pll = to_clk_pllv4(hw); val = readl_relaxed(pll->base); val |= PLL_EN; writel_relaxed(val, pll->base); return 0; } static void clk_pllv4_disable(struct clk_hw *hw) { u32 val; struct clk_pllv4 *pll = to_clk_pllv4(hw); val = readl_relaxed(pll->base); val &= ~PLL_EN; writel_relaxed(val, pll->base); } static int clk_pllv4_is_enabled(struct clk_hw *hw) { struct clk_pllv4 *pll = to_clk_pllv4(hw); if (readl_relaxed(pll->base) & PLL_EN) return 0; return 1; } static const struct clk_ops clk_pllv4_ops = { .recalc_rate = clk_pllv4_recalc_rate, .round_rate = clk_pllv4_round_rate, .set_rate = clk_pllv4_set_rate, .enable = clk_pllv4_enable, .disable = clk_pllv4_disable, .is_enabled = clk_pllv4_is_enabled, }; struct clk *imx_clk_pllv4(const char *name, const char *parent_name, void __iomem *base) { struct clk_pllv4 *pll; struct clk *clk; struct clk_init_data init; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) return ERR_PTR(-ENOMEM); pll->base = base; pll->div_mask = BM_PLL_DIV; pll->div_shift = BP_PLL_DIV; pll->cfg_offset = PLL_CFG_OFFSET; pll->num_offset = PLL_NUM_OFFSET; pll->denom_offset = PLL_DENOM_OFFSET; init.name = name; init.ops = &clk_pllv4_ops; 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; } |