diff options
author | Viresh Kumar <viresh.kumar@st.com> | 2012-04-09 23:32:35 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-05-12 15:19:23 -0400 |
commit | 55b8fd4f428501b0f35d62b8313311fd9863c188 (patch) | |
tree | 2c61fe9c307baa73048345adbb11e20e5eeb586e /drivers/clk/spear/clk-vco-pll.c | |
parent | e12ff34402bd3a6cbeab0423012066874bb10f4b (diff) |
SPEAr: clk: Add VCO-PLL Synthesizer clock
All SPEAr SoC's contain PLLs. Their Fout is derived based on following equations
- In normal mode
vco = (2 * M[15:8] * Fin)/N
- In Dithered mode
vco = (2 * M[15:0] * Fin)/(256 * N)
pll_rate = vco/2^p
vco and pll are very closely bound to each other,
"vco needs to program: mode, m & n" and "pll needs to program p",
both share common enable/disable logic and registers.
This patch adds in support for this type of clock.
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Reviewed-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/spear/clk-vco-pll.c')
-rw-r--r-- | drivers/clk/spear/clk-vco-pll.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c new file mode 100644 index 000000000000..dcd4bdf4b0d9 --- /dev/null +++ b/drivers/clk/spear/clk-vco-pll.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 ST Microelectronics | ||
3 | * Viresh Kumar <viresh.kumar@st.com> | ||
4 | * | ||
5 | * This file is licensed under the terms of the GNU General Public | ||
6 | * License version 2. This program is licensed "as is" without any | ||
7 | * warranty of any kind, whether express or implied. | ||
8 | * | ||
9 | * VCO-PLL clock implementation | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "clk-vco-pll: " fmt | ||
13 | |||
14 | #include <linux/clk-provider.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/err.h> | ||
18 | #include "clk.h" | ||
19 | |||
20 | /* | ||
21 | * DOC: VCO-PLL clock | ||
22 | * | ||
23 | * VCO and PLL rate are derived from following equations: | ||
24 | * | ||
25 | * In normal mode | ||
26 | * vco = (2 * M[15:8] * Fin)/N | ||
27 | * | ||
28 | * In Dithered mode | ||
29 | * vco = (2 * M[15:0] * Fin)/(256 * N) | ||
30 | * | ||
31 | * pll_rate = pll/2^p | ||
32 | * | ||
33 | * vco and pll are very closely bound to each other, "vco needs to program: | ||
34 | * mode, m & n" and "pll needs to program p", both share common enable/disable | ||
35 | * logic. | ||
36 | * | ||
37 | * clk_register_vco_pll() registers instances of both vco & pll. | ||
38 | * CLK_SET_RATE_PARENT flag is forced for pll, as it will always pass its | ||
39 | * set_rate to vco. A single rate table exists for both the clocks, which | ||
40 | * configures m, n and p. | ||
41 | */ | ||
42 | |||
43 | /* PLL_CTR register masks */ | ||
44 | #define PLL_MODE_NORMAL 0 | ||
45 | #define PLL_MODE_FRACTION 1 | ||
46 | #define PLL_MODE_DITH_DSM 2 | ||
47 | #define PLL_MODE_DITH_SSM 3 | ||
48 | #define PLL_MODE_MASK 3 | ||
49 | #define PLL_MODE_SHIFT 3 | ||
50 | #define PLL_ENABLE 2 | ||
51 | |||
52 | #define PLL_LOCK_SHIFT 0 | ||
53 | #define PLL_LOCK_MASK 1 | ||
54 | |||
55 | /* PLL FRQ register masks */ | ||
56 | #define PLL_NORM_FDBK_M_MASK 0xFF | ||
57 | #define PLL_NORM_FDBK_M_SHIFT 24 | ||
58 | #define PLL_DITH_FDBK_M_MASK 0xFFFF | ||
59 | #define PLL_DITH_FDBK_M_SHIFT 16 | ||
60 | #define PLL_DIV_P_MASK 0x7 | ||
61 | #define PLL_DIV_P_SHIFT 8 | ||
62 | #define PLL_DIV_N_MASK 0xFF | ||
63 | #define PLL_DIV_N_SHIFT 0 | ||
64 | |||
65 | #define to_clk_vco(_hw) container_of(_hw, struct clk_vco, hw) | ||
66 | #define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) | ||
67 | |||
68 | /* Calculates pll clk rate for specific value of mode, m, n and p */ | ||
69 | static unsigned long pll_calc_rate(struct pll_rate_tbl *rtbl, | ||
70 | unsigned long prate, int index, unsigned long *pll_rate) | ||
71 | { | ||
72 | unsigned long rate = prate; | ||
73 | unsigned int mode; | ||
74 | |||
75 | mode = rtbl[index].mode ? 256 : 1; | ||
76 | rate = (((2 * rate / 10000) * rtbl[index].m) / (mode * rtbl[index].n)); | ||
77 | |||
78 | if (pll_rate) | ||
79 | *pll_rate = (rate / (1 << rtbl[index].p)) * 10000; | ||
80 | |||
81 | return rate * 10000; | ||
82 | } | ||
83 | |||
84 | static long clk_pll_round_rate_index(struct clk_hw *hw, unsigned long drate, | ||
85 | unsigned long *prate, int *index) | ||
86 | { | ||
87 | struct clk_pll *pll = to_clk_pll(hw); | ||
88 | unsigned long prev_rate, vco_prev_rate, rate = 0; | ||
89 | unsigned long vco_parent_rate = | ||
90 | __clk_get_rate(__clk_get_parent(__clk_get_parent(hw->clk))); | ||
91 | |||
92 | if (!prate) { | ||
93 | pr_err("%s: prate is must for pll clk\n", __func__); | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | |||
97 | for (*index = 0; *index < pll->vco->rtbl_cnt; (*index)++) { | ||
98 | prev_rate = rate; | ||
99 | vco_prev_rate = *prate; | ||
100 | *prate = pll_calc_rate(pll->vco->rtbl, vco_parent_rate, *index, | ||
101 | &rate); | ||
102 | if (drate < rate) { | ||
103 | /* previous clock was best */ | ||
104 | if (*index) { | ||
105 | rate = prev_rate; | ||
106 | *prate = vco_prev_rate; | ||
107 | (*index)--; | ||
108 | } | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | return rate; | ||
114 | } | ||
115 | |||
116 | static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate, | ||
117 | unsigned long *prate) | ||
118 | { | ||
119 | int unused; | ||
120 | |||
121 | return clk_pll_round_rate_index(hw, drate, prate, &unused); | ||
122 | } | ||
123 | |||
124 | static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long | ||
125 | parent_rate) | ||
126 | { | ||
127 | struct clk_pll *pll = to_clk_pll(hw); | ||
128 | unsigned long flags = 0; | ||
129 | unsigned int p; | ||
130 | |||
131 | if (pll->vco->lock) | ||
132 | spin_lock_irqsave(pll->vco->lock, flags); | ||
133 | |||
134 | p = readl_relaxed(pll->vco->cfg_reg); | ||
135 | |||
136 | if (pll->vco->lock) | ||
137 | spin_unlock_irqrestore(pll->vco->lock, flags); | ||
138 | |||
139 | p = (p >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK; | ||
140 | |||
141 | return parent_rate / (1 << p); | ||
142 | } | ||
143 | |||
144 | static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate, | ||
145 | unsigned long prate) | ||
146 | { | ||
147 | struct clk_pll *pll = to_clk_pll(hw); | ||
148 | struct pll_rate_tbl *rtbl = pll->vco->rtbl; | ||
149 | unsigned long flags = 0, val; | ||
150 | int i; | ||
151 | |||
152 | clk_pll_round_rate_index(hw, drate, NULL, &i); | ||
153 | |||
154 | if (pll->vco->lock) | ||
155 | spin_lock_irqsave(pll->vco->lock, flags); | ||
156 | |||
157 | val = readl_relaxed(pll->vco->cfg_reg); | ||
158 | val &= ~(PLL_DIV_P_MASK << PLL_DIV_P_SHIFT); | ||
159 | val |= (rtbl[i].p & PLL_DIV_P_MASK) << PLL_DIV_P_SHIFT; | ||
160 | writel_relaxed(val, pll->vco->cfg_reg); | ||
161 | |||
162 | if (pll->vco->lock) | ||
163 | spin_unlock_irqrestore(pll->vco->lock, flags); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static struct clk_ops clk_pll_ops = { | ||
169 | .recalc_rate = clk_pll_recalc_rate, | ||
170 | .round_rate = clk_pll_round_rate, | ||
171 | .set_rate = clk_pll_set_rate, | ||
172 | }; | ||
173 | |||
174 | static inline unsigned long vco_calc_rate(struct clk_hw *hw, | ||
175 | unsigned long prate, int index) | ||
176 | { | ||
177 | struct clk_vco *vco = to_clk_vco(hw); | ||
178 | |||
179 | return pll_calc_rate(vco->rtbl, prate, index, NULL); | ||
180 | } | ||
181 | |||
182 | static long clk_vco_round_rate(struct clk_hw *hw, unsigned long drate, | ||
183 | unsigned long *prate) | ||
184 | { | ||
185 | struct clk_vco *vco = to_clk_vco(hw); | ||
186 | int unused; | ||
187 | |||
188 | return clk_round_rate_index(hw, drate, *prate, vco_calc_rate, | ||
189 | vco->rtbl_cnt, &unused); | ||
190 | } | ||
191 | |||
192 | static unsigned long clk_vco_recalc_rate(struct clk_hw *hw, | ||
193 | unsigned long parent_rate) | ||
194 | { | ||
195 | struct clk_vco *vco = to_clk_vco(hw); | ||
196 | unsigned long flags = 0; | ||
197 | unsigned int num = 2, den = 0, val, mode = 0; | ||
198 | |||
199 | if (vco->lock) | ||
200 | spin_lock_irqsave(vco->lock, flags); | ||
201 | |||
202 | mode = (readl_relaxed(vco->mode_reg) >> PLL_MODE_SHIFT) & PLL_MODE_MASK; | ||
203 | |||
204 | val = readl_relaxed(vco->cfg_reg); | ||
205 | |||
206 | if (vco->lock) | ||
207 | spin_unlock_irqrestore(vco->lock, flags); | ||
208 | |||
209 | den = (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK; | ||
210 | |||
211 | /* calculate numerator & denominator */ | ||
212 | if (!mode) { | ||
213 | /* Normal mode */ | ||
214 | num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK; | ||
215 | } else { | ||
216 | /* Dithered mode */ | ||
217 | num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK; | ||
218 | den *= 256; | ||
219 | } | ||
220 | |||
221 | if (!den) { | ||
222 | WARN(1, "%s: denominator can't be zero\n", __func__); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | return (((parent_rate / 10000) * num) / den) * 10000; | ||
227 | } | ||
228 | |||
229 | /* Configures new clock rate of vco */ | ||
230 | static int clk_vco_set_rate(struct clk_hw *hw, unsigned long drate, | ||
231 | unsigned long prate) | ||
232 | { | ||
233 | struct clk_vco *vco = to_clk_vco(hw); | ||
234 | struct pll_rate_tbl *rtbl = vco->rtbl; | ||
235 | unsigned long flags = 0, val; | ||
236 | int i; | ||
237 | |||
238 | clk_round_rate_index(hw, drate, prate, vco_calc_rate, vco->rtbl_cnt, | ||
239 | &i); | ||
240 | |||
241 | if (vco->lock) | ||
242 | spin_lock_irqsave(vco->lock, flags); | ||
243 | |||
244 | val = readl_relaxed(vco->mode_reg); | ||
245 | val &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); | ||
246 | val |= (rtbl[i].mode & PLL_MODE_MASK) << PLL_MODE_SHIFT; | ||
247 | writel_relaxed(val, vco->mode_reg); | ||
248 | |||
249 | val = readl_relaxed(vco->cfg_reg); | ||
250 | val &= ~(PLL_DIV_N_MASK << PLL_DIV_N_SHIFT); | ||
251 | val |= (rtbl[i].n & PLL_DIV_N_MASK) << PLL_DIV_N_SHIFT; | ||
252 | |||
253 | val &= ~(PLL_DITH_FDBK_M_MASK << PLL_DITH_FDBK_M_SHIFT); | ||
254 | if (rtbl[i].mode) | ||
255 | val |= (rtbl[i].m & PLL_DITH_FDBK_M_MASK) << | ||
256 | PLL_DITH_FDBK_M_SHIFT; | ||
257 | else | ||
258 | val |= (rtbl[i].m & PLL_NORM_FDBK_M_MASK) << | ||
259 | PLL_NORM_FDBK_M_SHIFT; | ||
260 | |||
261 | writel_relaxed(val, vco->cfg_reg); | ||
262 | |||
263 | if (vco->lock) | ||
264 | spin_unlock_irqrestore(vco->lock, flags); | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static struct clk_ops clk_vco_ops = { | ||
270 | .recalc_rate = clk_vco_recalc_rate, | ||
271 | .round_rate = clk_vco_round_rate, | ||
272 | .set_rate = clk_vco_set_rate, | ||
273 | }; | ||
274 | |||
275 | struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, | ||
276 | const char *vco_gate_name, const char *parent_name, | ||
277 | unsigned long flags, void __iomem *mode_reg, void __iomem | ||
278 | *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, | ||
279 | spinlock_t *lock, struct clk **pll_clk, | ||
280 | struct clk **vco_gate_clk) | ||
281 | { | ||
282 | struct clk_vco *vco; | ||
283 | struct clk_pll *pll; | ||
284 | struct clk *vco_clk, *tpll_clk, *tvco_gate_clk; | ||
285 | struct clk_init_data vco_init, pll_init; | ||
286 | const char **vco_parent_name; | ||
287 | |||
288 | if (!vco_name || !pll_name || !parent_name || !mode_reg || !cfg_reg || | ||
289 | !rtbl || !rtbl_cnt) { | ||
290 | pr_err("Invalid arguments passed"); | ||
291 | return ERR_PTR(-EINVAL); | ||
292 | } | ||
293 | |||
294 | vco = kzalloc(sizeof(*vco), GFP_KERNEL); | ||
295 | if (!vco) { | ||
296 | pr_err("could not allocate vco clk\n"); | ||
297 | return ERR_PTR(-ENOMEM); | ||
298 | } | ||
299 | |||
300 | pll = kzalloc(sizeof(*pll), GFP_KERNEL); | ||
301 | if (!pll) { | ||
302 | pr_err("could not allocate pll clk\n"); | ||
303 | goto free_vco; | ||
304 | } | ||
305 | |||
306 | /* struct clk_vco assignments */ | ||
307 | vco->mode_reg = mode_reg; | ||
308 | vco->cfg_reg = cfg_reg; | ||
309 | vco->rtbl = rtbl; | ||
310 | vco->rtbl_cnt = rtbl_cnt; | ||
311 | vco->lock = lock; | ||
312 | vco->hw.init = &vco_init; | ||
313 | |||
314 | pll->vco = vco; | ||
315 | pll->hw.init = &pll_init; | ||
316 | |||
317 | if (vco_gate_name) { | ||
318 | tvco_gate_clk = clk_register_gate(NULL, vco_gate_name, | ||
319 | parent_name, 0, mode_reg, PLL_ENABLE, 0, lock); | ||
320 | if (IS_ERR_OR_NULL(tvco_gate_clk)) | ||
321 | goto free_pll; | ||
322 | |||
323 | if (vco_gate_clk) | ||
324 | *vco_gate_clk = tvco_gate_clk; | ||
325 | vco_parent_name = &vco_gate_name; | ||
326 | } else { | ||
327 | vco_parent_name = &parent_name; | ||
328 | } | ||
329 | |||
330 | vco_init.name = vco_name; | ||
331 | vco_init.ops = &clk_vco_ops; | ||
332 | vco_init.flags = flags; | ||
333 | vco_init.parent_names = vco_parent_name; | ||
334 | vco_init.num_parents = 1; | ||
335 | |||
336 | pll_init.name = pll_name; | ||
337 | pll_init.ops = &clk_pll_ops; | ||
338 | pll_init.flags = CLK_SET_RATE_PARENT; | ||
339 | pll_init.parent_names = &vco_name; | ||
340 | pll_init.num_parents = 1; | ||
341 | |||
342 | vco_clk = clk_register(NULL, &vco->hw); | ||
343 | if (IS_ERR_OR_NULL(vco_clk)) | ||
344 | goto free_pll; | ||
345 | |||
346 | tpll_clk = clk_register(NULL, &pll->hw); | ||
347 | if (IS_ERR_OR_NULL(tpll_clk)) | ||
348 | goto free_pll; | ||
349 | |||
350 | if (pll_clk) | ||
351 | *pll_clk = tpll_clk; | ||
352 | |||
353 | return vco_clk; | ||
354 | |||
355 | free_pll: | ||
356 | kfree(pll); | ||
357 | free_vco: | ||
358 | kfree(vco); | ||
359 | |||
360 | pr_err("Failed to register vco pll clock\n"); | ||
361 | |||
362 | return ERR_PTR(-ENOMEM); | ||
363 | } | ||