diff options
author | Mike Turquette <mturquette@linaro.org> | 2014-09-27 15:50:40 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-09-27 15:50:40 -0400 |
commit | 5ad67d3e5e0a5059945a7726a407763a23f80d9e (patch) | |
tree | 365e8170b9553818317dd80724f1bacbc75e7916 | |
parent | 8791db53a9d2556b8908af300d8327eecb110d8a (diff) | |
parent | 0e5bdb3f9fa5c2bd4452c258de78122ef15f62d6 (diff) |
Merge tag 'v3.18-rockchip-cpuclk' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip into clk-next
CPU clock handling for Rockchip SoCs
-rw-r--r-- | drivers/clk/rockchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-cpu.c | 329 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-pll.c | 63 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3188.c | 161 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk-rk3288.c | 93 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.c | 21 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.h | 37 | ||||
-rw-r--r-- | include/dt-bindings/clock/rk3188-cru-common.h | 1 | ||||
-rw-r--r-- | include/dt-bindings/clock/rk3288-cru.h | 1 |
9 files changed, 629 insertions, 78 deletions
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index ee6b077381e1..bd8514d63634 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile | |||
@@ -5,6 +5,7 @@ | |||
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 | 7 | obj-y += clk-pll.o |
8 | obj-y += clk-cpu.o | ||
8 | obj-$(CONFIG_RESET_CONTROLLER) += softrst.o | 9 | obj-$(CONFIG_RESET_CONTROLLER) += softrst.o |
9 | 10 | ||
10 | obj-y += clk-rk3188.o | 11 | obj-y += clk-rk3188.o |
diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c new file mode 100644 index 000000000000..75c8c45ef728 --- /dev/null +++ b/drivers/clk/rockchip/clk-cpu.c | |||
@@ -0,0 +1,329 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 MundoReader S.L. | ||
3 | * Author: Heiko Stuebner <heiko@sntech.de> | ||
4 | * | ||
5 | * based on clk/samsung/clk-cpu.c | ||
6 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
7 | * Author: Thomas Abraham <thomas.ab@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs. | ||
14 | * The CPU clock is typically derived from a hierarchy of clock | ||
15 | * blocks which includes mux and divider blocks. There are a number of other | ||
16 | * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI | ||
17 | * clock for CPU domain. The rates of these auxiliary clocks are related to the | ||
18 | * CPU clock rate and this relation is usually specified in the hardware manual | ||
19 | * of the SoC or supplied after the SoC characterization. | ||
20 | * | ||
21 | * The below implementation of the CPU clock allows the rate changes of the CPU | ||
22 | * clock and the corresponding rate changes of the auxillary clocks of the CPU | ||
23 | * domain. The platform clock driver provides a clock register configuration | ||
24 | * for each configurable rate which is then used to program the clock hardware | ||
25 | * registers to acheive a fast co-oridinated rate change for all the CPU domain | ||
26 | * clocks. | ||
27 | * | ||
28 | * On a rate change request for the CPU clock, the rate change is propagated | ||
29 | * upto the PLL supplying the clock to the CPU domain clock blocks. While the | ||
30 | * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an | ||
31 | * alternate clock source. If required, the alternate clock source is divided | ||
32 | * down in order to keep the output clock rate within the previous OPP limits. | ||
33 | */ | ||
34 | |||
35 | #include <linux/of.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/io.h> | ||
38 | #include <linux/clk-provider.h> | ||
39 | #include "clk.h" | ||
40 | |||
41 | /** | ||
42 | * struct rockchip_cpuclk: information about clock supplied to a CPU core. | ||
43 | * @hw: handle between ccf and cpu clock. | ||
44 | * @alt_parent: alternate parent clock to use when switching the speed | ||
45 | * of the primary parent clock. | ||
46 | * @reg_base: base register for cpu-clock values. | ||
47 | * @clk_nb: clock notifier registered for changes in clock speed of the | ||
48 | * primary parent clock. | ||
49 | * @rate_count: number of rates in the rate_table | ||
50 | * @rate_table: pll-rates and their associated dividers | ||
51 | * @reg_data: cpu-specific register settings | ||
52 | * @lock: clock lock | ||
53 | */ | ||
54 | struct rockchip_cpuclk { | ||
55 | struct clk_hw hw; | ||
56 | |||
57 | struct clk_mux cpu_mux; | ||
58 | const struct clk_ops *cpu_mux_ops; | ||
59 | |||
60 | struct clk *alt_parent; | ||
61 | void __iomem *reg_base; | ||
62 | struct notifier_block clk_nb; | ||
63 | unsigned int rate_count; | ||
64 | struct rockchip_cpuclk_rate_table *rate_table; | ||
65 | const struct rockchip_cpuclk_reg_data *reg_data; | ||
66 | spinlock_t *lock; | ||
67 | }; | ||
68 | |||
69 | #define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw) | ||
70 | #define to_rockchip_cpuclk_nb(nb) \ | ||
71 | container_of(nb, struct rockchip_cpuclk, clk_nb) | ||
72 | |||
73 | static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings( | ||
74 | struct rockchip_cpuclk *cpuclk, unsigned long rate) | ||
75 | { | ||
76 | const struct rockchip_cpuclk_rate_table *rate_table = | ||
77 | cpuclk->rate_table; | ||
78 | int i; | ||
79 | |||
80 | for (i = 0; i < cpuclk->rate_count; i++) { | ||
81 | if (rate == rate_table[i].prate) | ||
82 | return &rate_table[i]; | ||
83 | } | ||
84 | |||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw, | ||
89 | unsigned long parent_rate) | ||
90 | { | ||
91 | struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw); | ||
92 | const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; | ||
93 | u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg); | ||
94 | |||
95 | clksel0 >>= reg_data->div_core_shift; | ||
96 | clksel0 &= reg_data->div_core_mask; | ||
97 | return parent_rate / (clksel0 + 1); | ||
98 | } | ||
99 | |||
100 | static const struct clk_ops rockchip_cpuclk_ops = { | ||
101 | .recalc_rate = rockchip_cpuclk_recalc_rate, | ||
102 | }; | ||
103 | |||
104 | static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk, | ||
105 | const struct rockchip_cpuclk_rate_table *rate) | ||
106 | { | ||
107 | int i; | ||
108 | |||
109 | /* alternate parent is active now. set the dividers */ | ||
110 | for (i = 0; i < ARRAY_SIZE(rate->divs); i++) { | ||
111 | const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i]; | ||
112 | |||
113 | if (!clksel->reg) | ||
114 | continue; | ||
115 | |||
116 | pr_debug("%s: setting reg 0x%x to 0x%x\n", | ||
117 | __func__, clksel->reg, clksel->val); | ||
118 | writel(clksel->val , cpuclk->reg_base + clksel->reg); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, | ||
123 | struct clk_notifier_data *ndata) | ||
124 | { | ||
125 | const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; | ||
126 | unsigned long alt_prate, alt_div; | ||
127 | |||
128 | alt_prate = clk_get_rate(cpuclk->alt_parent); | ||
129 | |||
130 | spin_lock(cpuclk->lock); | ||
131 | |||
132 | /* | ||
133 | * If the old parent clock speed is less than the clock speed | ||
134 | * of the alternate parent, then it should be ensured that at no point | ||
135 | * the armclk speed is more than the old_rate until the dividers are | ||
136 | * set. | ||
137 | */ | ||
138 | if (alt_prate > ndata->old_rate) { | ||
139 | /* calculate dividers */ | ||
140 | alt_div = DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1; | ||
141 | if (alt_div > reg_data->div_core_mask) { | ||
142 | pr_warn("%s: limiting alt-divider %lu to %d\n", | ||
143 | __func__, alt_div, reg_data->div_core_mask); | ||
144 | alt_div = reg_data->div_core_mask; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Change parents and add dividers in a single transaction. | ||
149 | * | ||
150 | * NOTE: we do this in a single transaction so we're never | ||
151 | * dividing the primary parent by the extra dividers that were | ||
152 | * needed for the alt. | ||
153 | */ | ||
154 | pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n", | ||
155 | __func__, alt_div, alt_prate, ndata->old_rate); | ||
156 | |||
157 | writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask, | ||
158 | reg_data->div_core_shift) | | ||
159 | HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), | ||
160 | cpuclk->reg_base + reg_data->core_reg); | ||
161 | } else { | ||
162 | /* select alternate parent */ | ||
163 | writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), | ||
164 | cpuclk->reg_base + reg_data->core_reg); | ||
165 | } | ||
166 | |||
167 | spin_unlock(cpuclk->lock); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, | ||
172 | struct clk_notifier_data *ndata) | ||
173 | { | ||
174 | const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; | ||
175 | const struct rockchip_cpuclk_rate_table *rate; | ||
176 | |||
177 | rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate); | ||
178 | if (!rate) { | ||
179 | pr_err("%s: Invalid rate : %lu for cpuclk\n", | ||
180 | __func__, ndata->new_rate); | ||
181 | return -EINVAL; | ||
182 | } | ||
183 | |||
184 | spin_lock(cpuclk->lock); | ||
185 | |||
186 | if (ndata->old_rate < ndata->new_rate) | ||
187 | rockchip_cpuclk_set_dividers(cpuclk, rate); | ||
188 | |||
189 | /* | ||
190 | * post-rate change event, re-mux to primary parent and remove dividers. | ||
191 | * | ||
192 | * NOTE: we do this in a single transaction so we're never dividing the | ||
193 | * primary parent by the extra dividers that were needed for the alt. | ||
194 | */ | ||
195 | |||
196 | writel(HIWORD_UPDATE(0, reg_data->div_core_mask, | ||
197 | reg_data->div_core_shift) | | ||
198 | HIWORD_UPDATE(0, 1, reg_data->mux_core_shift), | ||
199 | cpuclk->reg_base + reg_data->core_reg); | ||
200 | |||
201 | if (ndata->old_rate > ndata->new_rate) | ||
202 | rockchip_cpuclk_set_dividers(cpuclk, rate); | ||
203 | |||
204 | spin_unlock(cpuclk->lock); | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * This clock notifier is called when the frequency of the parent clock | ||
210 | * of cpuclk is to be changed. This notifier handles the setting up all | ||
211 | * the divider clocks, remux to temporary parent and handling the safe | ||
212 | * frequency levels when using temporary parent. | ||
213 | */ | ||
214 | static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb, | ||
215 | unsigned long event, void *data) | ||
216 | { | ||
217 | struct clk_notifier_data *ndata = data; | ||
218 | struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb); | ||
219 | int ret = 0; | ||
220 | |||
221 | pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", | ||
222 | __func__, event, ndata->old_rate, ndata->new_rate); | ||
223 | if (event == PRE_RATE_CHANGE) | ||
224 | ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata); | ||
225 | else if (event == POST_RATE_CHANGE) | ||
226 | ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata); | ||
227 | |||
228 | return notifier_from_errno(ret); | ||
229 | } | ||
230 | |||
231 | struct clk *rockchip_clk_register_cpuclk(const char *name, | ||
232 | const char **parent_names, u8 num_parents, | ||
233 | const struct rockchip_cpuclk_reg_data *reg_data, | ||
234 | const struct rockchip_cpuclk_rate_table *rates, | ||
235 | int nrates, void __iomem *reg_base, spinlock_t *lock) | ||
236 | { | ||
237 | struct rockchip_cpuclk *cpuclk; | ||
238 | struct clk_init_data init; | ||
239 | struct clk *clk, *cclk; | ||
240 | int ret; | ||
241 | |||
242 | if (num_parents != 2) { | ||
243 | pr_err("%s: needs two parent clocks\n", __func__); | ||
244 | return ERR_PTR(-EINVAL); | ||
245 | } | ||
246 | |||
247 | cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); | ||
248 | if (!cpuclk) | ||
249 | return ERR_PTR(-ENOMEM); | ||
250 | |||
251 | init.name = name; | ||
252 | init.parent_names = &parent_names[0]; | ||
253 | init.num_parents = 1; | ||
254 | init.ops = &rockchip_cpuclk_ops; | ||
255 | |||
256 | /* only allow rate changes when we have a rate table */ | ||
257 | init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0; | ||
258 | |||
259 | /* disallow automatic parent changes by ccf */ | ||
260 | init.flags |= CLK_SET_RATE_NO_REPARENT; | ||
261 | |||
262 | init.flags |= CLK_GET_RATE_NOCACHE; | ||
263 | |||
264 | cpuclk->reg_base = reg_base; | ||
265 | cpuclk->lock = lock; | ||
266 | cpuclk->reg_data = reg_data; | ||
267 | cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb; | ||
268 | cpuclk->hw.init = &init; | ||
269 | |||
270 | cpuclk->alt_parent = __clk_lookup(parent_names[1]); | ||
271 | if (!cpuclk->alt_parent) { | ||
272 | pr_err("%s: could not lookup alternate parent\n", | ||
273 | __func__); | ||
274 | ret = -EINVAL; | ||
275 | goto free_cpuclk; | ||
276 | } | ||
277 | |||
278 | ret = clk_prepare_enable(cpuclk->alt_parent); | ||
279 | if (ret) { | ||
280 | pr_err("%s: could not enable alternate parent\n", | ||
281 | __func__); | ||
282 | goto free_cpuclk; | ||
283 | } | ||
284 | |||
285 | clk = __clk_lookup(parent_names[0]); | ||
286 | if (!clk) { | ||
287 | pr_err("%s: could not lookup parent clock %s\n", | ||
288 | __func__, parent_names[0]); | ||
289 | ret = -EINVAL; | ||
290 | goto free_cpuclk; | ||
291 | } | ||
292 | |||
293 | ret = clk_notifier_register(clk, &cpuclk->clk_nb); | ||
294 | if (ret) { | ||
295 | pr_err("%s: failed to register clock notifier for %s\n", | ||
296 | __func__, name); | ||
297 | goto free_cpuclk; | ||
298 | } | ||
299 | |||
300 | if (nrates > 0) { | ||
301 | cpuclk->rate_count = nrates; | ||
302 | cpuclk->rate_table = kmemdup(rates, | ||
303 | sizeof(*rates) * nrates, | ||
304 | GFP_KERNEL); | ||
305 | if (!cpuclk->rate_table) { | ||
306 | pr_err("%s: could not allocate memory for cpuclk rates\n", | ||
307 | __func__); | ||
308 | ret = -ENOMEM; | ||
309 | goto unregister_notifier; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | cclk = clk_register(NULL, &cpuclk->hw); | ||
314 | if (IS_ERR(clk)) { | ||
315 | pr_err("%s: could not register cpuclk %s\n", __func__, name); | ||
316 | ret = PTR_ERR(clk); | ||
317 | goto free_rate_table; | ||
318 | } | ||
319 | |||
320 | return cclk; | ||
321 | |||
322 | free_rate_table: | ||
323 | kfree(cpuclk->rate_table); | ||
324 | unregister_notifier: | ||
325 | clk_notifier_unregister(clk, &cpuclk->clk_nb); | ||
326 | free_cpuclk: | ||
327 | kfree(cpuclk); | ||
328 | return ERR_PTR(ret); | ||
329 | } | ||
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index f2a1c7abf4d9..a3e886a38480 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c | |||
@@ -34,7 +34,6 @@ struct rockchip_clk_pll { | |||
34 | const struct clk_ops *pll_mux_ops; | 34 | const struct clk_ops *pll_mux_ops; |
35 | 35 | ||
36 | struct notifier_block clk_nb; | 36 | struct notifier_block clk_nb; |
37 | bool rate_change_remuxed; | ||
38 | 37 | ||
39 | void __iomem *reg_base; | 38 | void __iomem *reg_base; |
40 | int lock_offset; | 39 | int lock_offset; |
@@ -109,38 +108,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) | |||
109 | } | 108 | } |
110 | 109 | ||
111 | /** | 110 | /** |
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 | 111 | * PLL used in RK3066, RK3188 and RK3288 |
145 | */ | 112 | */ |
146 | 113 | ||
@@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, | |||
194 | const struct rockchip_pll_rate_table *rate; | 161 | const struct rockchip_pll_rate_table *rate; |
195 | unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); | 162 | unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); |
196 | struct regmap *grf = rockchip_clk_get_grf(); | 163 | struct regmap *grf = rockchip_clk_get_grf(); |
164 | struct clk_mux *pll_mux = &pll->pll_mux; | ||
165 | const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; | ||
166 | int rate_change_remuxed = 0; | ||
167 | int cur_parent; | ||
197 | int ret; | 168 | int ret; |
198 | 169 | ||
199 | if (IS_ERR(grf)) { | 170 | if (IS_ERR(grf)) { |
@@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, | |||
216 | pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", | 187 | pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", |
217 | __func__, rate->rate, rate->nr, rate->no, rate->nf); | 188 | __func__, rate->rate, rate->nr, rate->no, rate->nf); |
218 | 189 | ||
190 | cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); | ||
191 | if (cur_parent == PLL_MODE_NORM) { | ||
192 | pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); | ||
193 | rate_change_remuxed = 1; | ||
194 | } | ||
195 | |||
219 | /* enter reset mode */ | 196 | /* enter reset mode */ |
220 | writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), | 197 | writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), |
221 | pll->reg_base + RK3066_PLLCON(3)); | 198 | pll->reg_base + RK3066_PLLCON(3)); |
@@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, | |||
247 | rockchip_rk3066_pll_set_rate(hw, old_rate, prate); | 224 | rockchip_rk3066_pll_set_rate(hw, old_rate, prate); |
248 | } | 225 | } |
249 | 226 | ||
227 | if (rate_change_remuxed) | ||
228 | pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); | ||
229 | |||
250 | return ret; | 230 | return ret; |
251 | } | 231 | } |
252 | 232 | ||
@@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | |||
310 | struct clk_mux *pll_mux; | 290 | struct clk_mux *pll_mux; |
311 | struct clk *pll_clk, *mux_clk; | 291 | struct clk *pll_clk, *mux_clk; |
312 | char pll_name[20]; | 292 | char pll_name[20]; |
313 | int ret; | ||
314 | 293 | ||
315 | if (num_parents != 2) { | 294 | if (num_parents != 2) { |
316 | pr_err("%s: needs two parent clocks\n", __func__); | 295 | pr_err("%s: needs two parent clocks\n", __func__); |
@@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | |||
367 | pll->lock_offset = grf_lock_offset; | 346 | pll->lock_offset = grf_lock_offset; |
368 | pll->lock_shift = lock_shift; | 347 | pll->lock_shift = lock_shift; |
369 | pll->lock = lock; | 348 | pll->lock = lock; |
370 | pll->clk_nb.notifier_call = rockchip_pll_notifier_cb; | ||
371 | 349 | ||
372 | pll_clk = clk_register(NULL, &pll->hw); | 350 | pll_clk = clk_register(NULL, &pll->hw); |
373 | if (IS_ERR(pll_clk)) { | 351 | if (IS_ERR(pll_clk)) { |
@@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | |||
377 | goto err_pll; | 355 | goto err_pll; |
378 | } | 356 | } |
379 | 357 | ||
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 */ | 358 | /* create the mux on top of the real pll */ |
389 | pll->pll_mux_ops = &clk_mux_ops; | 359 | pll->pll_mux_ops = &clk_mux_ops; |
390 | pll_mux = &pll->pll_mux; | 360 | pll_mux = &pll->pll_mux; |
@@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | |||
417 | return mux_clk; | 387 | return mux_clk; |
418 | 388 | ||
419 | err_mux: | 389 | 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); | 390 | clk_unregister(pll_clk); |
428 | err_pll: | 391 | err_pll: |
429 | kfree(pll); | 392 | kfree(pll); |
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 0147614e94b8..ceabce595498 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <dt-bindings/clock/rk3188-cru-common.h> | 19 | #include <dt-bindings/clock/rk3188-cru-common.h> |
20 | #include "clk.h" | 20 | #include "clk.h" |
21 | 21 | ||
22 | #define RK3066_GRF_SOC_STATUS 0x15c | ||
22 | #define RK3188_GRF_SOC_STATUS 0xac | 23 | #define RK3188_GRF_SOC_STATUS 0xac |
23 | 24 | ||
24 | enum rk3188_plls { | 25 | enum rk3188_plls { |
@@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = { | |||
100 | { /* sentinel */ }, | 101 | { /* sentinel */ }, |
101 | }; | 102 | }; |
102 | 103 | ||
104 | #define RK3066_DIV_CORE_PERIPH_MASK 0x3 | ||
105 | #define RK3066_DIV_CORE_PERIPH_SHIFT 6 | ||
106 | #define RK3066_DIV_ACLK_CORE_MASK 0x7 | ||
107 | #define RK3066_DIV_ACLK_CORE_SHIFT 0 | ||
108 | #define RK3066_DIV_ACLK_HCLK_MASK 0x3 | ||
109 | #define RK3066_DIV_ACLK_HCLK_SHIFT 8 | ||
110 | #define RK3066_DIV_ACLK_PCLK_MASK 0x3 | ||
111 | #define RK3066_DIV_ACLK_PCLK_SHIFT 12 | ||
112 | #define RK3066_DIV_AHB2APB_MASK 0x3 | ||
113 | #define RK3066_DIV_AHB2APB_SHIFT 14 | ||
114 | |||
115 | #define RK3066_CLKSEL0(_core_peri) \ | ||
116 | { \ | ||
117 | .reg = RK2928_CLKSEL_CON(0), \ | ||
118 | .val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \ | ||
119 | RK3066_DIV_CORE_PERIPH_SHIFT) \ | ||
120 | } | ||
121 | #define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb) \ | ||
122 | { \ | ||
123 | .reg = RK2928_CLKSEL_CON(1), \ | ||
124 | .val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \ | ||
125 | RK3066_DIV_ACLK_CORE_SHIFT) | \ | ||
126 | HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \ | ||
127 | RK3066_DIV_ACLK_HCLK_SHIFT) | \ | ||
128 | HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \ | ||
129 | RK3066_DIV_ACLK_PCLK_SHIFT) | \ | ||
130 | HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK, \ | ||
131 | RK3066_DIV_AHB2APB_SHIFT), \ | ||
132 | } | ||
133 | |||
134 | #define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \ | ||
135 | { \ | ||
136 | .prate = _prate, \ | ||
137 | .divs = { \ | ||
138 | RK3066_CLKSEL0(_core_peri), \ | ||
139 | RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p), \ | ||
140 | }, \ | ||
141 | } | ||
142 | |||
143 | static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = { | ||
144 | RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1), | ||
145 | RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1), | ||
146 | RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1), | ||
147 | RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1), | ||
148 | RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1), | ||
149 | RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1), | ||
150 | RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0), | ||
151 | }; | ||
152 | |||
153 | static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = { | ||
154 | .core_reg = RK2928_CLKSEL_CON(0), | ||
155 | .div_core_shift = 0, | ||
156 | .div_core_mask = 0x1f, | ||
157 | .mux_core_shift = 8, | ||
158 | }; | ||
159 | |||
160 | #define RK3188_DIV_ACLK_CORE_MASK 0x7 | ||
161 | #define RK3188_DIV_ACLK_CORE_SHIFT 3 | ||
162 | |||
163 | #define RK3188_CLKSEL1(_aclk_core) \ | ||
164 | { \ | ||
165 | .reg = RK2928_CLKSEL_CON(1), \ | ||
166 | .val = HIWORD_UPDATE(_aclk_core, RK3188_DIV_ACLK_CORE_MASK,\ | ||
167 | RK3188_DIV_ACLK_CORE_SHIFT) \ | ||
168 | } | ||
169 | #define RK3188_CPUCLK_RATE(_prate, _core_peri, _aclk_core) \ | ||
170 | { \ | ||
171 | .prate = _prate, \ | ||
172 | .divs = { \ | ||
173 | RK3066_CLKSEL0(_core_peri), \ | ||
174 | RK3188_CLKSEL1(_aclk_core), \ | ||
175 | }, \ | ||
176 | } | ||
177 | |||
178 | static struct rockchip_cpuclk_rate_table rk3188_cpuclk_rates[] __initdata = { | ||
179 | RK3188_CPUCLK_RATE(1608000000, 2, 3), | ||
180 | RK3188_CPUCLK_RATE(1416000000, 2, 3), | ||
181 | RK3188_CPUCLK_RATE(1200000000, 2, 3), | ||
182 | RK3188_CPUCLK_RATE(1008000000, 2, 3), | ||
183 | RK3188_CPUCLK_RATE( 816000000, 2, 3), | ||
184 | RK3188_CPUCLK_RATE( 600000000, 1, 3), | ||
185 | RK3188_CPUCLK_RATE( 504000000, 1, 3), | ||
186 | RK3188_CPUCLK_RATE( 312000000, 0, 1), | ||
187 | }; | ||
188 | |||
189 | static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = { | ||
190 | .core_reg = RK2928_CLKSEL_CON(0), | ||
191 | .div_core_shift = 9, | ||
192 | .div_core_mask = 0x1f, | ||
193 | .mux_core_shift = 8, | ||
194 | }; | ||
195 | |||
103 | PNAME(mux_pll_p) = { "xin24m", "xin32k" }; | 196 | PNAME(mux_pll_p) = { "xin24m", "xin32k" }; |
104 | PNAME(mux_armclk_p) = { "apll", "gpll_armclk" }; | 197 | PNAME(mux_armclk_p) = { "apll", "gpll_armclk" }; |
105 | PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" }; | 198 | PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" }; |
@@ -173,17 +266,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { | |||
173 | GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, | 266 | GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, |
174 | RK2928_CLKGATE_CON(0), 3, GFLAGS), | 267 | RK2928_CLKGATE_CON(0), 3, GFLAGS), |
175 | 268 | ||
176 | DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, | ||
177 | RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), | ||
178 | GATE(0, "atclk_cpu", "pclk_cpu_pre", 0, | 269 | GATE(0, "atclk_cpu", "pclk_cpu_pre", 0, |
179 | RK2928_CLKGATE_CON(0), 6, GFLAGS), | 270 | RK2928_CLKGATE_CON(0), 6, GFLAGS), |
180 | GATE(0, "pclk_cpu", "pclk_cpu_pre", 0, | 271 | GATE(0, "pclk_cpu", "pclk_cpu_pre", 0, |
181 | RK2928_CLKGATE_CON(0), 5, GFLAGS), | 272 | RK2928_CLKGATE_CON(0), 5, GFLAGS), |
182 | DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, | ||
183 | RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), | ||
184 | COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, | ||
185 | RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, | ||
186 | RK2928_CLKGATE_CON(4), 9, GFLAGS), | ||
187 | GATE(0, "hclk_cpu", "hclk_cpu_pre", 0, | 273 | GATE(0, "hclk_cpu", "hclk_cpu_pre", 0, |
188 | RK2928_CLKGATE_CON(0), 4, GFLAGS), | 274 | RK2928_CLKGATE_CON(0), 4, GFLAGS), |
189 | 275 | ||
@@ -412,10 +498,18 @@ static struct clk_div_table div_aclk_cpu_t[] = { | |||
412 | }; | 498 | }; |
413 | 499 | ||
414 | static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { | 500 | static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { |
415 | COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, | ||
416 | RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS), | ||
417 | DIVTBL(0, "aclk_cpu_pre", "armclk", 0, | 501 | DIVTBL(0, "aclk_cpu_pre", "armclk", 0, |
418 | RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t), | 502 | RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t), |
503 | DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, | ||
504 | RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO | ||
505 | | CLK_DIVIDER_READ_ONLY), | ||
506 | DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, | ||
507 | RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO | ||
508 | | CLK_DIVIDER_READ_ONLY), | ||
509 | COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, | ||
510 | RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO | ||
511 | | CLK_DIVIDER_READ_ONLY, | ||
512 | RK2928_CLKGATE_CON(4), 9, GFLAGS), | ||
419 | 513 | ||
420 | GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0, | 514 | GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0, |
421 | RK2928_CLKGATE_CON(9), 4, GFLAGS), | 515 | RK2928_CLKGATE_CON(9), 4, GFLAGS), |
@@ -524,8 +618,6 @@ PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1", | |||
524 | "gpll", "cpll" }; | 618 | "gpll", "cpll" }; |
525 | 619 | ||
526 | static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { | 620 | static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { |
527 | COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, | ||
528 | RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS), | ||
529 | COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0, | 621 | COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0, |
530 | RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, | 622 | RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, |
531 | div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS), | 623 | div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS), |
@@ -533,6 +625,13 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { | |||
533 | /* do not source aclk_cpu_pre from the apll, to keep complexity down */ | 625 | /* do not source aclk_cpu_pre from the apll, to keep complexity down */ |
534 | COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT, | 626 | COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT, |
535 | RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS), | 627 | RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS), |
628 | DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, | ||
629 | RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), | ||
630 | DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, | ||
631 | RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), | ||
632 | COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, | ||
633 | RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, | ||
634 | RK2928_CLKGATE_CON(4), 9, GFLAGS), | ||
536 | 635 | ||
537 | GATE(CORE_L2C, "core_l2c", "armclk", 0, | 636 | GATE(CORE_L2C, "core_l2c", "armclk", 0, |
538 | RK2928_CLKGATE_CON(9), 4, GFLAGS), | 637 | RK2928_CLKGATE_CON(9), 4, GFLAGS), |
@@ -629,9 +728,6 @@ static void __init rk3188_common_clk_init(struct device_node *np) | |||
629 | pr_warn("%s: could not register clock usb480m: %ld\n", | 728 | pr_warn("%s: could not register clock usb480m: %ld\n", |
630 | __func__, PTR_ERR(clk)); | 729 | __func__, PTR_ERR(clk)); |
631 | 730 | ||
632 | rockchip_clk_register_plls(rk3188_pll_clks, | ||
633 | ARRAY_SIZE(rk3188_pll_clks), | ||
634 | RK3188_GRF_SOC_STATUS); | ||
635 | rockchip_clk_register_branches(common_clk_branches, | 731 | rockchip_clk_register_branches(common_clk_branches, |
636 | ARRAY_SIZE(common_clk_branches)); | 732 | ARRAY_SIZE(common_clk_branches)); |
637 | rockchip_clk_protect_critical(rk3188_critical_clocks, | 733 | rockchip_clk_protect_critical(rk3188_critical_clocks, |
@@ -644,16 +740,51 @@ static void __init rk3188_common_clk_init(struct device_node *np) | |||
644 | static void __init rk3066a_clk_init(struct device_node *np) | 740 | static void __init rk3066a_clk_init(struct device_node *np) |
645 | { | 741 | { |
646 | rk3188_common_clk_init(np); | 742 | rk3188_common_clk_init(np); |
743 | rockchip_clk_register_plls(rk3188_pll_clks, | ||
744 | ARRAY_SIZE(rk3188_pll_clks), | ||
745 | RK3066_GRF_SOC_STATUS); | ||
647 | rockchip_clk_register_branches(rk3066a_clk_branches, | 746 | rockchip_clk_register_branches(rk3066a_clk_branches, |
648 | ARRAY_SIZE(rk3066a_clk_branches)); | 747 | ARRAY_SIZE(rk3066a_clk_branches)); |
748 | rockchip_clk_register_armclk(ARMCLK, "armclk", | ||
749 | mux_armclk_p, ARRAY_SIZE(mux_armclk_p), | ||
750 | &rk3066_cpuclk_data, rk3066_cpuclk_rates, | ||
751 | ARRAY_SIZE(rk3066_cpuclk_rates)); | ||
649 | } | 752 | } |
650 | CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); | 753 | CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); |
651 | 754 | ||
652 | static void __init rk3188a_clk_init(struct device_node *np) | 755 | static void __init rk3188a_clk_init(struct device_node *np) |
653 | { | 756 | { |
757 | struct clk *clk1, *clk2; | ||
758 | unsigned long rate; | ||
759 | int ret; | ||
760 | |||
654 | rk3188_common_clk_init(np); | 761 | rk3188_common_clk_init(np); |
762 | rockchip_clk_register_plls(rk3188_pll_clks, | ||
763 | ARRAY_SIZE(rk3188_pll_clks), | ||
764 | RK3188_GRF_SOC_STATUS); | ||
655 | rockchip_clk_register_branches(rk3188_clk_branches, | 765 | rockchip_clk_register_branches(rk3188_clk_branches, |
656 | ARRAY_SIZE(rk3188_clk_branches)); | 766 | ARRAY_SIZE(rk3188_clk_branches)); |
767 | rockchip_clk_register_armclk(ARMCLK, "armclk", | ||
768 | mux_armclk_p, ARRAY_SIZE(mux_armclk_p), | ||
769 | &rk3188_cpuclk_data, rk3188_cpuclk_rates, | ||
770 | ARRAY_SIZE(rk3188_cpuclk_rates)); | ||
771 | |||
772 | /* reparent aclk_cpu_pre from apll */ | ||
773 | clk1 = __clk_lookup("aclk_cpu_pre"); | ||
774 | clk2 = __clk_lookup("gpll"); | ||
775 | if (clk1 && clk2) { | ||
776 | rate = clk_get_rate(clk1); | ||
777 | |||
778 | ret = clk_set_parent(clk1, clk2); | ||
779 | if (ret < 0) | ||
780 | pr_warn("%s: could not reparent aclk_cpu_pre to gpll\n", | ||
781 | __func__); | ||
782 | |||
783 | clk_set_rate(clk1, rate); | ||
784 | } else { | ||
785 | pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n", | ||
786 | __func__); | ||
787 | } | ||
657 | } | 788 | } |
658 | CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); | 789 | CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); |
659 | 790 | ||
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 21a5c74f1bec..d053529113f8 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c | |||
@@ -20,7 +20,7 @@ | |||
20 | #include "clk.h" | 20 | #include "clk.h" |
21 | 21 | ||
22 | #define RK3288_GRF_SOC_CON(x) (0x244 + x * 4) | 22 | #define RK3288_GRF_SOC_CON(x) (0x244 + x * 4) |
23 | #define RK3288_GRF_SOC_STATUS 0x280 | 23 | #define RK3288_GRF_SOC_STATUS1 0x284 |
24 | 24 | ||
25 | enum rk3288_plls { | 25 | enum rk3288_plls { |
26 | apll, dpll, cpll, gpll, npll, | 26 | apll, dpll, cpll, gpll, npll, |
@@ -101,6 +101,70 @@ struct rockchip_pll_rate_table rk3288_pll_rates[] = { | |||
101 | { /* sentinel */ }, | 101 | { /* sentinel */ }, |
102 | }; | 102 | }; |
103 | 103 | ||
104 | #define RK3288_DIV_ACLK_CORE_M0_MASK 0xf | ||
105 | #define RK3288_DIV_ACLK_CORE_M0_SHIFT 0 | ||
106 | #define RK3288_DIV_ACLK_CORE_MP_MASK 0xf | ||
107 | #define RK3288_DIV_ACLK_CORE_MP_SHIFT 4 | ||
108 | #define RK3288_DIV_L2RAM_MASK 0x7 | ||
109 | #define RK3288_DIV_L2RAM_SHIFT 0 | ||
110 | #define RK3288_DIV_ATCLK_MASK 0x1f | ||
111 | #define RK3288_DIV_ATCLK_SHIFT 4 | ||
112 | #define RK3288_DIV_PCLK_DBGPRE_MASK 0x1f | ||
113 | #define RK3288_DIV_PCLK_DBGPRE_SHIFT 9 | ||
114 | |||
115 | #define RK3288_CLKSEL0(_core_m0, _core_mp) \ | ||
116 | { \ | ||
117 | .reg = RK3288_CLKSEL_CON(0), \ | ||
118 | .val = HIWORD_UPDATE(_core_m0, RK3288_DIV_ACLK_CORE_M0_MASK, \ | ||
119 | RK3288_DIV_ACLK_CORE_M0_SHIFT) | \ | ||
120 | HIWORD_UPDATE(_core_mp, RK3288_DIV_ACLK_CORE_MP_MASK, \ | ||
121 | RK3288_DIV_ACLK_CORE_MP_SHIFT), \ | ||
122 | } | ||
123 | #define RK3288_CLKSEL37(_l2ram, _atclk, _pclk_dbg_pre) \ | ||
124 | { \ | ||
125 | .reg = RK3288_CLKSEL_CON(37), \ | ||
126 | .val = HIWORD_UPDATE(_l2ram, RK3288_DIV_L2RAM_MASK, \ | ||
127 | RK3288_DIV_L2RAM_SHIFT) | \ | ||
128 | HIWORD_UPDATE(_atclk, RK3288_DIV_ATCLK_MASK, \ | ||
129 | RK3288_DIV_ATCLK_SHIFT) | \ | ||
130 | HIWORD_UPDATE(_pclk_dbg_pre, \ | ||
131 | RK3288_DIV_PCLK_DBGPRE_MASK, \ | ||
132 | RK3288_DIV_PCLK_DBGPRE_SHIFT), \ | ||
133 | } | ||
134 | |||
135 | #define RK3288_CPUCLK_RATE(_prate, _core_m0, _core_mp, _l2ram, _atclk, _pdbg) \ | ||
136 | { \ | ||
137 | .prate = _prate, \ | ||
138 | .divs = { \ | ||
139 | RK3288_CLKSEL0(_core_m0, _core_mp), \ | ||
140 | RK3288_CLKSEL37(_l2ram, _atclk, _pdbg), \ | ||
141 | }, \ | ||
142 | } | ||
143 | |||
144 | static struct rockchip_cpuclk_rate_table rk3288_cpuclk_rates[] __initdata = { | ||
145 | RK3288_CPUCLK_RATE(1800000000, 2, 4, 2, 4, 4), | ||
146 | RK3288_CPUCLK_RATE(1704000000, 2, 4, 2, 4, 4), | ||
147 | RK3288_CPUCLK_RATE(1608000000, 2, 4, 2, 4, 4), | ||
148 | RK3288_CPUCLK_RATE(1512000000, 2, 4, 2, 4, 4), | ||
149 | RK3288_CPUCLK_RATE(1416000000, 2, 4, 2, 4, 4), | ||
150 | RK3288_CPUCLK_RATE(1200000000, 2, 4, 2, 4, 4), | ||
151 | RK3288_CPUCLK_RATE(1008000000, 2, 4, 2, 4, 4), | ||
152 | RK3288_CPUCLK_RATE( 816000000, 2, 4, 2, 4, 4), | ||
153 | RK3288_CPUCLK_RATE( 696000000, 2, 4, 2, 4, 4), | ||
154 | RK3288_CPUCLK_RATE( 600000000, 2, 4, 2, 4, 4), | ||
155 | RK3288_CPUCLK_RATE( 408000000, 2, 4, 2, 4, 4), | ||
156 | RK3288_CPUCLK_RATE( 312000000, 2, 4, 2, 4, 4), | ||
157 | RK3288_CPUCLK_RATE( 216000000, 2, 4, 2, 4, 4), | ||
158 | RK3288_CPUCLK_RATE( 126000000, 2, 4, 2, 4, 4), | ||
159 | }; | ||
160 | |||
161 | static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = { | ||
162 | .core_reg = RK3288_CLKSEL_CON(0), | ||
163 | .div_core_shift = 8, | ||
164 | .div_core_mask = 0x1f, | ||
165 | .mux_core_shift = 15, | ||
166 | }; | ||
167 | |||
104 | PNAME(mux_pll_p) = { "xin24m", "xin32k" }; | 168 | PNAME(mux_pll_p) = { "xin24m", "xin32k" }; |
105 | PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; | 169 | PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; |
106 | PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; | 170 | PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; |
@@ -166,35 +230,33 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { | |||
166 | RK3288_CLKGATE_CON(0), 1, GFLAGS), | 230 | RK3288_CLKGATE_CON(0), 1, GFLAGS), |
167 | GATE(0, "gpll_core", "gpll", 0, | 231 | GATE(0, "gpll_core", "gpll", 0, |
168 | RK3288_CLKGATE_CON(0), 2, GFLAGS), | 232 | RK3288_CLKGATE_CON(0), 2, GFLAGS), |
169 | COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, | ||
170 | RK3288_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS), | ||
171 | 233 | ||
172 | COMPOSITE_NOMUX(0, "armcore0", "armclk", 0, | 234 | COMPOSITE_NOMUX(0, "armcore0", "armclk", 0, |
173 | RK3288_CLKSEL_CON(36), 0, 3, DFLAGS, | 235 | RK3288_CLKSEL_CON(36), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, |
174 | RK3288_CLKGATE_CON(12), 0, GFLAGS), | 236 | RK3288_CLKGATE_CON(12), 0, GFLAGS), |
175 | COMPOSITE_NOMUX(0, "armcore1", "armclk", 0, | 237 | COMPOSITE_NOMUX(0, "armcore1", "armclk", 0, |
176 | RK3288_CLKSEL_CON(36), 4, 3, DFLAGS, | 238 | RK3288_CLKSEL_CON(36), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, |
177 | RK3288_CLKGATE_CON(12), 1, GFLAGS), | 239 | RK3288_CLKGATE_CON(12), 1, GFLAGS), |
178 | COMPOSITE_NOMUX(0, "armcore2", "armclk", 0, | 240 | COMPOSITE_NOMUX(0, "armcore2", "armclk", 0, |
179 | RK3288_CLKSEL_CON(36), 8, 3, DFLAGS, | 241 | RK3288_CLKSEL_CON(36), 8, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, |
180 | RK3288_CLKGATE_CON(12), 2, GFLAGS), | 242 | RK3288_CLKGATE_CON(12), 2, GFLAGS), |
181 | COMPOSITE_NOMUX(0, "armcore3", "armclk", 0, | 243 | COMPOSITE_NOMUX(0, "armcore3", "armclk", 0, |
182 | RK3288_CLKSEL_CON(36), 12, 3, DFLAGS, | 244 | RK3288_CLKSEL_CON(36), 12, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, |
183 | RK3288_CLKGATE_CON(12), 3, GFLAGS), | 245 | RK3288_CLKGATE_CON(12), 3, GFLAGS), |
184 | COMPOSITE_NOMUX(0, "l2ram", "armclk", 0, | 246 | COMPOSITE_NOMUX(0, "l2ram", "armclk", 0, |
185 | RK3288_CLKSEL_CON(37), 0, 3, DFLAGS, | 247 | RK3288_CLKSEL_CON(37), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, |
186 | RK3288_CLKGATE_CON(12), 4, GFLAGS), | 248 | RK3288_CLKGATE_CON(12), 4, GFLAGS), |
187 | COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0, | 249 | COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0, |
188 | RK3288_CLKSEL_CON(0), 0, 4, DFLAGS, | 250 | RK3288_CLKSEL_CON(0), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, |
189 | RK3288_CLKGATE_CON(12), 5, GFLAGS), | 251 | RK3288_CLKGATE_CON(12), 5, GFLAGS), |
190 | COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0, | 252 | COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0, |
191 | RK3288_CLKSEL_CON(0), 4, 4, DFLAGS, | 253 | RK3288_CLKSEL_CON(0), 4, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, |
192 | RK3288_CLKGATE_CON(12), 6, GFLAGS), | 254 | RK3288_CLKGATE_CON(12), 6, GFLAGS), |
193 | COMPOSITE_NOMUX(0, "atclk", "armclk", 0, | 255 | COMPOSITE_NOMUX(0, "atclk", "armclk", 0, |
194 | RK3288_CLKSEL_CON(37), 4, 5, DFLAGS, | 256 | RK3288_CLKSEL_CON(37), 4, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, |
195 | RK3288_CLKGATE_CON(12), 7, GFLAGS), | 257 | RK3288_CLKGATE_CON(12), 7, GFLAGS), |
196 | COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0, | 258 | COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0, |
197 | RK3288_CLKSEL_CON(37), 9, 5, DFLAGS, | 259 | RK3288_CLKSEL_CON(37), 9, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, |
198 | RK3288_CLKGATE_CON(12), 8, GFLAGS), | 260 | RK3288_CLKGATE_CON(12), 8, GFLAGS), |
199 | GATE(0, "pclk_dbg", "pclk_dbg_pre", 0, | 261 | GATE(0, "pclk_dbg", "pclk_dbg_pre", 0, |
200 | RK3288_CLKGATE_CON(12), 9, GFLAGS), | 262 | RK3288_CLKGATE_CON(12), 9, GFLAGS), |
@@ -733,12 +795,17 @@ static void __init rk3288_clk_init(struct device_node *np) | |||
733 | 795 | ||
734 | rockchip_clk_register_plls(rk3288_pll_clks, | 796 | rockchip_clk_register_plls(rk3288_pll_clks, |
735 | ARRAY_SIZE(rk3288_pll_clks), | 797 | ARRAY_SIZE(rk3288_pll_clks), |
736 | RK3288_GRF_SOC_STATUS); | 798 | RK3288_GRF_SOC_STATUS1); |
737 | rockchip_clk_register_branches(rk3288_clk_branches, | 799 | rockchip_clk_register_branches(rk3288_clk_branches, |
738 | ARRAY_SIZE(rk3288_clk_branches)); | 800 | ARRAY_SIZE(rk3288_clk_branches)); |
739 | rockchip_clk_protect_critical(rk3288_critical_clocks, | 801 | rockchip_clk_protect_critical(rk3288_critical_clocks, |
740 | ARRAY_SIZE(rk3288_critical_clocks)); | 802 | ARRAY_SIZE(rk3288_critical_clocks)); |
741 | 803 | ||
804 | rockchip_clk_register_armclk(ARMCLK, "armclk", | ||
805 | mux_armclk_p, ARRAY_SIZE(mux_armclk_p), | ||
806 | &rk3288_cpuclk_data, rk3288_cpuclk_rates, | ||
807 | ARRAY_SIZE(rk3288_cpuclk_rates)); | ||
808 | |||
742 | rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0), | 809 | rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0), |
743 | ROCKCHIP_SOFTRST_HIWORD_MASK); | 810 | ROCKCHIP_SOFTRST_HIWORD_MASK); |
744 | } | 811 | } |
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index d9c6db2151ba..fd3b5ef87e29 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c | |||
@@ -297,6 +297,27 @@ void __init rockchip_clk_register_branches( | |||
297 | } | 297 | } |
298 | } | 298 | } |
299 | 299 | ||
300 | void __init rockchip_clk_register_armclk(unsigned int lookup_id, | ||
301 | const char *name, const char **parent_names, | ||
302 | u8 num_parents, | ||
303 | const struct rockchip_cpuclk_reg_data *reg_data, | ||
304 | const struct rockchip_cpuclk_rate_table *rates, | ||
305 | int nrates) | ||
306 | { | ||
307 | struct clk *clk; | ||
308 | |||
309 | clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, | ||
310 | reg_data, rates, nrates, reg_base, | ||
311 | &clk_lock); | ||
312 | if (IS_ERR(clk)) { | ||
313 | pr_err("%s: failed to register clock %s: %ld\n", | ||
314 | __func__, name, PTR_ERR(clk)); | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | rockchip_clk_add_lookup(clk, lookup_id); | ||
319 | } | ||
320 | |||
300 | void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) | 321 | void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) |
301 | { | 322 | { |
302 | int i; | 323 | int i; |
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 2b0bca19db47..f4791fbb3da9 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h | |||
@@ -120,6 +120,38 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, | |||
120 | struct rockchip_pll_rate_table *rate_table, | 120 | struct rockchip_pll_rate_table *rate_table, |
121 | spinlock_t *lock); | 121 | spinlock_t *lock); |
122 | 122 | ||
123 | struct rockchip_cpuclk_clksel { | ||
124 | int reg; | ||
125 | u32 val; | ||
126 | }; | ||
127 | |||
128 | #define ROCKCHIP_CPUCLK_NUM_DIVIDERS 2 | ||
129 | struct rockchip_cpuclk_rate_table { | ||
130 | unsigned long prate; | ||
131 | struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS]; | ||
132 | }; | ||
133 | |||
134 | /** | ||
135 | * struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock | ||
136 | * @core_reg: register offset of the core settings register | ||
137 | * @div_core_shift: core divider offset used to divide the pll value | ||
138 | * @div_core_mask: core divider mask | ||
139 | * @mux_core_shift: offset of the core multiplexer | ||
140 | */ | ||
141 | struct rockchip_cpuclk_reg_data { | ||
142 | int core_reg; | ||
143 | u8 div_core_shift; | ||
144 | u32 div_core_mask; | ||
145 | int mux_core_reg; | ||
146 | u8 mux_core_shift; | ||
147 | }; | ||
148 | |||
149 | struct clk *rockchip_clk_register_cpuclk(const char *name, | ||
150 | const char **parent_names, u8 num_parents, | ||
151 | const struct rockchip_cpuclk_reg_data *reg_data, | ||
152 | const struct rockchip_cpuclk_rate_table *rates, | ||
153 | int nrates, void __iomem *reg_base, spinlock_t *lock); | ||
154 | |||
123 | #define PNAME(x) static const char *x[] __initconst | 155 | #define PNAME(x) static const char *x[] __initconst |
124 | 156 | ||
125 | enum rockchip_clk_branch_type { | 157 | enum rockchip_clk_branch_type { |
@@ -329,6 +361,11 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, | |||
329 | unsigned int nr_clk); | 361 | unsigned int nr_clk); |
330 | void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, | 362 | void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, |
331 | unsigned int nr_pll, int grf_lock_offset); | 363 | unsigned int nr_pll, int grf_lock_offset); |
364 | void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, | ||
365 | const char **parent_names, u8 num_parents, | ||
366 | const struct rockchip_cpuclk_reg_data *reg_data, | ||
367 | const struct rockchip_cpuclk_rate_table *rates, | ||
368 | int nrates); | ||
332 | void rockchip_clk_protect_critical(const char *clocks[], int nclocks); | 369 | void rockchip_clk_protect_critical(const char *clocks[], int nclocks); |
333 | 370 | ||
334 | #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) | 371 | #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) |
diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h index 750ee60e75fb..6a370503c954 100644 --- a/include/dt-bindings/clock/rk3188-cru-common.h +++ b/include/dt-bindings/clock/rk3188-cru-common.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #define PLL_GPLL 4 | 20 | #define PLL_GPLL 4 |
21 | #define CORE_PERI 5 | 21 | #define CORE_PERI 5 |
22 | #define CORE_L2C 6 | 22 | #define CORE_L2C 6 |
23 | #define ARMCLK 7 | ||
23 | 24 | ||
24 | /* sclk gates (special clocks) */ | 25 | /* sclk gates (special clocks) */ |
25 | #define SCLK_UART0 64 | 26 | #define SCLK_UART0 64 |
diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h index 750e5587a7aa..100a08c47692 100644 --- a/include/dt-bindings/clock/rk3288-cru.h +++ b/include/dt-bindings/clock/rk3288-cru.h | |||
@@ -19,6 +19,7 @@ | |||
19 | #define PLL_CPLL 3 | 19 | #define PLL_CPLL 3 |
20 | #define PLL_GPLL 4 | 20 | #define PLL_GPLL 4 |
21 | #define PLL_NPLL 5 | 21 | #define PLL_NPLL 5 |
22 | #define ARMCLK 6 | ||
22 | 23 | ||
23 | /* sclk gates (special clocks) */ | 24 | /* sclk gates (special clocks) */ |
24 | #define SCLK_GPU 64 | 25 | #define SCLK_GPU 64 |