aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLin Huang <hl@rock-chips.com>2016-08-21 23:36:17 -0400
committerHeiko Stuebner <heiko@sntech.de>2016-09-01 05:23:56 -0400
commita4f182bf81f18f91f1aef6289fcdfa6a2ac51b99 (patch)
tree36c23cf0b8be22260303e345259c426e209326ac
parent06b826fc28abe0b04798cdea270edfa47a6638dc (diff)
clk: rockchip: add new clock-type for the ddrclk
Changing the rate of the DDR clock needs special care, as the DDR is of course in use and will react badly if the rate changes under it. Over time different approaches to handle that were used. Past SoCs like the rk3288 and before would store some code in SRAM while the rk3368 used a SCPI variant and let a coprocessor handle that. New rockchip platforms like the rk3399 have a dcf controller to do ddr frequency scaling, and support for this controller will be implemented in the arm-trusted-firmware. This new clock-type should over time handle all these methods for handling DDR rate changes, but right now it will concentrate on the SIP interface used to talk to ARM trusted firmware. The SIP interface counterpart was merged from pull-request #684 [0] into the upstream arm-trusted-firmware codebase. [0] https://github.com/ARM-software/arm-trusted-firmware/pull/684 Signed-off-by: Lin Huang <hl@rock-chips.com> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-ddr.c154
-rw-r--r--drivers/clk/rockchip/clk.c9
-rw-r--r--drivers/clk/rockchip/clk.h33
4 files changed, 197 insertions, 0 deletions
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index f47a2fa962d2..b5f2c8ed12e1 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -8,6 +8,7 @@ obj-y += clk-pll.o
8obj-y += clk-cpu.o 8obj-y += clk-cpu.o
9obj-y += clk-inverter.o 9obj-y += clk-inverter.o
10obj-y += clk-mmc-phase.o 10obj-y += clk-mmc-phase.o
11obj-y += clk-ddr.o
11obj-$(CONFIG_RESET_CONTROLLER) += softrst.o 12obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
12 13
13obj-y += clk-rk3036.o 14obj-y += clk-rk3036.o
diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c
new file mode 100644
index 000000000000..8feba93672c5
--- /dev/null
+++ b/drivers/clk/rockchip/clk-ddr.c
@@ -0,0 +1,154 @@
1/*
2 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
3 * Author: Lin Huang <hl@rock-chips.com>
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 <linux/arm-smccc.h>
17#include <linux/clk.h>
18#include <linux/clk-provider.h>
19#include <linux/io.h>
20#include <linux/slab.h>
21#include <soc/rockchip/rockchip_sip.h>
22#include "clk.h"
23
24struct rockchip_ddrclk {
25 struct clk_hw hw;
26 void __iomem *reg_base;
27 int mux_offset;
28 int mux_shift;
29 int mux_width;
30 int div_shift;
31 int div_width;
32 int ddr_flag;
33 spinlock_t *lock;
34};
35
36#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
37
38static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
39 unsigned long prate)
40{
41 struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
42 unsigned long flags;
43 struct arm_smccc_res res;
44
45 spin_lock_irqsave(ddrclk->lock, flags);
46 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
47 ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
48 0, 0, 0, 0, &res);
49 spin_unlock_irqrestore(ddrclk->lock, flags);
50
51 return res.a0;
52}
53
54static unsigned long
55rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw,
56 unsigned long parent_rate)
57{
58 struct arm_smccc_res res;
59
60 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
61 ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
62 0, 0, 0, 0, &res);
63
64 return res.a0;
65}
66
67static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
68 unsigned long rate,
69 unsigned long *prate)
70{
71 struct arm_smccc_res res;
72
73 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
74 ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
75 0, 0, 0, 0, &res);
76
77 return res.a0;
78}
79
80static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
81{
82 struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
83 int num_parents = clk_hw_get_num_parents(hw);
84 u32 val;
85
86 val = clk_readl(ddrclk->reg_base +
87 ddrclk->mux_offset) >> ddrclk->mux_shift;
88 val &= GENMASK(ddrclk->mux_width - 1, 0);
89
90 if (val >= num_parents)
91 return -EINVAL;
92
93 return val;
94}
95
96static const struct clk_ops rockchip_ddrclk_sip_ops = {
97 .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
98 .set_rate = rockchip_ddrclk_sip_set_rate,
99 .round_rate = rockchip_ddrclk_sip_round_rate,
100 .get_parent = rockchip_ddrclk_get_parent,
101};
102
103struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
104 const char *const *parent_names,
105 u8 num_parents, int mux_offset,
106 int mux_shift, int mux_width,
107 int div_shift, int div_width,
108 int ddr_flag, void __iomem *reg_base,
109 spinlock_t *lock)
110{
111 struct rockchip_ddrclk *ddrclk;
112 struct clk_init_data init;
113 struct clk *clk;
114
115 ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
116 if (!ddrclk)
117 return ERR_PTR(-ENOMEM);
118
119 init.name = name;
120 init.parent_names = parent_names;
121 init.num_parents = num_parents;
122
123 init.flags = flags;
124 init.flags |= CLK_SET_RATE_NO_REPARENT;
125
126 switch (ddr_flag) {
127 case ROCKCHIP_DDRCLK_SIP:
128 init.ops = &rockchip_ddrclk_sip_ops;
129 break;
130 default:
131 pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
132 kfree(ddrclk);
133 return ERR_PTR(-EINVAL);
134 }
135
136 ddrclk->reg_base = reg_base;
137 ddrclk->lock = lock;
138 ddrclk->hw.init = &init;
139 ddrclk->mux_offset = mux_offset;
140 ddrclk->mux_shift = mux_shift;
141 ddrclk->mux_width = mux_width;
142 ddrclk->div_shift = div_shift;
143 ddrclk->div_width = div_width;
144 ddrclk->ddr_flag = ddr_flag;
145
146 clk = clk_register(NULL, &ddrclk->hw);
147 if (IS_ERR(clk)) {
148 pr_err("%s: could not register ddrclk %s\n", __func__, name);
149 kfree(ddrclk);
150 return NULL;
151 }
152
153 return clk;
154}
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 1f1c74f3744b..b886be30f34f 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -484,6 +484,15 @@ void __init rockchip_clk_register_branches(
484 list->gate_offset, list->gate_shift, 484 list->gate_offset, list->gate_shift,
485 list->gate_flags, flags, &ctx->lock); 485 list->gate_flags, flags, &ctx->lock);
486 break; 486 break;
487 case branch_ddrclk:
488 clk = rockchip_clk_register_ddrclk(
489 list->name, list->flags,
490 list->parent_names, list->num_parents,
491 list->muxdiv_offset, list->mux_shift,
492 list->mux_width, list->div_shift,
493 list->div_width, list->div_flags,
494 ctx->reg_base, &ctx->lock);
495 break;
487 } 496 }
488 497
489 /* none of the cases above matched */ 498 /* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 3747de5ce7c2..1653edd792a5 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -281,6 +281,20 @@ struct clk *rockchip_clk_register_mmc(const char *name,
281 const char *const *parent_names, u8 num_parents, 281 const char *const *parent_names, u8 num_parents,
282 void __iomem *reg, int shift); 282 void __iomem *reg, int shift);
283 283
284/*
285 * DDRCLK flags, including method of setting the rate
286 * ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk rate.
287 */
288#define ROCKCHIP_DDRCLK_SIP BIT(0)
289
290struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
291 const char *const *parent_names,
292 u8 num_parents, int mux_offset,
293 int mux_shift, int mux_width,
294 int div_shift, int div_width,
295 int ddr_flags, void __iomem *reg_base,
296 spinlock_t *lock);
297
284#define ROCKCHIP_INVERTER_HIWORD_MASK BIT(0) 298#define ROCKCHIP_INVERTER_HIWORD_MASK BIT(0)
285 299
286struct clk *rockchip_clk_register_inverter(const char *name, 300struct clk *rockchip_clk_register_inverter(const char *name,
@@ -299,6 +313,7 @@ enum rockchip_clk_branch_type {
299 branch_mmc, 313 branch_mmc,
300 branch_inverter, 314 branch_inverter,
301 branch_factor, 315 branch_factor,
316 branch_ddrclk,
302}; 317};
303 318
304struct rockchip_clk_branch { 319struct rockchip_clk_branch {
@@ -488,6 +503,24 @@ struct rockchip_clk_branch {
488 .child = ch, \ 503 .child = ch, \
489 } 504 }
490 505
506#define COMPOSITE_DDRCLK(_id, cname, pnames, f, mo, ms, mw, \
507 ds, dw, df) \
508 { \
509 .id = _id, \
510 .branch_type = branch_ddrclk, \
511 .name = cname, \
512 .parent_names = pnames, \
513 .num_parents = ARRAY_SIZE(pnames), \
514 .flags = f, \
515 .muxdiv_offset = mo, \
516 .mux_shift = ms, \
517 .mux_width = mw, \
518 .div_shift = ds, \
519 .div_width = dw, \
520 .div_flags = df, \
521 .gate_offset = -1, \
522 }
523
491#define MUX(_id, cname, pnames, f, o, s, w, mf) \ 524#define MUX(_id, cname, pnames, f, o, s, w, mf) \
492 { \ 525 { \
493 .id = _id, \ 526 .id = _id, \