xref: /OK3568_Linux_fs/kernel/drivers/clk/rockchip/clk-ddr.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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