1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
4 * Author: Lin Huang <hl@rock-chips.com>
5 */
6
7 #include <linux/arm-smccc.h>
8 #include <linux/clk.h>
9 #include <linux/clk-provider.h>
10 #include <linux/io.h>
11 #include <linux/of.h>
12 #include <linux/rockchip/rockchip_sip.h>
13 #include <linux/slab.h>
14 #include <soc/rockchip/rockchip_sip.h>
15 #ifdef CONFIG_ARM
16 #include <asm/psci.h>
17 #endif
18
19 #include "clk.h"
20
21 struct rockchip_ddrclk {
22 struct clk_hw hw;
23 void __iomem *reg_base;
24 int mux_offset;
25 int mux_shift;
26 int mux_width;
27 int div_shift;
28 int div_width;
29 int ddr_flag;
30 };
31
32 #define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
33
34 struct share_params_ddrclk {
35 u32 hz;
36 u32 lcdc_type;
37 };
38
39 struct rockchip_ddrclk_data {
40 void __iomem *params;
41 int (*dmcfreq_wait_complete)(void);
42 };
43
44 static struct rockchip_ddrclk_data ddr_data = {NULL, NULL};
45
rockchip_set_ddrclk_params(void __iomem * params)46 void rockchip_set_ddrclk_params(void __iomem *params)
47 {
48 ddr_data.params = params;
49 }
50 EXPORT_SYMBOL(rockchip_set_ddrclk_params);
51
rockchip_set_ddrclk_dmcfreq_wait_complete(int (* func)(void))52 void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void))
53 {
54 ddr_data.dmcfreq_wait_complete = func;
55 }
56 EXPORT_SYMBOL(rockchip_set_ddrclk_dmcfreq_wait_complete);
57
rockchip_ddrclk_sip_set_rate(struct clk_hw * hw,unsigned long drate,unsigned long prate)58 static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
59 unsigned long prate)
60 {
61 struct arm_smccc_res res;
62
63 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
64 ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
65 0, 0, 0, 0, &res);
66
67 if (res.a0)
68 return 0;
69 else
70 return -EPERM;
71 }
72
73 static unsigned long
rockchip_ddrclk_sip_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)74 rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw,
75 unsigned long parent_rate)
76 {
77 struct arm_smccc_res res;
78
79 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
80 ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
81 0, 0, 0, 0, &res);
82
83 return res.a0;
84 }
85
rockchip_ddrclk_sip_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)86 static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
87 unsigned long rate,
88 unsigned long *prate)
89 {
90 struct arm_smccc_res res;
91
92 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
93 ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
94 0, 0, 0, 0, &res);
95
96 return res.a0;
97 }
98
rockchip_ddrclk_get_parent(struct clk_hw * hw)99 static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
100 {
101 struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
102 u32 val;
103
104 val = readl(ddrclk->reg_base +
105 ddrclk->mux_offset) >> ddrclk->mux_shift;
106 val &= GENMASK(ddrclk->mux_width - 1, 0);
107
108 return val;
109 }
110
111 static const struct clk_ops rockchip_ddrclk_sip_ops = {
112 .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
113 .set_rate = rockchip_ddrclk_sip_set_rate,
114 .round_rate = rockchip_ddrclk_sip_round_rate,
115 .get_parent = rockchip_ddrclk_get_parent,
116 };
117
rockchip_ddrclk_sip_set_rate_v2(struct clk_hw * hw,unsigned long drate,unsigned long prate)118 static int rockchip_ddrclk_sip_set_rate_v2(struct clk_hw *hw,
119 unsigned long drate,
120 unsigned long prate)
121 {
122 struct share_params_ddrclk *p;
123 struct arm_smccc_res res;
124
125 p = (struct share_params_ddrclk *)ddr_data.params;
126 if (p)
127 p->hz = drate;
128
129 res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
130 ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE);
131
132 if ((int)res.a1 == SIP_RET_SET_RATE_TIMEOUT) {
133 if (ddr_data.dmcfreq_wait_complete)
134 ddr_data.dmcfreq_wait_complete();
135 }
136
137 return res.a0;
138 }
139
rockchip_ddrclk_sip_recalc_rate_v2(struct clk_hw * hw,unsigned long parent_rate)140 static unsigned long rockchip_ddrclk_sip_recalc_rate_v2
141 (struct clk_hw *hw, unsigned long parent_rate)
142 {
143 struct arm_smccc_res res;
144
145 res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
146 ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE);
147 if (!res.a0)
148 return res.a1;
149 else
150 return 0;
151 }
152
rockchip_ddrclk_sip_round_rate_v2(struct clk_hw * hw,unsigned long rate,unsigned long * prate)153 static long rockchip_ddrclk_sip_round_rate_v2(struct clk_hw *hw,
154 unsigned long rate,
155 unsigned long *prate)
156 {
157 struct share_params_ddrclk *p;
158 struct arm_smccc_res res;
159
160 p = (struct share_params_ddrclk *)ddr_data.params;
161 if (p)
162 p->hz = rate;
163
164 res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
165 ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE);
166 if (!res.a0)
167 return res.a1;
168 else
169 return 0;
170 }
171
172 static const struct clk_ops rockchip_ddrclk_sip_ops_v2 = {
173 .recalc_rate = rockchip_ddrclk_sip_recalc_rate_v2,
174 .set_rate = rockchip_ddrclk_sip_set_rate_v2,
175 .round_rate = rockchip_ddrclk_sip_round_rate_v2,
176 .get_parent = rockchip_ddrclk_get_parent,
177 };
178
rockchip_clk_register_ddrclk(const char * name,int flags,const char * const * parent_names,u8 num_parents,int mux_offset,int mux_shift,int mux_width,int div_shift,int div_width,int ddr_flag,void __iomem * reg_base)179 struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
180 const char *const *parent_names,
181 u8 num_parents, int mux_offset,
182 int mux_shift, int mux_width,
183 int div_shift, int div_width,
184 int ddr_flag, void __iomem *reg_base)
185 {
186 struct rockchip_ddrclk *ddrclk;
187 struct clk_init_data init;
188 struct clk *clk;
189
190 #ifdef CONFIG_ARM
191 if (!psci_smp_available())
192 return NULL;
193 #endif
194
195 ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
196 if (!ddrclk)
197 return ERR_PTR(-ENOMEM);
198
199 init.name = name;
200 init.parent_names = parent_names;
201 init.num_parents = num_parents;
202
203 init.flags = flags;
204 init.flags |= CLK_SET_RATE_NO_REPARENT;
205
206 switch (ddr_flag) {
207 #ifdef CONFIG_ROCKCHIP_DDRCLK_SIP
208 case ROCKCHIP_DDRCLK_SIP:
209 init.ops = &rockchip_ddrclk_sip_ops;
210 break;
211 #endif
212 #ifdef CONFIG_ROCKCHIP_DDRCLK_SIP_V2
213 case ROCKCHIP_DDRCLK_SIP_V2:
214 init.ops = &rockchip_ddrclk_sip_ops_v2;
215 break;
216 #endif
217 default:
218 pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
219 kfree(ddrclk);
220 return ERR_PTR(-EINVAL);
221 }
222
223 ddrclk->reg_base = reg_base;
224 ddrclk->hw.init = &init;
225 ddrclk->mux_offset = mux_offset;
226 ddrclk->mux_shift = mux_shift;
227 ddrclk->mux_width = mux_width;
228 ddrclk->div_shift = div_shift;
229 ddrclk->div_width = div_width;
230 ddrclk->ddr_flag = ddr_flag;
231
232 clk = clk_register(NULL, &ddrclk->hw);
233 if (IS_ERR(clk))
234 kfree(ddrclk);
235
236 return clk;
237 }
238 EXPORT_SYMBOL_GPL(rockchip_clk_register_ddrclk);
239