diff options
author | Heiko Stübner <heiko@sntech.de> | 2014-07-02 19:59:10 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-07-13 15:17:06 -0400 |
commit | 90c590254051f511299538c158e12fdad41ce163 (patch) | |
tree | d77e33a138a6b9ec55f2b7f0cdbf259cb3cffff4 /drivers/clk | |
parent | a245fecbb8064641d9cc317b347b5bdb2b7a4bb6 (diff) |
clk: rockchip: add clock type for pll clocks and pll used on rk3066
All known Rockchip SoCs down to the RK28xx (ARM9) use a similar pattern to
handle their plls:
|--\
xin32k ----------------|mux\
xin24m -----| pll |----|pll|--- pll output
\---------------|src/
|--/
The pll output is sourced from 1 of 3 sources, the actual pll being one of
them. To change the pll frequency it is imperative to remux it to another
source beforehand. This is done by adding a clock-listener to the pll that
handles the remuxing before and after the rate change.
The output mux is implemented as a separate clock to make use of already
existing common-clock features for disabling the pll if one of the other
two sources is used.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Acked-By: Max Schwarz <max.schwarz@online.de>
Tested-By: Max Schwarz <max.schwarz@online.de>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/rockchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-pll.c | 431 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.c | 35 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.h | 74 |
4 files changed, 541 insertions, 0 deletions
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 0068a8b560b3..2cb916496040 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile | |||
@@ -4,3 +4,4 @@ | |||
4 | 4 | ||
5 | obj-y += clk-rockchip.o | 5 | obj-y += clk-rockchip.o |
6 | obj-y += clk.o | 6 | obj-y += clk.o |
7 | obj-y += clk-pll.o | ||
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c new file mode 100644 index 000000000000..f2a1c7abf4d9 --- /dev/null +++ b/drivers/clk/rockchip/clk-pll.c | |||
@@ -0,0 +1,431 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 MundoReader S.L. | ||
3 | * Author: Heiko Stuebner <heiko@sntech.de> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <asm/div64.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/clk-provider.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include "clk.h" | ||
24 | |||
25 | #define PLL_MODE_MASK 0x3 | ||
26 | #define PLL_MODE_SLOW 0x0 | ||
27 | #define PLL_MODE_NORM 0x1 | ||
28 | #define PLL_MODE_DEEP 0x2 | ||
29 | |||
30 | struct rockchip_clk_pll { | ||
31 | struct clk_hw hw; | ||
32 | |||
33 | struct clk_mux pll_mux; | ||
34 | const struct clk_ops *pll_mux_ops; | ||
35 | |||
36 | struct notifier_block clk_nb; | ||
37 | bool rate_change_remuxed; | ||
38 | |||
39 | void __iomem *reg_base; | ||
40 | int lock_offset; | ||
41 | unsigned int lock_shift; | ||
42 | enum rockchip_pll_type type; | ||
43 | const struct rockchip_pll_rate_table *rate_table; | ||
44 | unsigned int rate_count; | ||
45 | spinlock_t *lock; | ||
46 | }; | ||
47 | |||
48 | #define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw) | ||
49 | #define to_rockchip_clk_pll_nb(nb) \ | ||
50 | container_of(nb, struct rockchip_clk_pll, clk_nb) | ||
51 | |||
52 | static const struct rockchip_pll_rate_table *rockchip_get_pll_settings( | ||
53 | struct rockchip_clk_pll *pll, unsigned long rate) | ||
54 | { | ||
55 | const struct rockchip_pll_rate_table *rate_table = pll->rate_table; | ||
56 | int i; | ||
57 | |||
58 | for (i = 0; i < pll->rate_count; i++) { | ||
59 | if (rate == rate_table[i].rate) | ||
60 | return &rate_table[i]; | ||
61 | } | ||
62 | |||
63 | return NULL; | ||
64 | } | ||
65 | |||
66 | static long rockchip_pll_round_rate(struct clk_hw *hw, | ||
67 | unsigned long drate, unsigned long *prate) | ||
68 | { | ||
69 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); | ||
70 | const struct rockchip_pll_rate_table *rate_table = pll->rate_table; | ||
71 | int i; | ||
72 | |||
73 | /* Assumming rate_table is in descending order */ | ||
74 | for (i = 0; i < pll->rate_count; i++) { | ||
75 | if (drate >= rate_table[i].rate) | ||
76 | return rate_table[i].rate; | ||
77 | } | ||
78 | |||
79 | /* return minimum supported value */ | ||
80 | return rate_table[i - 1].rate; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Wait for the pll to reach the locked state. | ||
85 | * The calling set_rate function is responsible for making sure the | ||
86 | * grf regmap is available. | ||
87 | */ | ||
88 | static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) | ||
89 | { | ||
90 | struct regmap *grf = rockchip_clk_get_grf(); | ||
91 | unsigned int val; | ||
92 | int delay = 24000000, ret; | ||
93 | |||
94 | while (delay > 0) { | ||
95 | ret = regmap_read(grf, pll->lock_offset, &val); | ||
96 | if (ret) { | ||
97 | pr_err("%s: failed to read pll lock status: %d\n", | ||
98 | __func__, ret); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | if (val & BIT(pll->lock_shift)) | ||
103 | return 0; | ||
104 | delay--; | ||
105 | } | ||
106 | |||
107 | pr_err("%s: timeout waiting for pll to lock\n", __func__); | ||
108 | return -ETIMEDOUT; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Set pll mux when changing the pll rate. | ||
113 | * This makes sure to move the pll mux away from the actual pll before | ||
114 | * changing its rate and back to the original parent after the change. | ||
115 | */ | ||
116 | static int rockchip_pll_notifier_cb(struct notifier_block *nb, | ||
117 | unsigned long event, void *data) | ||
118 | { | ||
119 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb); | ||
120 | struct clk_mux *pll_mux = &pll->pll_mux; | ||
121 | const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; | ||
122 | int cur_parent; | ||
123 | |||
124 | switch (event) { | ||
125 | case PRE_RATE_CHANGE: | ||
126 | cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); | ||
127 | if (cur_parent == PLL_MODE_NORM) { | ||
128 | pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); | ||
129 | pll->rate_change_remuxed = 1; | ||
130 | } | ||
131 | break; | ||
132 | case POST_RATE_CHANGE: | ||
133 | if (pll->rate_change_remuxed) { | ||
134 | pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); | ||
135 | pll->rate_change_remuxed = 0; | ||
136 | } | ||
137 | break; | ||
138 | } | ||
139 | |||
140 | return NOTIFY_OK; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * PLL used in RK3066, RK3188 and RK3288 | ||
145 | */ | ||
146 | |||
147 | #define RK3066_PLL_RESET_DELAY(nr) ((nr * 500) / 24 + 1) | ||
148 | |||
149 | #define RK3066_PLLCON(i) (i * 0x4) | ||
150 | #define RK3066_PLLCON0_OD_MASK 0xf | ||
151 | #define RK3066_PLLCON0_OD_SHIFT 0 | ||
152 | #define RK3066_PLLCON0_NR_MASK 0x3f | ||
153 | #define RK3066_PLLCON0_NR_SHIFT 8 | ||
154 | #define RK3066_PLLCON1_NF_MASK 0x1fff | ||
155 | #define RK3066_PLLCON1_NF_SHIFT 0 | ||
156 | #define RK3066_PLLCON2_BWADJ_MASK 0xfff | ||
157 | #define RK3066_PLLCON2_BWADJ_SHIFT 0 | ||
158 | #define RK3066_PLLCON3_RESET (1 << 5) | ||
159 | #define RK3066_PLLCON3_PWRDOWN (1 << 1) | ||
160 | #define RK3066_PLLCON3_BYPASS (1 << 0) | ||
161 | |||
162 | static unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw, | ||
163 | unsigned long prate) | ||
164 | { | ||
165 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); | ||
166 | u64 nf, nr, no, rate64 = prate; | ||
167 | u32 pllcon; | ||
168 | |||
169 | pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(3)); | ||
170 | if (pllcon & RK3066_PLLCON3_BYPASS) { | ||
171 | pr_debug("%s: pll %s is bypassed\n", __func__, | ||
172 | __clk_get_name(hw->clk)); | ||
173 | return prate; | ||
174 | } | ||
175 | |||
176 | pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1)); | ||
177 | nf = (pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK; | ||
178 | |||
179 | pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0)); | ||
180 | nr = (pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK; | ||
181 | no = (pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK; | ||
182 | |||
183 | rate64 *= (nf + 1); | ||
184 | do_div(rate64, nr + 1); | ||
185 | do_div(rate64, no + 1); | ||
186 | |||
187 | return (unsigned long)rate64; | ||
188 | } | ||
189 | |||
190 | static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, | ||
191 | unsigned long prate) | ||
192 | { | ||
193 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); | ||
194 | const struct rockchip_pll_rate_table *rate; | ||
195 | unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); | ||
196 | struct regmap *grf = rockchip_clk_get_grf(); | ||
197 | int ret; | ||
198 | |||
199 | if (IS_ERR(grf)) { | ||
200 | pr_debug("%s: grf regmap not available, aborting rate change\n", | ||
201 | __func__); | ||
202 | return PTR_ERR(grf); | ||
203 | } | ||
204 | |||
205 | pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n", | ||
206 | __func__, __clk_get_name(hw->clk), old_rate, drate, prate); | ||
207 | |||
208 | /* Get required rate settings from table */ | ||
209 | rate = rockchip_get_pll_settings(pll, drate); | ||
210 | if (!rate) { | ||
211 | pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, | ||
212 | drate, __clk_get_name(hw->clk)); | ||
213 | return -EINVAL; | ||
214 | } | ||
215 | |||
216 | pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", | ||
217 | __func__, rate->rate, rate->nr, rate->no, rate->nf); | ||
218 | |||
219 | /* enter reset mode */ | ||
220 | writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), | ||
221 | pll->reg_base + RK3066_PLLCON(3)); | ||
222 | |||
223 | /* update pll values */ | ||
224 | writel(HIWORD_UPDATE(rate->nr - 1, RK3066_PLLCON0_NR_MASK, | ||
225 | RK3066_PLLCON0_NR_SHIFT) | | ||
226 | HIWORD_UPDATE(rate->no - 1, RK3066_PLLCON0_OD_MASK, | ||
227 | RK3066_PLLCON0_OD_SHIFT), | ||
228 | pll->reg_base + RK3066_PLLCON(0)); | ||
229 | |||
230 | writel_relaxed(HIWORD_UPDATE(rate->nf - 1, RK3066_PLLCON1_NF_MASK, | ||
231 | RK3066_PLLCON1_NF_SHIFT), | ||
232 | pll->reg_base + RK3066_PLLCON(1)); | ||
233 | writel_relaxed(HIWORD_UPDATE(rate->bwadj, RK3066_PLLCON2_BWADJ_MASK, | ||
234 | RK3066_PLLCON2_BWADJ_SHIFT), | ||
235 | pll->reg_base + RK3066_PLLCON(2)); | ||
236 | |||
237 | /* leave reset and wait the reset_delay */ | ||
238 | writel(HIWORD_UPDATE(0, RK3066_PLLCON3_RESET, 0), | ||
239 | pll->reg_base + RK3066_PLLCON(3)); | ||
240 | udelay(RK3066_PLL_RESET_DELAY(rate->nr)); | ||
241 | |||
242 | /* wait for the pll to lock */ | ||
243 | ret = rockchip_pll_wait_lock(pll); | ||
244 | if (ret) { | ||
245 | pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", | ||
246 | __func__, old_rate); | ||
247 | rockchip_rk3066_pll_set_rate(hw, old_rate, prate); | ||
248 | } | ||
249 | |||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | static int rockchip_rk3066_pll_enable(struct clk_hw *hw) | ||
254 | { | ||
255 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); | ||
256 | |||
257 | writel(HIWORD_UPDATE(0, RK3066_PLLCON3_PWRDOWN, 0), | ||
258 | pll->reg_base + RK3066_PLLCON(3)); | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static void rockchip_rk3066_pll_disable(struct clk_hw *hw) | ||
264 | { | ||
265 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); | ||
266 | |||
267 | writel(HIWORD_UPDATE(RK3066_PLLCON3_PWRDOWN, | ||
268 | RK3066_PLLCON3_PWRDOWN, 0), | ||
269 | pll->reg_base + RK3066_PLLCON(3)); | ||
270 | } | ||
271 | |||
272 | static int rockchip_rk3066_pll_is_enabled(struct clk_hw *hw) | ||
273 | { | ||
274 | struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); | ||
275 | u32 pllcon = readl(pll->reg_base + RK3066_PLLCON(3)); | ||
276 | |||
277 | return !(pllcon & RK3066_PLLCON3_PWRDOWN); | ||
278 | } | ||
279 | |||
280 | static const struct clk_ops rockchip_rk3066_pll_clk_norate_ops = { | ||
281 | .recalc_rate = rockchip_rk3066_pll_recalc_rate, | ||
282 | .enable = rockchip_rk3066_pll_enable, | ||
283 | .disable = rockchip_rk3066_pll_disable, | ||
284 | .is_enabled = rockchip_rk3066_pll_is_enabled, | ||
285 | }; | ||
286 | |||
287 | static const struct clk_ops rockchip_rk3066_pll_clk_ops = { | ||
288 | .recalc_rate = rockchip_rk3066_pll_recalc_rate, | ||
289 | .round_rate = rockchip_pll_round_rate, | ||
290 | .set_rate = rockchip_rk3066_pll_set_rate, | ||
291 | .enable = rockchip_rk3066_pll_enable, | ||
292 | .disable = rockchip_rk3066_pll_disable, | ||
293 | .is_enabled = rockchip_rk3066_pll_is_enabled, | ||
294 | }; | ||
295 | |||
296 | /* | ||
297 | * Common registering of pll clocks | ||
298 | */ | ||
299 | |||
300 | struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | ||
301 | const char *name, const char **parent_names, u8 num_parents, | ||
302 | void __iomem *base, int con_offset, int grf_lock_offset, | ||
303 | int lock_shift, int mode_offset, int mode_shift, | ||
304 | struct rockchip_pll_rate_table *rate_table, | ||
305 | spinlock_t *lock) | ||
306 | { | ||
307 | const char *pll_parents[3]; | ||
308 | struct clk_init_data init; | ||
309 | struct rockchip_clk_pll *pll; | ||
310 | struct clk_mux *pll_mux; | ||
311 | struct clk *pll_clk, *mux_clk; | ||
312 | char pll_name[20]; | ||
313 | int ret; | ||
314 | |||
315 | if (num_parents != 2) { | ||
316 | pr_err("%s: needs two parent clocks\n", __func__); | ||
317 | return ERR_PTR(-EINVAL); | ||
318 | } | ||
319 | |||
320 | /* name the actual pll */ | ||
321 | snprintf(pll_name, sizeof(pll_name), "pll_%s", name); | ||
322 | |||
323 | pll = kzalloc(sizeof(*pll), GFP_KERNEL); | ||
324 | if (!pll) | ||
325 | return ERR_PTR(-ENOMEM); | ||
326 | |||
327 | init.name = pll_name; | ||
328 | |||
329 | /* keep all plls untouched for now */ | ||
330 | init.flags = CLK_IGNORE_UNUSED; | ||
331 | |||
332 | init.parent_names = &parent_names[0]; | ||
333 | init.num_parents = 1; | ||
334 | |||
335 | if (rate_table) { | ||
336 | int len; | ||
337 | |||
338 | /* find count of rates in rate_table */ | ||
339 | for (len = 0; rate_table[len].rate != 0; ) | ||
340 | len++; | ||
341 | |||
342 | pll->rate_count = len; | ||
343 | pll->rate_table = kmemdup(rate_table, | ||
344 | pll->rate_count * | ||
345 | sizeof(struct rockchip_pll_rate_table), | ||
346 | GFP_KERNEL); | ||
347 | WARN(!pll->rate_table, | ||
348 | "%s: could not allocate rate table for %s\n", | ||
349 | __func__, name); | ||
350 | } | ||
351 | |||
352 | switch (pll_type) { | ||
353 | case pll_rk3066: | ||
354 | if (!pll->rate_table) | ||
355 | init.ops = &rockchip_rk3066_pll_clk_norate_ops; | ||
356 | else | ||
357 | init.ops = &rockchip_rk3066_pll_clk_ops; | ||
358 | break; | ||
359 | default: | ||
360 | pr_warn("%s: Unknown pll type for pll clk %s\n", | ||
361 | __func__, name); | ||
362 | } | ||
363 | |||
364 | pll->hw.init = &init; | ||
365 | pll->type = pll_type; | ||
366 | pll->reg_base = base + con_offset; | ||
367 | pll->lock_offset = grf_lock_offset; | ||
368 | pll->lock_shift = lock_shift; | ||
369 | pll->lock = lock; | ||
370 | pll->clk_nb.notifier_call = rockchip_pll_notifier_cb; | ||
371 | |||
372 | pll_clk = clk_register(NULL, &pll->hw); | ||
373 | if (IS_ERR(pll_clk)) { | ||
374 | pr_err("%s: failed to register pll clock %s : %ld\n", | ||
375 | __func__, name, PTR_ERR(pll_clk)); | ||
376 | mux_clk = pll_clk; | ||
377 | goto err_pll; | ||
378 | } | ||
379 | |||
380 | ret = clk_notifier_register(pll_clk, &pll->clk_nb); | ||
381 | if (ret) { | ||
382 | pr_err("%s: failed to register clock notifier for %s : %d\n", | ||
383 | __func__, name, ret); | ||
384 | mux_clk = ERR_PTR(ret); | ||
385 | goto err_pll_notifier; | ||
386 | } | ||
387 | |||
388 | /* create the mux on top of the real pll */ | ||
389 | pll->pll_mux_ops = &clk_mux_ops; | ||
390 | pll_mux = &pll->pll_mux; | ||
391 | |||
392 | /* the actual muxing is xin24m, pll-output, xin32k */ | ||
393 | pll_parents[0] = parent_names[0]; | ||
394 | pll_parents[1] = pll_name; | ||
395 | pll_parents[2] = parent_names[1]; | ||
396 | |||
397 | init.name = name; | ||
398 | init.flags = CLK_SET_RATE_PARENT; | ||
399 | init.ops = pll->pll_mux_ops; | ||
400 | init.parent_names = pll_parents; | ||
401 | init.num_parents = ARRAY_SIZE(pll_parents); | ||
402 | |||
403 | pll_mux->reg = base + mode_offset; | ||
404 | pll_mux->shift = mode_shift; | ||
405 | pll_mux->mask = PLL_MODE_MASK; | ||
406 | pll_mux->flags = 0; | ||
407 | pll_mux->lock = lock; | ||
408 | pll_mux->hw.init = &init; | ||
409 | |||
410 | if (pll_type == pll_rk3066) | ||
411 | pll_mux->flags |= CLK_MUX_HIWORD_MASK; | ||
412 | |||
413 | mux_clk = clk_register(NULL, &pll_mux->hw); | ||
414 | if (IS_ERR(mux_clk)) | ||
415 | goto err_mux; | ||
416 | |||
417 | return mux_clk; | ||
418 | |||
419 | err_mux: | ||
420 | ret = clk_notifier_unregister(pll_clk, &pll->clk_nb); | ||
421 | if (ret) { | ||
422 | pr_err("%s: could not unregister clock notifier in error path : %d\n", | ||
423 | __func__, ret); | ||
424 | return mux_clk; | ||
425 | } | ||
426 | err_pll_notifier: | ||
427 | clk_unregister(pll_clk); | ||
428 | err_pll: | ||
429 | kfree(pll); | ||
430 | return mux_clk; | ||
431 | } | ||
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index aa15d5ae51d1..278cf9dd1e23 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/clk-provider.h> | 25 | #include <linux/clk-provider.h> |
26 | #include <linux/mfd/syscon.h> | ||
27 | #include <linux/regmap.h> | ||
26 | #include "clk.h" | 28 | #include "clk.h" |
27 | 29 | ||
28 | /** | 30 | /** |
@@ -105,11 +107,15 @@ static DEFINE_SPINLOCK(clk_lock); | |||
105 | static struct clk **clk_table; | 107 | static struct clk **clk_table; |
106 | static void __iomem *reg_base; | 108 | static void __iomem *reg_base; |
107 | static struct clk_onecell_data clk_data; | 109 | static struct clk_onecell_data clk_data; |
110 | static struct device_node *cru_node; | ||
111 | static struct regmap *grf; | ||
108 | 112 | ||
109 | void __init rockchip_clk_init(struct device_node *np, void __iomem *base, | 113 | void __init rockchip_clk_init(struct device_node *np, void __iomem *base, |
110 | unsigned long nr_clks) | 114 | unsigned long nr_clks) |
111 | { | 115 | { |
112 | reg_base = base; | 116 | reg_base = base; |
117 | cru_node = np; | ||
118 | grf = ERR_PTR(-EPROBE_DEFER); | ||
113 | 119 | ||
114 | clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); | 120 | clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); |
115 | if (!clk_table) | 121 | if (!clk_table) |
@@ -120,12 +126,41 @@ void __init rockchip_clk_init(struct device_node *np, void __iomem *base, | |||
120 | of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); | 126 | of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); |
121 | } | 127 | } |
122 | 128 | ||
129 | struct regmap *rockchip_clk_get_grf(void) | ||
130 | { | ||
131 | if (IS_ERR(grf)) | ||
132 | grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf"); | ||
133 | return grf; | ||
134 | } | ||
135 | |||
123 | void rockchip_clk_add_lookup(struct clk *clk, unsigned int id) | 136 | void rockchip_clk_add_lookup(struct clk *clk, unsigned int id) |
124 | { | 137 | { |
125 | if (clk_table && id) | 138 | if (clk_table && id) |
126 | clk_table[id] = clk; | 139 | clk_table[id] = clk; |
127 | } | 140 | } |
128 | 141 | ||
142 | void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list, | ||
143 | unsigned int nr_pll, int grf_lock_offset) | ||
144 | { | ||
145 | struct clk *clk; | ||
146 | int idx; | ||
147 | |||
148 | for (idx = 0; idx < nr_pll; idx++, list++) { | ||
149 | clk = rockchip_clk_register_pll(list->type, list->name, | ||
150 | list->parent_names, list->num_parents, | ||
151 | reg_base, list->con_offset, grf_lock_offset, | ||
152 | list->lock_shift, list->mode_offset, | ||
153 | list->mode_shift, list->rate_table, &clk_lock); | ||
154 | if (IS_ERR(clk)) { | ||
155 | pr_err("%s: failed to register clock %s\n", __func__, | ||
156 | list->name); | ||
157 | continue; | ||
158 | } | ||
159 | |||
160 | rockchip_clk_add_lookup(clk, list->id); | ||
161 | } | ||
162 | } | ||
163 | |||
129 | void __init rockchip_clk_register_branches( | 164 | void __init rockchip_clk_register_branches( |
130 | struct rockchip_clk_branch *list, | 165 | struct rockchip_clk_branch *list, |
131 | unsigned int nr_clk) | 166 | unsigned int nr_clk) |
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 5b051b011c18..fb7ce851d4a0 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h | |||
@@ -40,6 +40,77 @@ | |||
40 | #define RK2928_SOFTRST_CON(x) (x * 0x4 + 0x110) | 40 | #define RK2928_SOFTRST_CON(x) (x * 0x4 + 0x110) |
41 | #define RK2928_MISC_CON 0x134 | 41 | #define RK2928_MISC_CON 0x134 |
42 | 42 | ||
43 | enum rockchip_pll_type { | ||
44 | pll_rk3066, | ||
45 | }; | ||
46 | |||
47 | #define RK3066_PLL_RATE(_rate, _nr, _nf, _no) \ | ||
48 | { \ | ||
49 | .rate = _rate##U, \ | ||
50 | .nr = _nr, \ | ||
51 | .nf = _nf, \ | ||
52 | .no = _no, \ | ||
53 | .bwadj = (_nf >> 1), \ | ||
54 | } | ||
55 | |||
56 | struct rockchip_pll_rate_table { | ||
57 | unsigned long rate; | ||
58 | unsigned int nr; | ||
59 | unsigned int nf; | ||
60 | unsigned int no; | ||
61 | unsigned int bwadj; | ||
62 | }; | ||
63 | |||
64 | /** | ||
65 | * struct rockchip_pll_clock: information about pll clock | ||
66 | * @id: platform specific id of the clock. | ||
67 | * @name: name of this pll clock. | ||
68 | * @parent_name: name of the parent clock. | ||
69 | * @flags: optional flags for basic clock. | ||
70 | * @con_offset: offset of the register for configuring the PLL. | ||
71 | * @mode_offset: offset of the register for configuring the PLL-mode. | ||
72 | * @mode_shift: offset inside the mode-register for the mode of this pll. | ||
73 | * @lock_shift: offset inside the lock register for the lock status. | ||
74 | * @type: Type of PLL to be registered. | ||
75 | * @rate_table: Table of usable pll rates | ||
76 | */ | ||
77 | struct rockchip_pll_clock { | ||
78 | unsigned int id; | ||
79 | const char *name; | ||
80 | const char **parent_names; | ||
81 | u8 num_parents; | ||
82 | unsigned long flags; | ||
83 | int con_offset; | ||
84 | int mode_offset; | ||
85 | int mode_shift; | ||
86 | int lock_shift; | ||
87 | enum rockchip_pll_type type; | ||
88 | struct rockchip_pll_rate_table *rate_table; | ||
89 | }; | ||
90 | |||
91 | #define PLL(_type, _id, _name, _pnames, _flags, _con, _mode, _mshift, \ | ||
92 | _lshift, _rtable) \ | ||
93 | { \ | ||
94 | .id = _id, \ | ||
95 | .type = _type, \ | ||
96 | .name = _name, \ | ||
97 | .parent_names = _pnames, \ | ||
98 | .num_parents = ARRAY_SIZE(_pnames), \ | ||
99 | .flags = CLK_GET_RATE_NOCACHE | _flags, \ | ||
100 | .con_offset = _con, \ | ||
101 | .mode_offset = _mode, \ | ||
102 | .mode_shift = _mshift, \ | ||
103 | .lock_shift = _lshift, \ | ||
104 | .rate_table = _rtable, \ | ||
105 | } | ||
106 | |||
107 | struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | ||
108 | const char *name, const char **parent_names, u8 num_parents, | ||
109 | void __iomem *base, int con_offset, int grf_lock_offset, | ||
110 | int lock_shift, int reg_mode, int mode_shift, | ||
111 | struct rockchip_pll_rate_table *rate_table, | ||
112 | spinlock_t *lock); | ||
113 | |||
43 | #define PNAME(x) static const char *x[] __initconst | 114 | #define PNAME(x) static const char *x[] __initconst |
44 | 115 | ||
45 | enum rockchip_clk_branch_type { | 116 | enum rockchip_clk_branch_type { |
@@ -243,8 +314,11 @@ struct rockchip_clk_branch { | |||
243 | 314 | ||
244 | void rockchip_clk_init(struct device_node *np, void __iomem *base, | 315 | void rockchip_clk_init(struct device_node *np, void __iomem *base, |
245 | unsigned long nr_clks); | 316 | unsigned long nr_clks); |
317 | struct regmap *rockchip_clk_get_grf(void); | ||
246 | void rockchip_clk_add_lookup(struct clk *clk, unsigned int id); | 318 | void rockchip_clk_add_lookup(struct clk *clk, unsigned int id); |
247 | void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, | 319 | void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, |
248 | unsigned int nr_clk); | 320 | unsigned int nr_clk); |
321 | void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, | ||
322 | unsigned int nr_pll, int grf_lock_offset); | ||
249 | 323 | ||
250 | #endif | 324 | #endif |