1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2016 Freescale Semiconductor, Inc. 4 * Copyright 2017~2018 NXP 5 * 6 * Author: Dong Aisheng <aisheng.dong@nxp.com> 7 * 8 */ 9 10 #include <linux/clk-provider.h> 11 #include <linux/err.h> 12 #include <linux/iopoll.h> 13 #include <linux/slab.h> 14 15 #include "clk.h" 16 17 /* PLL Control Status Register (xPLLCSR) */ 18 #define PLL_CSR_OFFSET 0x0 19 #define PLL_VLD BIT(24) 20 #define PLL_EN BIT(0) 21 22 /* PLL Configuration Register (xPLLCFG) */ 23 #define PLL_CFG_OFFSET 0x08 24 #define BP_PLL_MULT 16 25 #define BM_PLL_MULT (0x7f << 16) 26 27 /* PLL Numerator Register (xPLLNUM) */ 28 #define PLL_NUM_OFFSET 0x10 29 30 /* PLL Denominator Register (xPLLDENOM) */ 31 #define PLL_DENOM_OFFSET 0x14 32 33 #define MAX_MFD 0x3fffffff 34 #define DEFAULT_MFD 1000000 35 36 struct clk_pllv4 { 37 struct clk_hw hw; 38 void __iomem *base; 39 }; 40 41 /* Valid PLL MULT Table */ 42 static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16}; 43 44 #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw) 45 46 #define LOCK_TIMEOUT_US USEC_PER_MSEC 47 48 static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll) 49 { 50 u32 csr; 51 52 return readl_poll_timeout(pll->base + PLL_CSR_OFFSET, 53 csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US); 54 } 55 56 static int clk_pllv4_is_enabled(struct clk_hw *hw) 57 { 58 struct clk_pllv4 *pll = to_clk_pllv4(hw); 59 60 if (readl_relaxed(pll->base) & PLL_EN) 61 return 1; 62 63 return 0; 64 } 65 66 static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, 67 unsigned long parent_rate) 68 { 69 struct clk_pllv4 *pll = to_clk_pllv4(hw); 70 u32 mult, mfn, mfd; 71 u64 temp64; 72 73 mult = readl_relaxed(pll->base + PLL_CFG_OFFSET); 74 mult &= BM_PLL_MULT; 75 mult >>= BP_PLL_MULT; 76 77 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); 78 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); 79 temp64 = parent_rate; 80 temp64 *= mfn; 81 do_div(temp64, mfd); 82 83 return (parent_rate * mult) + (u32)temp64; 84 } 85 86 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, 87 unsigned long *prate) 88 { 89 unsigned long parent_rate = *prate; 90 unsigned long round_rate, i; 91 u32 mfn, mfd = DEFAULT_MFD; 92 bool found = false; 93 u64 temp64; 94 95 for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { 96 round_rate = parent_rate * pllv4_mult_table[i]; 97 if (rate >= round_rate) { 98 found = true; 99 break; 100 } 101 } 102 103 if (!found) { 104 pr_warn("%s: unable to round rate %lu, parent rate %lu\n", 105 clk_hw_get_name(hw), rate, parent_rate); 106 return 0; 107 } 108 109 if (parent_rate <= MAX_MFD) 110 mfd = parent_rate; 111 112 temp64 = (u64)(rate - round_rate); 113 temp64 *= mfd; 114 do_div(temp64, parent_rate); 115 mfn = temp64; 116 117 /* 118 * NOTE: The value of numerator must always be configured to be 119 * less than the value of the denominator. If we can't get a proper 120 * pair of mfn/mfd, we simply return the round_rate without using 121 * the frac part. 122 */ 123 if (mfn >= mfd) 124 return round_rate; 125 126 temp64 = (u64)parent_rate; 127 temp64 *= mfn; 128 do_div(temp64, mfd); 129 130 return round_rate + (u32)temp64; 131 } 132 133 static bool clk_pllv4_is_valid_mult(unsigned int mult) 134 { 135 int i; 136 137 /* check if mult is in valid MULT table */ 138 for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { 139 if (pllv4_mult_table[i] == mult) 140 return true; 141 } 142 143 return false; 144 } 145 146 static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, 147 unsigned long parent_rate) 148 { 149 struct clk_pllv4 *pll = to_clk_pllv4(hw); 150 u32 val, mult, mfn, mfd = DEFAULT_MFD; 151 u64 temp64; 152 153 mult = rate / parent_rate; 154 155 if (!clk_pllv4_is_valid_mult(mult)) 156 return -EINVAL; 157 158 if (parent_rate <= MAX_MFD) 159 mfd = parent_rate; 160 161 temp64 = (u64)(rate - mult * parent_rate); 162 temp64 *= mfd; 163 do_div(temp64, parent_rate); 164 mfn = temp64; 165 166 val = readl_relaxed(pll->base + PLL_CFG_OFFSET); 167 val &= ~BM_PLL_MULT; 168 val |= mult << BP_PLL_MULT; 169 writel_relaxed(val, pll->base + PLL_CFG_OFFSET); 170 171 writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET); 172 writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET); 173 174 return 0; 175 } 176 177 static int clk_pllv4_enable(struct clk_hw *hw) 178 { 179 u32 val; 180 struct clk_pllv4 *pll = to_clk_pllv4(hw); 181 182 val = readl_relaxed(pll->base); 183 val |= PLL_EN; 184 writel_relaxed(val, pll->base); 185 186 return clk_pllv4_wait_lock(pll); 187 } 188 189 static void clk_pllv4_disable(struct clk_hw *hw) 190 { 191 u32 val; 192 struct clk_pllv4 *pll = to_clk_pllv4(hw); 193 194 val = readl_relaxed(pll->base); 195 val &= ~PLL_EN; 196 writel_relaxed(val, pll->base); 197 } 198 199 static const struct clk_ops clk_pllv4_ops = { 200 .recalc_rate = clk_pllv4_recalc_rate, 201 .round_rate = clk_pllv4_round_rate, 202 .set_rate = clk_pllv4_set_rate, 203 .enable = clk_pllv4_enable, 204 .disable = clk_pllv4_disable, 205 .is_enabled = clk_pllv4_is_enabled, 206 }; 207 208 struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name, 209 void __iomem *base) 210 { 211 struct clk_pllv4 *pll; 212 struct clk_hw *hw; 213 struct clk_init_data init; 214 int ret; 215 216 pll = kzalloc(sizeof(*pll), GFP_KERNEL); 217 if (!pll) 218 return ERR_PTR(-ENOMEM); 219 220 pll->base = base; 221 222 init.name = name; 223 init.ops = &clk_pllv4_ops; 224 init.parent_names = &parent_name; 225 init.num_parents = 1; 226 init.flags = CLK_SET_RATE_GATE; 227 228 pll->hw.init = &init; 229 230 hw = &pll->hw; 231 ret = clk_hw_register(NULL, hw); 232 if (ret) { 233 kfree(pll); 234 hw = ERR_PTR(ret); 235 } 236 237 return hw; 238 } 239