1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
4 */
5
6 #include <linux/clk.h>
7 #include <linux/delay.h>
8 #include <linux/err.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_platform.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
15 #include <linux/slab.h>
16 #include <linux/string.h>
17 #include <linux/clk-provider.h>
18
19 #define CLK_SEL_EXTERNAL_32K 0
20 #define CLK_SEL_INTERNAL_PVTM 1
21
22 #define wr_msk_bit(v, off, msk) ((v) << (off) | (msk << (16 + (off))))
23
24 struct rockchip_clock_pvtm;
25
26 struct rockchip_clock_pvtm_info {
27 u32 con;
28 u32 sta;
29 u32 sel_con;
30 u32 sel_shift;
31 u32 sel_value;
32 u32 sel_mask;
33 u32 div_shift;
34 u32 div_mask;
35
36 u32 (*get_value)(struct rockchip_clock_pvtm *pvtm,
37 unsigned int time_us);
38 int (*init_freq)(struct rockchip_clock_pvtm *pvtm);
39 int (*sel_enable)(struct rockchip_clock_pvtm *pvtm);
40 };
41
42 struct rockchip_clock_pvtm {
43 const struct rockchip_clock_pvtm_info *info;
44 struct regmap *grf;
45 struct clk *pvtm_clk;
46 struct clk *clk;
47 unsigned long rate;
48 };
49
xin32k_pvtm_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)50 static unsigned long xin32k_pvtm_recalc_rate(struct clk_hw *hw,
51 unsigned long parent_rate)
52 {
53 return 32768;
54 }
55
56 static const struct clk_ops xin32k_pvtm = {
57 .recalc_rate = xin32k_pvtm_recalc_rate,
58 };
59
rockchip_clock_pvtm_delay(unsigned int delay)60 static void rockchip_clock_pvtm_delay(unsigned int delay)
61 {
62 unsigned int ms = delay / 1000;
63 unsigned int us = delay % 1000;
64
65 if (ms > 0) {
66 if (ms < 20)
67 us += ms * 1000;
68 else
69 msleep(ms);
70 }
71
72 if (us >= 10)
73 usleep_range(us, us + 100);
74 else
75 udelay(us);
76 }
77
rockchip_clock_sel_internal_pvtm(struct rockchip_clock_pvtm * pvtm)78 static int rockchip_clock_sel_internal_pvtm(struct rockchip_clock_pvtm *pvtm)
79 {
80 int ret = 0;
81
82 ret = regmap_write(pvtm->grf, pvtm->info->sel_con,
83 wr_msk_bit(pvtm->info->sel_value,
84 pvtm->info->sel_shift,
85 pvtm->info->sel_mask));
86 if (ret != 0)
87 pr_err("%s: fail to write register\n", __func__);
88
89 return ret;
90 }
91
92 /* get pmu pvtm value */
rockchip_clock_pvtm_get_value(struct rockchip_clock_pvtm * pvtm,u32 time_us)93 static u32 rockchip_clock_pvtm_get_value(struct rockchip_clock_pvtm *pvtm,
94 u32 time_us)
95 {
96 const struct rockchip_clock_pvtm_info *info = pvtm->info;
97 u32 val = 0, sta = 0;
98 u32 clk_cnt, check_cnt;
99
100 /* 24m clk ,24cnt=1us */
101 clk_cnt = time_us * 24;
102
103 regmap_write(pvtm->grf, info->con + 0x4, clk_cnt);
104 regmap_write(pvtm->grf, info->con, wr_msk_bit(3, 0, 0x3));
105
106 rockchip_clock_pvtm_delay(time_us);
107
108 check_cnt = 100;
109 while (check_cnt) {
110 regmap_read(pvtm->grf, info->sta, &sta);
111 if (sta & 0x1)
112 break;
113 udelay(4);
114 check_cnt--;
115 }
116
117 if (check_cnt) {
118 regmap_read(pvtm->grf, info->sta + 0x4, &val);
119 } else {
120 pr_err("%s: wait pvtm_done timeout!\n", __func__);
121 val = 0;
122 }
123
124 regmap_write(pvtm->grf, info->con, wr_msk_bit(0, 0, 0x3));
125
126 return val;
127 }
128
rockchip_clock_pvtm_init_freq(struct rockchip_clock_pvtm * pvtm)129 static int rockchip_clock_pvtm_init_freq(struct rockchip_clock_pvtm *pvtm)
130 {
131 u32 pvtm_cnt = 0;
132 u32 div, time_us;
133 int ret = 0;
134
135 time_us = 1000;
136 pvtm_cnt = pvtm->info->get_value(pvtm, time_us);
137 pr_debug("get pvtm_cnt = %d\n", pvtm_cnt);
138
139 /* set pvtm_div to get rate */
140 div = DIV_ROUND_UP(1000 * pvtm_cnt, pvtm->rate);
141 if (div > pvtm->info->div_mask) {
142 pr_err("pvtm_div out of bounary! set max instead\n");
143 div = pvtm->info->div_mask;
144 }
145
146 pr_debug("set div %d, rate %luKHZ\n", div, pvtm->rate);
147 ret = regmap_write(pvtm->grf, pvtm->info->con,
148 wr_msk_bit(div, pvtm->info->div_shift,
149 pvtm->info->div_mask));
150 if (ret != 0)
151 goto out;
152
153 /* pmu pvtm oscilator enable */
154 ret = regmap_write(pvtm->grf, pvtm->info->con,
155 wr_msk_bit(1, 1, 0x1));
156 if (ret != 0)
157 goto out;
158
159 ret = pvtm->info->sel_enable(pvtm);
160 out:
161 if (ret != 0)
162 pr_err("%s: fail to write register\n", __func__);
163
164 return ret;
165 }
166
clock_pvtm_regitstor(struct device * dev,struct rockchip_clock_pvtm * pvtm)167 static int clock_pvtm_regitstor(struct device *dev,
168 struct rockchip_clock_pvtm *pvtm)
169 {
170 struct clk_init_data init = {};
171 struct clk_hw *clk_hw;
172
173 /* Init the xin32k_pvtm */
174 pvtm->info->init_freq(pvtm);
175
176 init.parent_names = NULL;
177 init.num_parents = 0;
178 init.name = "xin32k_pvtm";
179 init.ops = &xin32k_pvtm;
180
181 clk_hw = devm_kzalloc(dev, sizeof(*clk_hw), GFP_KERNEL);
182 if (!clk_hw)
183 return -ENOMEM;
184 clk_hw->init = &init;
185
186 /* optional override of the clockname */
187 of_property_read_string_index(dev->of_node, "clock-output-names",
188 0, &init.name);
189 pvtm->clk = devm_clk_register(dev, clk_hw);
190 if (IS_ERR(pvtm->clk))
191 return PTR_ERR(pvtm->clk);
192
193 return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
194 pvtm->clk);
195 }
196
197 static const struct rockchip_clock_pvtm_info rk3368_pvtm_data = {
198 .con = 0x180,
199 .sta = 0x190,
200 .sel_con = 0x100,
201 .sel_shift = 6,
202 .sel_value = CLK_SEL_INTERNAL_PVTM,
203 .sel_mask = 0x1,
204 .div_shift = 2,
205 .div_mask = 0x3f,
206
207 .sel_enable = rockchip_clock_sel_internal_pvtm,
208 .get_value = rockchip_clock_pvtm_get_value,
209 .init_freq = rockchip_clock_pvtm_init_freq,
210 };
211
212 static const struct of_device_id rockchip_clock_pvtm_match[] = {
213 {
214 .compatible = "rockchip,rk3368-pvtm-clock",
215 .data = (void *)&rk3368_pvtm_data,
216 },
217 {}
218 };
219 MODULE_DEVICE_TABLE(of, rockchip_clock_pvtm_match);
220
rockchip_clock_pvtm_probe(struct platform_device * pdev)221 static int rockchip_clock_pvtm_probe(struct platform_device *pdev)
222 {
223 struct device *dev = &pdev->dev;
224 struct device_node *np = pdev->dev.of_node;
225 const struct of_device_id *match;
226 struct rockchip_clock_pvtm *pvtm;
227 int error;
228 u32 rate;
229
230 pvtm = devm_kzalloc(dev, sizeof(*pvtm), GFP_KERNEL);
231 if (!pvtm)
232 return -ENOMEM;
233
234 match = of_match_node(rockchip_clock_pvtm_match, np);
235 if (!match)
236 return -ENXIO;
237
238 pvtm->info = (const struct rockchip_clock_pvtm_info *)match->data;
239 if (!pvtm->info)
240 return -EINVAL;
241
242 if (!dev->parent || !dev->parent->of_node)
243 return -EINVAL;
244
245 pvtm->grf = syscon_node_to_regmap(dev->parent->of_node);
246 if (IS_ERR(pvtm->grf))
247 return PTR_ERR(pvtm->grf);
248
249 if (!of_property_read_u32(np, "pvtm-rate", &rate))
250 pvtm->rate = rate;
251 else
252 pvtm->rate = 32768;
253
254 pvtm->pvtm_clk = devm_clk_get(&pdev->dev, "pvtm_pmu_clk");
255 if (IS_ERR(pvtm->pvtm_clk)) {
256 error = PTR_ERR(pvtm->pvtm_clk);
257 if (error != -EPROBE_DEFER)
258 dev_err(&pdev->dev,
259 "failed to get pvtm core clock: %d\n",
260 error);
261 goto out_probe;
262 }
263
264 error = clk_prepare_enable(pvtm->pvtm_clk);
265 if (error) {
266 dev_err(&pdev->dev, "failed to enable the clock: %d\n",
267 error);
268 goto out_probe;
269 }
270
271 platform_set_drvdata(pdev, pvtm);
272
273 error = clock_pvtm_regitstor(&pdev->dev, pvtm);
274 if (error) {
275 dev_err(&pdev->dev, "failed to registor clock: %d\n",
276 error);
277 goto out_clk_put;
278 }
279
280 return error;
281
282 out_clk_put:
283 clk_disable_unprepare(pvtm->pvtm_clk);
284 out_probe:
285 return error;
286 }
287
rockchip_clock_pvtm_remove(struct platform_device * pdev)288 static int rockchip_clock_pvtm_remove(struct platform_device *pdev)
289 {
290 struct rockchip_clock_pvtm *pvtm = platform_get_drvdata(pdev);
291 struct device_node *np = pdev->dev.of_node;
292
293 of_clk_del_provider(np);
294 clk_disable_unprepare(pvtm->pvtm_clk);
295
296 return 0;
297 }
298
299 static struct platform_driver rockchip_clock_pvtm_driver = {
300 .driver = {
301 .name = "rockchip-clcok-pvtm",
302 .of_match_table = rockchip_clock_pvtm_match,
303 },
304 .probe = rockchip_clock_pvtm_probe,
305 .remove = rockchip_clock_pvtm_remove,
306 };
307
308 module_platform_driver(rockchip_clock_pvtm_driver);
309
310 MODULE_DESCRIPTION("Rockchip Clock Pvtm Driver");
311 MODULE_LICENSE("GPL v2");
312