aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Turquette <mturquette@linaro.org>2014-09-27 15:50:40 -0400
committerMike Turquette <mturquette@linaro.org>2014-09-27 15:50:40 -0400
commit5ad67d3e5e0a5059945a7726a407763a23f80d9e (patch)
tree365e8170b9553818317dd80724f1bacbc75e7916
parent8791db53a9d2556b8908af300d8327eecb110d8a (diff)
parent0e5bdb3f9fa5c2bd4452c258de78122ef15f62d6 (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/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-cpu.c329
-rw-r--r--drivers/clk/rockchip/clk-pll.c63
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c161
-rw-r--r--drivers/clk/rockchip/clk-rk3288.c93
-rw-r--r--drivers/clk/rockchip/clk.c21
-rw-r--r--drivers/clk/rockchip/clk.h37
-rw-r--r--include/dt-bindings/clock/rk3188-cru-common.h1
-rw-r--r--include/dt-bindings/clock/rk3288-cru.h1
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 @@
5obj-y += clk-rockchip.o 5obj-y += clk-rockchip.o
6obj-y += clk.o 6obj-y += clk.o
7obj-y += clk-pll.o 7obj-y += clk-pll.o
8obj-y += clk-cpu.o
8obj-$(CONFIG_RESET_CONTROLLER) += softrst.o 9obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
9 10
10obj-y += clk-rk3188.o 11obj-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 */
54struct 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
73static 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
88static 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
100static const struct clk_ops rockchip_cpuclk_ops = {
101 .recalc_rate = rockchip_cpuclk_recalc_rate,
102};
103
104static 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
122static 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
171static 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 */
214static 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
231struct 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
322free_rate_table:
323 kfree(cpuclk->rate_table);
324unregister_notifier:
325 clk_notifier_unregister(clk, &cpuclk->clk_nb);
326free_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 */
116static 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
419err_mux: 389err_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 }
426err_pll_notifier:
427 clk_unregister(pll_clk); 390 clk_unregister(pll_clk);
428err_pll: 391err_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
24enum rk3188_plls { 25enum 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
143static 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
153static 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
178static 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
189static 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
103PNAME(mux_pll_p) = { "xin24m", "xin32k" }; 196PNAME(mux_pll_p) = { "xin24m", "xin32k" };
104PNAME(mux_armclk_p) = { "apll", "gpll_armclk" }; 197PNAME(mux_armclk_p) = { "apll", "gpll_armclk" };
105PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" }; 198PNAME(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
414static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { 500static 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
526static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { 620static 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)
644static void __init rk3066a_clk_init(struct device_node *np) 740static 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}
650CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); 753CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);
651 754
652static void __init rk3188a_clk_init(struct device_node *np) 755static 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}
658CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); 789CLK_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
25enum rk3288_plls { 25enum 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
144static 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
161static 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
104PNAME(mux_pll_p) = { "xin24m", "xin32k" }; 168PNAME(mux_pll_p) = { "xin24m", "xin32k" };
105PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; 169PNAME(mux_armclk_p) = { "apll_core", "gpll_core" };
106PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; 170PNAME(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
300void __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
300void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) 321void __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
123struct rockchip_cpuclk_clksel {
124 int reg;
125 u32 val;
126};
127
128#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 2
129struct 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 */
141struct 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
149struct 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
125enum rockchip_clk_branch_type { 157enum 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);
330void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, 362void 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);
364void 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);
332void rockchip_clk_protect_critical(const char *clocks[], int nclocks); 369void 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