• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <soc/rockchip/scpi.h>
16 #include <uapi/drm/drm_mode.h>
17 
18 #ifdef CONFIG_ARM
19 #include <asm/psci.h>
20 #endif
21 
22 #include "clk.h"
23 
24 #define MHZ (1000000)
25 #define DDR_RATE 12
26 struct rockchip_ddrclk {
27     struct clk_hw hw;
28     void __iomem *reg_base;
29     int mux_offset;
30     int mux_shift;
31     int mux_width;
32     int div_shift;
33     int div_width;
34     int ddr_flag;
35 };
36 
37 #define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
38 
39 struct share_params_ddrclk {
40     u32 hz;
41     u32 lcdc_type;
42 };
43 
44 struct rockchip_ddrclk_data {
45     void __iomem *params;
46     int (*dmcfreq_wait_complete)(void);
47 };
48 
49 static struct rockchip_ddrclk_data ddr_data = {NULL, NULL};
50 
rockchip_set_ddrclk_params(void __iomem * params)51 void rockchip_set_ddrclk_params(void __iomem *params)
52 {
53     ddr_data.params = params;
54 }
55 EXPORT_SYMBOL(rockchip_set_ddrclk_params);
56 
rockchip_set_ddrclk_dmcfreq_wait_complete(int (* func)(void))57 void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void))
58 {
59     ddr_data.dmcfreq_wait_complete = func;
60 }
61 EXPORT_SYMBOL(rockchip_set_ddrclk_dmcfreq_wait_complete);
62 
rockchip_ddrclk_sip_set_rate(struct clk_hw * hw,unsigned long drate,unsigned long prate)63 static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate)
64 {
65     struct arm_smccc_res res;
66 
67     arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0, ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, 0, 0, 0, 0, &res);
68 
69     if (res.a0) {
70         return 0;
71     } else {
72         return -EPERM;
73     }
74 }
75 
rockchip_ddrclk_sip_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)76 static unsigned long rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
77 {
78     struct arm_smccc_res res;
79 
80     arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, 0, 0, 0, 0, &res);
81 
82     return res.a0;
83 }
84 
rockchip_ddrclk_sip_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)85 static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
86 {
87     struct arm_smccc_res res;
88 
89     arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE, 0, 0, 0, 0, &res);
90 
91     return res.a0;
92 }
93 
rockchip_ddrclk_get_parent(struct clk_hw * hw)94 static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
95 {
96     struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
97     u32 val;
98 
99     val = readl(ddrclk->reg_base + ddrclk->mux_offset) >> ddrclk->mux_shift;
100     val &= GENMASK(ddrclk->mux_width - 1, 0);
101 
102     return val;
103 }
104 
105 static const struct clk_ops rockchip_ddrclk_sip_ops = {
106     .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
107     .set_rate = rockchip_ddrclk_sip_set_rate,
108     .round_rate = rockchip_ddrclk_sip_round_rate,
109     .get_parent = rockchip_ddrclk_get_parent,
110 };
111 
112 static u32 ddr_clk_cached;
113 
rockchip_ddrclk_scpi_set_rate(struct clk_hw * hw,unsigned long drate,unsigned long prate)114 static int rockchip_ddrclk_scpi_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate)
115 {
116     u32 ret;
117     u32 lcdc_type = 0;
118     struct share_params_ddrclk *p;
119 
120     p = (struct share_params_ddrclk *)ddr_data.params;
121     if (p) {
122         lcdc_type = p->lcdc_type;
123     }
124 
125     ret = scpi_ddr_set_clk_rate(drate / MHZ, lcdc_type);
126     if (ret) {
127         ddr_clk_cached = ret;
128         ret = 0;
129     } else {
130         ddr_clk_cached = 0;
131         ret = -1;
132     }
133 
134     return ret;
135 }
136 
rockchip_ddrclk_scpi_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)137 static unsigned long rockchip_ddrclk_scpi_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
138 {
139     if (ddr_clk_cached) {
140         return (MHZ * ddr_clk_cached);
141     } else {
142         return (MHZ * scpi_ddr_get_clk_rate());
143     }
144 }
145 
rockchip_ddrclk_scpi_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)146 static long rockchip_ddrclk_scpi_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
147 {
148     rate = rate / MHZ;
149     rate = (rate / DDR_RATE) * DDR_RATE;
150 
151     return (rate * MHZ);
152 }
153 
154 static const struct clk_ops rockchip_ddrclk_scpi_ops = {
155     .recalc_rate = rockchip_ddrclk_scpi_recalc_rate,
156     .set_rate = rockchip_ddrclk_scpi_set_rate,
157     .round_rate = rockchip_ddrclk_scpi_round_rate,
158     .get_parent = rockchip_ddrclk_get_parent,
159 };
160 
rockchip_ddrclk_sip_set_rate_v2(struct clk_hw * hw,unsigned long drate,unsigned long prate)161 static int rockchip_ddrclk_sip_set_rate_v2(struct clk_hw *hw, unsigned long drate, unsigned long prate)
162 {
163     struct share_params_ddrclk *p;
164     struct arm_smccc_res res;
165 
166     p = (struct share_params_ddrclk *)ddr_data.params;
167     if (p) {
168         p->hz = drate;
169     }
170 
171     res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE);
172     if ((int)res.a1 == SIP_RET_SET_RATE_TIMEOUT) {
173         if (ddr_data.dmcfreq_wait_complete) {
174             ddr_data.dmcfreq_wait_complete();
175         }
176     }
177 
178     return res.a0;
179 }
180 
rockchip_ddrclk_sip_recalc_rate_v2(struct clk_hw * hw,unsigned long parent_rate)181 static unsigned long rockchip_ddrclk_sip_recalc_rate_v2(struct clk_hw *hw, unsigned long parent_rate)
182 {
183     struct arm_smccc_res res;
184 
185     res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE);
186     if (!res.a0) {
187         return res.a1;
188     } else {
189         return 0;
190     }
191 }
192 
rockchip_ddrclk_sip_round_rate_v2(struct clk_hw * hw,unsigned long rate,unsigned long * prate)193 static long rockchip_ddrclk_sip_round_rate_v2(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
194 {
195     struct share_params_ddrclk *p;
196     struct arm_smccc_res res;
197 
198     p = (struct share_params_ddrclk *)ddr_data.params;
199     if (p) {
200         p->hz = rate;
201     }
202 
203     res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE);
204     if (!res.a0) {
205         return res.a1;
206     } else {
207         return 0;
208     }
209 }
210 
211 static const struct clk_ops rockchip_ddrclk_sip_ops_v2 = {
212     .recalc_rate = rockchip_ddrclk_sip_recalc_rate_v2,
213     .set_rate = rockchip_ddrclk_sip_set_rate_v2,
214     .round_rate = rockchip_ddrclk_sip_round_rate_v2,
215     .get_parent = rockchip_ddrclk_get_parent,
216 };
217 
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)218 struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, const char *const *parent_names, u8 num_parents,
219                                          int mux_offset, int mux_shift, int mux_width, int div_shift, int div_width,
220                                          int ddr_flag, void __iomem *reg_base)
221 {
222     struct rockchip_ddrclk *ddrclk;
223     struct clk_init_data init;
224     struct clk *clk;
225 
226 #ifdef CONFIG_ARM
227     if (!psci_smp_available()) {
228         return NULL;
229     }
230 #endif
231 
232     ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
233     if (!ddrclk) {
234         return ERR_PTR(-ENOMEM);
235     }
236 
237     init.name = name;
238     init.parent_names = parent_names;
239     init.num_parents = num_parents;
240 
241     init.flags = flags;
242     init.flags |= CLK_SET_RATE_NO_REPARENT;
243 
244     switch (ddr_flag) {
245         case ROCKCHIP_DDRCLK_SIP:
246             init.ops = &rockchip_ddrclk_sip_ops;
247             break;
248         case ROCKCHIP_DDRCLK_SCPI:
249             init.ops = &rockchip_ddrclk_scpi_ops;
250             break;
251         case ROCKCHIP_DDRCLK_SIP_V2:
252             init.ops = &rockchip_ddrclk_sip_ops_v2;
253             break;
254         default:
255             pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
256             kfree(ddrclk);
257             return ERR_PTR(-EINVAL);
258     }
259 
260     ddrclk->reg_base = reg_base;
261     ddrclk->hw.init = &init;
262     ddrclk->mux_offset = mux_offset;
263     ddrclk->mux_shift = mux_shift;
264     ddrclk->mux_width = mux_width;
265     ddrclk->div_shift = div_shift;
266     ddrclk->div_width = div_width;
267     ddrclk->ddr_flag = ddr_flag;
268 
269     clk = clk_register(NULL, &ddrclk->hw);
270     if (IS_ERR(clk)) {
271         kfree(ddrclk);
272     }
273 
274     return clk;
275 }
276 EXPORT_SYMBOL_GPL(rockchip_clk_register_ddrclk);
277